diff options
author | pzread <netfirewall@gmail.com> | 2013-06-27 01:06:21 +0800 |
---|---|---|
committer | pzread <netfirewall@gmail.com> | 2013-06-27 01:06:21 +0800 |
commit | b2ac7286c8ed3267b257f7ec88aa739a134089d8 (patch) | |
tree | 2825f48dbb5d74f05017f11ffad67b2b8f296e65 | |
parent | 17c8c94e097018ccaf15f8a9296b03b5195cc3f7 (diff) | |
download | taiwan-online-judge-b2ac7286c8ed3267b257f7ec88aa739a134089d8.tar.gz taiwan-online-judge-b2ac7286c8ed3267b257f7ec88aa739a134089d8.tar.zst taiwan-online-judge-b2ac7286c8ed3267b257f7ec88aa739a134089d8.zip |
Add square manage
-rw-r--r-- | src/css/manage_square.less | 23 | ||||
-rw-r--r-- | src/css/style.less | 48 | ||||
-rw-r--r-- | src/html/index.html | 7 | ||||
-rw-r--r-- | src/html/manage_square.html | 58 | ||||
-rw-r--r-- | src/js/com.js | 257 | ||||
-rw-r--r-- | src/js/index.js | 4 | ||||
-rw-r--r-- | src/js/manage.js | 201 | ||||
-rw-r--r-- | src/js/square.js | 2 | ||||
-rw-r--r-- | src/js/user.js | 5 | ||||
-rw-r--r-- | src/py/square.py | 12 | ||||
-rwxr-xr-x | src/py/user.py | 23 |
11 files changed, 628 insertions, 12 deletions
diff --git a/src/css/manage_square.less b/src/css/manage_square.less new file mode 100644 index 0000000..5e06355 --- /dev/null +++ b/src/css/manage_square.less @@ -0,0 +1,23 @@ +@import 'color.less'; +@import 'mixin.less'; + +#index_page{ + div.set{ + div.modal-body{ + min-height:384px; + + img.logo{ + width:96px; + } + } + } + table.list{ + tr.item{ + td.cate{ + span.label{ + margin-right:@SmallPad; + } + } + } + } +} diff --git a/src/css/style.less b/src/css/style.less index c754f57..7d6ef90 100644 --- a/src/css/style.less +++ b/src/css/style.less @@ -7,6 +7,10 @@ src:url('/DejaVuSansMono.woff'); } +div.small_modal{ + width:570px; + margin-left:-285px; +} div.medium_modal{ width:970px; margin-left:-485px; @@ -38,3 +42,47 @@ span.check_bold{ .uneditable-input{ cursor:default; } +span.tag{ + margin-right:@SmallPad; + font-size:13px; + + i{ + margin-left:@SmallPad; + cursor:pointer; + opacity:0.8; + + &:hover{ + opacity:1; + } + } +} +div.tagbox{ + position:relative; + + div{ + height:0px; + position:absolute; + top:4px; + left:7px; + z-index:1; + + span.tag{ + margin-bottom:3px; + } + } + input{ + z-index:0; + } + ul{ + position:absolute; + display:none; + + li{ + a:hover{ + color:@black; + background-color:inherit; + background-image:none; + } + } + } +} diff --git a/src/html/index.html b/src/html/index.html index 28632de..902411b 100644 --- a/src/html/index.html +++ b/src/html/index.html @@ -4,7 +4,8 @@ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> -<link href="/bootstrap-toj/css/bootstrap.min.css" rel="stylesheet"> +<link rel="stylesheet" href="/bootstrap-toj/css/bootstrap.min.css"> +<link rel="stylesheet" href="/bootstrap-toj/css/bootstrap-datetimepicker.min.css"> <link rel="stylesheet" href="/codemirror-3.13/lib/codemirror.css"> <link rel="stylesheet" href="/codemirror-3.13/theme/lesser-dark.css"> @@ -12,6 +13,7 @@ <script src="/jquery-2.0.2.min.js"></script> <script src="/bootstrap-toj/js/bootstrap.min.js"></script> +<script src="/bootstrap-toj/js/bootstrap-datetimepicker.min.js"></script> <script src="/codemirror-3.13/lib/codemirror.js"></script> <script src="/codemirror-3.13/mode/xml/xml.js"></script> <script src="/codemirror-3.13/mode/javascript/javascript.js"></script> @@ -27,6 +29,7 @@ <script src="/toj/js/home.js" type="text/javascript"></script> <script src="/toj/js/square.js" type="text/javascript"></script> <script src="/toj/js/mail.js" type="text/javascript"></script> +<script src="/toj/js/manage.js" type="text/javascript"></script> <script type="text/javascript"> @@ -43,6 +46,7 @@ $(document).ready(function(){ home.ready(); square.ready(); mail.ready(); + manage.ready(); j_win.on('resize',com.exheight); $(window).on('popstate',function(e){ @@ -89,6 +93,7 @@ $(document).ready(function(){ <li class="profile" style="display:none;"><a href="">個人</a></li> <li class="square" style="display:none;"><a href="/toj/square/user/">方塊</a></li> <li class="mail" style="display:none;"><a href="/toj/mail/inbox/">信箱</a></li> + <li class="manage" style="display:none;"><a href="/toj/manage/square/">管理</a></li> <li><a href="#">狀態</a></li> <li><a href="#">關於</a></li> </ul> diff --git a/src/html/manage_square.html b/src/html/manage_square.html new file mode 100644 index 0000000..b457b34 --- /dev/null +++ b/src/html/manage_square.html @@ -0,0 +1,58 @@ +<link href="/toj/css/manage_square.css" rel="stylesheet"> + +<div class="modal hide fade small_modal set"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h3>方塊設定</h3> + </div> + <div class="modal-body container-fluid"> + <div class="row-fluid"> + <div class="span6"> + <label>方塊名稱</label> + <input name="title" type="text"> + <label>方塊介紹</label> + <input name="intro" type="text"> + <label>方塊圖片</label> + <input name="logo" type="text"> + <img class="img-polaroid logo"></img> + </div> + <div class="span7"> + <label>公開狀態</label> + <select name="hidden"> + <option value=0>顯示</option> + <option value=1>隱藏</option> + </select> + <label>開始時間</label> + <div class="start"></div> + <label>結束時間</label> + <div class="end"></div> + <label>分類</label> + <div class="catebox"></div> + </div> + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-primary">確定</button> + <button class="btn">取消</button> + </div> +</div> + +<div class="row clearfix"> + <div class="span2 offset1"> + sdf + </div> + <table class="span8 table table-hover list"> + <thead> + <tr> + <th class="span1">#</th> + <th class="span3">名稱</th> + <th class="span1">狀態</th> + <th class="span2">分類</th> + <th class="span1"></th> + </tr> + </thead> + <tbody> + </tbody> + </table> +</div> + diff --git a/src/js/com.js b/src/js/com.js index 64d76a2..cab3928 100644 --- a/src/js/com.js +++ b/src/js/com.js @@ -540,7 +540,7 @@ var com = new function(){ date = data; } - ret = date.getFullYear() + '/' + date.getMonth() + '/' + date.getDate() + ' ' + + ret = date.getFullYear() + '/' + fix(date.getMonth() + 1) + '/' + fix(date.getDate()) + ' ' + fix(date.getHours()) + ':' + fix(date.getMinutes()); if(sec == true){ @@ -549,6 +549,9 @@ var com = new function(){ return ret; }; + that.get_defaultimg = function(hash){ + return 'http://www.gravatar.com/avatar/' + hash + '?f=y&d=identicon&s=256'; + }; that.create_codebox = function(j_div,mode,readonly){ var codebox; @@ -641,6 +644,258 @@ var com = new function(){ return offs; }; + that.create_datetimepicker = function(j_div){ + j_div.addClass('input-append date'); + j_div.append($('<input type="text" data-format="yyyy/MM/dd hh:mm:ss"><span class="add-on"><i date-time-icon="icon-time" date-date-icon="icon-calendar"></i></span>')); + + j_div.datetimepicker({'language':'pt-BR'}); + + return j_div.data('datetimepicker'); + }; + that.create_tagbox = function(j_div,words,restrict,duplicate){ + var i; + var width; + var inwidth; + var j_box; + var j_input; + var j_menu; + var last_text = ''; + var show = false; + + function _resize(){ + var j_tag; + var pos; + var left; + var top; + + left = 6; + top = 4; + if((j_tag = j_box.find('span.tag:last')).length == 1){ + pos = j_tag.position(); + left += pos.left + j_tag.width() + 14; + + top += pos.top + j_tag.height() + 1; + + if((inwidth - left) < 70){ + left = 6; + top += 6; + }else{ + top -= (j_tag.height() + 2); + } + } + + j_input.width(inwidth - left + 6); + j_input.css('padding-left',left); + j_input.css('padding-top',top); + } + function _match(value){ + var i; + var word; + var list; + var dup; + var spans; + var j_li; + var j_a; + var flag; + + if(value == ''){ + list = words; + }else{ + list = new Array(); + for(i = 0;i < words.length;i++){ + word = words[i]; + if(word.indexOf(value) == 0){ + list.push(word); + } + } + } + + if(duplicate != true){ + dup = new Object(); + spans = j_box.find('span.tag'); + for(i = 0;i < spans.length;i++){ + dup[$(spans[i]).attr('text')] = true; + } + } + + j_menu.empty(); + flag = false; + for(i = 0;i < list.length;i++){ + word = list[i]; + if(word in dup){ + continue; + } + flag = true; + + j_li = $('<li><a href=""></a></li>'); + j_li.attr('word',word); + j_li.on('mouseover',function(e){ + j_menu.find('li.active').removeClass('active'); + $(this).addClass('active'); + }); + + j_a = j_li.find('a') + j_a.text(word); + j_a.on('click',function(word){return function(e){ + _add_tag(word,false); + + return false; + }}(word)); + + j_menu.append(j_li); + } + + j_menu.find('li:first').addClass('active'); + + if(flag == true && show == true){ + j_menu.show(); + }else{ + j_menu.hide(); + } + } + function _move(direct){ + var j_li = j_menu.find('li.active') + + if(direct == 0){ + if(j_li.prev().length > 0){ + j_li.removeClass('active'); + j_li.prev().addClass('active'); + } + }else{ + if(j_li.next().length > 0){ + j_li.removeClass('active'); + j_li.next().addClass('active'); + } + } + } + + function _add_tag(text,force){ + var j_li; + var j_tag; + + j_input.val(''); + if(force == false){ + if(restrict == true){ + if((j_li = j_menu.find('li.active')).length == 0){ + return; + } + text = j_li.attr('word'); + }else if(duplicate != true){ + if(j_box.find('[text="' + text + '"]').length > 0){ + return; + } + }else if(text == ''){ + return; + } + } + + j_tag = that.create_tag(text); + j_tag.find('i').on('click',function(){ + _del_tag(); + }); + + j_box.append(j_tag); + + _match(''); + _resize(); + } + function _del_tag(){ + _match(''); + _resize(); + } + + j_div.empty(); + j_div.addClass('tagbox'); + j_div.append($('<div></div><input type="text"><ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu"></ul>')); + + j_box = j_div.find('div'); + j_input = j_div.find('input'); + j_menu = j_div.find('ul'); + + width = j_input.width() + 14; + inwidth = width - 14; + + j_div.width(width); + + j_input.width(inwidth); + j_input.on('keydown',function(e){ + if(e.keyCode == 8 && j_input.val() == ''){ + j_box.find('span.tag:last').remove(); + _del_tag(); + }else if(e.keyCode == 38){ + _move(0); + }else if(e.keyCode == 40){ + _move(1); + } + }); + j_input.on('keyup',function(e){ + var text = j_input.val(); + + if(e.keyCode == 13){ + _add_tag(text,false); + }else if(text != last_text){ + last_text = text; + _match(text); + } + }); + + j_input.on('focusin',function(e){ + show = true; + _match(''); + _resize(); + + return false; + }); + $(window).on('click',function(e){ + if($(e.target).parents('div.tagbox').is(j_div)){ + return; + } + + show = false; + j_input.val(''); + j_menu.hide(); + return false; + }); + + if(words == undefined){ + words = []; + }else{ + words = words.sort(); + } + j_menu.width(width - 2); + + j_div.add_tag = function(text){ + _add_tag(text,true); + } + j_div.refresh = function(){ + _match(''); + _resize(); + } + + _match(''); + _resize(); + + return j_div; + }; + that.create_tag = function(text,style){ + var j_span; + var j_i; + + j_span = $('<span class="label tag"></span>'); + j_span.attr('text',text); + if(style != undefined){ + j_span.addClass(style); + } + j_span.text(text); + + j_i = $('<i class="icon-remove-circle icon-white"></i>'); + j_span.append(j_i); + j_i.on('click',function(e){ + j_span.remove(); + }); + + return j_span; + }; that.is_callerr = function(result){ if(result.stat == false || typeof(result.data) == 'string'){ return true; diff --git a/src/js/index.js b/src/js/index.js index 33e22b4..d6b36d3 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -119,15 +119,13 @@ var index = new function(){ j_header.find('li.register').hide(); j_header.find('li.nickname').show(); j_header.find('li.logout').show(); - j_li = j_menu.find('div.menu li.profile'); j_li.find('a').attr('href','/toj/user:' + user.uid + '/main/'); j_li.show(); - j_menu.find('div.menu li.square').show(); - j_menu.find('div.menu li.mail').show(); + j_menu.find('div.menu li.manage').show(); }); _change(); diff --git a/src/js/manage.js b/src/js/manage.js new file mode 100644 index 0000000..1961f6d --- /dev/null +++ b/src/js/manage.js @@ -0,0 +1,201 @@ +'use strict' + +var manage = new function(){ + var that = this; + var j_index_page; + + that.ready = function(){ + var j_tabnav_square; + + var manage_node = new vus.node('manage'); + var square_node = new vus.node('square'); + + j_index_page = $('#index_page'); + + manage_node.url_chg = function(direct,url_upart,url_dpart,param){ + if(direct == 'in'){ + index.set_menu('管理'); + index.clear_tabnav(); + + j_tabnav_square = index.add_tabnav('方塊','/toj/manage/square/'); + + com.call_backend('core/user/','list_auth',function(result){ + console.log(result); + }); + } + + return 'cont'; + }; + com.vus_root.child_set(manage_node); + + square_node.url_chg = function(direct,url_upart,url_dpart,param){ + var j_set; + var j_list; + var set_timepicker_start; + var set_timepicker_end; + var catemap; + + function _item_set(j_item,id,title,status,cateid,intro,logo,hidden,start_time,end_time){ + var i; + var j_cate; + var j_label; + + j_item.find('td.id').text(id); + j_item.find('td.title').text(title); + + if(status == 1){ + j_item.find('td.status').append($('<span class="label label-warning">等待中</span>')); + }else if(status == 2){ + j_item.find('td.status').append($('<span class="label label-success">進行中</span>')); + }else if(status == 3){ + j_item.find('td.status').append($('<span class="label">已結束</span>')); + } + + j_cate = j_item.find('td.cate'); + for(i = 0;i < cateid.length;i++){ + j_label = $('<span class="label label-info"></span>'); + j_label.text(catemap[cateid[i]]); + j_cate.append(j_label); + } + + j_item.find('button.set').on('click',function(e){ + var i; + var j_catebox; + var j_tag; + var catelist; + var key; + + j_set.find('[name="title"]').val(title); + j_set.find('[name="intro"]').val(intro); + j_set.find('[name="logo"]').val(logo); + + if(logo == ''){ + logo = com.get_defaultimg(id); + } + j_set.find('img.logo').attr('src',logo); + + if(start_time != null){ + set_timepicker_start.setDate(new Date(start_time)); + }else{ + set_timepicker_start.setDate(null); + } + if(end_time != null){ + set_timepicker_end.setDate(new Date(end_time)); + }else{ + set_timepicker_end.setDate(null); + } + + catelist = new Array(); + for(key in catemap){ + if(key == 0){ + continue; + } + catelist.push(catemap[key]); + } + + j_catebox = com.create_tagbox(j_set.find('div.catebox'),catelist,true,false); + j_catebox.find('input').attr('placeholder','+加入分類'); + for(i = 0;i < cateid.length;i++){ + j_catebox.add_tag(catemap[cateid[i]]); + } + + j_set.on('shown',function(e){ + j_catebox.refresh(); + }); + + j_set.modal('show'); + }); + } + function _item_create(id,title,status,cateid,intro,logo,hidden,start_time,end_time){ + var j_item = $('<tr class="item"><td class="id"></td><td class="title"><td class="status"></td></td><td class="cate"></td><td class="oper"><button class="btn btn-small set"><i class="icon-cog"></i></button></td></tr>'); + + _item_set(j_item,id,title,status,cateid,intro,logo,hidden,start_time,end_time); + + return j_item; + } + function _update(){ + com.call_backend('core/square/','list_category',function(result){ + var i; + var data = result.data; + + if(com.is_callerr(result)){ + index.add_alert('','警告','管理發生錯誤'); + }else{ + catemap = new Object(); + for(i = 0;i < data.length;i++){ + catemap[data[i].cateid] = data[i].catename; + } + + com.call_backend('core/square/','list_square',function(result){ + var i; + var data = result.data; + var items; + var j_item; + var sqo; + + if(com.is_callerr(result)){ + index.add_alert('','警告','管理發生錯誤'); + }else{ + items = j_list.find('tr.item'); + + for(i = 0;i < Math.min(items.length,data.length);i++){ + sqo = data[i]; + + _item_set($(items[i]),sqo.sqid, + sqo.title, + sqo.status, + sqo.cateid, + sqo.intro, + sqo.logo, + sqo.hidden, + sqo.start_time, + sqo.end_time); + } + for(;i < data.length;i++){ + sqo = data[i]; + + j_item = _item_create(sqo.sqid, + sqo.title, + sqo.status, + sqo.cateid, + sqo.intro, + sqo.logo, + sqo.hidden, + sqo.start_time, + sqo.end_time); + j_list.append(j_item); + } + for(;i < items.length;i++){ + $(items[i]).remove(); + } + } + }); + } + }); + } + + if(direct == 'in'){ + com.loadpage('/toj/html/manage_square.html').done(function(){ + var j_start; + var j_end; + + j_list = j_index_page.find('table.list'); + j_set = j_index_page.find('div.set'); + j_tabnav_square.active(); + + j_start = j_set.find('div.start'); + set_timepicker_start = com.create_datetimepicker(j_start); + j_start.find('input').attr('placeholder','留空表示不限制'); + j_end = j_set.find('div.end'); + set_timepicker_end = com.create_datetimepicker(j_end); + j_end.find('input').attr('placeholder','留空表示不限制'); + + _update(); + }); + } + + return 'cont'; + }; + manage_node.child_set(square_node); + }; +}; diff --git a/src/js/square.js b/src/js/square.js index a8431f9..fead61d 100644 --- a/src/js/square.js +++ b/src/js/square.js @@ -176,7 +176,7 @@ var square = new function(){ } if((logo = sqo.logo) == ''){ - logo = 'http://www.gravatar.com/avatar/' + sqo.sqid + '?f=y&d=identicon&s=96'; + logo = com.get_defaultimg(sqo.sqid); } if(sqo.start_time == null){ start_time = null; diff --git a/src/js/user.js b/src/js/user.js index 9e78d83..9a18a1a 100644 --- a/src/js/user.js +++ b/src/js/user.js @@ -98,7 +98,6 @@ var user = new function(){ index.set_menu('使用者'); index.set_title(''); - index.clear_tabnav(); j_tabnav_main = index.add_tabnav('個人','/toj/user:' + user_node_uid + '/main/'); @@ -167,7 +166,7 @@ var user = new function(){ var url; if((url = data.avatar) == ''){ - url = 'http://www.gravatar.com/avatar/' + user_node_uid + '?f=y&d=identicon&s=256'; + url = com.get_defaultimg(user_node_uid); } j_index_page.find('img.avatar').attr('src',url); if((url = data.cover) == ''){ @@ -231,7 +230,7 @@ var user = new function(){ var url = $(this).val(); if(url == ''){ - url = 'http://www.gravatar.com/avatar/' + that.uid + '?f=y&d=identicon&s=256'; + url = com.get_defaultimg(that.uid); } j_img_avatar.attr('src',url); }); diff --git a/src/py/square.py b/src/py/square.py index ebdf9fb..8091448 100644 --- a/src/py/square.py +++ b/src/py/square.py @@ -202,9 +202,10 @@ class SquareMg: def _list_square_category(self, cateid, uid): cur = self.db.cursor() sqlsel = ('SELECT "SQUARE"."sqid", "title", "start_time", "end_time", ' - '"hidden", "sqmodid", "intro", "logo"') + '"hidden", "sqmodid", "intro", "logo", "cateid"') sqlfrom = (' FROM "SQUARE"') sqlwhere = (' WHERE true') + sqlorder = (' ORDER BY "SQUARE"."sqid" ASC') sqlarr = [] if uid != None: @@ -218,7 +219,7 @@ class SquareMg: sqlwhere = sqlwhere + (' AND %s = ANY ("cateid")') sqlarr.append(cateid) - sqlstr = sqlsel + sqlfrom + sqlwhere + ';' + sqlstr = sqlsel + sqlfrom + sqlwhere + sqlorder + ';' cur.execute(sqlstr, sqlarr) ret = [] @@ -232,8 +233,13 @@ class SquareMg: obj['sqmodid'] = data[5] obj['intro'] = data[6] obj['logo'] = data[7] + obj['cateid'] = data[8] + + if 0 in obj['cateid']: + obj['cateid'] = [] + if uid != None: - obj['active'] = data[8] + obj['active'] = data[9] nowtime = datetime.datetime.now(datetime.timezone.utc) diff --git a/src/py/user.py b/src/py/user.py index 43605e0..865233b 100755 --- a/src/py/user.py +++ b/src/py/user.py @@ -45,6 +45,8 @@ class UserMg: 'core/user/', 'set_user_info', self.set_user_info) Proxy.instance.register_call( 'core/user/', 'change_user_password', self.change_user_password) + Proxy.instance.register_call( + 'core/user/', 'list_auth', self.list_auth) @imc.async.caller def register( @@ -327,6 +329,19 @@ class UserMg: @imc.async.caller def oauth_login(self): raise NotImplementedError + + @imc.async.caller + def list_auth(self): + uid = self.get_current_uid() + if uid == None: + return 'Euid' + + idenid = self.get_idenid_by_uid(uid) + + with TOJAuth.change_current_iden(self._idendesc): + auth_list = TOJAuth.instance.get_user_auth_list(idenid) + + return auth_list def _password_hash(self, password): h = SHA512.new(bytes(password + config.USER_PASSHASH_SALT, 'utf-8')) @@ -354,6 +369,14 @@ class UserMg: ret['aboutme'] = data[5] ret['cover'] = data[6] + uid = self.get_current_uid() + if uid != ret['uid']: + try: + TOJAuth.check_access_func( + self._accessid, TOJAuth.ACCESS_EXECUTE) + except Exception: + del ret['email'] + return ret def get_idenid_by_uid(self, uid): |