Пример #1
0
def get_default_cv(uid):
    cvs = None
    while not cvs:
        cvs = db.select('select * from resumes where user_id=?', uid)
        if not cvs:
            cv_id = db.next_str()
            db.insert('resumes',
                      id=cv_id,
                      user_id=uid,
                      title='My Resume',
                      version=0)
            db.insert('sections',
                      id=db.next_str(),
                      user_id=uid,
                      resume_id=cv_id,
                      display_order=0,
                      kind='about',
                      title='About',
                      description='',
                      version=0)

    cv = cvs[0]
    cv.sections = db.select(
        'select * from sections where resume_id=? order by display_order',
        cv.id)
    for section in cv.sections:
        section.style = _SECTIONS_STYLE[section.kind]
        section.entries = db.select(
            'select * from entries where section_id=? order by display_order',
            section.id)
    return cv
Пример #2
0
def get_default_cv(uid):
    cvs = None
    while not cvs:
        cvs = db.select('select * from resumes where user_id=?', uid)
        if not cvs:
            cv_id = db.next_str()
            db.insert('resumes', id=cv_id, user_id=uid, title='My Resume', version=0)
            db.insert('sections', id=db.next_str(), user_id=uid, resume_id=cv_id, display_order=0, kind='about', title='About', description='', version=0)

    cv = cvs[0]
    cv.sections = db.select('select * from sections where resume_id=? order by display_order', cv.id)
    for section in cv.sections:
        section.style = _SECTIONS_STYLE[section.kind]
        section.entries = db.select('select * from entries where section_id=? order by display_order', section.id)
    return cv
Пример #3
0
def add_section():
    _check_user()
    i = ctx.request.input(kind='', title='', description='')
    kind = i.kind
    if not kind in _SECTIONS_SET:
        raise APIError('value', 'kind', 'Invalid kind.')
    title = i.title.strip()
    if not title:
        title = _SECTIONS_DICT.get(kind)
    description = i.description.strip()

    cv = get_default_cv(ctx.user.id)
    for s in cv.sections:
        if s.kind == kind:
            raise APIError('value', '', 'Section exist.')
    next_id = db.next_str()
    db.insert('sections',
              id=next_id,
              user_id=ctx.user.id,
              resume_id=cv.id,
              display_order=len(cv.sections),
              kind=kind,
              title=title,
              description=description,
              version=0)
    return dict(id=next_id, kind=kind, title=title, description=description)
Пример #4
0
def add_entry():
    _check_user()
    i = ctx.request.input(id='', title='', subtitle='', description='')
    title = i.title.strip()
    subtitle = i.subtitle.strip()
    description = i.description.strip()
    if not title:
        raise APIError('value', 'title', 'Title is empty')
    cv = get_default_cv(ctx.user.id)
    for s in cv.sections:
        if s.id == i.id:
            next_id = db.next_str()
            logging.info('NEXT-ID: ' + next_id)
            db.insert('entries',
                      id=next_id,
                      user_id=ctx.user.id,
                      resume_id=cv.id,
                      section_id=s.id,
                      display_order=len(s.entries),
                      title=title,
                      subtitle=subtitle,
                      description=description,
                      picture='',
                      version=0)
            return dict(id=next_id,
                        title=title,
                        subtitle=subtitle,
                        description=description)
    raise APIError('value', 'id', 'Invalid section id.')
Пример #5
0
def api_upload_attachment():
    i = ctx.request.input(name='', description='', link='')
    name = i.name.strip()
    description = i.description.strip()
    f = i.file
    ref_type = 'attachment'
    ref_id = db.next_str()
    fcontent = f.file.read()
    filename = f.filename
    fext = os.path.splitext(filename)[1]
    if not name:
        name = os.path.splitext(os.path.split(filename)[1])[0]

    preview = None
    w = h = 0
    res = store.upload_file(ref_type, ref_id, filename, fcontent)
    if res.mime.startswith('image/'):
        try:
            logging.info(
                'it seems an image was uploaded, so try to get size...')
            im = thumbnail.as_image(fcontent)
            w, h = im.size[0], im.size[1]
            logging.info('size got: %d x %d' % (w, h))
            if w > 160 or h > 120:
                logging.info(
                    'creating thumbnail for uploaded image (size %d x %d)...' %
                    (w, h))
                tn = thumbnail.create_thumbnail(im, 160, 120)
                pw, ph, pcontent = tn['width'], tn['height'], tn['data']
                logging.info(
                    'thumbnail was created successfully with size %d x %d.' %
                    (w, h))
                preview = store.upload_file(ref_type, ref_id, filename,
                                            fcontent)
            else:
                logging.info('No need to create thumbnail.')
                preview = res
        except:
            logging.exception('error when creating thumbnail.')

    current = time.time()
    attr = Dict( \
        id = ref_id, \
        website_id = ctx.website.id, \
        user_id = ctx.user.id, \
        resource_id = res.id, \
        preview_resource_id = preview and preview.id or '', \
        name = name, \
        description = description, \
        width = w, \
        height = h, \
        size = res.size, \
        mime = res.mime, \
        creation_time = current, \
        modified_time = current, \
        version = 0)
    db.insert('attachments', **attr)
    if i.link == u't':
        attr.filelink = '/api/resources/url?id=%s' % attr.resource_id
    return attr
