示例#1
0
    def GET(self, key):
        if not is_admin():
            return render_template('permission_denied', web.ctx.path, "Permission denied.")
    
        edition = web.ctx.site.get(key)
        if not edition:
            raise web.notfound()

        if edition.ocaid:
            ebook_key = "ebooks/" + edition.ocaid
            ebook = web.ctx.site.store.get(ebook_key) or {}
        else:
            ebook = None

        i = web.input(updatestatus=None)
        if i.updatestatus == 't':
            edition.update_loan_status()
        edition_loans = get_edition_loans(edition)
            
        user_loans = []
        user = accounts.get_current_user()
        if user:
            user_loans = get_loans(user)
            
        return render_template("borrow_admin", edition, edition_loans, ebook, user_loans, web.ctx.ip)
示例#2
0
    def GET(self):
        try:
            if 'counts_db' in config.admin:
                stats = admin.get_stats()
            else:
                stats = None
        except Exception:
            logger.error("Error in getting stats", exc_info=True)
            stats = None
        blog_posts = get_blog_feeds()

        lending_list = config.get("home", {}).get("lending_list")
        returncart_list = config.get("home", {}).get("returncart_list")

        user = accounts.get_current_user()
        loans = borrow.get_loans(user) if user else None

        popular_available, popular_waitlist = popular_carousel()
        return render_template(
            "home/index", stats=stats,
            blog_posts=blog_posts,
            lending_list=lending_list,
            returncart_list=returncart_list,
            user=user, loans=loans,
            popular_books=popular_available,
            waitlisted_books=popular_waitlist
        )
示例#3
0
 def GET(self):
     if not support_db:
         return "The Openlibrary support system is currently offline. Please try again later."
     i = web.input(path=None)
     user = accounts.get_current_user()
     email = user and user.email
     return render_template("support", email=email, url=i.path)
示例#4
0
 def _get_prevnext(self, caseid):
     """Redirects prev and next urls.
     """
     i = web.input(all="false", filter="new", _method="GET")
     sort = "status"
     desc = "false"
     cases = support_db.get_all_cases(i.filter, summarise=False, sortby=sort, desc=desc)
     cases = list(cases)
     if i.all == "false":
         current_user = accounts.get_current_user()
         email = current_user.get_email()
         cases = [x for x in cases if x.assignee == email]
         
     try:
         index = [case.caseid for case in cases].index(caseid)
     except ValueError:
         return None, None
         
     if index > 0:
         case = cases[index-1]
         prev = "/admin/support/" + str(case.caseno) + "?" + urllib.urlencode(i)
     else:
         prev = None
     
     if index < len(cases)-1:
         case = cases[index+1]
         next = "/admin/support/" + str(case.caseno) + "?" + urllib.urlencode(i)
     else:
         next = None
     
     return prev, next
示例#5
0
    def POST(self):
        if not support_db:
            return "Couldn't initialise connection to support database"
        form = web.input()
        email = form.get("email", "")
        topic = form.get("topic", "")
        description = form.get("question", "")
        url = form.get("url", "")
        user = accounts.get_current_user()
        useragent = web.ctx.env.get("HTTP_USER_AGENT","")
        if not all([email, topic, description]):
            return ""
        c = support_db.create_case(creator_name      = user and user.get_name() or "",
                                   creator_email     = email,
                                   creator_useragent = useragent,
                                   creator_username  = user and user.get_username() or "",
                                   subject           = topic,
                                   description       = description,
                                   url               = url,
                                   assignee          = config.get("support_case_default_address","*****@*****.**"))

        # Send an email to the creator of the case
        subject = "Case #%s: %s"%(c.caseno, topic)
        message = render_template("email/support_case", c)
        web.sendmail(config.get("support_case_control_address","*****@*****.**"), 
                     email, subject, message)

        return render_template("email/case_created", c)
示例#6
0
    def POST(self):
        form = web.input()
        email = form.get("email", "")
        topic = form.get("topic", "")
        description = form.get("question", "")
        url = form.get("url", "")
        user = accounts.get_current_user()
        useragent = web.ctx.env.get("HTTP_USER_AGENT","")
        if not all([email, topic, description]):
            return ""

        default_assignees = config.get("support_default_assignees",{})
        topic_key = str(topic.replace(" ","_").lower())
        if topic_key in default_assignees:
            assignee = default_assignees.get(topic_key)
        else:
            assignee = default_assignees.get("default", "*****@*****.**")
        stats.increment("ol.support.all")
        subject = "Support case *%s*"%topic

        url = web.ctx.home + url
        displayname = user and user.get_name() or ""
        username = user and user.get_username() or ""

        message = SUPPORT_EMAIL_TEMPLATE % locals()
        sendmail(email, assignee, subject, message)
        return render_template("email/case_created", assignee)
示例#7
0
 def GET(self):
     i = web.input(path=None)
     user = accounts.get_current_user()
     email = user and user.email
     template = render_template("support", email=email, url=i.path)
     template.v2 = True
     return template
