├── __init__.py
├── cache
├── cache.lock
└── cache.shelve.db
├── cron
└── crontab
├── modules
├── __init__.py
└── __init__.pyc
├── views
├── default
│ ├── user.html
│ ├── reset_password.html
│ ├── new_password.html
│ ├── profile.html
│ ├── category.html
│ ├── edit_comment.html
│ ├── post_chrome.html
│ ├── post.html
│ ├── register.html
│ ├── login_chrome.html
│ ├── login.html
│ ├── index.mobile.html
│ ├── about.html
│ ├── mylink.html
│ ├── downlink.html
│ ├── uplink.html
│ ├── index.html
│ ├── error.html
│ └── comments.html
├── generic.html
├── layout_chrome.html
├── web2py_ajax.html
├── layout_mobile.html
├── layout_wide.html
├── layout.html
└── appadmin.html
├── __init__.pyc
├── static
├── 404.png
├── eye.png
├── up.png
├── book.png
├── down.png
├── robots.txt
├── title.png
├── arrow_up.png
├── favicon.ico
├── arrow_down.png
├── google_btn.png
├── iphoneicon.png
├── img
│ ├── glyphicons-halflings.png
│ └── glyphicons-halflings-white.png
├── dropdown.js
├── bootstrap-dropdown.js
├── css
│ ├── main_ori.css
│ ├── bootstrap-responsive.css
│ └── main.css
├── bootstrap-collapse.js
├── bootstrap-modal.js
└── bootstrap-tooltip.js
├── languages
├── it.py
├── it-it.py
├── ko.py
└── ko-kr.py
├── .gitignore
├── README.md
├── LICENSE
├── models
├── helper_timesince.py
└── db.py
└── controllers
├── appadmin.py
└── default.py
/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cache/cache.lock:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cron/crontab:
--------------------------------------------------------------------------------
1 | #crontab
--------------------------------------------------------------------------------
/modules/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/views/default/user.html:
--------------------------------------------------------------------------------
1 | {{=form}}
--------------------------------------------------------------------------------
/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/__init__.pyc
--------------------------------------------------------------------------------
/static/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/404.png
--------------------------------------------------------------------------------
/static/eye.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/eye.png
--------------------------------------------------------------------------------
/static/up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/up.png
--------------------------------------------------------------------------------
/views/generic.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout.html'}}
2 | {{=BEAUTIFY(response._vars)}}
--------------------------------------------------------------------------------
/static/book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/book.png
--------------------------------------------------------------------------------
/static/down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/down.png
--------------------------------------------------------------------------------
/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /welcome
3 | Disallow: /admin
4 |
--------------------------------------------------------------------------------
/static/title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/title.png
--------------------------------------------------------------------------------
/static/arrow_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/arrow_up.png
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/favicon.ico
--------------------------------------------------------------------------------
/cache/cache.shelve.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/cache/cache.shelve.db
--------------------------------------------------------------------------------
/modules/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/modules/__init__.pyc
--------------------------------------------------------------------------------
/static/arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/arrow_down.png
--------------------------------------------------------------------------------
/static/google_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/google_btn.png
--------------------------------------------------------------------------------
/static/iphoneicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/iphoneicon.png
--------------------------------------------------------------------------------
/languages/it.py:
--------------------------------------------------------------------------------
1 | {
2 | 'Hello World':'Salve Mondo',
3 | 'Welcome to web2py':'Ciao da wek2py',
4 | }
5 |
--------------------------------------------------------------------------------
/languages/it-it.py:
--------------------------------------------------------------------------------
1 | {
2 | 'Hello World':'Salve Mondo',
3 | 'Welcome to web2py':'Ciao da wek2py',
4 | }
5 |
--------------------------------------------------------------------------------
/static/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/static/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungchi/feed9/HEAD/static/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.bak
3 | *.swp
4 | sessions
5 | databases
6 | cache
7 | errors
8 | main_ori.css
9 | static/ui.datepicker.js
10 | static/ui.datepicker.css
11 |
--------------------------------------------------------------------------------
/views/default/reset_password.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 |
3 | {{=form.custom.begin}}
4 |
{{=form.custom.widget.email}}
5 | {{=form.custom.submit}}
6 | {{=form.custom.end}}
7 |
8 | {{block page_name}}
9 | 비밀번호 찾기
10 | {{end}}
11 |
--------------------------------------------------------------------------------
/views/default/new_password.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 |
3 | {{=form.custom.begin}}
4 | {{=form.custom.widget.new_password}}
5 | {{=form.custom.widget.new_password2}}
6 | {{=form.custom.submit}}
7 | {{=form.custom.end}}
8 |
9 | {{block page_name}}
10 | 비밀번호 재설정
11 | {{end}}
12 |
--------------------------------------------------------------------------------
/views/default/profile.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 | {{=form.custom.begin}}
3 | {{=form.custom.widget.alias}}
4 | {{=form.custom.widget.email}}
5 | {{=form.custom.submit}}
6 | {{=form.custom.end}}
7 | {{block titlebar}}
8 | 프로필 - Feed9
9 | {{end}}
10 | {{block page_name}}
11 | 프로필
12 | {{end}}
--------------------------------------------------------------------------------
/languages/ko.py:
--------------------------------------------------------------------------------
1 | # coding: utf8
2 | {
3 | 'one year':'일년',
4 | '%(n)d years':'%(n)d년',
5 | 'one month':'한달',
6 | '%(n)d months':'%(n)d달',
7 | 'one week':'일주일',
8 | '%(n)d weeks':'%(n)d주',
9 | 'one day':'하루',
10 | '%(n)d days':'%(n)d일',
11 | 'one hour':'한시간',
12 | '%(n)d hours':'%(n)d시간',
13 | 'one minute':'일분',
14 | '%(n)d minutes':'%(n)d분',
15 | 'an instant':'방금',
16 | '%(n)d seconds':'%(n)d초',
17 | }
18 |
--------------------------------------------------------------------------------
/views/default/category.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 | {{
3 | from urlparse import urlparse
4 | }}
5 | {{if cat_list:}}
6 |
7 | {{for category in cat_list:}}
8 | {{=A(category[0],_href=URL(r=request,f='index',args=[category[1]]))}}
9 | {{pass}}
10 |
11 | {{pass}}
12 |
13 |
14 | {{block page_name}}
15 | 카테고리 목록
16 | {{end}}
17 |
--------------------------------------------------------------------------------
/languages/ko-kr.py:
--------------------------------------------------------------------------------
1 | # coding: utf8
2 | {
3 | 'one year':'일년',
4 | '%(n)d years':'%(n)d년',
5 | 'one month':'한달',
6 | '%(n)d months':'%(n)d달',
7 | 'one week':'일주일',
8 | '%(n)d weeks':'%(n)d주',
9 | 'one day':'하루',
10 | '%(n)d days':'%(n)d일',
11 | 'one hour':'한시간',
12 | '%(n)d hours':'%(n)d시간',
13 | 'one minute':'일분',
14 | '%(n)d minutes':'%(n)d분',
15 | 'an instant':'방금',
16 | '%(n)d seconds':'%(n)d초',
17 | }
18 |
--------------------------------------------------------------------------------
/views/default/edit_comment.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 |
3 | {{if form:}}
4 | {{=form.custom.begin}}
5 | {{=form.custom.widget.body}}
6 | 체크시 삭제
7 |
8 | {{=form.custom.end}}
9 | {{pass}}
10 |
11 | {{block page_name}}
12 | 코멘트 수정
13 | {{end}}
--------------------------------------------------------------------------------
/views/default/post_chrome.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_chrome.html'}}
2 | {{if form:}}
3 | {{=form.custom.begin}}
4 | {{=form.custom.widget.url}}
5 | {{=form.custom.widget.title}}
6 |
7 |
8 | 없음
9 | {{for item in cat_list:}}
10 | {{=item[0]}}
11 | {{pass}}
12 |
13 |
14 |
15 | {{=form.custom.submit}}
16 | {{=form.custom.end}}
17 | {{else:}}
18 | 올라갔다능
19 | {{pass}}
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | FEED9
2 | =====
3 |
4 | Feed9은 공유하는 링크들의 주소를 줄이고, 클릭횟수를 확인하고, 공유한 링크들을 관리하기 위해 만들어졌습니다. 누구나 회원가입해서 다른 사람의 링크를 보고 점수를 줄 수 있습니다.
5 |
6 | 회원가입 후에 제목(필수),링크,카테고리를 입력해서 링크를 올릴 수 있습니다. 각각의 링크는 Feed9의 주소가 부여되는데 그 주소를 짧은 주소로 이용해 외부에 공유하면 클릭 숫자가 집계됩니다.
7 |
8 | Feed9은 "투자와 인맥으로부터 자유로운 인디 웹서비스"입니다.(강제로 자유)
9 |
10 | 카테고리 신청, 피드백
11 | ---------------------
12 |
13 | admin@feed9.com, @sungchi
14 |
15 | web2py 소스 수정(이 저장소에는 web2py 소스가 없습니다):
16 |
17 | 1. gae_google_account.py : 사용자 model에 맞춰서 수정
18 | 2. routes.py : fancy URL 추가
19 | 3. gluon/tools.py : cas.get_user() 아랫부분 수정
--------------------------------------------------------------------------------
/views/default/post.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 | {{=form.custom.begin}}
3 | {{=form.custom.widget.url}}
4 | {{=form.custom.widget.title}}
5 |
6 | 없음
7 | {{for item in cat_list:}}
8 | {{=item[0]}}
9 |
10 | {{pass}}
11 |
12 | {{=form.custom.submit}}
13 | {{=form.custom.end}}
14 | {{block page_name}}
15 | 새로 올리기
16 | {{end}}
17 |
--------------------------------------------------------------------------------
/views/default/register.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 | 구글 아이디 로그인
3 | {{=form.custom.begin}}
4 | 닉네임 {{=form.custom.widget.alias}}
5 | 이메일 (아이디) {{=form.custom.widget.email}}
6 | 비밀번호 {{=form.custom.widget.password}}
7 | {{=form.custom.widget.password_two}}
8 | {{=form.custom.submit}}
9 | {{=form.custom.end}}
10 | {{block page_name}}
11 | 회원가입
12 | {{end}}
13 | {{block titlebar}}
14 | 회원가입 - Feed9
15 | {{end}}
16 |
--------------------------------------------------------------------------------
/views/default/login_chrome.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_chrome.html'}}
2 | 구글 아이디 로그인
3 | {{=login_form.custom.begin}}
4 |
5 |
{{=login_form.custom.widget.email}}
6 |
{{=login_form.custom.widget.password}}
7 |
{{=A('회원가입',_class="reset_link",_target="_blank",_href=URL(r=request,f='register'))}} 아이디 저장
8 |
{{=login_form.custom.submit}}
9 |
10 | {{=login_form.custom.end}}
11 |
--------------------------------------------------------------------------------
/views/default/login.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 | 구글 아이디 로그인
3 | {{=login_form.custom.begin}}
4 | {{=login_form.custom.widget.email}}
5 | {{=login_form.custom.widget.password}}
6 | {{=A('비밀번호 찾기',_class="reset_link",_href=URL(r=request,f='reset_password'))}} 아이디 저장
7 | {{=login_form.custom.submit}}
8 | {{=login_form.custom.end}}
9 |
10 |
11 | {{block titlebar}}
12 | 로그인 - Feed9
13 | {{end}}
14 | {{block page_name}}
15 | 로그인
16 | {{end}}
--------------------------------------------------------------------------------
/static/dropdown.js:
--------------------------------------------------------------------------------
1 | !function( $ ){
2 |
3 | "use strict"
4 |
5 | /* DROPDOWN PLUGIN DEFINITION
6 | * ========================== */
7 |
8 | $.fn.dropdown = function ( selector ) {
9 | return this.each(function () {
10 | $(this).delegate(selector || d, 'click', function (e) {
11 | var li = $(this).parent('li')
12 | , isActive = li.hasClass('open')
13 |
14 | clearMenus()
15 | !isActive && li.toggleClass('open')
16 | return false
17 | })
18 | })
19 | }
20 |
21 | /* APPLY TO STANDARD DROPDOWN ELEMENTS
22 | * =================================== */
23 |
24 | var d = 'a.menu, .dropdown-toggle'
25 |
26 | function clearMenus() {
27 | $(d).parent('li').removeClass('open')
28 | }
29 |
30 | $(function () {
31 |
32 | $('body').dropdown( '[data-dropdown] a.menu, [data-dropdown] .dropdown-toggle' )
33 | })
34 |
35 | }( window.jQuery || window.ender );
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright © 2012 sungchi, http://www.feed9.com/
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 |
--------------------------------------------------------------------------------
/models/helper_timesince.py:
--------------------------------------------------------------------------------
1 | import datetime, time
2 |
3 | def timesince(d0, t0):
4 | chunks = (
5 | (60 * 60 * 24 * 365, lambda n: T('one year') if n==1 else T('%(n)d years',dict(n=n))),
6 | (60 * 60 * 24 * 30, lambda n: T('one month') if n==1 else T('%(n)d months',dict(n=n))),
7 | (60 * 60 * 24 * 7, lambda n: T('one week') if n==1 else T('%(n)d weeks',dict(n=n))),
8 | (60 * 60 * 24, lambda n: T('one day') if n==1 else T('%(n)d days',dict(n=n))),
9 | (60 * 60, lambda n: T('one hour') if n==1 else T('%(n)d hours',dict(n=n))),
10 | (60, lambda n: T('one minute') if n==1 else T('%(n)d minutes',dict(n=n))),
11 | (1, lambda n: T('an instant') if n==1 else T('%(n)d seconds',dict(n=n))),
12 | )
13 | now = datetime.datetime.fromtimestamp(t0)
14 | d=datetime.datetime.fromtimestamp(d0)
15 | delta = now - (d - datetime.timedelta(0, 0, d.microsecond))
16 | since = delta.days * 24 * 60 * 60 + delta.seconds
17 | if since <= 0: return T('an instant')
18 | for seconds, name in chunks:
19 | count = since // seconds
20 | if count != 0: break
21 | return name(count)
22 |
--------------------------------------------------------------------------------
/views/layout_chrome.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{block titlebar}}Feed9{{end}}
10 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {{include}}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/views/default/index.mobile.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_mobile.html'}}
2 | {{
3 | from urlparse import urlparse
4 | }}
5 |
6 |
28 |
29 | {{if page>0:}}
30 |
이전 {{pass}}
31 | {{if page<3 and len(news)>25:}}
32 |
다음
33 | {{pass}}
34 |
35 | {{block page_name}}
36 | {{=alias}}
37 | {{end}}
38 | {{block titlebar}}
39 | {{=alias}} - Feed9
40 | {{end}}
41 |
42 | {{block footer_nav}}
43 | {{=A('화제',_href=URL(r=request,args=[category,'hot',page]))}}
44 | {{=A('최근',_href=URL(r=request,args=[category,'new',page]))}}
45 | {{=A('인기',_href=URL(r=request,args=[category,'score',page]))}}
46 | {{end}}
--------------------------------------------------------------------------------
/static/bootstrap-dropdown.js:
--------------------------------------------------------------------------------
1 | !function( $ ){
2 |
3 | "use strict"
4 |
5 | /* DROPDOWN CLASS DEFINITION
6 | * ========================= */
7 |
8 | var toggle = '[data-toggle="dropdown"]'
9 | , Dropdown = function ( element ) {
10 | var $el = $(element).on('click.dropdown.data-api', this.toggle)
11 | $('html').on('click.dropdown.data-api', function () {
12 | $el.parent().removeClass('open')
13 | })
14 | }
15 |
16 | Dropdown.prototype = {
17 |
18 | constructor: Dropdown
19 |
20 | , toggle: function ( e ) {
21 | var $this = $(this)
22 | , selector = $this.attr('data-target')
23 | , $parent
24 | , isActive
25 |
26 | if (!selector) {
27 | selector = $this.attr('href')
28 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
29 | }
30 |
31 | $parent = $(selector)
32 | $parent.length || ($parent = $this.parent())
33 |
34 | isActive = $parent.hasClass('open')
35 |
36 | clearMenus()
37 | !isActive && $parent.toggleClass('open')
38 |
39 | return false
40 | }
41 |
42 | }
43 |
44 | function clearMenus() {
45 | $(toggle).parent().removeClass('open')
46 | }
47 |
48 |
49 | /* DROPDOWN PLUGIN DEFINITION
50 | * ========================== */
51 |
52 | $.fn.dropdown = function ( option ) {
53 | return this.each(function () {
54 | var $this = $(this)
55 | , data = $this.data('dropdown')
56 | if (!data) $this.data('dropdown', (data = new Dropdown(this)))
57 | if (typeof option == 'string') data[option].call($this)
58 | })
59 | }
60 |
61 | $.fn.dropdown.Constructor = Dropdown
62 |
63 |
64 | /* APPLY TO STANDARD DROPDOWN ELEMENTS
65 | * =================================== */
66 |
67 | $(function () {
68 |
69 | $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
70 | })
71 |
72 | }( window.jQuery )
73 |
--------------------------------------------------------------------------------
/views/web2py_ajax.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
40 |
--------------------------------------------------------------------------------
/views/default/about.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 | {{
3 | from urlparse import urlparse
4 | }}
5 |
43 | {{block page_name}}
44 | Feed9 소개 "인터넷을 모읍니다"
45 | {{end}}
46 |
--------------------------------------------------------------------------------
/views/default/mylink.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 | {{
3 | from urlparse import urlparse
4 | }}
5 |
6 |
7 | {{if page>0:}}{{=A('<
50:}}{{=A('next>>',_href=URL(r=request,args=['all',page+1]))}}{{pass}}
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
23 |
24 | {{for k,item in enumerate(news):}}{{if k==50: continue}}
25 |
26 |
27 |
28 |
29 | {{=item.score}}
30 |
31 |
32 |
{{=item.clicks}}
33 |
34 |
35 |
36 | vote up
37 | vote down
38 |
39 |
40 |
41 |
42 |
43 | {{if item.category:}}
44 |
{{=A(item.category_alias,_href=URL(r=request,f='index',args=[item.category]))}}
45 | {{pass}}
46 | {{if item.url.strip().lower()=='self':}}
47 | {{=A(item.title,_href=URL(r=request,f='comments',args=[item.id]))}}
48 | {{else:}}
49 | {{=A(item.title,_href=URL(r=request,f='bookmark',args=[item.id]))}}
50 | {{url=urlparse(item.url)}}
({{=url.netloc}} ) {{pass}}
51 |
52 |
55 |
56 |
57 | {{pass}}
58 |
59 | {{if page>0:}}{{=A('<
50:}}{{=A('next>>',_href=URL(r=request,args=['all',page+1]))}}{{pass}}
61 |
62 | {{block page_name}}
63 | 내 링크내가 올린 링크 목록입니다
64 | {{end}}
65 |
--------------------------------------------------------------------------------
/views/default/downlink.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 | {{
3 | from urlparse import urlparse
4 | }}
5 |
6 |
7 | {{if page>0:}}{{=A('<
50:}}{{=A('next>>',_href=URL(r=request,args=['all',page+1]))}}{{pass}}
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
23 |
24 | {{for k,it in enumerate(news):}}{{if k==50: continue}}
25 | {{item=it.parentv}}
26 |
27 |
28 |
29 |
30 | {{=item.score}}
31 |
32 |
33 |
{{=item.clicks}}
34 |
35 |
36 |
37 | vote up
38 | vote down
39 |
40 |
41 |
42 |
43 |
44 | {{if item.category:}}
45 |
{{=A(item.category_alias,_href=URL(r=request,f='index',args=[item.category]))}}
46 | {{pass}}
47 | {{if item.url.strip().lower()=='self':}}
48 | {{=A(item.title,_href=URL(r=request,f='comments',args=[item.id]))}}
49 | {{else:}}
50 | {{=A(item.title,_href=URL(r=request,f='bookmark',args=[item.id]))}}
51 | {{url=urlparse(item.url)}}
({{=url.netloc}} ) {{pass}}
52 |
53 |
56 |
57 |
58 | {{pass}}
59 |
60 | {{if page>0:}}{{=A('<
50:}}{{=A('next>>',_href=URL(r=request,args=['all',page+1]))}}{{pass}}
62 |
63 | {{block page_name}}
64 | 점수내림내가 점수를 내린 링크 목록입니다
65 | {{end}}
66 |
--------------------------------------------------------------------------------
/views/default/uplink.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout_wide.html'}}
2 | {{
3 | from urlparse import urlparse
4 | }}
5 |
6 |
7 | {{if page>0:}}{{=A('<
50:}}{{=A('next>>',_href=URL(r=request,args=['all',page+1]))}}{{pass}}
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
23 |
24 | {{for k,it in enumerate(news):}}{{if k==50: continue}}
25 | {{item=it.parentv}}
26 |
27 |
28 |
29 |
30 | {{=item.score}}
31 |
32 |
33 |
{{=item.clicks}}
34 |
35 |
36 |
37 | vote up
38 | vote down
39 |
40 |
41 |
42 |
43 |
44 | {{if item.category:}}
45 |
{{=A(item.category_alias,_href=URL(r=request,f='index',args=[item.category]))}}
46 | {{pass}}
47 | {{if item.url.strip().lower()=='self':}}
48 | {{=A(item.title,_href=URL(r=request,f='comments',args=[item.id]))}}
49 | {{else:}}
50 | {{=A(item.title,_href=URL(r=request,f='bookmark',args=[item.id]))}}
51 | {{url=urlparse(item.url)}}
({{=url.netloc}} ) {{pass}}
52 |
53 |
56 |
57 |
58 | {{pass}}
59 |
60 | {{if page>0:}}{{=A('<
50:}}{{=A('next>>',_href=URL(r=request,args=['all',page+1]))}}{{pass}}
62 |
63 | {{block page_name}}
64 | 점수올림내가 점수를 올린 링크 목록입니다
65 | {{end}}
66 |
--------------------------------------------------------------------------------
/views/layout_mobile.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{block titlebar}}Feed9{{end}}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
홈
35 |
{{block page_name}}{{end}}
36 |
카테고리
37 |
38 |
39 |
40 | {{include}}
41 |
42 |
43 |
44 |
45 | {{block footer_nav}}
46 | {{end}}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
59 |
60 |
61 | {{if cat_list:}}
62 |
67 | {{pass}}
68 |
69 | 메인화면 돌아가기
70 |
71 |
72 |
73 |
74 |
75 |
76 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/static/css/main_ori.css:
--------------------------------------------------------------------------------
1 | * {margin:0; padding:0;}
2 | body, div, span, p, a, img, ul, ol, li, caption, table, thead, tbody, tfoot, tr, th, td, form, fieldset, legend, label, dl, dt, dd, blockquote, applet, object, h1, h2, h3, h4, h5 {border:0;}
3 | body {font:0.8em/1.5 "arial",sans-serif; text-align:center;}
4 | h1, h2, h3, h4, h5, h6 {margin:15px 0 10px 0; font-family:"arial",sans-serif;}
5 | ul, ol, dl, fieldset {margin:15px 0;}
6 | textarea {height:50px; width:400px}
7 | table {margin: 0px; border:0 padding:0; spacing:0;}
8 | th {text-align:center; font-weight:bold;}
9 | th, td {padding:2px 2px;}
10 | ul, ol {margin-left:30px;}
11 | ul ul, ol ol {margin:0; margin-left:20px;}
12 | ol {list-style-type:decimal;}
13 | li {display:list-item;}
14 | tr, dt {text-align:left; vertical-alignment: top}
15 | dd {margin-left:30px;}
16 | fieldset {position:relative; padding:10px;}
17 | legend {position:absolute; top:-1em; margin:0; padding:5px 10px; font-size:100%; font-weight:bold;}
18 | .box {min-height:1px;}
19 | .box:after {display:block; visibility:hidden; clear:both; line-height:0; font-size:0; content:".";}
20 | .relative {position:relative;}
21 | .clear {clear:both;}
22 | .main {position:relative; width:960px; margin:0 auto; text-align:left;}
23 | .score {font-size:+1.2em; font-weight: bold; text-align: center; vertical-alignment: middle;}
24 | .article {font-size:+1.3em;}
25 | .error { background-color: red; color: white; padding: 3px}
26 |
27 | #header {padding:10px 0; overflow:hidden}
28 |
29 | #logo {margin:0; margin-bottom:3px; font:normal 260%/1.2 "arial",sans-serif; letter-spacing:-1px;}
30 | #logo span {font-weight:bold;}
31 | #logo a {text-decoration:none;}
32 | #slogan {margin:0;}
33 |
34 | #search {position:absolute; top:0; right:0;}
35 | #search #search-input {width:170px; padding:4px; font:normal 100%/1.2 "arial",sans-serif;}
36 | #search #search-submit {padding:3px 5px; font:bold 100%/1.2 "arial",sans-serif;}
37 |
38 | #nav {clear:both; background:url("../title.png") 0 0 repeat-x; height: 35px;}
39 | #nav ul {margin:0; padding:0; list-style:none;}
40 | #nav li {display:inline; margin:0; padding:0;}
41 | #nav li a {display:block; float:left; height: 22px; padding:5px 20px; text-decoration:none; font-weight:bold;}
42 | #nav li #nav-active a {font-weight:bold; text-decoration:none;}
43 |
44 | #cols .main {padding-bottom:15px; background:url("../design/aside.gif") 100% 0 repeat-y;}
45 |
46 | #content {float:left; width:680px; padding:15px; }
47 | #content p {text-align:justify;}
48 |
49 | #content h1 {margin:0; margin-bottom:10px; font-size:160%;}
50 | #content h2, #content h3, #content h4 {margin-bottom:0; margin-bottom:10px; font-size:140%;}
51 | #content h3, #content h4 {letter-spacing:0; font-size:120%;}
52 |
53 | #aside {float:right; width:244px; padding-right:1px; overflow:hidden;}
54 | #aside ul {margin:5px 0 15px 0; padding:0; list-style:none;}
55 | #aside ul li {margin:0; padding:4px 10px 4px 0;}
56 |
57 | #footer {clear:both; padding:15px 0;}
58 | #footer p {margin:0;}
59 |
60 | /* COLOR */
61 |
62 | body {background: #2B99B2; color:#000;}
63 | a {color: #FF5C1F; text-decoration: none}
64 | a:hover {color: #FF5C1F; text-decoration: underline;}
65 | fieldset {border:1px solid #175966;}
66 | legend {background:#FFF; color:#175966;}
67 |
68 | #header {background:#000;}
69 | #logo {border-bottom:1px solid #464646; color:#FFF;}
70 | #logo a {color:#FF5C1F;}
71 | #slogan {color:#B0B0B0;}
72 | #search #search-input {border:1px solid #FFF;}
73 | #search #search-submit {background:#175966; color:#FFF;}
74 |
75 | #nav {background-color:#175966;}
76 | #nav li a {border-left:1px solid #175966; color:#FF5C1F;}
77 | #nav li.last a {border-right:1px solid #175966;}
78 | #nav li#nav-active a {background:#FFF; color:#FF5C1F;}
79 | #nav li a:hover {background:#FFF; color:#FF5C1F;}
80 |
81 | #cols .main {background-color:#FFF;}
82 |
83 | #content h2 {border-bottom:1px solid #175966}
84 | #content h2, h3, h4, h5, h6 { color:#175966;}
85 |
86 | #aside h3 {color:#175966;}
87 | #aside ul li {border-bottom:1px solid #175966;}
88 |
89 | #footer {background:#000; color:#B0B0B0;}
90 | #footer a {color:#FF5C1F;}
91 | #footer a:hover {text-decoration: underline;}
92 |
93 | .main_box {border: 0px solid #175966; vertical-alignment: middle; text-align: center;}
94 |
95 | #flash { margin: 0 auto; text-align: center; clear: both; border: 1px #000000; background-color: #FF0000; color: white; margin-top: 0.0em; margin-bottom: 1.0em; padding-top: 1.0em; padding-bottom: 1.0em; }
--------------------------------------------------------------------------------
/static/bootstrap-collapse.js:
--------------------------------------------------------------------------------
1 | /* =============================================================
2 | * bootstrap-collapse.js v2.0.0
3 | * http://twitter.github.com/bootstrap/javascript.html#collapse
4 | * =============================================================
5 | * Copyright 2012 Twitter, Inc.
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ============================================================ */
19 |
20 | !function( $ ){
21 |
22 | "use strict"
23 |
24 | var Collapse = function ( element, options ) {
25 | this.$element = $(element)
26 | this.options = $.extend({}, $.fn.collapse.defaults, options)
27 |
28 | if (this.options["parent"]) {
29 | this.$parent = $(this.options["parent"])
30 | }
31 |
32 | this.options.toggle && this.toggle()
33 | }
34 |
35 | Collapse.prototype = {
36 |
37 | constructor: Collapse
38 |
39 | , dimension: function () {
40 | var hasWidth = this.$element.hasClass('width')
41 | return hasWidth ? 'width' : 'height'
42 | }
43 |
44 | , show: function () {
45 | var dimension = this.dimension()
46 | , scroll = $.camelCase(['scroll', dimension].join('-'))
47 | , actives = this.$parent && this.$parent.find('.in')
48 | , hasData
49 |
50 | if (actives && actives.length) {
51 | hasData = actives.data('collapse')
52 | actives.collapse('hide')
53 | hasData || actives.data('collapse', null)
54 | }
55 |
56 | this.$element[dimension](0)
57 | this.transition('addClass', 'show', 'shown')
58 | this.$element[dimension](this.$element[0][scroll])
59 |
60 | }
61 |
62 | , hide: function () {
63 | var dimension = this.dimension()
64 | this.reset(this.$element[dimension]())
65 | this.transition('removeClass', 'hide', 'hidden')
66 | this.$element[dimension](0)
67 | }
68 |
69 | , reset: function ( size ) {
70 | var dimension = this.dimension()
71 |
72 | this.$element
73 | .removeClass('collapse')
74 | [dimension](size || 'auto')
75 | [0].offsetWidth
76 |
77 | this.$element.addClass('collapse')
78 | }
79 |
80 | , transition: function ( method, startEvent, completeEvent ) {
81 | var that = this
82 | , complete = function () {
83 | if (startEvent == 'show') that.reset()
84 | that.$element.trigger(completeEvent)
85 | }
86 |
87 | this.$element
88 | .trigger(startEvent)
89 | [method]('in')
90 |
91 | $.support.transition && this.$element.hasClass('collapse') ?
92 | this.$element.one($.support.transition.end, complete) :
93 | complete()
94 | }
95 |
96 | , toggle: function () {
97 | this[this.$element.hasClass('in') ? 'hide' : 'show']()
98 | }
99 |
100 | }
101 |
102 | /* COLLAPSIBLE PLUGIN DEFINITION
103 | * ============================== */
104 |
105 | $.fn.collapse = function ( option ) {
106 | return this.each(function () {
107 | var $this = $(this)
108 | , data = $this.data('collapse')
109 | , options = typeof option == 'object' && option
110 | if (!data) $this.data('collapse', (data = new Collapse(this, options)))
111 | if (typeof option == 'string') data[option]()
112 | })
113 | }
114 |
115 | $.fn.collapse.defaults = {
116 | toggle: true
117 | }
118 |
119 | $.fn.collapse.Constructor = Collapse
120 |
121 |
122 | /* COLLAPSIBLE DATA-API
123 | * ==================== */
124 |
125 | $(function () {
126 | $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
127 | var $this = $(this), href
128 | , target = $this.attr('data-target')
129 | || e.preventDefault()
130 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
131 | , option = $(target).data('collapse') ? 'toggle' : $this.data()
132 | $(target).collapse(option)
133 | })
134 | })
135 |
136 | }( window.jQuery )
137 |
--------------------------------------------------------------------------------
/views/default/index.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout.html'}}
2 | {{
3 | from urlparse import urlparse
4 | }}
5 |
6 |
7 | {{=A('최근',_href=URL(r=request,args=[category,'new',page]))}}
8 | {{=A('화제',_href=URL(r=request,args=[category,'hot',page]))}}
9 | {{=A('인기',_href=URL(r=request,args=[category,'score',page]))}}
10 |
11 | {{ if page>0 or (page<3 and len(news)>25):}}
12 |
13 | {{if page>0:}}{{=A('<
25:}}{{=A('next>>',_href=URL(r=request,args=[category,sort,page+1]))}}{{pass}}
15 |
16 | {{pass}}
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
30 |
31 |
32 | {{for k,item in enumerate(news):}}{{if k==25: continue}}
33 |
34 |
35 |
36 |
37 | {{=item.score}}
38 |
39 |
40 |
{{=item.clicks}}
41 |
42 |
43 |
44 | vote up
45 | vote down
46 |
47 |
48 |
49 |
50 | {{if category=='all' and item.category:}}
51 |
{{=A(item.category_alias,_href=URL(r=request,f='index',args=[item.category]))}}
52 | {{pass}}
53 | {{if item.url.strip().lower()=='self':}}
54 | {{=A(item.title,_href=URL(r=request,f='comments',args=[item.id]))}}
55 | {{else:}}
56 | {{=A(item.title,_href=URL(r=request,f='bookmark',args=[item.id]))}}
57 | {{url=urlparse(item.url)}}
({{=url.netloc}} ) {{pass}}
58 |
59 |
62 |
63 |
75 |
76 | {{pass}}
77 |
78 | {{if page>0:}}{{=A('<
25:}}{{=A('next>>',_href=URL(r=request,args=[category,sort,page+1]))}}{{pass}}
80 |
81 | {{block category_name}}
82 | {{=alias}}{{=dec}}
83 | {{end}}
84 | {{block titlebar}}
85 | {{=alias}} - Feed9
86 | {{end}}
87 |
--------------------------------------------------------------------------------
/models/db.py:
--------------------------------------------------------------------------------
1 | if request.controller.endswith('_examples'): response.generic_patterns.append('*')
2 |
3 | import time
4 | from math import log
5 | now=time.time()
6 |
7 | from gluon.settings import settings
8 | from gluon.tools import Auth
9 | import mail_setting
10 | from gluon.contrib.login_methods.gae_google_account import GaeGoogleAccount
11 | from gluon.contrib.login_methods.extended_login_form import ExtendedLoginForm
12 | # if running on Google App Engine
13 | if settings.web2py_runtime_gae:
14 | from gluon.contrib.gql import *
15 | from gluon.contrib.gae_memcache import MemcacheClient
16 | from gluon.contrib.memdb import MEMDB
17 | cache.memcache = MemcacheClient(request)
18 | cache.ram = cache.disk = cache.memcache
19 | # connect to Google BigTable
20 | db = DAL('gae')
21 | # and store sessions there
22 | #session.connect(request, response, db=db)
23 | session.connect(request,response,MEMDB(cache.memcache))
24 | else:
25 | # if not, use SQLite or other DB
26 | db = DAL('sqlite://storage.sqlite')
27 |
28 | auth = Auth(db)
29 | auth.settings.table_user_name = 'person'
30 | auth.settings.request_reset_password_next = URL('index')
31 | auth.settings.reset_password_next = URL('login')
32 | auth.settings.expiration = 3600*24*30
33 | auth.settings.long_expiration = 3600*24*30 # one month
34 | auth.settings.remember_me_form = True
35 | #auth.settings.login_next = URL('index')
36 | auth.messages.reset_password= "다음 링크를 클릭하시면 비밀번호 재설정 페이지로 이동합니다. http://"+request.env.http_host+URL(r=request,f='new_password')+'/%(key)s'
37 | auth.settings.formstyle='divs'
38 | mail = auth.settings.mailer
39 | mail.settings.server = 'gae'
40 | mail.settings.sender = mail_setting.sender()
41 | mail.settings.login = mail_setting.login()
42 | db.define_table(
43 | auth.settings.table_user_name,
44 | Field('alias', length=128, default='',unique=True),
45 | Field('email', length=128, default='', unique=True),
46 | Field('password', 'password', length=512,
47 | readable=False, label='Password'),
48 | Field('registration_key', length=512,
49 | writable=False, readable=False, default=''),
50 | Field('reset_password_key', length=512,
51 | writable=False, readable=False, default=''),
52 | Field('registration_id', length=512,
53 | writable=False, readable=False, default=''))
54 |
55 | custom_auth_table = db[auth.settings.table_user_name]
56 | custom_auth_table.password.requires = [IS_NOT_EMPTY(),CRYPT()]
57 | custom_auth_table.email.requires = [
58 | IS_EMAIL(error_message=auth.messages.invalid_email),
59 | IS_NOT_IN_DB(db, custom_auth_table.email),IS_NOT_EMPTY()]
60 |
61 | auth.settings.table_user = custom_auth_table
62 | auth.define_tables(username=False)
63 | #auth.settings.login_form = ExtendedLoginForm(auth, GaeGoogleAccount())
64 |
65 | db.define_table('category',
66 | Field('name'),
67 | Field('alias',default='새 카테고리'),
68 | Field('description'))
69 |
70 | db.category.name.requires=[IS_MATCH('[a-z_]+'),
71 | IS_NOT_IN_DB(db,db.category.name)]
72 | db.category.description.requires=IS_NOT_EMPTY()
73 |
74 | db.define_table('news',
75 | Field('post_time','double',default=now),
76 | Field('clicks','integer',default=1),
77 | Field('comments','integer',default=0),
78 | Field('score','integer',default=1),
79 | Field('hotness','double',compute=lambda r: log(r['clicks'],10)+log(r['score'],10)+log(max(r['comments'],1),10)+log(max((r['post_time']-(now-2592000.0)),1),10),default=1.0),
80 | Field('category'),
81 | Field('category_alias'),
82 | Field('author',db.person),
83 | Field('author_alias'),
84 | Field('url',length=2048),
85 | Field('title',length=2048),
86 | Field('flagged','boolean',default=False))
87 |
88 | #db.news.url.requires=[IS_NOT_EMPTY()]
89 | db.news.title.requires=IS_NOT_EMPTY()
90 |
91 | db.define_table('comment',
92 | Field('score','integer',default=1),
93 | Field('post_time','double',default=now),
94 | Field('author',db.person),
95 | Field('author_alias'),
96 | Field('parente','integer',default=0),
97 | Field('news',db.news),
98 | Field('body','text'),
99 | Field('flagged','boolean',default=False))
100 |
101 | db.comment.body.requires=IS_NOT_EMPTY()
102 |
103 | db.define_table('vote',
104 | Field('choice','integer',default=0),
105 | Field('parentv',db.news),
106 | Field('voter',db.person))
107 |
108 | db.define_table('vote_cmt',
109 | Field('choice','integer',default=0),
110 | Field('parentv',db.comment),
111 | Field('voter',db.person))
112 |
113 | if len(db().select(db.category.ALL))==0:
114 | db.category.insert(name='politics',alias='시사정치',description='최근 많이 이야기되는 시사, 정치 뉴스들')
115 | db.category.insert(name='programming',alias='프로그래밍',description='삶을 윤택하게 만드는 코드')
116 | db.category.insert(name='technology',alias='테크놀로지',description='새로운 기술과 신제품')
117 | db.category.insert(name='movie',alias='영화',description='새 예고편, 영화 소식, 좋은 리뷰들')
118 | db.category.insert(name='accident',alias='사건사고',description='')
119 | db.category.insert(name='diablo',alias='디아블로',description='그냥 디아블로가 좋아...')
120 | db.category.insert(name='game',alias='게임',description='게임은 사회를 좀먹게 하는 질병입니다')
121 | db.category.insert(name='drama',alias='미드',description='미드 소식, 동영상, 추천 정보들...')
122 | db.category.insert(name='funny',alias='유머',description='조금만 재미있어도 공유합니다.')
123 | db.category.insert(name='body',alias='사람사진',description='주로 여자 사진이 될 것 같지만')
124 |
--------------------------------------------------------------------------------
/views/layout_wide.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
32 |
33 |
34 | {{block titlebar}}Feed9{{end}}
35 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
Feed9
60 |
61 |
66 |
67 |
68 | {{if auth.user_id:}}
69 | {{=A('점수올림',_href=URL(r=request,f='uplink/'))}}
70 | {{=A('점수내림',_href=URL(r=request,f='downlink/'))}}
71 | {{=A('내 링크',_href=URL(r=request,f='mylink/'))}}
72 | {{=A(I(_class='icon-edit icon-white'),'올리기',_class="post_btn",_href=URL(r=request,f='post'))}}
73 |
74 | {{=auth.user.alias}}
75 |
79 |
80 | {{else:}}
81 | {{=A('회원가입',_href=URL(r=request,f='register'))}}
82 |
83 | 로그인
84 |
97 |
98 | {{pass}}
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
111 |
112 |
113 | {{if response.flash:}}
{{=response.flash}}
{{pass}}
114 | {{include}}
115 |
116 |
117 |
118 |
119 |
122 |
123 |
124 |
125 | {{include 'web2py_ajax.html'}}
126 | {{block comment_hide}}
127 | {{end}}
128 |
129 |
130 |
--------------------------------------------------------------------------------
/views/default/error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
32 |
33 |
34 | {{block titlebar}}Feed9{{end}}
35 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
Feed9
60 |
61 |
66 |
67 |
68 | {{if auth.user_id:}}
69 | {{=A('점수올림',_href=URL(r=request,f='uplink/'))}}
70 | {{=A('점수내림',_href=URL(r=request,f='downlink/'))}}
71 | {{=A('내 링크',_href=URL(r=request,f='mylink/'))}}
72 | {{=A(I(_class='icon-edit icon-white'),'올리기',_class="post_btn",_href=URL(r=request,f='post'))}}
73 |
74 | {{=auth.user.alias}}
75 |
79 |
80 | {{else:}}
81 | {{=A('회원가입',_href=URL(r=request,f='register'))}}
82 |
83 | 로그인
84 |
97 |
98 | {{pass}}
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
111 |
112 |
113 |
114 |
page not found.
115 |
116 |
117 |
118 |
119 |
120 |
123 |
124 |
125 |
126 | {{include 'web2py_ajax.html'}}
127 | {{block comment_hide}}
128 | {{end}}
129 |
130 |
131 |
--------------------------------------------------------------------------------
/views/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
32 |
33 |
34 | {{block titlebar}}Feed9{{end}}
35 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
Feed9
60 |
61 |
66 |
67 |
68 | {{if auth.user_id:}}
69 | {{=A('점수올림',_href=URL(r=request,f='uplink/'))}}
70 | {{=A('점수내림',_href=URL(r=request,f='downlink/'))}}
71 | {{=A('내 링크',_href=URL(r=request,f='mylink/'))}}
72 | {{=A(I(_class='icon-edit icon-white'),'올리기',_class="post_btn",_href=URL(r=request,f='post'))}}
73 |
74 | {{=auth.user.alias}}
75 |
79 |
80 | {{else:}}
81 | {{=A('회원가입',_href=URL(r=request,f='register'))}}
82 |
83 | 로그인
84 |
97 |
98 | {{pass}}
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
111 |
112 |
113 | {{if response.flash:}}
{{=response.flash}}
{{pass}}
114 | {{include}}
115 |
116 |
130 |
131 |
132 |
133 |
136 |
137 |
138 |
139 | {{include 'web2py_ajax.html'}}
140 | {{block comment_hide}}
141 | {{end}}
142 |
143 |
144 |
--------------------------------------------------------------------------------
/views/default/comments.html:
--------------------------------------------------------------------------------
1 | {{extend 'layout.html'}}
2 | {{from gluon.contrib.markdown import WIKI}}
3 | {{
4 | from urlparse import urlparse
5 | }}
6 |
7 | {{def listcomments(id):}}
8 | {{if not tree.has_key(id): return}}
9 |
10 | {{for item in tree[id]:}}
11 |
12 |
13 |
14 |
15 | {{=item.score}}
16 |
17 |
18 |
19 | vote up
20 | vote down
21 |
22 |
23 |
24 |
25 | {{=WIKI(item.body.strip())}}
26 | {{=A('고유주소',_href=URL(r=request,f='permalink',args=[item.id]))}} {{=A('신고',_href=URL(r=request,f='report_comment',args=[item.id]))}} {{=A('코멘트',_href="javascript:toggle(%i)"%item.id if auth.user_id else URL(r=request,f='login'))}} {{if item.author==auth.user_id:}}{{=A('수정',_href=URL(r=request,f='edit_comment',args=[item.id]))}} {{pass}}
27 | {{=timesince(item.post_time,now)}} 전 {{=A(item.author_alias,_href=URL(r=request,f='person',args=[item.author]))}}
28 |
29 |
30 | {{if forms:}}
31 | {{=forms[item.id].custom.begin}}
32 |
33 |
{{=forms[item.id].custom.widget.body}}
34 |
35 | {{=forms[item.id].custom.end}}
36 |
{{pass}}
37 |
38 | {{listcomments(item.id)}}
39 |
40 |
41 | {{pass}}
42 | {{return}}
43 |
44 |
45 |
46 |
47 |
51 |
52 |
53 |
54 |
57 |
58 |
59 | {{if item:}}
60 |
61 |
62 |
63 |
64 | {{=item.score}}
65 |
66 |
67 |
{{=item.clicks}}
68 |
69 |
70 |
71 | vote up
72 | vote down
73 |
74 |
75 |
76 |
77 |
78 | {{if item.category:}}
79 |
{{=A(item.category_alias,_href=URL(r=request,f='index',args=[item.category]))}}
80 | {{pass}}
81 | {{if item.url.strip().lower()=='self':}}
82 | {{=A(item.title,_href=URL(r=request,f='comments',args=[item.id]))}}
83 | {{else:}}
84 | {{=A(item.title,_href=URL(r=request,f='bookmark',args=[item.id]))}}
85 | {{url=urlparse(item.url)}}
({{=url.netloc}} ) {{pass}}
86 |
87 |
90 |
91 |
92 | {{pass}}
93 |
94 | 코멘트
95 |
96 |
103 |
104 |
105 |
106 | {{if form:}}
107 | {{=form.custom.begin}}
108 | {{=form.custom.widget.body}}
109 |
110 | {{=form.custom.end}}
111 | {{else:}}
112 |
113 | {{=A('로그인',_class='btn',_href=URL(r=request,f='login'))}} {{=A('회원가입',_class='btn',_href=URL(r=request,f='register'))}}
114 |
115 | {{pass}}
116 |
117 | {{listcomments(parent)}}
118 | {{block comment_hide}}
119 |
128 |
129 | {{end}}
130 | {{block titlebar}}
131 | {{if item:}}{{=item.title[:100]}}{{else:}}코멘트{{pass}} - Feed9
132 | {{end}}
133 |
--------------------------------------------------------------------------------
/static/bootstrap-modal.js:
--------------------------------------------------------------------------------
1 | !function( $ ){
2 |
3 | "use strict"
4 |
5 | /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
6 | * ======================================================= */
7 |
8 | var transitionEnd
9 |
10 | $(document).ready(function () {
11 |
12 | $.support.transition = (function () {
13 | var thisBody = document.body || document.documentElement
14 | , thisStyle = thisBody.style
15 | , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
16 | return support
17 | })()
18 |
19 | // set CSS transition event type
20 | if ( $.support.transition ) {
21 | transitionEnd = "TransitionEnd"
22 | if ( $.browser.webkit ) {
23 | transitionEnd = "webkitTransitionEnd"
24 | } else if ( $.browser.mozilla ) {
25 | transitionEnd = "transitionend"
26 | } else if ( $.browser.opera ) {
27 | transitionEnd = "oTransitionEnd"
28 | }
29 | }
30 |
31 | })
32 |
33 |
34 | /* MODAL PUBLIC CLASS DEFINITION
35 | * ============================= */
36 |
37 | var Modal = function ( content, options ) {
38 | this.settings = $.extend({}, $.fn.modal.defaults, options)
39 | this.$element = $(content)
40 | .delegate('.close', 'click.modal', $.proxy(this.hide, this))
41 |
42 | if ( this.settings.show ) {
43 | this.show()
44 | }
45 |
46 | return this
47 | }
48 |
49 | Modal.prototype = {
50 |
51 | toggle: function () {
52 | return this[!this.isShown ? 'show' : 'hide']()
53 | }
54 |
55 | , show: function () {
56 | var that = this
57 | this.isShown = true
58 | this.$element.trigger('show')
59 |
60 | escape.call(this)
61 | backdrop.call(this, function () {
62 | var transition = $.support.transition && that.$element.hasClass('fade')
63 |
64 | that.$element
65 | .appendTo(document.body)
66 | .show()
67 |
68 | if (transition) {
69 | that.$element[0].offsetWidth // force reflow
70 | }
71 |
72 | that.$element.addClass('in')
73 |
74 | transition ?
75 | that.$element.one(transitionEnd, function () { that.$element.trigger('shown') }) :
76 | that.$element.trigger('shown')
77 |
78 | })
79 |
80 | return this
81 | }
82 |
83 | , hide: function (e) {
84 | e && e.preventDefault()
85 |
86 | if ( !this.isShown ) {
87 | return this
88 | }
89 |
90 | var that = this
91 | this.isShown = false
92 |
93 | escape.call(this)
94 |
95 | this.$element
96 | .trigger('hide')
97 | .removeClass('in')
98 |
99 | $.support.transition && this.$element.hasClass('fade') ?
100 | hideWithTransition.call(this) :
101 | hideModal.call(this)
102 |
103 | return this
104 | }
105 |
106 | }
107 |
108 |
109 | /* MODAL PRIVATE METHODS
110 | * ===================== */
111 |
112 | function hideWithTransition() {
113 | // firefox drops transitionEnd events :{o
114 | var that = this
115 | , timeout = setTimeout(function () {
116 | that.$element.unbind(transitionEnd)
117 | hideModal.call(that)
118 | }, 500)
119 |
120 | this.$element.one(transitionEnd, function () {
121 | clearTimeout(timeout)
122 | hideModal.call(that)
123 | })
124 | }
125 |
126 | function hideModal (that) {
127 | this.$element
128 | .hide()
129 | .trigger('hidden')
130 |
131 | backdrop.call(this)
132 | }
133 |
134 | function backdrop ( callback ) {
135 | var that = this
136 | , animate = this.$element.hasClass('fade') ? 'fade' : ''
137 | if ( this.isShown && this.settings.backdrop ) {
138 | var doAnimate = $.support.transition && animate
139 |
140 | this.$backdrop = $('
')
141 | .appendTo(document.body)
142 |
143 | if ( this.settings.backdrop != 'static' ) {
144 | this.$backdrop.click($.proxy(this.hide, this))
145 | }
146 |
147 | if ( doAnimate ) {
148 | this.$backdrop[0].offsetWidth // force reflow
149 | }
150 |
151 | this.$backdrop.addClass('in')
152 |
153 | doAnimate ?
154 | this.$backdrop.one(transitionEnd, callback) :
155 | callback()
156 |
157 | } else if ( !this.isShown && this.$backdrop ) {
158 | this.$backdrop.removeClass('in')
159 |
160 | $.support.transition && this.$element.hasClass('fade')?
161 | this.$backdrop.one(transitionEnd, $.proxy(removeBackdrop, this)) :
162 | removeBackdrop.call(this)
163 |
164 | } else if ( callback ) {
165 | callback()
166 | }
167 | }
168 |
169 | function removeBackdrop() {
170 | this.$backdrop.remove()
171 | this.$backdrop = null
172 | }
173 |
174 | function escape() {
175 | var that = this
176 | if ( this.isShown && this.settings.keyboard ) {
177 | $(document).bind('keyup.modal', function ( e ) {
178 | if ( e.which == 27 ) {
179 | that.hide()
180 | }
181 | })
182 | } else if ( !this.isShown ) {
183 | $(document).unbind('keyup.modal')
184 | }
185 | }
186 |
187 |
188 | /* MODAL PLUGIN DEFINITION
189 | * ======================= */
190 |
191 | $.fn.modal = function ( options ) {
192 | var modal = this.data('modal')
193 |
194 | if (!modal) {
195 |
196 | if (typeof options == 'string') {
197 | options = {
198 | show: /show|toggle/.test(options)
199 | }
200 | }
201 |
202 | return this.each(function () {
203 | $(this).data('modal', new Modal(this, options))
204 | })
205 | }
206 |
207 | if ( options === true ) {
208 | return modal
209 | }
210 |
211 | if ( typeof options == 'string' ) {
212 | modal[options]()
213 | } else if ( modal ) {
214 | modal.toggle()
215 | }
216 |
217 | return this
218 | }
219 |
220 | $.fn.modal.Modal = Modal
221 |
222 | $.fn.modal.defaults = {
223 | backdrop: false
224 | , keyboard: false
225 | , show: false
226 | }
227 |
228 |
229 | /* MODAL DATA- IMPLEMENTATION
230 | * ========================== */
231 |
232 | $(document).ready(function () {
233 | $('body').delegate('[data-controls-modal]', 'click', function (e) {
234 | e.preventDefault()
235 | var $this = $(this).data('show', true)
236 | $('#' + $this.attr('data-controls-modal')).modal( $this.data() )
237 | })
238 | })
239 |
240 | }( window.jQuery || window.ender );
241 |
--------------------------------------------------------------------------------
/static/bootstrap-tooltip.js:
--------------------------------------------------------------------------------
1 | /* ===========================================================
2 | * bootstrap-tooltip.js v2.0.0
3 | * http://twitter.github.com/bootstrap/javascript.html#tooltips
4 | * Inspired by the original jQuery.tipsy by Jason Frame
5 | * ===========================================================
6 | * Copyright 2012 Twitter, Inc.
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | * ========================================================== */
20 |
21 | !function( $ ) {
22 |
23 | "use strict"
24 |
25 | /* TOOLTIP PUBLIC CLASS DEFINITION
26 | * =============================== */
27 |
28 | var Tooltip = function ( element, options ) {
29 | this.init('tooltip', element, options)
30 | }
31 |
32 | Tooltip.prototype = {
33 |
34 | constructor: Tooltip
35 |
36 | , init: function ( type, element, options ) {
37 | var eventIn
38 | , eventOut
39 |
40 | this.type = type
41 | this.$element = $(element)
42 | this.options = this.getOptions(options)
43 | this.enabled = true
44 |
45 | if (this.options.trigger != 'manual') {
46 | eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
47 | eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
48 | this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
49 | this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
50 | }
51 |
52 | this.options.selector ?
53 | (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
54 | this.fixTitle()
55 | }
56 |
57 | , getOptions: function ( options ) {
58 | options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
59 |
60 | if (options.delay && typeof options.delay == 'number') {
61 | options.delay = {
62 | show: options.delay
63 | , hide: options.delay
64 | }
65 | }
66 |
67 | return options
68 | }
69 |
70 | , enter: function ( e ) {
71 | var self = $(e.currentTarget)[this.type](this._options).data(this.type)
72 |
73 | if (!self.options.delay || !self.options.delay.show) {
74 | self.show()
75 | } else {
76 | self.hoverState = 'in'
77 | setTimeout(function() {
78 | if (self.hoverState == 'in') {
79 | self.show()
80 | }
81 | }, self.options.delay.show)
82 | }
83 | }
84 |
85 | , leave: function ( e ) {
86 | var self = $(e.currentTarget)[this.type](this._options).data(this.type)
87 |
88 | if (!self.options.delay || !self.options.delay.hide) {
89 | self.hide()
90 | } else {
91 | self.hoverState = 'out'
92 | setTimeout(function() {
93 | if (self.hoverState == 'out') {
94 | self.hide()
95 | }
96 | }, self.options.delay.hide)
97 | }
98 | }
99 |
100 | , show: function () {
101 | var $tip
102 | , inside
103 | , pos
104 | , actualWidth
105 | , actualHeight
106 | , placement
107 | , tp
108 |
109 | if (this.hasContent() && this.enabled) {
110 | $tip = this.tip()
111 | this.setContent()
112 |
113 | if (this.options.animation) {
114 | $tip.addClass('fade')
115 | }
116 |
117 | placement = typeof this.options.placement == 'function' ?
118 | this.options.placement.call(this, $tip[0], this.$element[0]) :
119 | this.options.placement
120 |
121 | inside = /in/.test(placement)
122 |
123 | $tip
124 | .remove()
125 | .css({ top: 0, left: 0, display: 'block' })
126 | .appendTo(inside ? this.$element : document.body)
127 |
128 | pos = this.getPosition(inside)
129 |
130 | actualWidth = $tip[0].offsetWidth
131 | actualHeight = $tip[0].offsetHeight
132 |
133 | switch (inside ? placement.split(' ')[1] : placement) {
134 | case 'bottom':
135 | tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
136 | break
137 | case 'top':
138 | tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
139 | break
140 | case 'left':
141 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
142 | break
143 | case 'right':
144 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
145 | break
146 | }
147 |
148 | $tip
149 | .css(tp)
150 | .addClass(placement)
151 | .addClass('in')
152 | }
153 | }
154 |
155 | , setContent: function () {
156 | var $tip = this.tip()
157 | $tip.find('.tooltip-inner').html(this.getTitle())
158 | $tip.removeClass('fade in top bottom left right')
159 | }
160 |
161 | , hide: function () {
162 | var that = this
163 | , $tip = this.tip()
164 |
165 | $tip.removeClass('in')
166 |
167 | function removeWithAnimation() {
168 | var timeout = setTimeout(function () {
169 | $tip.off($.support.transition.end).remove()
170 | }, 500)
171 |
172 | $tip.one($.support.transition.end, function () {
173 | clearTimeout(timeout)
174 | $tip.remove()
175 | })
176 | }
177 |
178 | $.support.transition && this.$tip.hasClass('fade') ?
179 | removeWithAnimation() :
180 | $tip.remove()
181 | }
182 |
183 | , fixTitle: function () {
184 | var $e = this.$element
185 | if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
186 | $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
187 | }
188 | }
189 |
190 | , hasContent: function () {
191 | return this.getTitle()
192 | }
193 |
194 | , getPosition: function (inside) {
195 | return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
196 | width: this.$element[0].offsetWidth
197 | , height: this.$element[0].offsetHeight
198 | })
199 | }
200 |
201 | , getTitle: function () {
202 | var title
203 | , $e = this.$element
204 | , o = this.options
205 |
206 | title = $e.attr('data-original-title')
207 | || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
208 |
209 | title = title.toString().replace(/(^\s*|\s*$)/, "")
210 |
211 | return title
212 | }
213 |
214 | , tip: function () {
215 | return this.$tip = this.$tip || $(this.options.template)
216 | }
217 |
218 | , validate: function () {
219 | if (!this.$element[0].parentNode) {
220 | this.hide()
221 | this.$element = null
222 | this.options = null
223 | }
224 | }
225 |
226 | , enable: function () {
227 | this.enabled = true
228 | }
229 |
230 | , disable: function () {
231 | this.enabled = false
232 | }
233 |
234 | , toggleEnabled: function () {
235 | this.enabled = !this.enabled
236 | }
237 |
238 | , toggle: function () {
239 | this[this.tip().hasClass('in') ? 'hide' : 'show']()
240 | }
241 |
242 | }
243 |
244 |
245 | /* TOOLTIP PLUGIN DEFINITION
246 | * ========================= */
247 |
248 | $.fn.tooltip = function ( option ) {
249 | return this.each(function () {
250 | var $this = $(this)
251 | , data = $this.data('tooltip')
252 | , options = typeof option == 'object' && option
253 | if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
254 | if (typeof option == 'string') data[option]()
255 | })
256 | }
257 |
258 | $.fn.tooltip.Constructor = Tooltip
259 |
260 | $.fn.tooltip.defaults = {
261 | animation: true
262 | , delay: 0
263 | , selector: false
264 | , placement: 'top'
265 | , trigger: 'hover'
266 | , title: ''
267 | , template: ''
268 | }
269 |
270 | }( window.jQuery )
271 |
--------------------------------------------------------------------------------
/views/appadmin.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 | {{if request.function=='index':}}
13 | {{=T("Available databases and tables")}}
14 | {{if not databases:}}{{=T("No databases in this application")}}{{pass}}
15 | {{for db in sorted(databases):}}
16 | {{for table in databases[db].tables:}}
17 | {{qry='%s.%s.id>0'%(db,table)}}
18 | {{tbl=databases[db][table]}}
19 | {{if hasattr(tbl,'_primarykey'):}}
20 | {{if tbl._primarykey:}}
21 | {{firstkey=tbl[tbl._primarykey[0]]}}
22 | {{if firstkey.type in ['string','text']:}}
23 | {{qry='%s.%s.%s!=""'%(db,table,firstkey.name)}}
24 | {{else:}}
25 | {{qry='%s.%s.%s>0'%(db,table,firstkey.name)}}
26 | {{pass}}
27 | {{else:}}
28 | {{qry=''}}
29 | {{pass}}
30 | {{pass}}
31 |
32 | {{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
33 |
34 | [ {{=A(str(T('insert new'))+' '+table,_href=URL('insert',args=[db,table]))}} ]
35 |
36 | {{pass}}
37 | {{pass}}
38 |
39 | {{elif request.function=='select':}}
40 | {{=XML(str(T("database %s select"))%A(request.args[0],_href=URL('index'))) }}
41 |
42 | {{if table:}}
43 | [ {{=A(str(T('insert new %s'))%table,_href=URL('insert',args=[request.args[0],table]))}} ]
44 | {{=T("Rows in table")}}
45 | {{else:}}
46 | {{=T("Rows selected")}}
47 | {{pass}}
48 | {{=form}}
49 | {{=T('The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.')}}
50 | {{=T('Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.')}}
51 | {{=T('"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN')}}
52 |
53 | {{=nrows}} {{=T("selected")}}
54 | {{if start>0:}}[ {{=A(T('previous 100 rows'),_href=URL('select',args=request.args[0],vars=dict(start=start-100)))}} ]{{pass}}
55 | {{if stop
58 | {{linkto=URL('update',args=request.args[0])}}
59 | {{upload=URL('download',args=request.args[0])}}
60 | {{=SQLTABLE(rows,linkto,upload,orderby=True,_class='sortable')}}
61 |
62 | {{pass}}
63 | {{=T("Import/Export")}}
64 | [ {{=T("export as csv file")}} ]
65 | {{if table:}}
66 | {{=FORM(str(T('or import from csv file'))+" ",INPUT(_type='file',_name='csvfile'),INPUT(_type='hidden',_value=table,_name='table'),INPUT(_type='submit',_value='import'))}}
67 | {{pass}}
68 |
69 |
70 | {{elif request.function=='insert':}}
71 | {{=T("database")}} {{=A(request.args[0],_href=URL('index'))}}
72 | {{if hasattr(table,'_primarykey'):}}
73 | {{fieldname=table._primarykey[0]}}
74 | {{dbname=request.args[0]}}
75 | {{tablename=request.args[1]}}
76 | {{cond = table[fieldname].type in ['string','text'] and '!=""' or '>0'}}
77 | {{=T("table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
78 | {{else:}}
79 | {{=T("table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
80 | {{pass}}
81 |
82 | {{=T("New Record")}}
83 | {{=form}}
84 | {{elif request.function=='update':}}
85 | {{=T("database")}} {{=A(request.args[0],_href=URL('index'))}}
86 | {{if hasattr(table,'_primarykey'):}}
87 | {{fieldname=request.vars.keys()[0]}}
88 | {{dbname=request.args[0]}}
89 | {{tablename=request.args[1]}}
90 | {{cond = table[fieldname].type in ['string','text'] and '!=""' or '>0'}}
91 | {{=T("table")}} {{=A(tablename,_href=URL('select',args=dbname,vars=dict(query='%s.%s.%s%s'%(dbname,tablename,fieldname,cond))))}}
92 | {{=T("record")}} {{=A('%s=%s'%request.vars.items()[0],_href=URL('update',args=request.args[:2],vars=request.vars))}}
93 | {{else:}}
94 | {{=T("table")}} {{=A(request.args[1],_href=URL('select',args=request.args[0],vars=dict(query='%s.%s.id>0'%tuple(request.args[:2]))))}}
95 | {{=T("record id")}} {{=A(request.args[2],_href=URL('update',args=request.args[:3]))}}
96 | {{pass}}
97 |
98 | {{=T("Edit current record")}} {{=form}}
99 |
100 | {{elif request.function=='state':}}
101 | {{=T("Internal State")}}
102 | {{=T("Current request")}}
103 | {{=BEAUTIFY(request)}}
104 | {{=T("Current response")}}
105 | {{=BEAUTIFY(response)}}
106 | {{=T("Current session")}}
107 | {{=BEAUTIFY(session)}}
108 |
109 |
110 | {{elif request.function == 'ccache':}}
111 | Cache
112 |
113 |
114 |
117 |
118 |
119 |
Overview
120 |
Number of entries: {{=total['entries']}}
121 | {{if total['entries'] > 0:}}
122 |
123 |
124 | Hit Ratio:
125 | {{=total['ratio']}}%
126 | ({{=total['hits']}} hits
127 | and {{=total['misses']}} misses)
128 |
129 |
130 | Size of cache:
131 | {{if object_stats:}}
132 | {{=total['objects']}} items,
133 | {{=total['bytes']}} bytes
134 | {{if total['bytes'] > 524287:}}
135 | ({{="%.0d" % (total['bytes'] / 1048576)}} MB )
136 | {{pass}}
137 | {{else:}} not available (requires the Python guppy library)
138 | {{pass}}
139 |
140 |
141 | Cache contains items up to
142 | {{="%02d" % total['oldest'][0]}} hours
143 | {{="%02d" % total['oldest'][1]}} minutes
144 | {{="%02d" % total['oldest'][2]}} seconds old.
145 |
146 | {{=BUTTON(T('Cache Keys'), _onclick='jQuery("#all_keys").toggle();')}}
147 |
148 | {{=total['keys']}}
149 |
150 |
151 | {{pass}}
152 |
153 |
RAM
154 |
Number of entries: {{=ram['entries']}}
155 | {{if ram['entries'] > 0:}}
156 |
157 | Hit Ratio:
158 | {{=ram['ratio']}}%
159 | ({{=ram['hits']}} hits
160 | and {{=ram['misses']}} misses)
161 |
162 |
163 | Size of cache:
164 | {{if object_stats:}}
165 | {{=ram['objects']}} items,
166 | {{=ram['bytes']}} bytes
167 | {{if ram['bytes'] > 524287:}}
168 | ({{=ram['bytes'] / 1048576}} MB )
169 | {{pass}}
170 | {{else:}} not available (requires the Python guppy library)
171 | {{pass}}
172 |
173 |
174 | RAM contains items up to
175 | {{="%02d" % ram['oldest'][0]}} hours
176 | {{="%02d" % ram['oldest'][1]}} minutes
177 | {{="%02d" % ram['oldest'][2]}} seconds old.
178 |
179 | {{=BUTTON(T('RAM Cache Keys'), _onclick='jQuery("#ram_keys").toggle();')}}
180 |
181 | {{=ram['keys']}}
182 |
183 |
184 | {{pass}}
185 |
186 |
DISK
187 |
Number of entries: {{=disk['entries']}}
188 | {{if disk['entries'] > 0:}}
189 |
190 |
191 | Hit Ratio:
192 | {{=disk['ratio']}}%
193 | ({{=disk['hits']}} hits
194 | and {{=disk['misses']}} misses)
195 |
196 |
197 | Size of cache:
198 | {{if object_stats:}}
199 | {{=disk['objects']}} items,
200 | {{=disk['bytes']}} bytes
201 | {{if disk['bytes'] > 524287:}}
202 | ({{=disk['bytes'] / 1048576}} MB )
203 | {{pass}}
204 | {{else:}} not available (requires the Python guppy library)
205 | {{pass}}
206 |
207 |
208 | DISK contains items up to
209 | {{="%02d" % disk['oldest'][0]}} hours
210 | {{="%02d" % disk['oldest'][1]}} minutes
211 | {{="%02d" % disk['oldest'][2]}} seconds old.
212 |
213 | {{=BUTTON(T('Disk Cache Keys'), _onclick='jQuery("#disk_keys").toggle();')}}
214 |
215 | {{=disk['keys']}}
216 |
217 |
218 | {{pass}}
219 |
220 |
221 |
224 |
225 |
226 |
227 | {{=form}}
228 |
229 |
230 |
231 |
232 | {{pass}}
233 |
--------------------------------------------------------------------------------
/static/css/bootstrap-responsive.css:
--------------------------------------------------------------------------------
1 | .hidden {
2 | display: none;
3 | visibility: hidden;
4 | }
5 | @media (max-width: 480px) {
6 | .nav-collapse {
7 | -webkit-transform: translate3d(0, 0, 0);
8 | }
9 | .page-header h1 small {
10 | display: block;
11 | line-height: 18px;
12 | }
13 | input[class*="span"],
14 | select[class*="span"],
15 | textarea[class*="span"],
16 | .uneditable-input {
17 | display: block;
18 | width: 100%;
19 | height: 28px;
20 | /* Make inputs at least the height of their button counterpart */
21 |
22 | /* Makes inputs behave like true block-level elements */
23 |
24 | -webkit-box-sizing: border-box;
25 | /* Older Webkit */
26 |
27 | -moz-box-sizing: border-box;
28 | /* Older FF */
29 |
30 | -ms-box-sizing: border-box;
31 | /* IE8 */
32 |
33 | box-sizing: border-box;
34 | /* CSS3 spec*/
35 |
36 | }
37 | .input-prepend input[class*="span"], .input-append input[class*="span"] {
38 | width: auto;
39 | }
40 | input[type="checkbox"], input[type="radio"] {
41 | border: 1px solid #ccc;
42 | }
43 | .form-horizontal .control-group > label {
44 | float: none;
45 | width: auto;
46 | padding-top: 0;
47 | text-align: left;
48 | }
49 | .form-horizontal .controls {
50 | margin-left: 0;
51 | }
52 | .form-horizontal .control-list {
53 | padding-top: 0;
54 | }
55 | .form-horizontal .form-actions {
56 | padding-left: 10px;
57 | padding-right: 10px;
58 | }
59 | .modal {
60 | position: absolute;
61 | top: 10px;
62 | left: 10px;
63 | right: 10px;
64 | width: auto;
65 | margin: 0;
66 | }
67 | .modal.fade.in {
68 | top: auto;
69 | }
70 | .modal-header .close {
71 | padding: 10px;
72 | margin: -10px;
73 | }
74 | .carousel-caption {
75 | position: static;
76 | }
77 | .register span { display:none; }
78 | .register input[type="text"], .register input[type="password"] { width:100%; }
79 | .nav-tabs, .nav-pills {padding-bottom: 3px;}
80 | .content div.span8, .content div.span3 {padding-top:10px;}
81 | }
82 | @media (max-width: 768px) {
83 | .container {
84 | width: auto;
85 | padding: 0 20px;
86 | }
87 | .row-fluid {
88 | width: 100%;
89 | }
90 | .row {
91 | margin-left: 0;
92 | }
93 | .row > [class*="span"], .row-fluid > [class*="span"] {
94 | float: none;
95 | display: block;
96 | width: auto;
97 | margin: 0;
98 | }
99 | div.navbar {margin-bottom:0px;}
100 | .stat , .rate, div.span3.sidebar, .share, .navbar .nav > li.addon,.span1.post-btn {display:none;}
101 | .article .row { padding-left: 0px;}
102 | .navbar .nav > li.cat-page {display:block;}
103 | .article .footing {font-size:13px;}
104 | h1 {font-size: 24px;}
105 | h1 small {font-size:14px;}
106 | .post_input #post_radio {width:90px;}
107 | .text.span11 { width:100%; }
108 | .sub_cmt .article, .cmt .article { padding-left:0px; }
109 | .row .span6.article {width:100%;}
110 | }
111 | @media (min-width: 768px) and (max-width: 980px) {
112 | .row {
113 | margin-left: -20px;
114 | *zoom: 1;
115 | }
116 | .row:before, .row:after {
117 | display: table;
118 | content: "";
119 | }
120 | .row:after {
121 | clear: both;
122 | }
123 | [class*="span"] {
124 | float: left;
125 | margin-left: 20px;
126 | }
127 | .span1 {
128 | width: 42px;
129 | }
130 | .span2 {
131 | width: 104px;
132 | }
133 | .span3 {
134 | width: 166px;
135 | }
136 | .span4 {
137 | width: 228px;
138 | }
139 | .span5 {
140 | width: 290px;
141 | }
142 | .span6 {
143 | width: 352px;
144 | }
145 | .span7 {
146 | width: 414px;
147 | }
148 | .span8 {
149 | width: 476px;
150 | }
151 | .span9 {
152 | width: 538px;
153 | }
154 | .span10 {
155 | width: 600px;
156 | }
157 | .span11, .container {
158 | width: 662px;
159 | }
160 | .span12{
161 | width: 724px;
162 | }
163 | .offset1 {
164 | margin-left: 82px;
165 | }
166 | .offset2 {
167 | margin-left: 144px;
168 | }
169 | .offset3 {
170 | margin-left: 206px;
171 | }
172 | .offset4 {
173 | margin-left: 268px;
174 | }
175 | .offset5 {
176 | margin-left: 330px;
177 | }
178 | .offset6 {
179 | margin-left: 392px;
180 | }
181 | .offset7 {
182 | margin-left: 454px;
183 | }
184 | .offset8 {
185 | margin-left: 516px;
186 | }
187 | .offset9 {
188 | margin-left: 578px;
189 | }
190 | .offset10 {
191 | margin-left: 640px;
192 | }
193 | .offset11 {
194 | margin-left: 702px;
195 | }
196 |
197 | input.span1, textarea.span1, .uneditable-input.span1 {
198 | width: 32px;
199 | }
200 | input.span2, textarea.span2, .uneditable-input.span2 {
201 | width: 94px;
202 | }
203 | input.span3, textarea.span3, .uneditable-input.span3 {
204 | width: 156px;
205 | }
206 | input.span4, textarea.span4, .uneditable-input.span4 {
207 | width: 218px;
208 | }
209 | input.span5, textarea.span5, .uneditable-input.span5 {
210 | width: 280px;
211 | }
212 | input.span6, textarea.span6, .uneditable-input.span6 {
213 | width: 342px;
214 | }
215 | input.span7, textarea.span7, .uneditable-input.span7 {
216 | width: 404px;
217 | }
218 | input.span8, textarea.span8, .uneditable-input.span8 {
219 | width: 466px;
220 | }
221 | input.span9, textarea.span9, .uneditable-input.span9 {
222 | width: 528px;
223 | }
224 | input.span10, textarea.span10, .uneditable-input.span10 {
225 | width: 590px;
226 | }
227 | input.span11, textarea.span11, .uneditable-input.span11 {
228 | width: 652px;
229 | }
230 | input.span12, textarea.span12, .uneditable-input.span12 {
231 | width: 714px;
232 | }
233 | }
234 | @media (max-width: 980px) {
235 | body {
236 | padding-top: 0;
237 | }
238 | .navbar-fixed-top {
239 | position: static;
240 | margin-bottom: 18px;
241 | }
242 | .navbar-fixed-top .navbar-inner {
243 | padding: 5px;
244 | }
245 | .navbar .container {
246 | width: auto;
247 | padding: 0;
248 | }
249 | .navbar .brand {
250 | padding-left: 10px;
251 | padding-right: 10px;
252 | margin: 0 0 0 -5px;
253 | }
254 | .navbar .nav-collapse {
255 | clear: left;
256 | }
257 | .navbar .nav {
258 | float: none;
259 | margin: 0 0 9px;
260 | }
261 | .navbar .nav > li {
262 | float: none;
263 | }
264 | .navbar .nav > li > a {
265 | margin-bottom: 2px;
266 | }
267 | .navbar .nav > .divider-vertical {
268 | display: none;
269 | }
270 | .navbar .nav > li > a, .navbar .dropdown-menu a {
271 | padding: 6px 15px;
272 | font-weight: bold;
273 | color: #999999;
274 | -webkit-border-radius: 3px;
275 | -moz-border-radius: 3px;
276 | border-radius: 3px;
277 | }
278 | .navbar .nav > li > a, .navbar .dropdown-menu a.google-btn {padding:0;color:#fff;}
279 | .navbar .dropdown-menu li + li a {
280 | margin-bottom: 2px;
281 | }
282 | .navbar .nav > li > a:hover, .navbar .dropdown-menu a:hover {
283 | background-color: #222222;
284 | }
285 | .navbar .dropdown-menu {
286 | position: static;
287 | top: auto;
288 | left: auto;
289 | float: none;
290 | display: block;
291 | max-width: none;
292 | margin: 0 15px;
293 | padding: 0;
294 | background-color: transparent;
295 | border: none;
296 | -webkit-border-radius: 0;
297 | -moz-border-radius: 0;
298 | border-radius: 0;
299 | -webkit-box-shadow: none;
300 | -moz-box-shadow: none;
301 | box-shadow: none;
302 | }
303 | .navbar .dropdown-menu:before, .navbar .dropdown-menu:after {
304 | display: none;
305 | }
306 | .navbar .dropdown-menu .divider {
307 | display: none;
308 | }
309 | .navbar-form, .navbar-search {
310 | float: none;
311 | padding: 9px 15px;
312 | margin: 9px 0;
313 | border-top: 1px solid #222222;
314 | border-bottom: 1px solid #222222;
315 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
316 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
317 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
318 | }
319 | .navbar .nav.pull-right {
320 | float: none;
321 | margin-left: 0;
322 | }
323 | .navbar-static .navbar-inner {
324 | padding-left: 10px;
325 | padding-right: 10px;
326 | }
327 | .btn-navbar {
328 | display: block;
329 | }
330 | .nav-collapse {
331 | overflow: hidden;
332 | height: 0;
333 | }
334 | .drop-login .login-btn { float:left; }
335 | .login-row .span2 { width:150px; }
336 | .login .span2 { width: 300px; height: 25px; }
337 | .login .login_remember {line-height: 28px;}
338 | .login #auth_user_remember { display: inline; }
339 | .span1.post-btn { margin-left: 0px; width: 60px;}
340 | .btn-group .btn { display:none; }
341 | .span6.article {width:414px;}
342 | }
343 | @media (min-width: 980px) {
344 | .nav-collapse.collapse {
345 | height: auto !important;
346 | }
347 | .login .span2 { width: 300px; height: 25px; }
348 | .login .login_remember {line-height: 28px;}
349 | .login #auth_user_remember { display: inline; }
350 | .login-row .span2 {width:160px;}
351 | .row.heading { padding-right:5px; }
352 | }
353 | @media (min-width: 1200px) {
354 | .row {
355 | margin-left: -30px;
356 | *zoom: 1;
357 | }
358 | .row:before, .row:after {
359 | display: table;
360 | content: "";
361 | }
362 | .row:after {
363 | clear: both;
364 | }
365 | [class*="span"] {
366 | float: left;
367 | margin-left: 30px;
368 | }
369 | .span1 {
370 | width: 70px;
371 | }
372 | .span2 {
373 | width: 170px;
374 | }
375 | .span3 {
376 | width: 270px;
377 | }
378 | .span4 {
379 | width: 370px;
380 | }
381 | .span5 {
382 | width: 470px;
383 | }
384 | .span6 {
385 | width: 570px;
386 | }
387 | .span7 {
388 | width: 670px;
389 | }
390 | .span8 {
391 | width: 770px;
392 | }
393 | .span9 {
394 | width: 870px;
395 | }
396 | .span10 {
397 | width: 970px;
398 | }
399 | .span11, .container {
400 | width: 1070px;
401 | }
402 | .span12 {
403 | width: 1170px;
404 | }
405 | .offset1 {
406 | margin-left: 130px;
407 | }
408 | .offset2 {
409 | margin-left: 230px;
410 | }
411 | .offset3 {
412 | margin-left: 330px;
413 | }
414 | .offset4 {
415 | margin-left: 430px;
416 | }
417 | .offset5 {
418 | margin-left: 530px;
419 | }
420 | .offset6 {
421 | margin-left: 630px;
422 | }
423 | .offset7 {
424 | margin-left: 730px;
425 | }
426 | .offset8 {
427 | margin-left: 830px;
428 | }
429 | .offset9 {
430 | margin-left: 930px;
431 | }
432 | .offset10 {
433 | margin-left: 1030px;
434 | }
435 | .offset11 {
436 | margin-left: 1130px;
437 | }
438 |
439 | input.span1, textarea.span1, .uneditable-input.span1 {
440 | width: 60px;
441 | }
442 | input.span2, textarea.span2, .uneditable-input.span2 {
443 | width: 160px;
444 | }
445 | input.span3, textarea.span3, .uneditable-input.span3 {
446 | width: 260px;
447 | }
448 | input.span4, textarea.span4, .uneditable-input.span4 {
449 | width: 360px;
450 | }
451 | input.span5, textarea.span5, .uneditable-input.span5 {
452 | width: 460px;
453 | }
454 | input.span6, textarea.span6, .uneditable-input.span6 {
455 | width: 560px;
456 | }
457 | input.span7, textarea.span7, .uneditable-input.span7 {
458 | width: 660px;
459 | }
460 | input.span8, textarea.span8, .uneditable-input.span8 {
461 | width: 760px;
462 | }
463 | input.span9, textarea.span9, .uneditable-input.span9 {
464 | width: 860px;
465 | }
466 | input.span10, textarea.span10, .uneditable-input.span10 {
467 | width: 960px;
468 | }
469 | input.span11, textarea.span11, .uneditable-input.span11 {
470 | width: 1060px;
471 | }
472 | input.span12, textarea.span12, .uneditable-input.span12 {
473 | width: 1160px;
474 | }
475 | .thumbnails {
476 | margin-left: -30px;
477 | }
478 | .thumbnails > li {
479 | margin-left: 30px;
480 | }
481 | .login .span2 { width: 300px; height: 25px; }
482 | .login .login_remember {line-height: 28px;}
483 | .login #auth_user_remember { display: inline; }
484 | }
485 |
--------------------------------------------------------------------------------
/controllers/appadmin.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # ##########################################################
4 | # ## make sure administrator is on localhost
5 | # ###########################################################
6 |
7 | import os
8 | import socket
9 | import datetime
10 | import copy
11 | import gluon.contenttype
12 | import gluon.fileutils
13 |
14 | # ## critical --- make a copy of the environment
15 |
16 | global_env = copy.copy(globals())
17 | global_env['datetime'] = datetime
18 |
19 | http_host = request.env.http_host.split(':')[0]
20 | remote_addr = request.env.remote_addr
21 | try:
22 | hosts = (http_host, socket.gethostname(),
23 | socket.gethostbyname(http_host),
24 | '::1','127.0.0.1','::ffff:127.0.0.1')
25 | except:
26 | hosts = (http_host, )
27 |
28 | if request.env.http_x_forwarded_for or request.env.wsgi_url_scheme\
29 | in ['https', 'HTTPS']:
30 | session.secure()
31 | elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"):
32 | raise HTTP(200, T('appadmin is disabled because insecure channel'))
33 |
34 | if (request.application=='admin' and not session.authorized) or \
35 | (request.application!='admin' and not gluon.fileutils.check_credentials(request)):
36 | redirect(URL('admin', 'default', 'index',
37 | vars=dict(send=URL(args=request.args,vars=request.vars))))
38 |
39 | ignore_rw = True
40 | response.view = 'appadmin.html'
41 | response.menu = [[T('design'), False, URL('admin', 'default', 'design',
42 | args=[request.application])], [T('db'), False,
43 | URL('index')], [T('state'), False,
44 | URL('state')], [T('cache'), False,
45 | URL('ccache')]]
46 |
47 | # ##########################################################
48 | # ## auxiliary functions
49 | # ###########################################################
50 |
51 |
52 | def get_databases(request):
53 | dbs = {}
54 | for (key, value) in global_env.items():
55 | cond = False
56 | try:
57 | cond = isinstance(value, GQLDB)
58 | except:
59 | cond = isinstance(value, SQLDB)
60 | if cond:
61 | dbs[key] = value
62 | return dbs
63 |
64 |
65 | databases = get_databases(None)
66 |
67 |
68 | def eval_in_global_env(text):
69 | exec ('_ret=%s' % text, {}, global_env)
70 | return global_env['_ret']
71 |
72 |
73 | def get_database(request):
74 | if request.args and request.args[0] in databases:
75 | return eval_in_global_env(request.args[0])
76 | else:
77 | session.flash = T('invalid request')
78 | redirect(URL('index'))
79 |
80 |
81 | def get_table(request):
82 | db = get_database(request)
83 | if len(request.args) > 1 and request.args[1] in db.tables:
84 | return (db, request.args[1])
85 | else:
86 | session.flash = T('invalid request')
87 | redirect(URL('index'))
88 |
89 |
90 | def get_query(request):
91 | try:
92 | return eval_in_global_env(request.vars.query)
93 | except Exception:
94 | return None
95 |
96 |
97 | def query_by_table_type(tablename,db,request=request):
98 | keyed = hasattr(db[tablename],'_primarykey')
99 | if keyed:
100 | firstkey = db[tablename][db[tablename]._primarykey[0]]
101 | cond = '>0'
102 | if firstkey.type in ['string', 'text']:
103 | cond = '!=""'
104 | qry = '%s.%s.%s%s' % (request.args[0], request.args[1], firstkey.name, cond)
105 | else:
106 | qry = '%s.%s.id>0' % tuple(request.args[:2])
107 | return qry
108 |
109 |
110 |
111 | # ##########################################################
112 | # ## list all databases and tables
113 | # ###########################################################
114 |
115 |
116 | def index():
117 | return dict(databases=databases)
118 |
119 |
120 | # ##########################################################
121 | # ## insert a new record
122 | # ###########################################################
123 |
124 |
125 | def insert():
126 | (db, table) = get_table(request)
127 | form = SQLFORM(db[table], ignore_rw=ignore_rw)
128 | if form.accepts(request.vars, session):
129 | response.flash = T('new record inserted')
130 | return dict(form=form,table=db[table])
131 |
132 |
133 | # ##########################################################
134 | # ## list all records in table and insert new record
135 | # ###########################################################
136 |
137 |
138 | def download():
139 | import os
140 | db = get_database(request)
141 | return response.download(request,db)
142 |
143 | def csv():
144 | import gluon.contenttype
145 | response.headers['Content-Type'] = \
146 | gluon.contenttype.contenttype('.csv')
147 | db = get_database(request)
148 | query = get_query(request)
149 | if not query:
150 | return None
151 | response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\
152 | % tuple(request.vars.query.split('.')[:2])
153 | return str(db(query,ignore_common_filters=True).select())
154 |
155 |
156 | def import_csv(table, file):
157 | table.import_from_csv_file(file)
158 |
159 | def select():
160 | import re
161 | db = get_database(request)
162 | dbname = request.args[0]
163 | regex = re.compile('(?P\w+)\.(?P\w+)=(?P\d+)')
164 | if len(request.args)>1 and hasattr(db[request.args[1]],'_primarykey'):
165 | regex = re.compile('(?P\w+)\.(?P\w+)=(?P.+)')
166 | if request.vars.query:
167 | match = regex.match(request.vars.query)
168 | if match:
169 | request.vars.query = '%s.%s.%s==%s' % (request.args[0],
170 | match.group('table'), match.group('field'),
171 | match.group('value'))
172 | else:
173 | request.vars.query = session.last_query
174 | query = get_query(request)
175 | if request.vars.start:
176 | start = int(request.vars.start)
177 | else:
178 | start = 0
179 | nrows = 0
180 | stop = start + 100
181 | table = None
182 | rows = []
183 | orderby = request.vars.orderby
184 | if orderby:
185 | orderby = dbname + '.' + orderby
186 | if orderby == session.last_orderby:
187 | if orderby[0] == '~':
188 | orderby = orderby[1:]
189 | else:
190 | orderby = '~' + orderby
191 | session.last_orderby = orderby
192 | session.last_query = request.vars.query
193 | form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
194 | _name='query', _value=request.vars.query or '',
195 | requires=IS_NOT_EMPTY(error_message=T("Cannot be empty")))), TR(T('Update:'),
196 | INPUT(_name='update_check', _type='checkbox',
197 | value=False), INPUT(_style='width:400px',
198 | _name='update_fields', _value=request.vars.update_fields
199 | or '')), TR(T('Delete:'), INPUT(_name='delete_check',
200 | _class='delete', _type='checkbox', value=False), ''),
201 | TR('', '', INPUT(_type='submit', _value='submit'))),
202 | _action=URL(r=request,args=request.args))
203 | if request.vars.csvfile != None:
204 | try:
205 | import_csv(db[request.vars.table],
206 | request.vars.csvfile.file)
207 | response.flash = T('data uploaded')
208 | except Exception, e:
209 | response.flash = DIV(T('unable to parse csv file'),PRE(str(e)))
210 | if form.accepts(request.vars, formname=None):
211 | # regex = re.compile(request.args[0] + '\.(?P\w+)\.id\>0')
212 | regex = re.compile(request.args[0] + '\.(?P\w+)\..+')
213 |
214 | match = regex.match(form.vars.query.strip())
215 | if match:
216 | table = match.group('table')
217 | try:
218 | nrows = db(query).count()
219 | if form.vars.update_check and form.vars.update_fields:
220 | db(query).update(**eval_in_global_env('dict(%s)'
221 | % form.vars.update_fields))
222 | response.flash = T('%s rows updated', nrows)
223 | elif form.vars.delete_check:
224 | db(query).delete()
225 | response.flash = T('%s rows deleted', nrows)
226 | nrows = db(query).count()
227 | if orderby:
228 | rows = db(query,ignore_common_filters=True).select(limitby=(start, stop), orderby=eval_in_global_env(orderby))
229 | else:
230 | rows = db(query,ignore_common_filters=True).select(limitby=(start, stop))
231 | except Exception, e:
232 | (rows, nrows) = ([], 0)
233 | response.flash = DIV(T('Invalid Query'),PRE(str(e)))
234 | return dict(
235 | form=form,
236 | table=table,
237 | start=start,
238 | stop=stop,
239 | nrows=nrows,
240 | rows=rows,
241 | query=request.vars.query,
242 | )
243 |
244 |
245 | # ##########################################################
246 | # ## edit delete one record
247 | # ###########################################################
248 |
249 |
250 | def update():
251 | (db, table) = get_table(request)
252 | keyed = hasattr(db[table],'_primarykey')
253 | record = None
254 | if keyed:
255 | key = [f for f in request.vars if f in db[table]._primarykey]
256 | if key:
257 | record = db(db[table][key[0]] == request.vars[key[0]], ignore_common_filters=True).select().first()
258 | else:
259 | record = db(db[table].id == request.args(2),ignore_common_filters=True).select().first()
260 |
261 | if not record:
262 | qry = query_by_table_type(table, db)
263 | session.flash = T('record does not exist')
264 | redirect(URL('select', args=request.args[:1],
265 | vars=dict(query=qry)))
266 |
267 | if keyed:
268 | for k in db[table]._primarykey:
269 | db[table][k].writable=False
270 |
271 | form = SQLFORM(db[table], record, deletable=True, delete_label=T('Check to delete'),
272 | ignore_rw=ignore_rw and not keyed,
273 | linkto=URL('select',
274 | args=request.args[:1]), upload=URL(r=request,
275 | f='download', args=request.args[:1]))
276 |
277 | if form.accepts(request.vars, session):
278 | session.flash = T('done!')
279 | qry = query_by_table_type(table, db)
280 | redirect(URL('select', args=request.args[:1],
281 | vars=dict(query=qry)))
282 | return dict(form=form,table=db[table])
283 |
284 |
285 | # ##########################################################
286 | # ## get global variables
287 | # ###########################################################
288 |
289 |
290 | def state():
291 | return dict()
292 |
293 | def ccache():
294 | form = FORM(
295 | P(TAG.BUTTON("Clear CACHE?", _type="submit", _name="yes", _value="yes")),
296 | P(TAG.BUTTON("Clear RAM", _type="submit", _name="ram", _value="ram")),
297 | P(TAG.BUTTON("Clear DISK", _type="submit", _name="disk", _value="disk")),
298 | )
299 |
300 | if form.accepts(request.vars, session):
301 | clear_ram = False
302 | clear_disk = False
303 | session.flash = ""
304 | if request.vars.yes:
305 | clear_ram = clear_disk = True
306 | if request.vars.ram:
307 | clear_ram = True
308 | if request.vars.disk:
309 | clear_disk = True
310 |
311 | if clear_ram:
312 | cache.ram.clear()
313 | session.flash += "Ram Cleared "
314 | if clear_disk:
315 | cache.disk.clear()
316 | session.flash += "Disk Cleared"
317 |
318 | redirect(URL(r=request))
319 |
320 | try:
321 | from guppy import hpy; hp=hpy()
322 | except ImportError:
323 | hp = False
324 |
325 | import shelve, os, copy, time, math
326 | from gluon import portalocker
327 |
328 | ram = {
329 | 'entries': 0,
330 | 'bytes': 0,
331 | 'objects': 0,
332 | 'hits': 0,
333 | 'misses': 0,
334 | 'ratio': 0,
335 | 'oldest': time.time(),
336 | 'keys': []
337 | }
338 | disk = copy.copy(ram)
339 | total = copy.copy(ram)
340 | disk['keys'] = []
341 | total['keys'] = []
342 |
343 | def GetInHMS(seconds):
344 | hours = math.floor(seconds / 3600)
345 | seconds -= hours * 3600
346 | minutes = math.floor(seconds / 60)
347 | seconds -= minutes * 60
348 | seconds = math.floor(seconds)
349 |
350 | return (hours, minutes, seconds)
351 |
352 | for key, value in cache.ram.storage.items():
353 | if isinstance(value, dict):
354 | ram['hits'] = value['hit_total'] - value['misses']
355 | ram['misses'] = value['misses']
356 | try:
357 | ram['ratio'] = ram['hits'] * 100 / value['hit_total']
358 | except (KeyError, ZeroDivisionError):
359 | ram['ratio'] = 0
360 | else:
361 | if hp:
362 | ram['bytes'] += hp.iso(value[1]).size
363 | ram['objects'] += hp.iso(value[1]).count
364 | ram['entries'] += 1
365 | if value[0] < ram['oldest']:
366 | ram['oldest'] = value[0]
367 | ram['keys'].append((key, GetInHMS(time.time() - value[0])))
368 |
369 | locker = open(os.path.join(request.folder,
370 | 'cache/cache.lock'), 'a')
371 | portalocker.lock(locker, portalocker.LOCK_EX)
372 | disk_storage = shelve.open(os.path.join(request.folder, 'cache/cache.shelve'))
373 | try:
374 | for key, value in disk_storage.items():
375 | if isinstance(value, dict):
376 | disk['hits'] = value['hit_total'] - value['misses']
377 | disk['misses'] = value['misses']
378 | try:
379 | disk['ratio'] = disk['hits'] * 100 / value['hit_total']
380 | except (KeyError, ZeroDivisionError):
381 | disk['ratio'] = 0
382 | else:
383 | if hp:
384 | disk['bytes'] += hp.iso(value[1]).size
385 | disk['objects'] += hp.iso(value[1]).count
386 | disk['entries'] += 1
387 | if value[0] < disk['oldest']:
388 | disk['oldest'] = value[0]
389 | disk['keys'].append((key, GetInHMS(time.time() - value[0])))
390 |
391 | finally:
392 | portalocker.unlock(locker)
393 | locker.close()
394 | disk_storage.close()
395 |
396 | total['entries'] = ram['entries'] + disk['entries']
397 | total['bytes'] = ram['bytes'] + disk['bytes']
398 | total['objects'] = ram['objects'] + disk['objects']
399 | total['hits'] = ram['hits'] + disk['hits']
400 | total['misses'] = ram['misses'] + disk['misses']
401 | total['keys'] = ram['keys'] + disk['keys']
402 | try:
403 | total['ratio'] = total['hits'] * 100 / (total['hits'] + total['misses'])
404 | except (KeyError, ZeroDivisionError):
405 | total['ratio'] = 0
406 |
407 | if disk['oldest'] < ram['oldest']:
408 | total['oldest'] = disk['oldest']
409 | else:
410 | total['oldest'] = ram['oldest']
411 |
412 | ram['oldest'] = GetInHMS(time.time() - ram['oldest'])
413 | disk['oldest'] = GetInHMS(time.time() - disk['oldest'])
414 | total['oldest'] = GetInHMS(time.time() - total['oldest'])
415 |
416 | def key_table(keys):
417 | return TABLE(
418 | TR(TD(B('Key')), TD(B('Time in Cache (h:m:s)'))),
419 | *[TR(TD(k[0]), TD('%02d:%02d:%02d' % k[1])) for k in keys],
420 | **dict(_class='cache-keys',
421 | _style="border-collapse: separate; border-spacing: .5em;"))
422 |
423 | ram['keys'] = key_table(ram['keys'])
424 | disk['keys'] = key_table(disk['keys'])
425 | total['keys'] = key_table(total['keys'])
426 |
427 | return dict(form=form, total=total,
428 | ram=ram, disk=disk, object_stats=hp != False)
429 |
430 |
431 |
432 |
--------------------------------------------------------------------------------
/controllers/default.py:
--------------------------------------------------------------------------------
1 | from urlparse import urlparse
2 | import time
3 | now=time.time()
4 |
5 | def author_func():
6 | login_form=auth.login(next=request.env.http_referer)
7 | login_form.element('input[name=email]')['_class']='span2'
8 | login_form.element('input[name=email]')['_placeholder']='email'
9 | login_form.element('input[name=password]')['_class']='span2'
10 | login_form.element('input[name=password]')['_placeholder']='password'
11 | login_form.element('input[type=submit]')['_class']='btn'
12 | login_form.element('input[type=submit]')['_value']='로그인'
13 | session.old_referer = request.env.http_referer
14 | auth.settings.login_form=login_form
15 | return login_form
16 |
17 | def register():
18 | if auth.user_id:
19 | redirect(URL(r=request,f='index'))
20 | login_form = author_func()
21 | form = auth.register()
22 | form.element('input[name=alias]')['_class']='span3'
23 | form.element('input[name=alias]')['_placeholder']='닉네임'
24 | form.element('input[name=email]')['_class']='span3'
25 | form.element('input[name=email]')['_placeholder']='이메일'
26 | form.element('input[name=password]')['_class']='span3'
27 | form.element('input[name=password]')['_placeholder']='비밀번호'
28 | form.element('input[name=password_two]')['_class']='span3'
29 | form.element('input[name=password_two]')['_placeholder']='비밀번호 확인'
30 | form.element('input[type=submit]')['_class']='btn'
31 | form.element('input[type=submit]')['_value']='회원가입'
32 | return dict(login_form=login_form,form=form)
33 |
34 | def profile():
35 | if not auth.user_id:
36 | redirect(URL(r=request,f='login'))
37 | form=auth.profile()
38 | form.element('input[name=alias]')['_class']='span3'
39 | form.element('input[name=email]')['_class']='span3'
40 | form.element('input[type=submit]')['_class']='btn'
41 | form.element('input[type=submit]')['_value']='저장'
42 | return dict(form=form)
43 |
44 | #built-in user function
45 | def user():
46 | auth.settings.login_form=GaeGoogleAccount()
47 | return dict(form=auth.login(next=URL(r=request, c='default', f='index')))
48 |
49 |
50 | def login():
51 | if auth.user_id:
52 | redirect(session.old_referer)
53 | cat_list=[[r.alias,r.name] for r in db().select(db.category.ALL)]
54 | login_form=author_func()
55 | return dict(cat_list=cat_list,login_form=login_form)
56 |
57 |
58 | def logout():
59 | return auth.logout(next=request.env.http_referer)
60 |
61 | def index():
62 | sorts={
63 | 'hot':~db.news.hotness,
64 | 'score':~db.news.score,
65 | 'new':~db.news.post_time}
66 | try: page=int(request.args[2])
67 | except: page=0
68 | try: sort=request.args[1]
69 | except: sort='new'
70 | orderby=sorts[sort]
71 | limitby=(25*page,25*(page+1)+1)
72 | try:
73 | cat=request.args[0]
74 | if sort == 'hot' or sort == 'score':#orderby 넣어야함
75 | news=db((db.news.category==cat) & (db.news.post_time>(now-2592000.0))).select(cache=(cache.ram,1800))
76 | if sort=='hot': news=news.sort(lambda row: -row.hotness)[limitby[0]:limitby[1]]
77 | if sort=='score': news=news.sort(lambda row:~row.score)[limitby[0]:limitby[1]]
78 | else:
79 | news=db(db.news.category==cat).select(cache=(cache.ram,1800),orderby=orderby,limitby=limitby) #no cache
80 | #news=news.sort(lambda row: -row.post_time)[limitby[0]:limitby[1]]
81 | category=db(db.category.name==cat).select(cache=(cache.ram,1800))[0]
82 | alias=category.alias
83 | dec=category.description
84 | except:
85 | cat="all"
86 | alias='모든 이야기'
87 | dec='이것저것 인기순으로 모아놓습니다'
88 |
89 | if sort == 'hot' or sort == 'score':
90 | news=db(db.news.post_time>(now-2592000.0)).select(cache=(cache.ram,1800))
91 | if sort=='hot': news=news.sort(lambda row: -row.hotness)[limitby[0]:limitby[1]]
92 | if sort=='score': news=news.sort(lambda row:~row.score)[limitby[0]:limitby[1]]
93 | else:
94 | news=db().select(db.news.ALL,cache=(cache.ram,1800),orderby=orderby,limitby=limitby)
95 | #news=news.sort(lambda row: -row.post_time)[limitby[0]:limitby[1]]
96 |
97 | db.person.email.requires=IS_NOT_EMPTY()
98 | form = author_func()
99 | cat_list=[[r.alias,r.name] for r in db().select(db.category.ALL,cache=(cache.ram,1800))]
100 |
101 | return response.render(dict(login_form=form,cat_list=cat_list,category=cat,alias=alias,news=news,dec=dec,sort=sort,page=page))
102 |
103 | def bookmark():
104 | try: item=db(db.news.id==request.args[0]).select()[0]
105 | except: redirect(URL(r=request,f='index'))
106 | item.update_record(clicks=item.clicks+1,score=item.score,comments=item.comments,post_time=item.post_time)
107 | redirect(item.url)
108 |
109 | def post():
110 | if not auth.user_id:
111 | redirect(URL(r=request,f='login'))
112 | form=SQLFORM(db.news,fields=['url','title','category'])
113 | form.vars.author=auth.user_id
114 | form.vars.author_alias=auth.user.alias
115 | form.element('input[name=url]')['_class']='span11'
116 | form.element('input[name=url]')['_placeholder']='URL 주소 (입력하지 않으면 스스로를 가르키는 링크)'
117 | form.element('input[name=title]')['_class']='span11'
118 | form.element('input[name=title]')['_placeholder']='링크 설명을 입력하세요.'
119 | form.element('input[type=submit]')['_class']='btn large'
120 | form.element('input[type=submit]')['_value']='올리기'
121 | if request.vars.category:
122 | form.vars.category_alias=db(db.category.name==request.vars.category).select()[0].alias
123 | if not request.vars.url:
124 | request.vars.url='self'
125 | login_form = author_func()
126 | if form.accepts(request.vars,session):
127 | session.flash='news posted'
128 | if not form.vars.category:
129 | redirect(URL(r=request,f='mylink'))
130 | else:
131 | redirect(URL(r=request,f='index',args=[form.vars.category,'new']))
132 | cat_list=[[r.alias,r.name] for r in db().select(db.category.ALL)]
133 | return dict(login_form=login_form,form=form,cat_list=cat_list)
134 |
135 | def report():
136 | try:
137 | db(db.news.id==request.args[0]).update(flagged=True)
138 | session.flash='thanks for your feedback'
139 | except:
140 | session.flash='internal error'
141 | redirect(request.env.http_referer)
142 |
143 | def delete():
144 | if not auth.user_id: redirect(request.env.http_referer)
145 | try:
146 | news=db(db.news.id==request.args[0]).select()[0]
147 | if news.author==auth.user_id:
148 | db(db.news.id==request.args[0]).delete()
149 | session.flash='news item deleted'
150 | except:
151 | session.flash='internal error'
152 | redirect(URL(r=request,f='index',args=[news.category]))
153 |
154 | def vote():
155 | if not auth.user_id: redirect(request.env.http_referer)
156 | news=db(db.news.id==request.args[1]).select()[0]
157 | try:
158 | vote = db((db.vote.voter==auth.user_id) & (db.vote.parentv==news)).select()[0]
159 | except:
160 | vote = db.vote.insert(parentv=news,voter=auth.user_id)
161 | if request.args[0]=='up':
162 | if vote.choice == -1:
163 | vote.update_record(choice=1)
164 | news.update_record(score=news.score+2)
165 | elif vote.choice == 0:
166 | vote.update_record(choice=1)
167 | news.update_record(score=news.score+1)
168 | elif vote.choice == 1:
169 | vote.update_record(choice=0)
170 | news.update_record(score=news.score-1)
171 | elif request.args[0]=='down':
172 | if vote.choice == 1:
173 | vote.update_record(choice=-1)
174 | news.update_record(score=news.score-2)
175 | elif vote.choice == 0:
176 | vote.update_record(choice=-1)
177 | news.update_record(score=news.score-1)
178 | elif vote.choice == -1:
179 | vote.update_record(choice=0)
180 | news.update_record(score=news.score+1)
181 | # redirect(request.env.http_referer)
182 | return str(news.score)
183 |
184 | def permalink():
185 | try: comment=db(db.comment.id==request.args[0]).select()[0]
186 | except: redirect(request.env.http_referer)
187 | comments=db(db.comment.news==comment.news).select(orderby=db.comment.score)
188 | news=comment.news
189 | items=[]
190 | tree={}
191 | forms={}
192 | pivot=None
193 | for c in comments:
194 | if not tree.has_key(c.parente): tree[c.parente]=[c]
195 | else: tree[c.parente].append(c)
196 | if c.id==comment.id: pivot=c.parente
197 | if auth.user_id:
198 | f=SQLFORM(db.comment,fields=['body'],labels={'body':''})
199 | f.vars.author=auth.user_id
200 | f.vars.author_alias=auth.user.alias
201 | f.vars.news=news
202 | f.vars.parente=c.id
203 | if f.accepts(request.vars,formname=str(c.id)):
204 | session.flash='comment posted'
205 | redirect(URL(r=request,args=request.args))
206 | forms[c.id]=f
207 | tree[pivot]=[comment]
208 | login_form = author_func()
209 | response.view='default/comments.html'
210 | cat_list=[[r.alias,r.name] for r in db().select(db.category.ALL)]
211 | return dict(cat_list=cat_list,item=None,login_form=login_form,form=None,tree=tree,forms=forms,parent=pivot)
212 |
213 | def vote_comment():
214 | if not auth.user_id: redirect(request.env.http_referer)
215 | comment=db(db.comment.id==request.args[1]).select()[0]
216 | try:
217 | vote = db((db.vote_cmt.voter==auth.user_id) & (db.vote_cmt.parentv==comment)).select()[0]
218 | except:
219 | vote = db.vote_cmt.insert(parentv=comment,voter=auth.user_id)
220 | if request.args[0]=='up':
221 | if vote.choice == -1:
222 | vote.update_record(choice=1)
223 | comment.update_record(score=comment.score+2)
224 | elif vote.choice == 0:
225 | vote.update_record(choice=1)
226 | comment.update_record(score=comment.score+1)
227 | elif vote.choice == 1:
228 | vote.update_record(choice=0)
229 | comment.update_record(score=comment.score-1)
230 | elif request.args[0]=='down':
231 | if vote.choice == 1:
232 | vote.update_record(choice=-1)
233 | comment.update_record(score=comment.score-2)
234 | elif vote.choice == 0:
235 | vote.update_record(choice=-1)
236 | comment.update_record(score=comment.score-1)
237 | elif vote.choice == -1:
238 | vote.update_record(choice=0)
239 | comment.update_record(score=comment.score+1)
240 | return str(comment.score)
241 |
242 | def report_comment():
243 | try:
244 | db(db.comment.id==request.args[0]).update(flagged=True)
245 | session.flash='thanks for your feedback'
246 | except:
247 | session.flash='internal error'
248 | redirect(request.env.http_referer)
249 |
250 | def person():
251 | session.flash='sorry, not yet implemented'
252 | redirect(request.env.http_referer)
253 |
254 |
255 | def comments():
256 | try: news=int(request.args[0])
257 | except: redirect(URL(r=request,f='index'))
258 | try:
259 | item=db(db.news.id==news).select()[0]
260 | comments=db(db.comment.news==news).select(orderby=~db.comment.score)
261 | except: redirect(URL(r=request,f='index'))
262 | if auth.user_id:
263 | form=SQLFORM(db.comment,fields=['body'],labels={'body':''})
264 | form.element('textarea[name=body]')['_class']='text span7'
265 | form.element('textarea[name=body]')['_cols']='70'
266 | form.element('textarea[name=body]')['_rows']='4'
267 | form.element('textarea[name=body]')['_placeholder']='코멘트 남기기...'
268 | form.element('input[type=submit]')['_class']='btn'
269 | form.element('input[type=submit]')['_value']='올리기'
270 | form.vars.author=auth.user_id
271 | form.vars.author_alias=auth.user.alias
272 | form.vars.news=news
273 | if form.accepts(request.vars,formname='0'):
274 | item.update_record(comments=item.comments+1)
275 | response.flash='comment posted'
276 | redirect(URL(r=request,args=request.args))
277 | else: form=None
278 | items=[]
279 | tree={}
280 | forms={}
281 | for c in comments:
282 | if not tree.has_key(c.parente): tree[c.parente]=[c]
283 | else: tree[c.parente].append(c)
284 | if auth.user_id:
285 | f=SQLFORM(db.comment,fields=['body'],labels={'body':''})
286 | f.element('textarea[name=body]')['_class']='text sub_cmt'
287 | f.element('textarea[name=body]')['_cols']='70'
288 | f.element('textarea[name=body]')['_rows']='4'
289 | f.element('textarea[name=body]')['_placeholder']='코멘트 남기기...'
290 | f.element('input[type=submit]')['_class']='btn'
291 | f.element('input[type=submit]')['_value']='올리기'
292 | f.vars.author=auth.user_id
293 | f.vars.author_alias=auth.user.alias
294 | f.vars.news=news
295 | f.vars.parente=c.id
296 | if f.accepts(request.vars,formname=str(c.id)):
297 | item.update_record(comments=item.comments+1)
298 | session.flash='comment posted'
299 | redirect(URL(r=request,args=request.args))
300 | forms[c.id]=f
301 | login_form = author_func()
302 | cat_list=[[r.alias,r.name] for r in db().select(db.category.ALL)]
303 | return dict(cat_list=cat_list,item=item,login_form=login_form,form=form,tree=tree,forms=forms,parent=0)
304 |
305 | def edit_comment():
306 | if not auth.user_id: redirect(request.env.http_referer)
307 | id=request.args[0]
308 | try:
309 | comment=db(db.comment.id==id).select()[0]
310 | if not comment.author==auth.user_id: raise Exception
311 | except: redirect(URL(r=request,f='index'))
312 | form=SQLFORM(db.comment,comment,fields=['body'],showid=False,deletable=True,labels={'body':'Comment'})
313 | form.element('textarea[name=body]')['_class']='text span11'
314 | form.element('textarea[name=body]')['_cols']='70'
315 | form.element('textarea[name=body]')['_rows']='4'
316 | form.element('input[type=submit]')['_class']='btn'
317 | form.element('input[type=submit]')['_value']='올리기'
318 | if form.accepts(request.vars,session):
319 | session.flash='comment edited'
320 | redirect(URL(r=request,f='comments',args=[comment.news]))
321 | login_form = author_func()
322 | cat_list=[[r.alias,r.name] for r in db().select(db.category.ALL)]
323 | return dict(cat_list=cat_list,login_form=login_form,form=form)
324 |
325 | def about():
326 | return dict(login_form=author_func())
327 |
328 | def uplink():
329 | try: page=int(request.args[1])
330 | except: page=0
331 | limitby=(50*page,50*(page+1)+1)
332 | news=db((db.vote.voter==auth.user_id) & (db.vote.choice==1)).select(limitby=limitby)
333 | form = author_func()
334 | return dict(login_form=form,news=news,page=page)
335 |
336 | def downlink():
337 | try: page=int(request.args[1])
338 | except: page=0
339 | limitby=(50*page,50*(page+1)+1)
340 | news=db((db.vote.voter==auth.user_id) & (db.vote.choice==-1)).select(limitby=limitby)
341 | form = author_func()
342 | return dict(login_form=form,news=news,page=page)
343 |
344 | def mylink():
345 | try: page=int(request.args[1])
346 | except: page=0
347 | limitby=(50*page,50*(page+1)+1)
348 | news=db(db.news.author==auth.user_id).select(orderby=~db.news.post_time,limitby=limitby)
349 | form = author_func()
350 | return dict(login_form=form,news=news,page=page)
351 | def error():
352 | return dict(login_form=author_func())
353 | def reset_password():
354 | form=auth.request_reset_password()
355 | form.element('input[name=email]')['_class']='span5'
356 | form.element('input[name=email]')['_placeholder']='email'
357 | form.element('input[type=submit]')['_class']='btn'
358 | form.element('input[type=submit]')['_value']='패스워드 리셋요청'
359 | return dict(login_form=author_func(),form=form)
360 |
361 | def new_password():
362 | form=auth.reset_password()
363 | form.element('input[name=new_password]')['_class']='span3'
364 | form.element('input[name=new_password]')['_placeholder']='새 비밀번호'
365 | form.element('input[name=new_password2]')['_class']='span3'
366 | form.element('input[name=new_password2]')['_placeholder']='새 비밀번호 확인'
367 | form.element('input[type=submit]')['_class']='btn'
368 | form.element('input[type=submit]')['_value']='비밀번호 재설정'
369 | return dict(login_form=author_func(),form=form)
370 | ### todo:
371 | """
372 | allow different types of news sorting
373 | """
374 |
375 | ## test article upload
376 | def testukn():
377 | if auth.user_id:
378 | for i in range(150):
379 | db.news.insert(category='movie',author=auth.user_id,author_alias=auth.user.alias,url="http://plan9.kr",title="테스트로 입력하는 글제목")
380 | redirect(request.env.http_referer)
381 |
382 | def post_chrome():
383 | if not auth.user_id:
384 | redirect(URL(r=request,f='login_chrome'))
385 | form=SQLFORM(db.news,fields=['url','title','category'])
386 | form.vars.author=auth.user_id
387 | form.vars.author_alias=auth.user.alias
388 | form.element('input[name=url]')['_class']='span6'
389 | form.element('input[name=url]')['_placeholder']='URL 주소 (입력하지 않으면 스스로를 가르키는 링크)'
390 | #form.element('input[name=url]')['_value']='http://'+request.args[1]
391 | form.element('input[name=url]')['_value']=request.vars.url
392 | form.element('input[name=title]')['_class']='span6'
393 | form.element('input[name=title]')['_placeholder']='링크 설명을 입력하세요.'
394 | #form.element('input[name=title]')['_value']=request.args[0]
395 | form.element('input[name=title]')['_value']=request.vars.title
396 | form.element('input[type=submit]')['_class']='btn large'
397 | form.element('input[type=submit]')['_value']='올리기'
398 | form.element('input[type=submit]')['_action']=URL(r=request,args=request.args)
399 | if request.vars.category:
400 | form.vars.category_alias=db(db.category.name==request.vars.category).select()[0].alias
401 | if not request.vars.url:
402 | request.vars.url='self'
403 | login_form = author_func()
404 | if form.accepts(request,session):
405 | return "올라간듯"+' '+"업로드 페이지로 이동"+" "
406 | cat_list=[[r.alias,r.name] for r in db().select(db.category.ALL)]
407 | return dict(form=form,cat_list=cat_list)
408 |
409 | def login_chrome():
410 | if auth.user_id:
411 | redirect(URL(r=request,f='post_chrome'))
412 | cat_list=[[r.alias,r.name] for r in db().select(db.category.ALL)]
413 | login_form=auth.login(next=URL(r=request,f='post_chrome'))
414 | login_form.element('form')['_name']='ext'
415 | login_form.element('input[name=email]')['_class']='span3'
416 | login_form.element('input[name=email]')['_placeholder']='email'
417 | login_form.element('input[name=password]')['_class']='span3'
418 | login_form.element('input[name=password]')['_placeholder']='password'
419 | login_form.element('input[type=submit]')['_class']='btn'
420 | login_form.element('input[type=submit]')['_value']='로그인'
421 | return dict(cat_list=cat_list,login_form=login_form)
422 |
423 | def category():
424 | cat_list=[[r.alias,r.name] for r in db().select(db.category.ALL)]
425 | form = author_func()
426 | return dict(cat_list=cat_list,login_form=form)
427 |
--------------------------------------------------------------------------------
/static/css/main.css:
--------------------------------------------------------------------------------
1 | html,body{margin:0;padding:0;}
2 | h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,cite,code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var,dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;font-weight:normal;font-style:normal;font-size:100%;line-height:1;font-family:inherit;}
3 | table{border-collapse:collapse;border-spacing:0;}
4 | ol,ul{list-style:none;}
5 | q:before,q:after,blockquote:before,blockquote:after{content:"";}
6 | html{overflow-y:scroll;font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
7 | a:focus{outline:thin dotted;}
8 | a:hover,a:active{outline:0;}
9 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
10 | audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
11 | audio:not([controls]){display:none;}
12 | sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}
13 | sup{top:-0.5em;}
14 | sub{bottom:-0.25em;}
15 | img{border:0;-ms-interpolation-mode:bicubic;}
16 | button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;}
17 | button,input{line-height:normal;*overflow:visible;}
18 | button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
19 | button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
20 | input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
21 | input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}
22 | textarea{overflow:auto;vertical-align:top;}
23 | body{background-color:#ffffff;margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:18px;color:#404040;}
24 | .container{width:940px;margin-left:auto;margin-right:auto;zoom:1;}.container:before,.container:after{display:table;content:"";zoom:1;}
25 | .container:after{clear:both;}
26 | .container-fluid{position:relative;min-width:940px;padding-left:20px;padding-right:20px;zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";zoom:1;}
27 | .container-fluid:after{clear:both;}
28 | .container-fluid>.sidebar{position:absolute;top:0;left:20px;width:220px;}
29 | .container-fluid>.content{margin-left:240px;}
30 | a{color:#0069d6;text-decoration:none;line-height:inherit;font-weight:inherit;}a:hover{color:#00438a;text-decoration:underline;}
31 | .pull-right{float:right;}
32 | .pull-left{float:left;}
33 | .hide{display:none;}
34 | .show{display:block;}
35 | .row{zoom:1;margin-left:-20px;}.row:before,.row:after{display:table;content:"";zoom:1;}
36 | .row:after{clear:both;}
37 | .row>[class*="span"]{display:inline;float:left;margin-left:20px;}
38 | .span1{width:40px;}
39 | .span2{width:100px;}
40 | .span3{width:160px;}
41 | .span4{width:220px;}
42 | .span5{width:280px;}
43 | .span6{width:340px;}
44 | .span7{width:400px;}
45 | .span8{width:460px;}
46 | .span9{width:520px;}
47 | .span10{width:580px;}
48 | .span11{width:640px;}
49 | .span12{width:700px;}
50 | .span13{width:760px;}
51 | .span14{width:820px;}
52 | .span15{width:880px;}
53 | .span16{width:940px;}
54 | .span17{width:1000px;}
55 | .span18{width:1060px;}
56 | .span19{width:1120px;}
57 | .span20{width:1180px;}
58 | .span21{width:1240px;}
59 | .span22{width:1300px;}
60 | .span23{width:1360px;}
61 | .span24{width:1420px;}
62 | .row>.offset1{margin-left:80px;}
63 | .row>.offset2{margin-left:140px;}
64 | .row>.offset3{margin-left:200px;}
65 | .row>.offset4{margin-left:260px;}
66 | .row>.offset5{margin-left:320px;}
67 | .row>.offset6{margin-left:380px;}
68 | .row>.offset7{margin-left:440px;}
69 | .row>.offset8{margin-left:500px;}
70 | .row>.offset9{margin-left:560px;}
71 | .row>.offset10{margin-left:620px;}
72 | .row>.offset11{margin-left:680px;}
73 | .row>.offset12{margin-left:740px;}
74 | .span-one-third{width:300px;}
75 | .span-two-thirds{width:620px;}
76 | .row>.offset-one-third{margin-left:340px;}
77 | .row>.offset-two-thirds{margin-left:660px;}
78 | p{font-size:13px;font-weight:normal;line-height:18px;margin-bottom:9px;}p small{font-size:11px;color:#bfbfbf;}
79 | h1,h2,h3,h4,h5,h6{font-weight:bold;color:#404040;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:#bfbfbf;}
80 | h1{margin-bottom:18px;font-size:30px;line-height:36px;}h1 small{font-size:18px;}
81 | h2{font-size:24px;line-height:36px;}h2 small{font-size:14px;}
82 | h3,h4,h5,h6{line-height:36px;}
83 | h3{font-size:18px;}h3 small{font-size:14px;}
84 | h4{font-size:16px;}h4 small{font-size:12px;}
85 | h5{font-size:14px;}
86 | h6{font-size:13px;color:#bfbfbf;text-transform:uppercase;}
87 | ul,ol{margin:0 0 18px 25px;}
88 | ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
89 | ul{list-style:disc;}
90 | ol{list-style:decimal;}
91 | li{line-height:18px;color:#808080;}
92 | ul.unstyled{list-style:none;margin-left:0;}
93 | dl{margin-bottom:18px;}dl dt,dl dd{line-height:18px;}
94 | dl dt{font-weight:bold;}
95 | dl dd{margin-left:9px;}
96 | hr{margin:20px 0 19px;border:0;border-bottom:1px solid #eee;}
97 | strong{font-style:inherit;font-weight:bold;}
98 | em{font-style:italic;font-weight:inherit;line-height:inherit;}
99 | .muted{color:#bfbfbf;}
100 | blockquote{margin-bottom:18px;border-left:5px solid #eee;padding-left:15px;}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;}
101 | blockquote small{display:block;font-size:12px;font-weight:300;line-height:18px;color:#bfbfbf;}blockquote small:before{content:'\2014 \00A0';}
102 | address{display:block;line-height:18px;margin-bottom:18px;}
103 | code,pre{padding:0 3px 2px;font-family:Monaco, Andale Mono, Courier New, monospace;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
104 | code{background-color:#fee9cc;color:rgba(0, 0, 0, 0.75);padding:1px 3px;}
105 | pre{background-color:#f5f5f5;display:block;padding:8.5px;margin:0 0 18px;line-height:18px;font-size:12px;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;white-space:pre;white-space:pre-wrap;word-wrap:break-word;}
106 | form{margin-bottom:18px;}
107 | fieldset{margin-bottom:18px;padding-top:18px;}fieldset legend{display:block;padding-left:150px;font-size:19.5px;line-height:1;color:#404040;*padding:0 0 5px 145px;*line-height:1.5;}
108 | form .clearfix{margin-bottom:18px;zoom:1;}form .clearfix:before,form .clearfix:after{display:table;content:"";zoom:1;}
109 | form .clearfix:after{clear:both;}
110 | label,input,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:normal;}
111 | label{padding-top:6px;font-size:13px;line-height:18px;float:left;width:130px;text-align:right;color:#404040;}
112 | form .input{margin-left:150px;}
113 | input[type=checkbox],input[type=radio]{cursor:pointer;}
114 | input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;font-size:13px;line-height:18px;color:#808080;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
115 | select{padding:initial;}
116 | input[type=checkbox],input[type=radio]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;border:none;}
117 | input[type=file]{background-color:#ffffff;padding:initial;border:initial;line-height:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
118 | input[type=button],input[type=reset],input[type=submit]{width:auto;height:auto;}
119 | select,input[type=file]{height:27px;*height:auto;line-height:27px;*margin-top:4px;}
120 | select[multiple]{height:inherit;background-color:#ffffff;}
121 | textarea{height:auto;}
122 | .uneditable-input{background-color:#ffffff;display:block;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
123 | :-moz-placeholder{color:#bfbfbf;}
124 | ::-webkit-input-placeholder{color:#bfbfbf;}
125 | input,textarea{-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);}
126 | input:focus,textarea:focus{outline:0;border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);}
127 | input[type=file]:focus,input[type=checkbox]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:1px dotted #666;}
128 | form .clearfix.error>label,form .clearfix.error .help-block,form .clearfix.error .help-inline{color:#b94a48;}
129 | form .clearfix.error input,form .clearfix.error textarea{color:#b94a48;border-color:#ee5f5b;}form .clearfix.error input:focus,form .clearfix.error textarea:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
130 | form .clearfix.error .input-prepend .add-on,form .clearfix.error .input-append .add-on{color:#b94a48;background-color:#fce6e6;border-color:#b94a48;}
131 | form .clearfix.warning>label,form .clearfix.warning .help-block,form .clearfix.warning .help-inline{color:#c09853;}
132 | form .clearfix.warning input,form .clearfix.warning textarea{color:#c09853;border-color:#ccae64;}form .clearfix.warning input:focus,form .clearfix.warning textarea:focus{border-color:#be9a3f;-webkit-box-shadow:0 0 6px #e5d6b1;-moz-box-shadow:0 0 6px #e5d6b1;box-shadow:0 0 6px #e5d6b1;}
133 | form .clearfix.warning .input-prepend .add-on,form .clearfix.warning .input-append .add-on{color:#c09853;background-color:#d2b877;border-color:#c09853;}
134 | form .clearfix.success>label,form .clearfix.success .help-block,form .clearfix.success .help-inline{color:#468847;}
135 | form .clearfix.success input,form .clearfix.success textarea{color:#468847;border-color:#57a957;}form .clearfix.success input:focus,form .clearfix.success textarea:focus{border-color:#458845;-webkit-box-shadow:0 0 6px #9acc9a;-moz-box-shadow:0 0 6px #9acc9a;box-shadow:0 0 6px #9acc9a;}
136 | form .clearfix.success .input-prepend .add-on,form .clearfix.success .input-append .add-on{color:#468847;background-color:#bcddbc;border-color:#468847;}
137 | .input-mini,input.mini,textarea.mini,select.mini{width:60px;}
138 | .input-small,input.small,textarea.small,select.small{width:90px;}
139 | .input-medium,input.medium,textarea.medium,select.medium{width:150px;}
140 | .input-large,input.large,textarea.large,select.large{width:210px;}
141 | .input-xlarge,input.xlarge,textarea.xlarge,select.xlarge{width:270px;}
142 | .input-xxlarge,input.xxlarge,textarea.xxlarge,select.xxlarge{width:530px;}
143 | textarea.xxlarge{overflow-y:auto;}
144 | input.span1,textarea.span1{display:inline-block;float:none;width:30px;margin-left:0;}
145 | input.span2,textarea.span2{display:inline-block;float:none;width:90px;margin-left:0;}
146 | input.span3,textarea.span3{display:inline-block;float:none;width:150px;margin-left:0;}
147 | input.span4,textarea.span4{display:inline-block;float:none;width:210px;margin-left:0;}
148 | input.span5,textarea.span5{display:inline-block;float:none;width:270px;margin-left:0;}
149 | input.span6,textarea.span6{display:inline-block;float:none;width:330px;margin-left:0;}
150 | input.span7,textarea.span7{display:inline-block;float:none;width:390px;margin-left:0;}
151 | input.span8,textarea.span8{display:inline-block;float:none;width:450px;margin-left:0;}
152 | input.span9,textarea.span9{display:inline-block;float:none;width:510px;margin-left:0;}
153 | input.span10,textarea.span10{display:inline-block;float:none;width:570px;margin-left:0;}
154 | input.span11,textarea.span11{display:inline-block;float:none;width:630px;margin-left:0;}
155 | input.span12,textarea.span12{display:inline-block;float:none;width:690px;margin-left:0;}
156 | input.span13,textarea.span13{display:inline-block;float:none;width:750px;margin-left:0;}
157 | input.span14,textarea.span14{display:inline-block;float:none;width:810px;margin-left:0;}
158 | input.span15,textarea.span15{display:inline-block;float:none;width:870px;margin-left:0;}
159 | input.span16,textarea.span16{display:inline-block;float:none;width:930px;margin-left:0;}
160 | input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#f5f5f5;border-color:#ddd;cursor:not-allowed;}
161 | .actions{background:#f5f5f5;margin-top:18px;margin-bottom:18px;padding:17px 20px 18px 150px;border-top:1px solid #ddd;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;}.actions .secondary-action{float:right;}.actions .secondary-action a{line-height:30px;}.actions .secondary-action a:hover{text-decoration:underline;}
162 | .help-inline,.help-block{font-size:13px;line-height:18px;color:#bfbfbf;}
163 | .help-inline{padding-left:5px;*position:relative;*top:-5px;}
164 | .help-block{display:block;max-width:600px;}
165 | .inline-inputs{color:#808080;}.inline-inputs span{padding:0 2px 0 1px;}
166 | .input-prepend input,.input-append input{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
167 | .input-prepend .add-on,.input-append .add-on{position:relative;background:#f5f5f5;border:1px solid #ccc;z-index:2;float:left;display:block;width:auto;min-width:16px;height:18px;padding:4px 4px 4px 5px;margin-right:-1px;font-weight:normal;line-height:18px;color:#bfbfbf;text-align:center;text-shadow:0 1px 0 #ffffff;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
168 | .input-prepend .active,.input-append .active{background:#a9dba9;border-color:#46a546;}
169 | .input-prepend .add-on{*margin-top:1px;}
170 | .input-append input{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
171 | .input-append .add-on{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;margin-right:0;margin-left:-1px;}
172 | .inputs-list{margin:0 0 5px;width:100%;}.inputs-list li{display:block;padding:0;width:100%;}
173 | .inputs-list label{display:block;float:none;width:auto;padding:0;margin-left:20px;line-height:18px;text-align:left;white-space:normal;}.inputs-list label strong{color:#808080;}
174 | .inputs-list label small{font-size:11px;font-weight:normal;}
175 | .inputs-list .inputs-list{margin-left:25px;margin-bottom:10px;padding-top:0;}
176 | .inputs-list:first-child{padding-top:6px;}
177 | .inputs-list li+li{padding-top:2px;}
178 | .inputs-list input[type=radio],.inputs-list input[type=checkbox]{margin-bottom:0;margin-left:-20px;float:left;}
179 | .form-stacked{padding-left:20px;}.form-stacked fieldset{padding-top:9px;}
180 | .form-stacked legend{padding-left:0;}
181 | .form-stacked label{display:block;float:none;width:auto;font-weight:bold;text-align:left;line-height:20px;padding-top:0;}
182 | .form-stacked .clearfix{margin-bottom:9px;}.form-stacked .clearfix div.input{margin-left:0;}
183 | .form-stacked .inputs-list{margin-bottom:0;}.form-stacked .inputs-list li{padding-top:0;}.form-stacked .inputs-list li label{font-weight:normal;padding-top:0;}
184 | .form-stacked div.clearfix.error{padding-top:10px;padding-bottom:10px;padding-left:10px;margin-top:0;margin-left:-10px;}
185 | .form-stacked .actions{margin-left:-20px;padding-left:20px;}
186 | table{width:100%;margin-bottom:18px;padding:0;font-size:13px;border-collapse:collapse;}table th,table td{padding:10px 10px 9px;line-height:18px;text-align:left;}
187 | table th{padding-top:9px;font-weight:bold;vertical-align:middle;}
188 | table td{vertical-align:top;border-top:1px solid #ddd;}
189 | table tbody th{border-top:1px solid #ddd;vertical-align:top;}
190 | .condensed-table th,.condensed-table td{padding:5px 5px 4px;}
191 | .bordered-table{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.bordered-table th+th,.bordered-table td+td,.bordered-table th+td{border-left:1px solid #ddd;}
192 | .bordered-table thead tr:first-child th:first-child,.bordered-table tbody tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
193 | .bordered-table thead tr:first-child th:last-child,.bordered-table tbody tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
194 | .bordered-table tbody tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
195 | .bordered-table tbody tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
196 | table .span1{width:20px;}
197 | table .span2{width:60px;}
198 | table .span3{width:100px;}
199 | table .span4{width:140px;}
200 | table .span5{width:180px;}
201 | table .span6{width:220px;}
202 | table .span7{width:260px;}
203 | table .span8{width:300px;}
204 | table .span9{width:340px;}
205 | table .span10{width:380px;}
206 | table .span11{width:420px;}
207 | table .span12{width:460px;}
208 | table .span13{width:500px;}
209 | table .span14{width:540px;}
210 | table .span15{width:580px;}
211 | table .span16{width:620px;}
212 | .zebra-striped tbody tr:nth-child(odd) td,.zebra-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
213 | .zebra-striped tbody tr:hover td,.zebra-striped tbody tr:hover th{background-color:#f5f5f5;}
214 | table .header{cursor:pointer;}table .header:after{content:"";float:right;margin-top:7px;border-width:0 4px 4px;border-style:solid;border-color:#000 transparent;visibility:hidden;}
215 | table .headerSortUp,table .headerSortDown{background-color:rgba(141, 192, 219, 0.25);text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);}
216 | table .header:hover:after{visibility:visible;}
217 | table .headerSortDown:after,table .headerSortDown:hover:after{visibility:visible;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;}
218 | table .headerSortUp:after{border-bottom:none;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000;visibility:visible;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;}
219 | table .blue{color:#049cdb;border-bottom-color:#049cdb;}
220 | table .headerSortUp.blue,table .headerSortDown.blue{background-color:#ade6fe;}
221 | table .green{color:#46a546;border-bottom-color:#46a546;}
222 | table .headerSortUp.green,table .headerSortDown.green{background-color:#cdeacd;}
223 | table .red{color:#9d261d;border-bottom-color:#9d261d;}
224 | table .headerSortUp.red,table .headerSortDown.red{background-color:#f4c8c5;}
225 | table .yellow{color:#ffc40d;border-bottom-color:#ffc40d;}
226 | table .headerSortUp.yellow,table .headerSortDown.yellow{background-color:#fff6d9;}
227 | table .orange{color:#f89406;border-bottom-color:#f89406;}
228 | table .headerSortUp.orange,table .headerSortDown.orange{background-color:#fee9cc;}
229 | table .purple{color:#7a43b6;border-bottom-color:#7a43b6;}
230 | table .headerSortUp.purple,table .headerSortDown.purple{background-color:#e2d5f0;}
231 | .topbar{height:40px;position:fixed;top:0;left:0;right:0;z-index:10000;overflow:visible;}.topbar a{color:#bfbfbf;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
232 | .topbar h3 a:hover,.topbar .brand:hover,.topbar ul .active>a{background-color:#333;background-color:rgba(255, 255, 255, 0.05);color:#ffffff;text-decoration:none;}
233 | .topbar h3{position:relative;}
234 | .topbar h3 a,.topbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;color:#ffffff;font-size:20px;font-weight:200;line-height:1;}
235 | .topbar p{margin:0;line-height:40px;}.topbar p a:hover{background-color:transparent;color:#ffffff;}
236 | .topbar form{float:left;margin:5px 0 0 0;position:relative;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
237 | .topbar form.pull-right{float:right;}
238 | .topbar input{background-color:#444;background-color:rgba(255, 255, 255, 0.3);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:normal;font-weight:13px;line-height:1;padding:4px 9px;color:#ffffff;color:rgba(255, 255, 255, 0.75);border:1px solid #111;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.topbar input:-moz-placeholder{color:#e6e6e6;}
239 | .topbar input::-webkit-input-placeholder{color:#e6e6e6;}
240 | .topbar input:hover{background-color:#bfbfbf;background-color:rgba(255, 255, 255, 0.5);color:#ffffff;}
241 | .topbar input:focus,.topbar input.focused{outline:0;background-color:#ffffff;color:#404040;text-shadow:0 1px 0 #ffffff;border:0;padding:5px 10px;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);}
242 | .topbar-inner,.topbar .fill{background-color:#222;background-color:#222222;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222));background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
243 | .topbar div>ul,.nav{display:block;float:left;margin:0 10px 0 0;position:relative;left:0;}.topbar div>ul>li,.nav>li{display:block;float:left;}
244 | .topbar div>ul a,.nav a{display:block;float:none;padding:10px 10px 11px;line-height:19px;text-decoration:none;}.topbar div>ul a:hover,.nav a:hover{color:#ffffff;text-decoration:none;}
245 | .topbar div>ul .active>a,.nav .active>a{background-color:#222;background-color:rgba(0, 0, 0, 0.5);}
246 | .topbar div>ul.secondary-nav,.nav.secondary-nav{float:right;margin-left:10px;margin-right:0;}.topbar div>ul.secondary-nav .menu-dropdown,.nav.secondary-nav .menu-dropdown,.topbar div>ul.secondary-nav .dropdown-menu,.nav.secondary-nav .dropdown-menu{right:0;border:0;}
247 | .topbar div>ul a.menu:hover,.nav a.menu:hover,.topbar div>ul li.open .menu,.nav li.open .menu,.topbar div>ul .dropdown-toggle:hover,.nav .dropdown-toggle:hover,.topbar div>ul .dropdown.open .dropdown-toggle,.nav .dropdown.open .dropdown-toggle{background:#444;background:rgba(255, 255, 255, 0.05);}
248 | .topbar div>ul .menu-dropdown,.nav .menu-dropdown,.topbar div>ul .dropdown-menu,.nav .dropdown-menu{background-color:#333;}.topbar div>ul .menu-dropdown a.menu,.nav .menu-dropdown a.menu,.topbar div>ul .dropdown-menu a.menu,.nav .dropdown-menu a.menu,.topbar div>ul .menu-dropdown .dropdown-toggle,.nav .menu-dropdown .dropdown-toggle,.topbar div>ul .dropdown-menu .dropdown-toggle,.nav .dropdown-menu .dropdown-toggle{color:#ffffff;}.topbar div>ul .menu-dropdown a.menu.open,.nav .menu-dropdown a.menu.open,.topbar div>ul .dropdown-menu a.menu.open,.nav .dropdown-menu a.menu.open,.topbar div>ul .menu-dropdown .dropdown-toggle.open,.nav .menu-dropdown .dropdown-toggle.open,.topbar div>ul .dropdown-menu .dropdown-toggle.open,.nav .dropdown-menu .dropdown-toggle.open{background:#444;background:rgba(255, 255, 255, 0.05);}
249 | .topbar div>ul .menu-dropdown li a,.nav .menu-dropdown li a,.topbar div>ul .dropdown-menu li a,.nav .dropdown-menu li a{color:#999;text-shadow:0 1px 0 rgba(0, 0, 0, 0.5);}.topbar div>ul .menu-dropdown li a:hover,.nav .menu-dropdown li a:hover,.topbar div>ul .dropdown-menu li a:hover,.nav .dropdown-menu li a:hover{background-color:#191919;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#292929), to(#191919));background-image:-moz-linear-gradient(top, #292929, #191919);background-image:-ms-linear-gradient(top, #292929, #191919);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #292929), color-stop(100%, #191919));background-image:-webkit-linear-gradient(top, #292929, #191919);background-image:-o-linear-gradient(top, #292929, #191919);background-image:linear-gradient(top, #292929, #191919);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#292929', endColorstr='#191919', GradientType=0);color:#ffffff;}
250 | .topbar div>ul .menu-dropdown .active a,.nav .menu-dropdown .active a,.topbar div>ul .dropdown-menu .active a,.nav .dropdown-menu .active a{color:#ffffff;}
251 | .topbar div>ul .menu-dropdown .divider,.nav .menu-dropdown .divider,.topbar div>ul .dropdown-menu .divider,.nav .dropdown-menu .divider{background-color:#222;border-color:#444;}
252 | .topbar ul .menu-dropdown li a,.topbar ul .dropdown-menu li a, .topbar ul .menu-dropdown form {padding:4px 15px;}
253 | li.menu,.dropdown{position:relative;}
254 | a.menu:after,.dropdown-toggle:after{width:0;height:0;display:inline-block;content:"↓";text-indent:-99999px;vertical-align:top;margin-top:8px;margin-left:4px;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #ffffff;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
255 | .menu-dropdown,.dropdown-menu{background-color:#ffffff;float:left;display:none;position:absolute;top:40px;z-index:900;min-width:160px;max-width:220px;_width:160px;margin-left:0;margin-right:0;padding:6px 0;zoom:1;border-color:#999;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:0 1px 1px;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.menu-dropdown li,.dropdown-menu li{float:none;display:block;background-color:none;}
256 | .menu-dropdown .divider,.dropdown-menu .divider{height:1px;margin:5px 0;overflow:hidden;background-color:#eee;border-bottom:1px solid #ffffff;}
257 | .topbar .dropdown-menu a,.dropdown-menu a{display:block;padding:4px 15px;clear:both;font-weight:normal;line-height:18px;color:#808080;text-shadow:0 1px 0 #ffffff;}.topbar .dropdown-menu a:hover,.dropdown-menu a:hover,.topbar .dropdown-menu a.hover,.dropdown-menu a.hover{background-color:#dddddd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#eeeeee), to(#dddddd));background-image:-moz-linear-gradient(top, #eeeeee, #dddddd);background-image:-ms-linear-gradient(top, #eeeeee, #dddddd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #dddddd));background-image:-webkit-linear-gradient(top, #eeeeee, #dddddd);background-image:-o-linear-gradient(top, #eeeeee, #dddddd);background-image:linear-gradient(top, #eeeeee, #dddddd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0);color:#404040;text-decoration:none;-webkit-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);}
258 | .open .menu,.dropdown.open .menu,.open .dropdown-toggle,.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);}
259 | .open .menu-dropdown,.dropdown.open .menu-dropdown,.open .dropdown-menu,.dropdown.open .dropdown-menu{display:block;}
260 | .tabs,.pills{margin:0 0 18px;padding:0;list-style:none;zoom:1;}.tabs:before,.pills:before,.tabs:after,.pills:after{display:table;content:"";zoom:1;}
261 | .tabs:after,.pills:after{clear:both;}
262 | .tabs>li,.pills>li{float:left;}.tabs>li>a,.pills>li>a{display:block;}
263 | .tabs{border-color:#ddd;border-style:solid;border-width:0 0 1px;}.tabs>li{position:relative;margin-bottom:-1px;}.tabs>li>a{padding:0 15px;margin-right:2px;line-height:34px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.tabs>li>a:hover{text-decoration:none;background-color:#eee;border-color:#eee #eee #ddd;}
264 | .tabs .active>a,.tabs .active>a:hover{color:#808080;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
265 | .tabs .menu-dropdown,.tabs .dropdown-menu{top:35px;border-width:1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
266 | .tabs a.menu:after,.tabs .dropdown-toggle:after{border-top-color:#999;margin-top:15px;margin-left:5px;}
267 | .tabs li.open.menu .menu,.tabs .open.dropdown .dropdown-toggle{border-color:#999;}
268 | .tabs li.open a.menu:after,.tabs .dropdown.open .dropdown-toggle:after{border-top-color:#555;}
269 | .pills a{margin:5px 3px 5px 0;padding:0 15px;line-height:30px;text-shadow:0 1px 1px #ffffff;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}.pills a:hover{color:#ffffff;text-decoration:none;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#00438a;}
270 | .pills .active a{color:#ffffff;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#0069d6;}
271 | .pills-vertical>li{float:none;}
272 | .tab-content>.tab-pane,.pill-content>.pill-pane,.tab-content>div,.pill-content>div{display:none;}
273 | .tab-content>.active,.pill-content>.active{display:block;}
274 | .breadcrumb{padding:7px 14px;margin:0 0 18px;background-color:#f5f5f5;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5));background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline;text-shadow:0 1px 0 #ffffff;}
275 | .breadcrumb .divider{padding:0 5px;color:#bfbfbf;}
276 | .breadcrumb .active a{color:#404040;}
277 | .hero-unit{background-color:#f5f5f5;margin-bottom:30px;padding:60px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;}
278 | .hero-unit p{font-size:18px;font-weight:200;line-height:27px;}
279 | footer{margin-top:17px;padding-top:17px;border-top:1px solid #eee;}
280 | .page-header{margin-bottom:17px;border-bottom:1px solid #ddd;-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}.page-header h1{margin-bottom:8px;}
281 | .btn.danger,.alert-message.danger,.btn.danger:hover,.alert-message.danger:hover,.btn.error,.alert-message.error,.btn.error:hover,.alert-message.error:hover,.btn.success,.alert-message.success,.btn.success:hover,.alert-message.success:hover,.btn.info,.alert-message.info,.btn.info:hover,.alert-message.info:hover{color:#ffffff;}
282 | .btn .close,.alert-message .close{font-family:Arial,sans-serif;line-height:18px;}
283 | .btn.danger,.alert-message.danger,.btn.error,.alert-message.error{background-color:#c43c35;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#c43c35 #c43c35 #882a25;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
284 | .btn.success,.alert-message.success{background-color:#57a957;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#57a957 #57a957 #3d773d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
285 | .btn.info,.alert-message.info{background-color:#339bb9;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#339bb9 #339bb9 #22697d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
286 | .btn, .topbar input.btn {cursor:pointer;display:inline-block;background-color:#e6e6e6;background-repeat:no-repeat;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);padding:5px 14px 6px;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);color:#333;font-size:13px;line-height:normal;border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-webkit-transition:0.1s linear all;-moz-transition:0.1s linear all;-ms-transition:0.1s linear all;-o-transition:0.1s linear all;transition:0.1s linear all;}.btn:hover{background-position:0 -15px;color:#333;text-decoration:none;}
287 | .btn:focus{outline:1px dotted #666;}
288 | .btn.primary{color:#ffffff;background-color:#0064cd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));background-image:-moz-linear-gradient(top, #049cdb, #0064cd);background-image:-ms-linear-gradient(top, #049cdb, #0064cd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));background-image:-webkit-linear-gradient(top, #049cdb, #0064cd);background-image:-o-linear-gradient(top, #049cdb, #0064cd);background-image:linear-gradient(top, #049cdb, #0064cd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#0064cd #0064cd #003f81;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
289 | .btn.active,.btn:active{-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);}
290 | .btn.disabled{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
291 | .btn[disabled]{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
292 | .btn.large{font-size:15px;line-height:normal;padding:9px 14px 9px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
293 | .btn.small{padding:7px 9px 7px;font-size:11px;}
294 | :root .alert-message,:root .btn{border-radius:0 \0;}
295 | button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0;}
296 | .close{float:right;color:#000000;font-size:20px;font-weight:bold;line-height:13.5px;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=25);-khtml-opacity:0.25;-moz-opacity:0.25;opacity:0.25;}.close:hover{color:#000000;text-decoration:none;filter:alpha(opacity=40);-khtml-opacity:0.4;-moz-opacity:0.4;opacity:0.4;}
297 | .alert-message{position:relative;padding:7px 15px;margin-bottom:18px;color:#404040;background-color:#eedc94;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94));background-image:-moz-linear-gradient(top, #fceec1, #eedc94);background-image:-ms-linear-gradient(top, #fceec1, #eedc94);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94));background-image:-webkit-linear-gradient(top, #fceec1, #eedc94);background-image:-o-linear-gradient(top, #fceec1, #eedc94);background-image:linear-gradient(top, #fceec1, #eedc94);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#eedc94 #eedc94 #e4c652;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);border-width:1px;border-style:solid;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);}.alert-message .close{margin-top:1px;*margin-top:0;}
298 | .alert-message a{font-weight:bold;color:#404040;}
299 | .alert-message.danger p a,.alert-message.error p a,.alert-message.success p a,.alert-message.info p a{color:#ffffff;}
300 | .alert-message h5{line-height:18px;}
301 | .alert-message p{margin-bottom:0;}
302 | .alert-message div{margin-top:5px;margin-bottom:2px;line-height:28px;}
303 | .alert-message .btn{-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);}
304 | .alert-message.block-message{background-image:none;background-color:#fdf5d9;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);padding:14px;border-color:#fceec1;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.alert-message.block-message ul,.alert-message.block-message p{margin-right:30px;}
305 | .alert-message.block-message ul{margin-bottom:0;}
306 | .alert-message.block-message li{color:#404040;}
307 | .alert-message.block-message .alert-actions{margin-top:5px;}
308 | .alert-message.block-message.error,.alert-message.block-message.success,.alert-message.block-message.info{color:#404040;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
309 | .alert-message.block-message.error{background-color:#fddfde;border-color:#fbc7c6;}
310 | .alert-message.block-message.success{background-color:#d1eed1;border-color:#bfe7bf;}
311 | .alert-message.block-message.info{background-color:#ddf4fb;border-color:#c6edf9;}
312 | .alert-message.block-message.danger p a,.alert-message.block-message.error p a,.alert-message.block-message.success p a,.alert-message.block-message.info p a{color:#404040;}
313 | .pagination{height:36px;margin:18px 0;}.pagination ul{float:left;margin:0;border:1px solid #ddd;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
314 | .pagination li{display:inline;}
315 | .pagination a{float:left;padding:0 14px;line-height:34px;border-right:1px solid;border-right-color:#ddd;border-right-color:rgba(0, 0, 0, 0.15);*border-right-color:#ddd;text-decoration:none;}
316 | .pagination a:hover,.pagination .active a{background-color:#c7eefe;}
317 | .pagination .disabled a,.pagination .disabled a:hover{background-color:transparent;color:#bfbfbf;}
318 | .pagination .next a{border:0;}
319 | .well{background-color:#f5f5f5;margin-bottom:20px;padding:19px;min-height:20px;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
320 | .modal-backdrop{background-color:#000000;position:fixed;top:0;left:0;right:0;bottom:0;z-index:10000;}.modal-backdrop.fade{opacity:0;}
321 | .modal-backdrop,.modal-backdrop.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}
322 | .modal{position:fixed;top:50%;left:50%;z-index:11000;width:550px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal .close{margin-top:7px;}
323 | .modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
324 | .modal.fade.in{top:50%;}
325 | .modal-header{border-bottom:1px solid #eee;padding:5px 15px;}
326 | .modal-body{padding:15px;}
327 | .modal-body form{margin-bottom:0;}
328 | .modal-footer{ font-size:30px;text-align:right;background-color:#f5f5f5;padding:14px 15px 15px;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;zoom:1;margin-bottom:0;}.modal-footer:before,.modal-footer:after{display:table;content:"";zoom:1;}
329 | .modal-footer:after{clear:both;}
330 | .modal-footer .btn{float:right;margin-left:5px;}
331 | .modal .popover,.modal .twipsy{z-index:12000;}
332 | .twipsy{display:block;position:absolute;visibility:visible;padding:5px;font-size:11px;z-index:1000;filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}.twipsy.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}
333 | .twipsy.above .twipsy-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
334 | .twipsy.left .twipsy-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
335 | .twipsy.below .twipsy-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
336 | .twipsy.right .twipsy-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
337 | .twipsy-inner{padding:3px 8px;background-color:#000000;color:white;text-align:center;max-width:200px;text-decoration:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
338 | .twipsy-arrow{position:absolute;width:0;height:0;}
339 | .popover{position:absolute;top:0;left:0;z-index:1000;padding:5px;display:none;}.popover.above .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
340 | .popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
341 | .popover.below .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
342 | .popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
343 | .popover .arrow{position:absolute;width:0;height:0;}
344 | .popover .inner{background:#000000;background:rgba(0, 0, 0, 0.8);padding:3px;overflow:hidden;width:280px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
345 | .popover .title{background-color:#f5f5f5;padding:9px 15px;line-height:1;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;border-bottom:1px solid #eee;}
346 | .popover .content{background-color:#ffffff;padding:14px;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover .content p,.popover .content ul,.popover .content ol{margin-bottom:0;}
347 | .fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;}
348 | .label{padding:1px 3px 2px;font-size:9.75px;font-weight:bold;color:#ffffff;text-transform:uppercase;white-space:nowrap;background-color:#bfbfbf;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}.label.important{background-color:#c43c35;}
349 | .label.warning{background-color:#f89406;}
350 | .label.success{background-color:#46a546;}
351 | .label.notice{background-color:#62cffc;}
352 | .media-grid{margin-left:-20px;margin-bottom:0;zoom:1;}.media-grid:before,.media-grid:after{display:table;content:"";zoom:1;}
353 | .media-grid:after{clear:both;}
354 | .media-grid li{display:inline;}
355 | .media-grid a{float:left;padding:4px;margin:0 0 18px 20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}.media-grid a img{display:block;}
356 | .media-grid a:hover{border-color:#0069d6;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
357 |
358 | .post { padding-bottom: 10px; margin-bottom: 10px; border-bottom: solid 1px #e8e8e8; }
359 | .stat { width:70%; float:left; text-align: center; }
360 | .stat .score { font: bold 25px/33px arial; }
361 | .stat .click { }
362 | .rate { width:30%; float:left;}
363 | .rate .vote_up, .rate .vote_down {text-indent: -9999em; font-size: 1px; display: block; margin: 0 auto; width: 24px; height: 24px;}
364 | .rate .vote_up { background: url('../arrow_up.png') no-repeat; margin-bottom: 2px; }
365 | .rate .vote_down { background: url('../arrow_down.png') no-repeat; }
366 | .article .row { padding-left: 20px;}
367 | .src , .src a { color: #999; }
368 | .wide { min-height: 500px; }
369 | .all_cat a, .cat_list a { font-size: 12px; color: #E64B50; text-decoration: none; display: inline-block; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; background-color: white; border: 1px solid rgba(0, 0, 0, 0.1); padding: 5px 10px; line-height: 14px; margin-bottom: 4px; margin-right: 2px; }
370 | .all_cat a:hover, .cat_list a:hover{ color: #FFF; background-color: #E64B50; }
371 | .topbar div > ul a.post_btn { padding:0; margin-top: 8px; margin-right: 5px; width:21px; height:24px; background-image: url('../book.png'); background-position: top; text-indent: -9999px; }
372 | .topbar div > ul a.post_btn:hover { background-position: bottom; }
373 | footer a {text-decoration: none; color: #404040;}
374 | footer a:hover {text-decoration: none; color: #333;}
375 | .article .heading { min-height: 28px; margin-bottom: 5px; }
376 | .article .heading a { font-size:13px; text-decoration: none; font-weight: bold; }
377 | .article .footing { color: #666; font-size: 12px; }
378 | .article .footing a { color:#268BD2; }
379 | .article .footing a:hover { color:#195F91; text-decoration: none; }
380 | .article .heading .all_cat a {font-size: 11px; margin-bottom: 0; font-weight: lighter; border-radius: 7px; -moz-border-radius: 7px; -webkit-border-radius: 7px; padding: 2px 7px; }
381 | .drop-login .login-form { height: 98px; }
382 | .drop-login .login-row { margin-bottom: 5px; }
383 | .drop-login .remember { width: 90px; float: left; line-height: 22px;}
384 | .drop-login .remember span { padding-left: 5px; color: #ccc; }
385 | .drop-login .login-btn {width: 70px; float: right;}
386 | .page_nav { text-align: right;}
387 | #pn { margin-top: -47px; }
388 | .page_nav a,.cmt_util a { text-decoration: none; }
389 | .cmt_body { margin-bottom: 5px;}
390 | .cmt_util { text-align: right; }
391 | .sub_cmt {margin-left: 30px;}
392 | .span_cmt { width: 78px; }
393 | .span_cmt .rate { margin-right: 15px; }
394 | .span_cmt .stat { width: 40%; text-align: left; }
395 | .span_cmt .stat .score { font: bold 20px/50px arial; }
396 | .sub_cmt { width:94%; }
397 | .cmt_del label { width: 90px; }
398 | .login, .register, .new_pswd,.profile { margin-bottom: 10px; }
399 | .register span { display:inline-block; width: 100px; text-align: right; padding-right: 10px; }
400 | .login .span3 , .register .span3, .new_pswd .span3,.profile .span3 { height: 25px; width: 300px; }
401 | .login label { margin-right: 20px; }
402 | .login .reset_link { display:block; width: 90px; float:left; line-height: 30px; }
403 | .reset_password { float:left; margin-right: 10px; }
404 | .reset_password input {height: 20px; }
405 | .post_input { margin-bottom: 10px; }
406 | .post_input .span14 { height:25px; }
407 | .post_input #post_radio { text-align: left; width: 100px; }
408 | .post_submit { clear:both; margin-top: 20px; text-align: right; }
409 | .facebook_like { margin-top: 100px; }
410 |
411 | .chrome {padding-left: 100px; padding-top: 35px;}
--------------------------------------------------------------------------------