Пример #6
0
def upload_resource(ref_type, ref_id, fname, fp):
    ' upload resource and return resource object '
    uname, uprovider = get_enabled_upload()
    if uname is None:
        return dict(error=_('No uploader selected'))

    filename = os.path.split(fname)[1]
    if len(filename) > 50:
        filename = filename[-50:]
    ext = os.path.splitext(filename)[1].lower()
    mime = mimetypes.types_map.get(ext, 'application/octet-stream')
    current = time.time()
    fcontent = fp if isinstance(fp, str) else fp.read()

    m = dict( \
            id = db.next_str(), \
            ref_id = ref_id, \
            ref_type = ref_type, \
            deleted = False, \
            size = len(fcontent), \
            filename = filename, \
            mime = mime, \
            uploader = uname, \
            ref = '', \
            url = '', \
            creation_time = current, \
            modified_time = current, \
            version = 0 \
    )
    r = create_upload_provider(uname).upload(ref_type, ext, fcontent)
    for k in r:
        if k in m:
            m[k] = r[k]
    db.insert('resources', **m)
    return m
Пример #7
0
def api_create_article():
    i = ctx.request.input(category_id='', name='', tags='', content='', draft='')
    name = i.name.strip()
    content = i.content.strip()
    category_id = i.category_id
    if not name:
        raise APIValueError('name', 'name cannot be empty.')
    if not content:
        raise APIValueError('content', 'content cannot be empty.')
    if not category_id:
        raise APIValueError('category_id', 'category_id cannot be empty.')
    cat = _get_category(category_id)
    draft = True
    if ctx.user.role_id < ROLE_CONTRIBUTORS:
        draft = True if i.draft else False
    current = time.time()
    html_content, summary = html.parse_md(content, 800)
    article = Dict( \
        id=db.next_str(), \
        website_id=ctx.website.id, \
        user_id=ctx.user.id, \
        user_name=ctx.user.name, \
        category_id=category_id, \
        draft=draft, \
        name=name, \
        tags=_format_tags(i.tags), \
        read_count=0, \
        summary=summary, \
        content=content, \
        creation_time=current, \
        modified_time=current, \
        version=0)
    db.insert('articles', **article)
    return article
Пример #8
0
def do_register():
    i = ctx.request.input(name='', email='', passwd='')

    name = i.name.strip()
    if not name:
        raise APIError('value', '', 'Invalid name.')

    email = i.email.strip().lower()
    check_email(email)

    passwd = i.passwd
    check_md5_passwd(passwd)

    us = db.select('select * from users where email=?', email)
    if us:
        raise APIError('register', '', 'Email already registered.')

    uid = db.next_str()
    db.insert('users',
              id=uid,
              name=name,
              email=email,
              passwd=passwd,
              version=0)

    make_session_cookie(uid, passwd)
    return {'id': uid}
Пример #9
0
def upload_file(ref_type, ref_id, filename, fcontent):
    fileext = os.path.splitext(filename)[1].lower()
    filesize = len(fcontent)
    dt = datetime.now()
    fpath = os.path.join(ctx.website.id, str(ref_type), str(dt.year),
                         str(dt.month), str(dt.day),
                         '%s%s' % (uuid.uuid4().hex, fileext))
    sname = get_enabled_store_name()
    url, the_ref = get_store_instance(sname).upload(fpath, fcontent)
    logging.info('uploaded file: %s' % url)
    ref = '%s:%s' % (sname, the_ref)
    r = Dict( \
        id = db.next_str(), \
        website_id = ctx.website.id, \
        ref_id = ref_id, \
        ref_type = ref_type, \
        deleted = False, \
        size = filesize, \
        filename = filename, \
        mime = mimetypes.types_map.get(fileext, 'application/octet-stream'), \
        ref = ref, \
        url = url, \
        creation_time = time.time(), \
        version = 0)
    db.insert('resources', **r)
    return r