示例#8
0
    def POST(self, work_id):
        user = accounts.get_current_user()
        i = web.input(edition_id=None, action="add", redir=False, bookshelf_id=None)
        key = i.edition_id if i.edition_id else ('/works/OL%sW' % work_id)

        if not user:
            raise web.seeother('/account/login?redirect=%s' % key)

        username = user.key.split('/')[2]
        current_status = models.Bookshelves.get_users_read_status_of_work(username, work_id)

        try:
            bookshelf_id = int(i.bookshelf_id)
            if bookshelf_id not in models.Bookshelves.PRESET_BOOKSHELVES.values():
                raise ValueError
        except ValueError:
            return delegate.RawText(simplejson.dumps({
                'error': 'Invalid bookshelf'
            }), content_type="application/json")

        if bookshelf_id == current_status:
            work_bookshelf = models.Bookshelves.remove(
                username=username, work_id=work_id, bookshelf_id=i.bookshelf_id)

        else:
            edition_id = int(i.edition_id.split('/')[2][2:-1]) if i.edition_id else None
            work_bookshelf = models.Bookshelves.add(
                username=username, bookshelf_id=bookshelf_id,
                work_id=work_id, edition_id=edition_id)

        if i.redir:
            raise web.seeother(key)
        return delegate.RawText(simplejson.dumps({
            'bookshelves_affected': work_bookshelf
        }), content_type="application/json")
示例#9
0
    def upload(self, key, i):
        """Uploads a cover to coverstore and returns the response."""
        olid = key.split("/")[-1]

        if i.file is not None and hasattr(i.file, 'value'):
            data = i.file.value
        else:
            data = None

        if i.url and i.url.strip() == "http://":
            i.url = ""

        user = accounts.get_current_user()
        params = dict(author=user and user.key, data=data, source_url=i.url, olid=olid, ip=web.ctx.ip)

        upload_url = '%s/%s/upload2' % (get_coverstore_url(), self.cover_category)

        if upload_url.startswith("//"):
            upload_url = "http:" + upload_url

        try:
            response = urllib2.urlopen(upload_url, urllib.urlencode(params))
            out = response.read()
        except urllib2.HTTPError as e:
            out = e.read()

        return web.storage(simplejson.loads(out))
示例#10
0
def add_cover(cover_url, ekey):
    olid = ekey.split("/")[-1]
    coverstore_url = config.get('coverstore_url').rstrip('/')
    upload_url = coverstore_url + '/b/upload2' 
    if upload_url.startswith("//"):
        upload_url = "{0}:{1}".format(web.ctx.get("protocol", "http"), upload_url)
    user = accounts.get_current_user()
    params = {
        'author': user.key,
        'data': None,
        'source_url': cover_url,
        'olid': olid,
        'ip': web.ctx.ip,
    }
    for attempt in range(10):
        try:
            res = urllib.urlopen(upload_url, urllib.urlencode(params))
        except IOError:
            print 'retry, attempt', attempt
            sleep(2)
            continue
        body = res.read()
        if body != '':
            reply = json.loads(body)
        if res.getcode() == 200 and body != '':
            if 'id' in reply:
                break
        print 'retry, attempt', attempt
        sleep(2)
    if not reply or reply.get('message') == 'Invalid URL':
        return
    cover_id = int(reply['id'])
    return cover_id
示例#11
0
def create_edition_from_amazon_metadata(isbn):
    """Fetches amazon metadata by isbn from affiliates API, attempts to
    create OL edition from metadata, and returns the resulting edition key
    `/key/OL..M` if successful or None otherwise
    """
    md = get_amazon_metadata(isbn)
    if md:
        # Save token of currently logged in user (or no-user)
        account = accounts.get_current_user()
        auth_token = account.generate_login_code() if account else ''

        try:
            # Temporarily behave (act) as ImportBot for import
            tmp_account = accounts.find(username='******')
            web.ctx.conn.set_auth_token(tmp_account.generate_login_code())
            reply = load(clean_amazon_metadata_for_load(md),
                         account=tmp_account)
        except Exception as e:
            web.ctx.conn.set_auth_token(auth_token)
            raise e

        # Return auth token to original user or no-user
        web.ctx.conn.set_auth_token(auth_token)

        if reply and reply.get('success'):
            return reply['edition']['key']
示例#12
0
 def POST_reopencase(self, form, case):
     user = accounts.get_current_user()
     by = user.get_email()
     text = "Case reopened"
     case.add_worklog_entry(by = by,
                            text = text)
     case.change_status("new", by)
     add_flash_message("info", "Case reopened")
示例#13
0
 def GET(self, key='loans'):
     user = accounts.get_current_user()
     is_public = user.preferences().get('public_readlog', 'no') == 'yes'
     readlog = ReadingLog()
     works = readlog.get_works(key)
     return render['account/books'](
         works, key, reading_log=readlog.reading_log_counts,
         lists=readlog.lists, user=user, public=is_public)
示例#14
0
    def POST(self, key):
        i = web.input("v", _comment=None)
        v = i.v and safeint(i.v, None)

        if v is None:
            raise web.seeother(web.changequery({}))

        user = accounts.get_current_user()
        is_admin = user and user.key in [m.key for m in web.ctx.site.get('/usergroup/admin').members]
        if not (is_admin and web.ctx.site.can_write(key)):
            return render.permission_denied(web.ctx.fullpath, "Permission denied to edit " + key + ".")

        thing = web.ctx.site.get(key, i.v)

        if not thing:
            raise web.notfound()

        def revert(thing):
            if thing.type.key == "/type/delete" and thing.revision > 1:
                prev = web.ctx.site.get(thing.key, thing.revision-1)
                if prev.type.key in ["/type/delete", "/type/redirect"]:
                    return revert(prev)
                else:
                    prev._save("revert to revision %d" % prev.revision)
                    return prev
            elif thing.type.key == "/type/redirect":
                redirect = web.ctx.site.get(thing.location)
                if redirect and redirect.type.key not in ["/type/delete", "/type/redirect"]:
                    return redirect
                else:
                    # bad redirect. Try the previous revision
                    prev = web.ctx.site.get(thing.key, thing.revision-1)
                    return revert(prev)
            else:
                return thing

        def process(value):
            if isinstance(value, list):
                return [process(v) for v in value]
            elif isinstance(value, client.Thing):
                if value.key:
                    if value.type.key in ['/type/delete', '/type/revert']:
                        return revert(value)
                    else:
                        return value
                else:
                    for k in value.keys():
                        value[k] = process(value[k])
                    return value
            else:
                return value

        for k in thing.keys():
            thing[k] = process(thing[k])

        comment = i._comment or "reverted to revision %d" % v
        thing._save(comment)
        raise web.seeother(key)
示例#15
0
 def POST_closecase(self, form, case):
     user = accounts.get_current_user()
     by = user.get_email()
     text = "Case closed"
     case.add_worklog_entry(by = by,
                            text = text)
     case.change_status("closed", by)
     add_flash_message("info", "Case closed")
     raise web.redirect("/admin/support")
示例#16
0
 def __init__(self, user=None):
     self.user = user or accounts.get_current_user()
     #self.user.update_loan_status()
     self.KEYS = {
         'waitlists': self.get_waitlisted_editions,
         'loans': self.get_loans,
         'want-to-read': self.get_want_to_read,
         'currently-reading': self.get_currently_reading,
         'already-read': self.get_already_read
     }
示例#17
0
    def save(self, formdata):
        """Update work and edition documents according to the specified formdata."""
        comment = formdata.pop('_comment', '')

        user = accounts.get_current_user()
        delete = user and user.is_admin() and formdata.pop('_delete', '')

        formdata = utils.unflatten(formdata)
        work_data, edition_data = self.process_input(formdata)

        self.process_new_fields(formdata)

        saveutil = DocSaveHelper()

        if delete:
            if self.edition:
                self.delete(self.edition.key, comment=comment)

            if self.work and self.work.edition_count == 0:
                self.delete(self.work.key, comment=comment)
            return

        if work_data:
            # Create any new authors that were added
            for i, author in enumerate(work_data.get("authors") or []):
                if author['author']['key'] == "__new__":
                    a = self.new_author(formdata['authors'][i])
                    author['author']['key'] = a.key
                    saveutil.save(a)

            if self.work is None:
                self.work = self.new_work(self.edition)
                edition_data.works = [{'key': self.work.key}]
            self.work.update(work_data)
            saveutil.save(self.work)

        if self.edition and edition_data:
            identifiers = edition_data.pop('identifiers', [])
            self.edition.set_identifiers(identifiers)

            classifications = edition_data.pop('classifications', [])
            self.edition.set_classifications(classifications)

            self.edition.set_physical_dimensions(edition_data.pop('physical_dimensions', None))
            self.edition.set_weight(edition_data.pop('weight', None))
            self.edition.set_toc_text(edition_data.pop('table_of_contents', ''))

            if edition_data.pop('translation', None) != 'yes':
                edition_data.translation_of = None
                edition_data.translated_from = None

            self.edition.update(edition_data)
            saveutil.save(self.edition)

        saveutil.commit(comment=comment, action="edit-book")
示例#18
0
def verify_password(password):
    user = accounts.get_current_user()
    if user is None:
        return False
    
    try:
        username = user.key.split('/')[-1]
        web.ctx.site.login(username, password)
    except ClientException:
        return False
        
    return True
示例#19
0
    def POST(self):
        user = accounts.get_current_user()
        key = user.key + '/preferences'
        prefs = web.ctx.site.get(key)

        d = (prefs and prefs.dict()) or {'key': key, 'type': {'key': '/type/object'}}

        d['notifications'] = web.input()

        web.ctx.site.save(d, 'save notifications')

        add_flash_message('note', _("Notification preferences have been updated successfully."))
        web.seeother("/account")
示例#20
0
 def POST_closecase(self, form, case):
     # Next/Prev links should be computed before the case is updated
     prev_url, next_url = self._get_prevnext(case.caseid)
     
     user = accounts.get_current_user()
     by = user.get_email()
     text = "Case closed"
     case.add_worklog_entry(by = by,
                            text = text)
     case.change_status("closed", by)
     add_flash_message("info", "Case closed")
     
     raise web.redirect(next_url or "/admin/support")
示例#21
0
 def GET(self, typ = "new"):
     current_user = accounts.get_current_user()
     if not support_db:
         return render_template("admin/cases", None, None, True, False)
     i = web.input(sort="status", desc = "false", all = "false")
     sortby = i['sort']
     desc = i['desc']
     cases = support_db.get_all_cases(typ, summarise = False, sortby = sortby, desc = desc)
     if i['all'] == "false":
         cases = (x for x in cases if x.assignee == current_user.get_email())
         summary = support_db.get_all_cases(typ, summarise = True, user = current_user.get_email())
     else:
         summary = support_db.get_all_cases(typ, summarise = True)
     total = sum(int(x) for x in summary.values())
     desc = desc == "false" and "true" or "false"
     return render_template("admin/cases", summary, total, cases, desc)