Пример #10
0
def create_website(email, name, domain):
    # generate password:
    L = []
    for i in range(10):
        n = int(random.random() * 62)
        if n < 10:
            L.append(chr(n + 48))
        elif n < 36:
            L.append(chr(n + 55))
        else:
            L.append(chr(n + 61))
    passwd = ''.join(L)
    md5passwd = hashlib.md5(passwd).hexdigest()
    current = time.time()
    website = dict(
            id=db.next_str(),
            disabled=False,
            domain=domain,
            name=name,
            creation_time=current,
            modified_time=current,
            version=0)
    with db.transaction():
        db.insert('websites', **website)
        create_user(website['id'], email, md5passwd, name, ROLE_ADMINISTRATORS, locked=True)
    return passwd
Пример #11
0
def _create_wiki_page(wiki_id, parent_id, display_order, name, content):
    wp_id = db.next_str()
    content_id = texts.set(wp_id, content)
    return WikiPages( \
        _id=wp_id, \
        wiki_id=wiki_id, \
        parent_id=parent_id, \
        display_order=display_order, \
        name=name, \
        content_id=content_id).insert()
Пример #12
0
def api_upload_attachment():
    i = ctx.request.input(name='', description='', link='')
    name = i.name.strip()
    description = i.description.strip()
    f = i.file
    ref_type = 'attachment'
    ref_id = db.next_str()
    fcontent = f.file.read()
    filename = f.filename
    fext = os.path.splitext(filename)[1]
    if not name:
        name = os.path.splitext(os.path.split(filename)[1])[0]

    preview = None
    w = h = 0
    res = store.upload_file(ref_type, ref_id, filename, fcontent)
    if res.mime.startswith('image/'):
        try:
            logging.info('it seems an image was uploaded, so try to get size...')
            im = thumbnail.as_image(fcontent)
            w, h = im.size[0], im.size[1]
            logging.info('size got: %d x %d' % (w, h))
            if w > 160 or h > 120:
                logging.info('creating thumbnail for uploaded image (size %d x %d)...' % (w, h))
                tn = thumbnail.create_thumbnail(im, 160, 120)
                pw, ph, pcontent = tn['width'], tn['height'], tn['data']
                logging.info('thumbnail was created successfully with size %d x %d.' % (w, h))
                preview = store.upload_file(ref_type, ref_id, filename, fcontent)
            else:
                logging.info('No need to create thumbnail.')
                preview = res
        except:
            logging.exception('error when creating thumbnail.')

    current = time.time()
    attr = Dict( \
        id = ref_id, \
        website_id = ctx.website.id, \
        user_id = ctx.user.id, \
        resource_id = res.id, \
        preview_resource_id = preview and preview.id or '', \
        name = name, \
        description = description, \
        width = w, \
        height = h, \
        size = res.size, \
        mime = res.mime, \
        creation_time = current, \
        modified_time = current, \
        version = 0)
    db.insert('attachments', **attr)
    if i.link==u't':
        attr.filelink = '/api/resources/url?id=%s' % attr.resource_id
    return attr
Пример #13
0
def get_menus():
    '''
    Get navigation menus as list, each element is a Dict object.
    '''
    menus = db.select('select * from menus order by display_order, name')
    if menus:
        return menus
    current = time.time()
    menu = Dict(id=db.next_str(), name=u'Home', description=u'', type='latest_articles', display_order=0, ref='', url='/latest', creation_time=current, modified_time=current, version=0)
    db.insert('menus', **menu)
    return [menu]
Пример #14
0
def set_text(name, value):
    '''
    Set text by name and value.
    '''
    pos = name.find('_')
    if pos<=0:
        raise ValueError('bad setting name: %s must be xxx_xxx' % name)
    kind = name[:pos]
    current = time.time()
    if 0==db.update('update texts set value=?, modified_time=?, version=version+1 where name=?', value, current, name):
        st = dict(id=db.next_str(), kind=kind, name=name, value=value, creation_time=current, modified_time=current, version=0)
        db.insert('texts', **st)