示例#22
0
    def _prevent_ocaid_deletion(self, edition):
        # Allow admins to modify ocaid
        user = accounts.get_current_user()
        if user and user.is_admin():
            return

        # read ocaid from form data
        try:
            ocaid = [id['value'] for id in edition.get('identifiers', []) if id['name'] == 'ocaid'][0]
        except IndexError:
            ocaid = None
        
        # 'self.edition' is the edition doc from the db and 'edition' is the doc from formdata
        if self.edition and self.edition.get('ocaid') and self.edition.get('ocaid') != ocaid:
            logger.warn("Attempt to change ocaid of %s from %r to %r.", self.edition.key, self.edition.get('ocaid'), ocaid)
            raise ValidationException("Changing Internet Archive ID is not allowed.")
示例#23
0
    def POST(self):
        # if not support_db:
        #     return "Couldn't initialise connection to support database"
        form = web.input()
        email = form.get("email", "")
        topic = form.get("topic", "")
        description = form.get("question", "")
        url = form.get("url", "")
        user = accounts.get_current_user()
        useragent = web.ctx.env.get("HTTP_USER_AGENT", "")
        if not all([email, topic, description]):
            return ""

        default_assignees = config.get("support_default_assignees", {})
        topic_key = str(topic.replace(" ", "_").lower())
        if topic_key in default_assignees:
            # This is set to False to prevent cases from being created
            # even if there is a designated assignee. This prevents
            # the database from being updated.
            create_case = False
            assignee = default_assignees.get(topic_key)
        else:
            create_case = False
            assignee = default_assignees.get("default", "*****@*****.**")
        if create_case:
            c = support_db.create_case(
                creator_name=user and user.get_name() or "",
                creator_email=email,
                creator_useragent=useragent,
                creator_username=user and user.get_username() or "",
                subject=topic,
                description=description,
                url=url,
                assignee=assignee,
            )
            stats.increment("support.all")
        else:
            stats.increment("support.all")
            subject = "Support case *%s*" % topic

            url = web.ctx.home + url
            displayname = user and user.get_name() or ""
            username = user and user.get_username() or ""

            message = SUPPORT_EMAIL_TEMPLATE % locals()
            sendmail(email, assignee, subject, message)
        return render_template("email/case_created", assignee)
示例#24
0
    def POST(self):
        f = forms.ChangeEmail()
        i = web.input()

        if not f.validates(i):
            return render['account/email'](self.get_email(), f)
        else:
            user = accounts.get_current_user()
            username = user.key.split('/')[-1]

            displayname = user.displayname or username

            send_email_change_email(username, i.email)

            title = _("Hi %(user)s", user=user.displayname or username)
            message = _("We've sent an email to %(email)s. You'll need to read that and click on the verification link to update your email.", email=i.email)
            return render.message(title, message)
示例#25
0
 def POST_update(self, form, case):
     casenote = form.get("casenote2", False)
     assignee = form.get("assignee", False)
     user = accounts.get_current_user()
     by = user.get_email()
     text = casenote or ""
     if case.status == "closed":
         case.change_status("new", by)
     if assignee != case.assignee:
         case.reassign(assignee, by, text)
         subject = "Case #%s has been assigned to you"%case.caseno
         message = render_template("admin/email_reassign", case, text)
         web.sendmail(config.get("support_case_control_address","*****@*****.**"), assignee, subject, message)
     else:
         case.add_worklog_entry(by = by,
                                text = text)
     add_flash_message("info", "Case updated")
示例#26
0
    def POST(self):
        f = forms.ChangePassword()
        i = web.input()

        if not f.validates(i):
            return render['account/password'](f)

        user = accounts.get_current_user()
        username = user.key.split("/")[-1]

        if self.try_login(username, i.password):
            accounts.update_account(username, password=i.new_password)
            add_flash_message('note', _('Your password has been updated successfully.'))
            raise web.seeother('/account')
        else:
            f.note = "Invalid password"
            return render['account/password'](f)
示例#27
0
 def POST_sendreply(self, form, case):
     user = accounts.get_current_user()
     assignee = case.assignee
     casenote = form.get("casenote1", "")
     casenote = "%s replied:\n\n%s"%(user.get_name(), casenote)
     case.add_worklog_entry(by = user.get_email(),
                            text = casenote)
     case.change_status("replied", user.get_email())
     email_to = form.get("email", False)
     subject = "Case #%s: %s"%(case.caseno, case.subject)
     if assignee != user.get_email():
         case.reassign(user.get_email(), user.get_name(), "")
     if email_to:
         message = render_template("admin/email", case, casenote)
         web.sendmail(config.get("support_case_control_address","*****@*****.**"), email_to, subject, message)
     add_flash_message("info", "Reply sent")
     raise web.redirect("/admin/support")
示例#28
0
    def GET(self, key):
        if not is_admin():
            return render_template('permission_denied', web.ctx.path, "Permission denied.")
    
        edition = web.ctx.site.get(key)
        
        if not edition:
            raise web.notfound()

        edition_loans = get_edition_loans(edition)
            
        user_loans = []
        user = accounts.get_current_user()
        if user:
            user_loans = get_loans(user)
            
        return render_template("borrow_admin_no_update", edition, edition_loans, user_loans, web.ctx.ip)
示例#29
0
 def GET(self, item_id):
     i = web.input(_method='GET', callback=None, loan=None, token=None)
     
     resource_id = 'bookreader:%s' % item_id
     content_type = "application/json"
     
     # check that identifier is valid
     
     user = accounts.get_current_user()
     auth_json = simplejson.dumps( get_ia_auth_dict(user, item_id, resource_id, i.loan, i.token ) )
             
     output = auth_json
     
     if i.callback:
         content_type = "text/javascript"
         output = '%s ( %s );' % (i.callback, output)
     
     return delegate.RawText(output, content_type=content_type)