Пример #15
0
def _create_wiki_page(wiki_id, parent_id, display_order, name, content):
    current = time.time()
    p = dict(id=db.next_str(), \
        website_id=ctx.website.id, \
        wiki_id=wiki_id, \
        parent_id=parent_id, \
        display_order=display_order, \
        name=name, \
        content=content, \
        creation_time=current, \
        modified_time=current, \
        version=0)
    db.insert('wiki_pages', **p)
    return p
Пример #16
0
def _get_categories():
    cats = db.select('select * from categories where website_id=? order by display_order, name', ctx.website.id)
    if not cats:
        logging.info('create default uncategorized...')
        current = time.time()
        uncategorized = Dict(id=db.next_str(), \
            website_id=ctx.website.id, \
            name='Uncategorized', description='', \
            locked=True, display_order=0, \
            creation_time=current, modified_time=current, \
            version=0)
        db.insert('categories', **uncategorized)
        cats = [uncategorized]
    return cats
Пример #17
0
def add_entry():
    _check_user()
    i = ctx.request.input(id='', title='', subtitle='', description='')
    title = i.title.strip()
    subtitle = i.subtitle.strip()
    description = i.description.strip()
    if not title:
        raise APIError('value', 'title', 'Title is empty')
    cv = get_default_cv(ctx.user.id)
    for s in cv.sections:
        if s.id==i.id:
            next_id = db.next_str()
            logging.info('NEXT-ID: ' + next_id)
            db.insert('entries', id=next_id, user_id=ctx.user.id, resume_id=cv.id, section_id=s.id, display_order=len(s.entries), title=title, subtitle=subtitle, description=description, picture='', version=0)
            return dict(id=next_id, title=title, subtitle=subtitle, description=description)
    raise APIError('value', 'id', 'Invalid section id.')
Пример #18
0
def _create_wiki_page(wiki_id, parent_id, display_order, name, content):
    current = time.time()
    p = dict(
        id=db.next_str(),
        website_id=ctx.website.id,
        wiki_id=wiki_id,
        parent_id=parent_id,
        display_order=display_order,
        name=name,
        content=content,
        creation_time=current,
        modified_time=current,
        version=0,
    )
    db.insert("wiki_pages", **p)
    return p
Пример #19
0
def make_comment(ref_type, ref_id, user, content):
    '''
    Make a comment.

    Args:
        ref_type: the ref type, e.g. 'article'.
        ref_id: the ref id, e.g., article id.
        user: current user.
        content: comment content.
    Returns:
        the comment object as dict.
    '''
    cid = db.next_str()
    kw = dict(id=cid, ref_type=ref_type, ref_id=ref_id, user_id=user.id, image_url=user.image_url, name=user.name, content=content, creation_time=time.time(), version=0)
    db.insert('comments', **kw)
    return kw
Пример #20
0
def _get_categories():
    cats = db.select(
        'select * from categories where website_id=? order by display_order, name',
        ctx.website.id)
    if not cats:
        logging.info('create default uncategorized...')
        current = time.time()
        uncategorized = Dict(id=db.next_str(), \
            website_id=ctx.website.id, \
            name='Uncategorized', description='', \
            locked=True, display_order=0, \
            creation_time=current, modified_time=current, \
            version=0)
        db.insert('categories', **uncategorized)
        cats = [uncategorized]
    return cats
Пример #21
0
def create_user(website_id, email, passwd, name, role_id, locked=False):
    current = time.time()
    user = dict(
        id=db.next_str(),
        website_id=website_id,
        locked=locked,
        name=name,
        role_id=role_id,
        email=email,
        verified=False,
        passwd=passwd,
        image_url='http://www.gravatar.com/avatar/%s' % hashlib.md5(str(email)).hexdigest(),
        creation_time=current,
        modified_time=current,
        version=0)
    db.insert('users', **user)
    return user
Пример #22
0
def api_create_wiki():
    ' create a new wiki. '
    i = ctx.request.input(name='', description='', content='', cover=None)
    if not i.cover:
        raise APIValueError('cover', 'Cover cannot be empty.')
    name = assert_not_empty(i.name, 'name')
    description = i.description.strip()
    content = assert_not_empty(i.content, 'content')
    f = i.cover
    atta = uploaders.upload_cover(name, f.file.read())
    wiki_id = db.next_str()
    wiki = Wikis( \
        _id=wiki_id, \
        cover_id = atta._id, \
        name=name, \
        description=description, \
        content_id=texts.set(wiki_id, content)).insert()
    return wiki