示例#30
0
    def _prevent_system_subjects_deletion(self, work):
        # Allow admins to modify system systems
        user = accounts.get_current_user()
        if user and user.is_admin():
            return
            
        # Note: work is the new work object from the formdata and self.work is the work doc from the database.
        old_subjects = self.work and self.work.get("subjects") or []

        # If condition is added to handle the possibility of bad data
        set_old_subjects = set(s.lower() for s in old_subjects if isinstance(s, basestring))
        set_new_subjects = set(s.lower() for s in work.subjects)

        for s in SYSTEM_SUBJECTS:
            # if a system subject has been removed
            if s.lower() in set_old_subjects and s.lower() not in set_new_subjects:
                work_key = self.work and self.work.key
                logger.warn("Prevented removal of system subject %r from %s.", s, work_key)
                work.subjects.append(s)
示例#31
0
    def POST(self, path):
        book = get_book(path)
        record = get_scan_record(path)
        user = accounts.get_current_user()
        delegate.admin_login()
        q = {
            'key': '/scan_record' + path,
            'scan_status': {
                'connect': 'update',
                'value': 'WAITING_FOR_BOOK'
            },
            'sponsor': {
                'connect': 'update',
                'key': user.key
            },
            'request_date': {
                'connect': 'update',
                'value': datetime.datetime.utcnow().isoformat()
            }
        }
        try:
            web.ctx.site.write(q)
        finally:
            web.ctx.headers = []

        def get_to():
            if config.get('plugin_scod') is not None:
                return config.plugin_scod.get('email_recipients', [])
            else:
                return config.get('scan_email_recipients', [])

        to = get_to()
        if to:
            scan_record = get_scan_record(path)
            message = render.scan_request_email(book, scan_record)
            web.sendmail(config.from_address, to, message.subject.strip(),
                         message)

        to = get_email(user)
        message = render.scan_waiting_email(book, scan_record)
        web.sendmail(config.from_address, to, message.subject.strip(), message)
        return render.scan_inprogress(book)
示例#32
0
def add_cover(cover_url, ekey, account=None):
    """
    Adds a cover to coverstore and returns the cover id.

    :param str cover_url: URL of cover image
    :param str ekey: Edition key /book/OL..M
    :rtype: int or None
    :return: Cover id, or None if upload did not succeed
    """
    olid = ekey.split("/")[-1]
    coverstore_url = config.get('coverstore_url').rstrip('/')
    upload_url = coverstore_url + '/b/upload2'
    if upload_url.startswith("//"):
        upload_url = "{0}:{1}".format(web.ctx.get("protocol", "http"),
                                      upload_url)
    user = account or accounts.get_current_user()
    params = {
        'author': user.get('key') or user.get('_key'),
        'data': None,
        'source_url': cover_url,
        'olid': olid,
        'ip': web.ctx.ip,
    }
    reply = None
    for attempt in range(10):
        try:
            res = urllib.urlopen(upload_url, urllib.urlencode(params))
        except IOError:
            sleep(2)
            continue
        body = res.read()
        if res.getcode() == 500:
            raise CoverNotSaved(body)
        if body not in ['', 'None']:
            reply = json.loads(body)
            if res.getcode() == 200 and 'id' in reply:
                break
        sleep(2)
    if not reply or reply.get('message') == 'Invalid URL':
        return
    cover_id = int(reply['id'])
    return cover_id
示例#33
0
def create_edition_from_amazon_metadata(id_, id_type='isbn'):
    """Fetches Amazon metadata by id from Amazon Product Advertising API, attempts to
    create OL edition from metadata, and returns the resulting edition
    key `/key/OL..M` if successful or None otherwise.

    :param str id_: The item id: isbn (10/13), or Amazon ASIN.
    :param str id_type: 'isbn' or 'asin'.
    :return: Edition key '/key/OL..M' or None
    :rtype: str or None
    """

    md = get_amazon_metadata(id_, id_type=id_type)

    if md and md.get('product_group') == 'Book':
        with accounts.RunAs('ImportBot'):
            reply = load(
                clean_amazon_metadata_for_load(md),
                account=accounts.get_current_user())
            if reply and reply.get('success'):
                return reply['edition'].get('key')
示例#34
0
    def POST(self):
        user = accounts.get_current_user()
        key = user.key + '/preferences'
        prefs = web.ctx.site.get(key)

        d = (prefs and prefs.dict()) or {
            'key': key,
            'type': {
                'key': '/type/object'
            }
        }

        d['notifications'] = web.input()

        web.ctx.site.save(d, 'save notifications')

        add_flash_message(
            'note',
            _("Notification preferences have been updated successfully."))
        web.seeother("/account")
示例#35
0
    def POST(self, key):
        doc = web.ctx.site.get(key)
        if doc is None or doc.type.key != "/type/library":
            raise web.notfound()
        elif not web.ctx.site.can_write(key):
            raise render_template("permission_denied")
        else:
            i = web.input(note="")

            user = accounts.get_current_user()
            author = user and {"key": user.key}
            timestamp = {"type": "/type/datetime", "value": datetime.datetime.utcnow().isoformat()}

            note = {"note": i.note, "author": {"key": user.key}, "timestamp": timestamp}

            if not doc.notes:
                doc.notes = []
            doc.notes.append(note)
        doc._save(comment="Added a note.")
        raise web.seeother(key)