Пример #23
0
def add_section():
    _check_user()
    i = ctx.request.input(kind='', title='', description='')
    kind = i.kind
    if not kind in _SECTIONS_SET:
        raise APIError('value', 'kind', 'Invalid kind.')
    title = i.title.strip()
    if not title:
        title = _SECTIONS_DICT.get(kind)
    description = i.description.strip()

    cv = get_default_cv(ctx.user.id)
    for s in cv.sections:
        if s.kind==kind:
            raise APIError('value', '', 'Section exist.')
    next_id = db.next_str()
    db.insert('sections', id=next_id, user_id=ctx.user.id, resume_id=cv.id, display_order=len(cv.sections), kind=kind, title=title, description=description, version=0)
    return dict(id=next_id, kind=kind, title=title, description=description)
Пример #24
0
def api_create_category():
    i = ctx.request.input(name='', description='')
    name = i.name.strip()
    description = i.description.strip()
    if not name:
        raise APIValueError('name', 'name cannot be empty')
    num = len(_get_categories())
    if num >= 100:
        raise APIError('operation:failed', 'category', 'cannot create new category for the maximum number of categories was reached.')
    logging.info('create new category...')
    current = time.time()
    cat = Dict(id=db.next_str(), \
            website_id=ctx.user.website_id, \
            name=name, description=description, \
            locked=False, display_order=num, \
            creation_time=current, modified_time=current, \
            version=0)
    db.insert('categories', **cat)
    return cat
Пример #25
0
def _set_setting(website_id, kind, key, value):
    '''
    Set setting by kind, key and value.
    '''
    if len(kind) == 0 or len(kind) > 50 or len(key) == 0 or len(key) > 50:
        raise ValueError('invalid setting name.')
    if not isinstance(value, (str, unicode)):
        value = str(value)
    name = '%s:%s' % (kind, key)
    settings = dict( \
        id = db.next_str(), \
        website_id = website_id, \
        kind = kind, \
        name = name, \
        value = value, \
        creation_time = time.time(), \
        version = 0)
    db.update('delete from settings where name=? and website_id=?', name,
              website_id)
    db.insert('settings', **settings)
Пример #26
0
def set_text(kind, key, value):
    '''
    Set text by kind, key and value.
    '''
    if len(kind) == 0 or len(kind) > 50 or len(key) == 0 or len(key) > 50:
        raise ValueError('invalid setting name.')
    if not isinstance(value, (str, unicode)):
        value = str(value)
    name = '%s:%s' % (kind, key)
    text = dict( \
        id = db.next_str(), \
        website_id = ctx.website.id, \
        kind = kind, \
        name = name, \
        value = value, \
        creation_time = time.time(), \
        version = 0)
    db.update('delete from texts where name=? and website_id=?', name,
              ctx.website.id)
    db.insert('texts', **text)
    cache.client.delete('TEXT:%s:%s:%s' % (ctx.website.id, kind, key))
Пример #27
0
def api_create_wiki():
    ' create a new wiki. '
    i = ctx.request.input(name='', description='', content='')
    name = i.name.strip()
    if not name:
        raise APIValueError('name', 'name cannot be empty')
    content = i.content.strip()
    if not content:
        raise APIValueError('content', 'content cannot be empty')
    current = time.time()
    wiki = Dict( \
        id=db.next_str(), \
        website_id=ctx.website.id, \
        name=name, \
        description=i.description.strip(), \
        content=content, \
        creation_time=current, \
        modified_time=current, \
        version=0)
    db.insert('wikis', **wiki)
    return wiki
Пример #28
0
def do_register():
    i = ctx.request.input(name='', email='', passwd='')

    name = i.name.strip()
    if not name:
        raise APIError('value', '', 'Invalid name.')

    email = i.email.strip().lower()
    check_email(email)

    passwd = i.passwd
    check_md5_passwd(passwd)

    us = db.select('select * from users where email=?', email)
    if us:
        raise APIError('register', '', 'Email already registered.')

    uid = db.next_str()
    db.insert('users', id=uid, name=name, email=email, passwd=passwd, version=0)

    make_session_cookie(uid, passwd)
    return {'id': uid}
Пример #29
0
def api_create_category():
    i = ctx.request.input(name='', description='')
    name = i.name.strip()
    description = i.description.strip()
    if not name:
        raise APIValueError('name', 'name cannot be empty')
    num = len(_get_categories())
    if num >= 100:
        raise APIError(
            'operation:failed', 'category',
            'cannot create new category for the maximum number of categories was reached.'
        )
    logging.info('create new category...')
    current = time.time()
    cat = Dict(id=db.next_str(), \
            website_id=ctx.user.website_id, \
            name=name, description=description, \
            locked=False, display_order=num, \
            creation_time=current, modified_time=current, \
            version=0)
    db.insert('categories', **cat)
    return cat