示例#36
0
 def GET(self):
     user = accounts.get_current_user()
     username = user.key.split('/')[-1]
     books = Bookshelves.get_users_logged_books(username, limit=10000)
     csv = []
     csv.append('Work Id,Edition Id,Bookshelf\n')
     mapping = {1: 'Want to Read', 2: 'Currently Reading', 3: 'Already Read'}
     for book in books:
         row = [
             'OL{}W'.format(book['work_id']),
             'OL{}M'.format(book['edition_id']) if book['edition_id'] else '',
             '{}\n'.format(mapping[book['bookshelf_id']]),
         ]
         csv.append(','.join(row))
     web.header('Content-Type', 'text/csv')
     web.header(
         'Content-disposition', 'attachment; filename=OpenLibrary_ReadingLog.csv'
     )
     csv = ''.join(csv)
     return delegate.RawText(csv, content_type="text/csv")
示例#37
0
    def POST(self, work_id):
        """
        Add a note to a work (or a work and an edition)
        GET params:
        - edition_id str (optional)
        - redir bool: if patron not logged in, redirect back to page after login

        :param str work_id: e.g. OL123W
        :rtype: json
        :return: the note
        """
        user = accounts.get_current_user()
        i = web.input(notes=None, edition_id=None, redir=None)
        edition_id = int(
            extract_numeric_id_from_olid(i.edition_id)) if i.edition_id else -1

        if not user:
            raise web.seeother('/account/login?redirect=/works/%s' % work_id)

        username = user.key.split('/')[2]

        def response(msg, status="success"):
            return delegate.RawText(json.dumps({
                status: msg
            }), content_type="application/json")

        if i.notes is None:
            Booknotes.remove(username, work_id, edition_id=edition_id)
            return response('removed note')

        Booknotes.add(
            username=username,
            work_id=work_id,
            notes=i.notes,
            edition_id=edition_id
        )

        if i.redir:
            raise web.seeother("/works/%s" % work_id)

        return response('note added')
示例#38
0
    def POST(self, work_id):
        user = accounts.get_current_user()

        if not user:
            raise web.seeother('/account/login')

        data = json.loads(web.data())

        Observations.persist_observation(
            data['username'],
            work_id,
            data['observation'],
            data['action']
        )

        def response(msg, status="success"):
            return delegate.RawText(json.dumps({
                status: msg
            }), content_type="application/json")

        return response('Observations added')
示例#39
0
 def GET(self, typ="new"):
     i = web.input(sort="status", desc="false", all="false", filter="new")
     typ = i.filter
     current_user = accounts.get_current_user()
     if not support_db:
         return render_template("admin/cases", None, None, True, False)
     sortby = i['sort']
     desc = i['desc']
     cases = support_db.get_all_cases(typ,
                                      summarise=False,
                                      sortby=sortby,
                                      desc=desc)
     if i['all'] == "false":
         email = current_user.get_email()
         cases = (x for x in cases if x.assignee == email)
         summary = support_db.get_all_cases(typ, summarise=True, user=email)
     else:
         summary = support_db.get_all_cases(typ, summarise=True)
     total = sum(int(x) for x in summary.values())
     desc = desc == "false" and "true" or "false"
     return render_template("admin/cases", summary, total, cases, desc)
示例#40
0
    def GET(self, key):
        if not is_admin():
            return render_template(
                'permission_denied', web.ctx.path, "Permission denied."
            )

        edition = web.ctx.site.get(key)

        if not edition:
            raise web.notfound()

        edition_loans = get_edition_loans(edition)

        user_loans = []
        user = accounts.get_current_user()
        if user:
            user_loans = get_loans(user)

        return render_template(
            "borrow_admin_no_update", edition, edition_loans, user_loans, web.ctx.ip
        )
示例#41
0
 def GET(self, key='loans'):
     i = web.input(page=1)
     user = accounts.get_current_user()
     is_public = user.preferences().get('public_readlog', 'no') == 'yes'
     readlog = ReadingLog()
     sponsorships = get_sponsored_editions(user)
     if key == 'sponsorships':
         books = (web.ctx.site.get(
             web.ctx.site.things({
                 'type': '/type/edition',
                 'isbn_%s' % len(s['isbn']): s['isbn']
             })[0]) for s in sponsorships)
     else:
         books = readlog.get_works(key, page=i.page)
     page = render['account/books'](
         books, key, sponsorship_count=len(sponsorships),
         reading_log_counts=readlog.reading_log_counts, lists=readlog.lists,
         user=user, public=is_public
     )
     page.v2 = True
     return page
示例#42
0
    def GET(self, key):
        edition = web.ctx.site.get(key)

        if not edition:
            raise web.notfound()

        edition.update_loan_status()

        loans = []
        user = accounts.get_current_user()
        if user:
            user.update_loan_status()
            loans = get_loans(user)

        # Check if we recently did a return
        i = web.input(r=None)
        if i.r == 't':
            have_returned = True
        else:
            have_returned = False

        return render_template("borrow", edition, loans, have_returned)
示例#43
0
    def _prevent_system_subjects_deletion(self, work):
        # Allow admins to modify system systems
        user = accounts.get_current_user()
        if user and user.is_admin():
            return

        # Note: work is the new work object from the formdata and self.work is the work doc from the database.
        old_subjects = self.work and self.work.get("subjects") or []

        # If condition is added to handle the possibility of bad data
        set_old_subjects = set(s.lower() for s in old_subjects
                               if isinstance(s, six.string_types))
        set_new_subjects = set(s.lower() for s in work.subjects)

        for s in SYSTEM_SUBJECTS:
            # if a system subject has been removed
            if s.lower() in set_old_subjects and s.lower(
            ) not in set_new_subjects:
                work_key = self.work and self.work.key
                logger.warn("Prevented removal of system subject %r from %s.",
                            s, work_key)
                work.subjects.append(s)