Пример #30
0
def api_create_wiki():
    " create a new wiki. "
    i = ctx.request.input(name="", description="", content="")
    name = i.name.strip()
    if not name:
        raise APIValueError("name", "name cannot be empty")
    content = i.content.strip()
    if not content:
        raise APIValueError("content", "content cannot be empty")
    current = time.time()
    wiki = Dict(
        id=db.next_str(),
        website_id=ctx.website.id,
        name=name,
        description=i.description.strip(),
        content=content,
        creation_time=current,
        modified_time=current,
        version=0,
    )
    db.insert("wikis", **wiki)
    return wiki
Пример #31
0
def api_create_article():
    i = ctx.request.input(category_id='',
                          name='',
                          tags='',
                          content='',
                          draft='')
    name = i.name.strip()
    content = i.content.strip()
    category_id = i.category_id
    if not name:
        raise APIValueError('name', 'name cannot be empty.')
    if not content:
        raise APIValueError('content', 'content cannot be empty.')
    if not category_id:
        raise APIValueError('category_id', 'category_id cannot be empty.')
    cat = _get_category(category_id)
    draft = True
    if ctx.user.role_id < ROLE_CONTRIBUTORS:
        draft = True if i.draft else False
    current = time.time()
    html_content, summary = html.parse_md(content, 800)
    article = Dict( \
        id=db.next_str(), \
        website_id=ctx.website.id, \
        user_id=ctx.user.id, \
        user_name=ctx.user.name, \
        category_id=category_id, \
        draft=draft, \
        name=name, \
        tags=_format_tags(i.tags), \
        read_count=0, \
        summary=summary, \
        content=content, \
        creation_time=current, \
        modified_time=current, \
        version=0)
    db.insert('articles', **article)
    return article
Пример #32
0
def api_create_page():
    i = ctx.request.input(name='', tags='', content='', draft='false')
    name = i.name.strip()
    content = i.content.strip()
    if not name:
        raise APIValueError('name', 'name cannot be empty.')
    if not content:
        raise APIValueError('content', 'content cannot be empty.')
    draft = boolean(i.draft)
    current = time.time()
    page = Dict( \
        id=db.next_str(), \
        website_id=ctx.website.id, \
        draft=draft, \
        name=name, \
        tags=_format_tags(i.tags), \
        read_count=0, \
        content=content, \
        creation_time=current, \
        modified_time=current, \
        version=0)
    db.insert('pages', **page)
    return page
Пример #33
0
def api_create_page():
    i = ctx.request.input(name='', tags='', content='', draft='false')
    name = i.name.strip()
    content = i.content.strip()
    if not name:
        raise APIValueError('name', 'name cannot be empty.')
    if not content:
        raise APIValueError('content', 'content cannot be empty.')
    draft = boolean(i.draft)
    current = time.time()
    page = Dict( \
        id=db.next_str(), \
        website_id=ctx.website.id, \
        draft=draft, \
        name=name, \
        tags=_format_tags(i.tags), \
        read_count=0, \
        content=content, \
        creation_time=current, \
        modified_time=current, \
        version=0)
    db.insert('pages', **page)
    return page
Пример #34
0
def upload_file(ref_type, ref_id, filename, fcontent):
    fileext = os.path.splitext(filename)[1].lower()
    filesize = len(fcontent)
    dt = datetime.now()
    fpath = os.path.join(ctx.website.id, str(ref_type), str(dt.year), str(dt.month), str(dt.day), '%s%s' % (uuid.uuid4().hex, fileext))
    sname = get_enabled_store_name()
    url, the_ref = get_store_instance(sname).upload(fpath, fcontent)
    logging.info('uploaded file: %s' % url)
    ref = '%s:%s' % (sname, the_ref)
    r = Dict( \
        id = db.next_str(), \
        website_id = ctx.website.id, \
        ref_id = ref_id, \
        ref_type = ref_type, \
        deleted = False, \
        size = filesize, \
        filename = filename, \
        mime = mimetypes.types_map.get(fileext, 'application/octet-stream'), \
        ref = ref, \
        url = url, \
        creation_time = time.time(), \
        version = 0)
    db.insert('resources', **r)
    return r