示例#44
0
    def _prevent_ocaid_deletion(self, edition):
        # Allow admins to modify ocaid
        user = accounts.get_current_user()
        if user and user.is_admin():
            return

        # read ocaid from form data
        try:
            ocaid = [
                id['value'] for id in edition.get('identifiers', [])
                if id['name'] == 'ocaid'
            ][0]
        except IndexError:
            ocaid = None

        # 'self.edition' is the edition doc from the db and 'edition' is the doc from formdata
        if self.edition and self.edition.get(
                'ocaid') and self.edition.get('ocaid') != ocaid:
            logger.warn("Attempt to change ocaid of %s from %r to %r.",
                        self.edition.key, self.edition.get('ocaid'), ocaid)
            raise ValidationException(
                "Changing Internet Archive ID is not allowed.")
示例#45
0
    def POST(self, work_id):
        """Registers new ratings for this work"""
        user = accounts.get_current_user()
        i = web.input(edition_id=None, rating=None, redir=False)
        key = i.edition_id if i.edition_id else ('/works/OL%sW' % work_id)
        edition_id = int(extract_numeric_id_from_olid(
            i.edition_id)) if i.edition_id else None

        if not user:
            raise web.seeother('/account/login?redirect=%s' % key)

        username = user.key.split('/')[2]

        def response(msg, status="success"):
            return delegate.RawText(json.dumps({status: msg}),
                                    content_type="application/json")

        if i.rating is None:
            models.Ratings.remove(username, work_id)
            r = response('removed rating')

        else:
            try:
                rating = int(i.rating)
                if rating not in models.Ratings.VALID_STAR_RATINGS:
                    raise ValueError
            except ValueError:
                return response('invalid rating', status="error")

            models.Ratings.add(username=username,
                               work_id=work_id,
                               rating=rating,
                               edition_id=edition_id)
            r = response('rating added')

        if i.redir:
            raise web.seeother(key)
        return r
示例#46
0
    def GET(self, key):
        if not is_admin():
            return render_template('permission_denied', web.ctx.path, "Permission denied.")
    
        edition = web.ctx.site.get(key)
        if not edition:
            raise web.notfound()

        if edition.ocaid:
            lending.sync_loan(edition.ocaid)
            ebook_key = "ebooks/" + edition.ocaid
            ebook = web.ctx.site.store.get(ebook_key) or {}
        else:
            ebook = None

        edition_loans = get_edition_loans(edition)
            
        user_loans = []
        user = accounts.get_current_user()
        if user:
            user_loans = get_loans(user)
            
        return render_template("borrow_admin", edition, edition_loans, ebook, user_loans, web.ctx.ip)
示例#47
0
    def GET(self, key):
        if not is_admin():
            return render_template('permission_denied', web.ctx.path,
                                   "Permission denied.")

        edition = web.ctx.site.get(key)
        ebook_key = "ebooks" + key
        ebook = web.ctx.site.store.get(ebook_key) or {}

        if not edition:
            raise web.notfound()

        i = web.input(updatestatus=None)
        if i.updatestatus == 't':
            edition.update_loan_status()
        edition_loans = get_edition_loans(edition)

        user_loans = []
        user = accounts.get_current_user()
        if user:
            user_loans = get_loans(user)

        return render_template("borrow_admin", edition, edition_loans, ebook,
                               user_loans, web.ctx.ip)
示例#48
0
    def POST(self, work_id: str):
        user = accounts.get_current_user()
        if not (user and (user.is_admin() or user.is_librarian())):
            return web.HTTPError('403 Forbidden')

        web_input = web.input(comment=None)

        comment = web_input.get('comment')

        work: Work = web.ctx.site.get(f'/works/{work_id}')
        if work is None:
            return web.HTTPError(status='404 Not Found')

        editions: list[dict] = self.get_editions_of_work(work)
        keys_to_delete: list = [el.get('key') for el in [*editions, work.dict()]]
        delete_payload: list[dict] = [
            {'key': key, 'type': {'key': '/type/delete'}}
            for key in keys_to_delete
        ]

        web.ctx.site.save_many(delete_payload, comment)
        return delegate.RawText(json.dumps({
            'status': 'ok',
        }), content_type="application/json")
示例#49
0
 def GET(self):
     user = accounts.get_current_user()
     user.update_loan_status()
     loans = borrow.get_loans(user)
     return render['account/borrow'](user, loans)
示例#50
0
 def GET(self):
     user = accounts.get_current_user()
     raise web.seeother(user.key + '/lists')
示例#51
0
 def GET(self):
     user = accounts.get_current_user()
     email = user.email
     return render['account/notifications'](user.preferences(), email)
示例#52
0
 def GET(self):
     user = accounts.get_current_user()
     return render['account/privacy'](user.preferences())
示例#53
0
 def get_email(self):
     user = accounts.get_current_user()
     return user.get_account()['email']
示例#54
0
 def GET(self):
     user = accounts.get_current_user()
     page = render.account(user)
     page.v2 = True
     return page
示例#55
0
 def GET(self):
     user = accounts.get_current_user()
     username = user.key.split('/')[-1]
     raise web.seeother('/people/%s/books' % (username))
示例#56
0
 def GET(self, rest='loans'):
     user = accounts.get_current_user()
     username = user.key.split('/')[-1]
     raise web.seeother('/people/%s/books/%s' % (username, rest))
示例#57
0
 def POST(self):
     user = accounts.get_current_user()
     user.save_preferences(web.input())
     add_flash_message('note', _("Notification preferences have been updated successfully."))
     web.seeother("/account")
示例#58
0
 def GET(self):
     user = accounts.get_current_user()
     return render.account(user)
示例#59
0
def is_admin():
    """"Returns True if the current user is in admin usergroup."""
    user = accounts.get_current_user()
    return user and user.key in [
        m.key for m in web.ctx.site.get('/usergroup/admin').members
    ]
示例#60
0
    def POST(self, key):
        """Called when the user wants to borrow the edition"""

        i = web.input(action='borrow', format=None, ol_host=None)

        if i.ol_host:
            ol_host = i.ol_host
        else:
            ol_host = 'openlibrary.org'

        edition = web.ctx.site.get(key)
        if not edition:
            raise web.notfound()

        # Make a call to availability v2 update the subjects according
        # to result if `open`, redirect to bookreader
        response = lending.get_availability_of_ocaid(edition.ocaid)
        availability = response[edition.ocaid] if response else {}
        if availability and availability['status'] == 'open':
            raise web.seeother('https://archive.org/stream/' + edition.ocaid +
                               '?ref=ol')

        error_redirect = ('https://archive.org/stream/' + edition.ocaid +
                          '?ref=ol')
        user = accounts.get_current_user()

        if user:
            account = OpenLibraryAccount.get_by_email(user.email)
            ia_itemname = account.itemname if account else None
        if not user or not ia_itemname:
            web.setcookie(config.login_cookie_name, "", expires=-1)
            raise web.seeother("/account/login?redirect=%s/borrow?action=%s" %
                               (edition.url(), i.action))

        action = i.action

        # Intercept a 'borrow' action if the user has already
        # borrowed the book and convert to a 'read' action.
        # Added so that direct bookreader links being routed through
        # here can use a single action of 'borrow', regardless of
        # whether the book has been checked out or not.
        if action == 'borrow' and user.has_borrowed(edition):
            action = 'read'

        if action == 'borrow':
            resource_type = i.format or 'bookreader'

            if resource_type not in ['epub', 'pdf', 'bookreader']:
                raise web.seeother(error_redirect)

            user_meets_borrow_criteria = user_can_borrow_edition(
                user, edition, resource_type)

            if user_meets_borrow_criteria:
                # This must be called before the loan is initiated,
                # otherwise the user's waitlist status will be cleared
                # upon loan creation
                track_loan = False if is_users_turn_to_borrow(
                    user, edition) else True

                loan = lending.create_loan(identifier=edition.ocaid,
                                           resource_type=resource_type,
                                           user_key=ia_itemname,
                                           book_key=key)

                if loan:
                    loan_link = loan['loan_link']
                    if resource_type == 'bookreader':
                        if track_loan:
                            # As of 2017-12-14, Petabox will be
                            # responsible for tracking borrows which
                            # are the result of waitlist redemptions,
                            # so we don't want to track them here to
                            # avoid double accounting. When a reader
                            # is at the head of a waitlist and goes to
                            # claim their loan, Petabox now checks
                            # whether the waitlist was initiated from
                            # OL, and if it was, petabox tracks
                            # ol.loans.bookreader accordingly via
                            # lending.create_loan.
                            stats.increment('ol.loans.bookreader')

                        raise web.seeother(
                            make_bookreader_auth_link(
                                loan.get_key(), edition.ocaid,
                                '/stream/' + edition.ocaid, ol_host))
                    elif resource_type == 'pdf':
                        stats.increment('ol.loans.pdf')
                        raise web.seeother(loan_link)
                    elif resource_type == 'epub':
                        stats.increment('ol.loans.epub')
                        raise web.seeother(loan_link)
                else:
                    raise web.seeother(error_redirect)
            else:
                raise web.seeother(error_redirect)

        elif action == 'return':
            # Check that this user has the loan
            user.update_loan_status()
            loans = get_loans(user)

            # We pick the first loan that the user has for this book that is returnable.
            # Assumes a user can't borrow multiple formats (resource_type) of the same book.
            user_loan = None
            for loan in loans:
                # Handle the case of multiple edition records for the same
                # ocaid and the user borrowed from one and returning from another
                has_loan = (loan['book'] == edition.key
                            or loan['ocaid'] == edition.ocaid)
                if has_loan:
                    user_loan = loan
                    break

            if not user_loan:
                # $$$ add error message
                raise web.seeother(error_redirect)

            user_loan.return_loan()

            # Show the page with "you've returned this". Use a dummy slug.
            # $$$ this would do better in a session variable that can be cleared
            #     after the message is shown once
            raise web.seeother(edition.url())

        elif action == 'read':
            # Look for loans for this book
            user.update_loan_status()
            loans = get_loans(user)
            for loan in loans:
                if loan['book'] == edition.key:
                    raise web.seeother(
                        make_bookreader_auth_link(loan['_key'], edition.ocaid,
                                                  '/stream/' + edition.ocaid,
                                                  ol_host))
        elif action == 'join-waitinglist':
            return self.POST_join_waitinglist(edition, user)
        elif action == 'leave-waitinglist':
            return self.POST_leave_waitinglist(edition, user, i)

        # Action not recognized
        raise web.seeother(error_redirect)