From 258d9a481e7020023ee10299ab8e684cd57ac0e7 Mon Sep 17 00:00:00 2001 From: Jeromy Date: Sat, 1 Dec 2012 10:12:29 +0800 Subject: [PATCH] 1. add calenda support, can export ics file 2. add duration and record params 3. only call crate api when the creater joined the meeting. 4. only show meetings for the creater --- bbb_django/bbb/admin.py | 4 +- .../bbb/locale/zh_CN/LC_MESSAGES/django.mo | Bin 2491 -> 2724 bytes .../bbb/locale/zh_CN/LC_MESSAGES/django.po | 118 +-- bbb_django/bbb/models.py | 26 +- bbb_django/bbb/static/css/base.css | 772 ++++++++++++++++++ bbb_django/bbb/static/css/forms.css | 354 ++++++++ bbb_django/bbb/static/css/reset.css | 7 + bbb_django/bbb/static/css/widgets.css | 174 ++++ bbb_django/bbb/static/img/default-bg.gif | Bin 0 -> 844 bytes bbb_django/bbb/static/img/logo.png | Bin 0 -> 7893 bytes bbb_django/bbb/static/img/nav-bg.gif | Bin 0 -> 273 bytes bbb_django/bbb/static/js/DateTimeShortcuts.js | 284 +++++++ bbb_django/bbb/static/js/calendar.js | 156 ++++ bbb_django/bbb/static/js/core.js | 221 +++++ bbb_django/bbb/templates/base.html | 15 +- bbb_django/bbb/templates/create.html | 8 +- bbb_django/bbb/templates/meetings.html | 24 +- bbb_django/bbb/urls.py | 10 +- bbb_django/bbb/views/core.py | 76 +- bbb_django/migrate.sh | 4 + requirements.txt | 2 + 21 files changed, 2165 insertions(+), 90 deletions(-) create mode 100644 bbb_django/bbb/static/css/base.css create mode 100644 bbb_django/bbb/static/css/forms.css create mode 100644 bbb_django/bbb/static/css/reset.css create mode 100644 bbb_django/bbb/static/css/widgets.css create mode 100644 bbb_django/bbb/static/img/default-bg.gif create mode 100644 bbb_django/bbb/static/img/logo.png create mode 100644 bbb_django/bbb/static/img/nav-bg.gif create mode 100644 bbb_django/bbb/static/js/DateTimeShortcuts.js create mode 100644 bbb_django/bbb/static/js/calendar.js create mode 100644 bbb_django/bbb/static/js/core.js create mode 100755 bbb_django/migrate.sh diff --git a/bbb_django/bbb/admin.py b/bbb_django/bbb/admin.py index 3dc10dd..b1e2d1c 100644 --- a/bbb_django/bbb/admin.py +++ b/bbb_django/bbb/admin.py @@ -10,7 +10,7 @@ class MeetingAdmin(admin.ModelAdmin): list_display = ('name','id') - list_filter = ['name'] - search_fields = ['name'] + list_filter = ['name', 'user'] + search_fields = ['name','user' ] admin.site.register(Meeting, MeetingAdmin) diff --git a/bbb_django/bbb/locale/zh_CN/LC_MESSAGES/django.mo b/bbb_django/bbb/locale/zh_CN/LC_MESSAGES/django.mo index 1b29141827d69ef1d5c426681edafd33cc6f4f59..da916b638b5ad37f4e20a34b6929798cafb3d5bc 100644 GIT binary patch delta 1255 zcmXxjTS!zv7{Ku{U2kcYwqEm^b~DWlgbFe+f|3$a1W7?8ZwtD?UMhq#%7-NlwobJr z#b!6H$gn~Sg&umThv+FHh@RYag&wjV>!JUr-wyN5?|f&@nVFrPGne(yKJ+6q;fA49 z6Pt+9IAiwXzcnM=|sZ$g={L+!yN@&WDlp-gZQ*W!p8M0xH3Qq41(LY&4j{EX%J8*`c8g2 zRD&tF7dPNRq?#5Q>2F6FxEp1G1A2cDGs!P$eiNxCM3ah-QQn(I8UHQH4}Onn%x^w$ zk%3>ZmhBH?8+ie<%8O@E7BHl_Uk&K}Ti8JVIP$9biL$^ys>~{RI?4j_Y2>+b9FmSo zF2p_BaY#L?Hf#S0wHK+ztDaZ=DC1nijTlm2qwLT}&A%hnET^#lvXZ}K^5PoyOY#gg zTP;NCFIOEXJ65OtjcSYbx1&tpR6W{%4rL+3DB}fF*?)H2Y$xPYN?BP5vU@ArF9XHO zme_^58Rc*l6WfR!LcXGWVk;s4vm8$Ox@5spN(nn5UxS>#l_J}fOUSR!CRm`UBIKl2 z5V0bkQWha6xj=8qVdXHJ9h#S*{K`tr(=ku4i*To0my{iGopySyv+;e_xA>W)&i-y! zSEt*$k}#Opc*DKGgvblDT#sk^Ic93_sLeKobOt|m6dSznXd5+lJ!i<2XkBYCtwa_4Dy{z79P{{w@>t~~$% delta 1042 zcmXxjPi%`}9Ki9X)<1h|HruRhW9wF9k%+V`OB^_e0~rUqShz@aO=TL|bvQ^5c4$nb zyhLWqB*ICFB`jte5*ZQ^2a5x9S*wnl6E42L?&rz-dq3~*dEWPVo<7ghdv%kw>G>Mx zbwg<<))Pk^#&qDV72GJN%8WUTr_qh?)lcdd^*cVGy?~W?*=fvbyp2?u9J=rca+!%` z(`Jf?3MyWs4DeB%Ll5~kZO>yl`4X z!CCby$}e8j+{sJwUOhJ%IDj&-Et+?#yVSkf-lg`StVpl652)uY)?Ydrq=L&_;U)u* zpw#D(6*QZPc7pko-jjXaMAQ*-xc(P82T}q=BO&`QrHzpOq{x=7C7Osz*?&Fgq{}B$ zl#}Ztf32)^ERZHQLjzYJ15wW2NGKv6Fmz z+@5tOyuq!Zpg$DcxubJ@kLRGn9xm^;f0Yk46=$-=yO$Ow9-2ZXo4\n" "Language-Team: LANGUAGE \n" @@ -16,87 +16,99 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: models.py:26 +#: models.py:29 msgid "unlimited" msgstr "无限制" -#: models.py:27 +#: models.py:30 msgid "15 min" msgstr "15分钟" -#: models.py:28 +#: models.py:31 msgid "30 min" msgstr "30分钟" -#: models.py:29 +#: models.py:32 msgid "1 hour" msgstr "1小时" -#: models.py:30 +#: models.py:33 msgid "2 hour" msgstr "2小时" -#: models.py:36 models.py:177 +#: models.py:34 +msgid "3 hour" +msgstr "3小时" + +#: models.py:39 +msgid "user" +msgstr "用户" + +#: models.py:40 models.py:184 msgid "meeting name" msgstr "会议名称" -#: models.py:37 models.py:178 +#: models.py:41 models.py:185 msgid "attendee password" msgstr "会议密码" -#: models.py:38 models.py:180 +#: models.py:42 models.py:187 msgid "moderator password" msgstr "管理员密码" -#: models.py:39 models.py:182 +#: models.py:43 models.py:189 msgid "welcome message" msgstr "欢迎信息" -#: models.py:48 -msgid "meeting" -msgstr "会议" - -#: models.py:49 -msgid "meetings" -msgstr "会议列表" - -#: models.py:182 -msgid "Welcome!" -msgstr "欢迎!" - -#: models.py:183 +#: models.py:44 models.py:190 msgid "record" msgstr "录制" -#: models.py:184 +#: models.py:45 models.py:191 msgid "duration" msgstr "时长" -#: models.py:185 +#: models.py:46 models.py:192 msgid "start time" msgstr "开始时间" -#: models.py:198 +#: models.py:47 +msgid "started" +msgstr "已开始" + +#: models.py:53 +msgid "meeting" +msgstr "会议" + +#: models.py:54 +msgid "meetings" +msgstr "会议列表" + +#: models.py:189 +msgid "Welcome!" +msgstr "欢迎!" + +#: models.py:205 msgid "Your name" msgstr "姓名" -#: models.py:199 +#: models.py:206 msgid "password" msgstr "密码" -#: templates/base.html:218 templates/home.html:4 +#: templates/base.html:230 templates/home.html:4 msgid "Home" msgstr "主页" -#: templates/base.html:220 +#: templates/base.html:232 msgid "Logged in as" msgstr "登陆帐号" -#: templates/base.html:220 +#: templates/base.html:232 msgid "Log out" msgstr "退出" -#: templates/base.html:222 +#: templates/base.html:234 msgid "Log in" msgstr "登陆" @@ -104,15 +116,16 @@ msgstr "登陆" msgid "Create a meeting" msgstr "创建会议" -#: templates/create.html:8 templates/home.html:9 +#: templates/create.html:18 templates/home.html:9 msgid "Create Meeting" msgstr "创建会议" -#: templates/create.html:15 +#: templates/create.html:26 msgid "create" msgstr "创建" -#: templates/home.html:10 templates/meetings.html:5 +#: templates/home.html:10 templates/meeting_list.html:5 +#: templates/meeting_list.html.py:8 templates/meetings.html:5 #: templates/meetings.html.py:8 msgid "Meetings" msgstr "会议列表" @@ -136,7 +149,8 @@ msgid "" msgstr "" "通过这里的链接邀请其他人参加会议" -#: templates/join.html:17 templates/meetings.html:20 +#: templates/join.html:17 templates/meeting_list.html:20 +#: templates/meetings.html:17 msgid "Join" msgstr "加入会议" @@ -144,44 +158,56 @@ msgstr "加入会议" msgid "Log In" msgstr "登陆" -#: templates/meetings.html:12 +#: templates/meeting_list.html:12 templates/meetings.html:12 msgid "view details" msgstr "查看详细信息" -#: templates/meetings.html:16 +#: templates/meeting_list.html:16 templates/meetings.html:16 msgid "Running" msgstr "运行中" -#: templates/meetings.html:18 +#: templates/meeting_list.html:18 msgid "Not running" msgstr "未开始" -#: templates/meetings.html:20 +#: templates/meeting_list.html:20 templates/meetings.html:17 msgid "End" msgstr "结束会议" -#: templates/meetings.html:22 +#: templates/meeting_list.html:22 templates/meetings.html:19 msgid "Ended" msgstr "已结束" -#: templates/meetings.html:37 +#: templates/meeting_list.html:37 templates/meetings.html:39 msgid "There are no meetings at the moment." msgstr "当前没有会议" -#: views/core.py:77 +#: templates/meetings.html:22 +msgid "Not started" +msgstr "未开始" + +#: templates/meetings.html:23 +msgid "Calendar" +msgstr "日程" + +#: templates/meetings.html:24 +msgid "Start" +msgstr "开启会议" + +#: views/core.py:134 #, python-format msgid "Successfully ended meeting %s" msgstr "成功结束会议%s" -#: views/core.py:81 +#: views/core.py:138 #, python-format msgid "Unable to end meeting %s" msgstr "未能结束会议%s" -#: views/core.py:103 +#: views/core.py:165 #, python-format -msgid "Successfully created meeting %s" -msgstr "成功创建会议%s" +msgid "Successfully schdulered meeting %s" +msgstr "成功预定会议%s" #~ msgid "invite_url" #~ msgstr "邀请链接" diff --git a/bbb_django/bbb/models.py b/bbb_django/bbb/models.py index 69e9305..caa14d2 100755 --- a/bbb_django/bbb/models.py +++ b/bbb_django/bbb/models.py @@ -4,6 +4,8 @@ from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from django.contrib.admin import widgets +from django.contrib.auth.models import User +from django.core.urlresolvers import reverse from urllib2 import urlopen from urllib import urlencode @@ -29,18 +31,20 @@ def parse(response): (30, _('30 min')), (60, _('1 hour')), (120, _('2 hour')), + (180, _('3 hour')), ) class Meeting(models.Model): - + user = models.ForeignKey(User, verbose_name=_('user')) name = models.CharField(max_length=100, verbose_name=_('meeting name')) attendee_password = models.CharField(max_length=50, verbose_name=_('attendee password')) moderator_password = models.CharField(max_length=50, verbose_name=_('moderator password')) welcome = models.CharField(max_length=100, verbose_name=_('welcome message')) - record = models.BooleanField(default=False) - duration = models.IntegerField(default=0, choices=MEETING_DURATION) - start_time = models.DateTimeField() + record = models.BooleanField(default=False, verbose_name=_('record')) + duration = models.IntegerField(default=0, choices=MEETING_DURATION, verbose_name=_('duration')) + start_time = models.DateTimeField(verbose_name=_('start time')) + started = models.BooleanField(default=False, verbose_name=_('started')) #def __unicode__(self): # return self.name @@ -120,22 +124,22 @@ def get_meetings(self): result = parse(urlopen(url).read()) if result: # Create dict of values for easy use in template - d = [] + d = {} r = result[1].findall('meeting') for m in r: meeting_name = m.find('meetingName').text meeting_id = m.find('meetingID').text password = m.find('moderatorPW').text - d.append({ + d[meeting_id] = { 'name': meeting_name, 'meeting_id': meeting_id, 'running': m.find('running').text, - 'moderator_pw': password, - 'attendee_pw': m.find('attendeePW').text, + #'moderator_pw': password, + #'attendee_pw': m.find('attendeePW').text, 'info': Meeting.meeting_info( meeting_id, password) - }) + } print d return d else: @@ -152,6 +156,8 @@ def start(self): ('voiceBridge', voicebridge), #('welcome', _("Welcome!").encode('utf8')), ('welcome', self.welcome.encode('utf8')), + ('record', self.record), + #('duration', self.duration), )) hashed = self.api_call(query, call) url = settings.BBB_API_URL + call + '?' + hashed @@ -181,7 +187,7 @@ class CreateForm(forms.Form): moderator_password = forms.CharField(label=_('moderator password'), widget=forms.PasswordInput(render_value=False)) welcome = forms.CharField(label=_('welcome message'), initial=_('Welcome!')) - record = forms.BooleanField(label=_('record')) + record = forms.BooleanField(label=_('record'), initial=False, required=False) duration = forms.ChoiceField(label=_('duration'), choices=MEETING_DURATION) start_time = forms.DateTimeField(label=_('start time'), widget=widgets.AdminSplitDateTime()) diff --git a/bbb_django/bbb/static/css/base.css b/bbb_django/bbb/static/css/base.css new file mode 100644 index 0000000..c5e385d --- /dev/null +++ b/bbb_django/bbb/static/css/base.css @@ -0,0 +1,772 @@ +/* + DJANGO Admin styles +*/ + +body { + margin: 0; + padding: 0; + font-size: 12px; + font-family: "Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif; + color: #333; + background: #fff; +} + +/* LINKS */ + +a:link, a:visited { + color: #5b80b2; + text-decoration: none; +} + +a:hover { + color: #036; +} + +a img { + border: none; +} + +a.section:link, a.section:visited { + color: white; + text-decoration: none; +} + +/* GLOBAL DEFAULTS */ + +p, ol, ul, dl { + margin: .2em 0 .8em 0; +} + +p { + padding: 0; + line-height: 140%; +} + +h1,h2,h3,h4,h5 { + font-weight: bold; +} + +h1 { + font-size: 18px; + color: #666; + padding: 0 6px 0 0; + margin: 0 0 .2em 0; +} + +h2 { + font-size: 16px; + margin: 1em 0 .5em 0; +} + +h2.subhead { + font-weight: normal; + margin-top: 0; +} + +h3 { + font-size: 14px; + margin: .8em 0 .3em 0; + color: #666; + font-weight: bold; +} + +h4 { + font-size: 12px; + margin: 1em 0 .8em 0; + padding-bottom: 3px; +} + +h5 { + font-size: 10px; + margin: 1.5em 0 .5em 0; + color: #666; + text-transform: uppercase; + letter-spacing: 1px; +} + +ul li { + list-style-type: square; + padding: 1px 0; +} + +ul.plainlist { + margin-left: 0 !important; +} + +ul.plainlist li { + list-style-type: none; +} + +li ul { + margin-bottom: 0; +} + +li, dt, dd { + font-size: 11px; + line-height: 14px; +} + +dt { + font-weight: bold; + margin-top: 4px; +} + +dd { + margin-left: 0; +} + +form { + margin: 0; + padding: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +blockquote { + font-size: 11px; + color: #777; + margin-left: 2px; + padding-left: 10px; + border-left: 5px solid #ddd; +} + +code, pre { + font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace; + background: inherit; + color: #666; + font-size: 11px; +} + +pre.literal-block { + margin: 10px; + background: #eee; + padding: 6px 8px; +} + +code strong { + color: #930; +} + +hr { + clear: both; + color: #eee; + background-color: #eee; + height: 1px; + border: none; + margin: 0; + padding: 0; + font-size: 1px; + line-height: 1px; +} + +/* TEXT STYLES & MODIFIERS */ + +.small { + font-size: 11px; +} + +.tiny { + font-size: 10px; +} + +p.tiny { + margin-top: -2px; +} + +.mini { + font-size: 9px; +} + +p.mini { + margin-top: -3px; +} + +.help, p.help { + font-size: 10px !important; + color: #999; +} + +p img, h1 img, h2 img, h3 img, h4 img, td img { + vertical-align: middle; +} + +.quiet, a.quiet:link, a.quiet:visited { + color: #999 !important; + font-weight: normal !important; +} + +.quiet strong { + font-weight: bold !important; +} + +.float-right { + float: right; +} + +.float-left { + float: left; +} + +.clear { + clear: both; +} + +.align-left { + text-align: left; +} + +.align-right { + text-align: right; +} + +.example { + margin: 10px 0; + padding: 5px 10px; + background: #efefef; +} + +.nowrap { + white-space: nowrap; +} + +/* TABLES */ + +table { + border-collapse: collapse; + border-color: #ccc; +} + +td, th { + font-size: 11px; + line-height: 13px; + border-bottom: 1px solid #eee; + vertical-align: top; + padding: 5px; + font-family: "Lucida Grande", Verdana, Arial, sans-serif; +} + +th { + text-align: left; + font-size: 12px; + font-weight: bold; +} + +thead th, +tfoot td { + color: #666; + padding: 2px 5px; + font-size: 11px; + background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x; + border-left: 1px solid #ddd; + border-bottom: 1px solid #ddd; +} + +tfoot td { + border-bottom: none; + border-top: 1px solid #ddd; +} + +thead th:first-child, +tfoot td:first-child { + border-left: none !important; +} + +thead th.optional { + font-weight: normal !important; +} + +fieldset table { + border-right: 1px solid #eee; +} + +tr.row-label td { + font-size: 9px; + padding-top: 2px; + padding-bottom: 0; + border-bottom: none; + color: #666; + margin-top: -1px; +} + +tr.alt { + background: #f6f6f6; +} + +.row1 { + background: #EDF3FE; +} + +.row2 { + background: white; +} + +/* SORTABLE TABLES */ + +thead th a:link, thead th a:visited { + color: #666; + display: block; +} + +table thead th.sorted { + background-position: bottom left !important; +} + +table thead th.sorted a { + padding-right: 13px; +} + +table thead th.ascending a { + background: url(../img/admin/arrow-up.gif) right .4em no-repeat; +} + +table thead th.descending a { + background: url(../img/admin/arrow-down.gif) right .4em no-repeat; +} + +/* ORDERABLE TABLES */ + +table.orderable tbody tr td:hover { + cursor: move; +} + +table.orderable tbody tr td:first-child { + padding-left: 14px; + background-image: url(../img/admin/nav-bg-grabber.gif); + background-repeat: repeat-y; +} + +table.orderable-initalized .order-cell, body>tr>td.order-cell { + display: none; +} + +/* FORM DEFAULTS */ + +input, textarea, select, .form-row p { + margin: 2px 0; + padding: 2px 3px; + vertical-align: middle; + font-family: "Lucida Grande", Verdana, Arial, sans-serif; + font-weight: normal; + font-size: 11px; +} + +textarea { + vertical-align: top !important; +} + +input[type=text], input[type=password], textarea, select, .vTextField { + border: 1px solid #ccc; +} + +/* FORM BUTTONS */ + +.button, input[type=submit], input[type=button], .submit-row input { + background: white url(../img/admin/nav-bg.gif) bottom repeat-x; + padding: 3px 5px; + color: black; + border: 1px solid #bbb; + border-color: #ddd #aaa #aaa #ddd; +} + +.button:active, input[type=submit]:active, input[type=button]:active { + background-image: url(../img/admin/nav-bg-reverse.gif); + background-position: top; +} + +.button[disabled], input[type=submit][disabled], input[type=button][disabled] { + background-image: url(../img/admin/nav-bg.gif); + background-position: bottom; + opacity: 0.4; +} + +.button.default, input[type=submit].default, .submit-row input.default { + border: 2px solid #5b80b2; + background: #7CA0C7 url(../img/admin/default-bg.gif) bottom repeat-x; + font-weight: bold; + color: white; + float: right; +} + +.button.default:active, input[type=submit].default:active { + background-image: url(../img/admin/default-bg-reverse.gif); + background-position: top; +} + +.button[disabled].default, input[type=submit][disabled].default, input[type=button][disabled].default { + background-image: url(../img/admin/default-bg.gif); + background-position: bottom; + opacity: 0.4; +} + + +/* MODULES */ + +.module { + border: 1px solid #ccc; + margin-bottom: 5px; + background: white; +} + +.module p, .module ul, .module h3, .module h4, .module dl, .module pre { + padding-left: 10px; + padding-right: 10px; +} + +.module blockquote { + margin-left: 12px; +} + +.module ul, .module ol { + margin-left: 1.5em; +} + +.module h3 { + margin-top: .6em; +} + +.module h2, .module caption, .inline-group h2 { + margin: 0; + padding: 2px 5px 3px 5px; + font-size: 11px; + text-align: left; + font-weight: bold; + background: #7CA0C7 url(../img/admin/default-bg.gif) top left repeat-x; + color: white; +} + +.module table { + border-collapse: collapse; +} + +/* MESSAGES & ERRORS */ + +ul.messagelist { + padding: 0 0 5px 0; + margin: 0; +} + +ul.messagelist li { + font-size: 12px; + display: block; + padding: 4px 5px 4px 25px; + margin: 0 0 3px 0; + border-bottom: 1px solid #ddd; + color: #666; + background: #ffc url(../img/admin/icon_success.gif) 5px .3em no-repeat; +} + +ul.messagelist li.warning{ + background-image: url(../img/admin/icon_alert.gif); +} + +ul.messagelist li.error{ + background-image: url(../img/admin/icon_error.gif); +} + +.errornote { + font-size: 12px !important; + display: block; + padding: 4px 5px 4px 25px; + margin: 0 0 3px 0; + border: 1px solid red; + color: red; + background: #ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat; +} + +ul.errorlist { + margin: 0 !important; + padding: 0 !important; +} + +.errorlist li { + font-size: 12px !important; + display: block; + padding: 4px 5px 4px 25px; + margin: 0 0 3px 0; + border: 1px solid red; + color: white; + background: red url(../img/admin/icon_alert.gif) 5px .3em no-repeat; +} + +.errorlist li a { + color: white; + text-decoration: underline; +} + +td ul.errorlist { + margin: 0 !important; + padding: 0 !important; +} + +td ul.errorlist li { + margin: 0 !important; +} + +.errors { + background: #ffc; +} + +.errors input, .errors select, .errors textarea { + border: 1px solid red; +} + +div.system-message { + background: #ffc; + margin: 10px; + padding: 6px 8px; + font-size: .8em; +} + +div.system-message p.system-message-title { + padding: 4px 5px 4px 25px; + margin: 0; + color: red; + background: #ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat; +} + +.description { + font-size: 12px; + padding: 5px 0 0 12px; +} + +/* BREADCRUMBS */ + +div.breadcrumbs { + background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x; + padding: 2px 8px 3px 8px; + font-size: 11px; + color: #999; + border-top: 1px solid white; + border-bottom: 1px solid #ccc; + text-align: left; +} + +/* ACTION ICONS */ + +.addlink { + padding-left: 12px; + background: url(../img/admin/icon_addlink.gif) 0 .2em no-repeat; +} + +.changelink { + padding-left: 12px; + background: url(../img/admin/icon_changelink.gif) 0 .2em no-repeat; +} + +.deletelink { + padding-left: 12px; + background: url(../img/admin/icon_deletelink.gif) 0 .25em no-repeat; +} + +a.deletelink:link, a.deletelink:visited { + color: #CC3434; +} + +a.deletelink:hover { + color: #993333; +} + +/* OBJECT TOOLS */ + +.object-tools { + font-size: 10px; + font-weight: bold; + font-family: Arial,Helvetica,sans-serif; + padding-left: 0; + float: right; + position: relative; + margin-top: -2.4em; + margin-bottom: -2em; +} + +.form-row .object-tools { + margin-top: 5px; + margin-bottom: 5px; + float: none; + height: 2em; + padding-left: 3.5em; +} + +.object-tools li { + display: block; + float: left; + background: url(../img/admin/tool-left.gif) 0 0 no-repeat; + padding: 0 0 0 8px; + margin-left: 2px; + height: 16px; +} + +.object-tools li:hover { + background: url(../img/admin/tool-left_over.gif) 0 0 no-repeat; +} + +.object-tools a:link, .object-tools a:visited { + display: block; + float: left; + color: white; + padding: .1em 14px .1em 8px; + height: 14px; + background: #999 url(../img/admin/tool-right.gif) 100% 0 no-repeat; +} + +.object-tools a:hover, .object-tools li:hover a { + background: #5b80b2 url(../img/admin/tool-right_over.gif) 100% 0 no-repeat; +} + +.object-tools a.viewsitelink, .object-tools a.golink { + background: #999 url(../img/admin/tooltag-arrowright.gif) top right no-repeat; + padding-right: 28px; +} + +.object-tools a.viewsitelink:hover, .object-tools a.golink:hover { + background: #5b80b2 url(../img/admin/tooltag-arrowright_over.gif) top right no-repeat; +} + +.object-tools a.addlink { + background: #999 url(../img/admin/tooltag-add.gif) top right no-repeat; + padding-right: 28px; +} + +.object-tools a.addlink:hover { + background: #5b80b2 url(../img/admin/tooltag-add_over.gif) top right no-repeat; +} + +/* OBJECT HISTORY */ + +table#change-history { + width: 100%; +} + +table#change-history tbody th { + width: 16em; +} + +/* PAGE STRUCTURE */ + +#container { + position: relative; + width: 100%; + min-width: 760px; + padding: 0; +} + +#content { + margin: 10px 15px; +} + +#header { + width: 100%; +} + +#content-main { + float: left; + width: 100%; +} + +#content-related { + float: right; + width: 18em; + position: relative; + margin-right: -19em; +} + +#footer { + clear: both; + padding: 10px; +} + +/* COLUMN TYPES */ + +.colMS { + margin-right: 20em !important; +} + +.colSM { + margin-left: 20em !important; +} + +.colSM #content-related { + float: left; + margin-right: 0; + margin-left: -19em; +} + +.colSM #content-main { + float: right; +} + +.popup .colM { + width: 95%; +} + +.subcol { + float: left; + width: 46%; + margin-right: 15px; +} + +.dashboard #content { + width: 500px; +} + +/* HEADER */ + +#header { + background: #417690; + color: #ffc; + overflow: hidden; +} + +#header a:link, #header a:visited { + color: white; +} + +#header a:hover { + text-decoration: underline; +} + +#branding h1 { + padding: 0 10px; + font-size: 18px; + margin: 8px 0; + font-weight: normal; + color: #f4f379; +} + +#branding h2 { + padding: 0 10px; + font-size: 14px; + margin: -8px 0 8px 0; + font-weight: normal; + color: #ffc; +} + +#user-tools { + position: absolute; + top: 0; + right: 0; + padding: 1.2em 10px; + font-size: 11px; + text-align: right; +} + +/* SIDEBAR */ + +#content-related h3 { + font-size: 12px; + color: #666; + margin-bottom: 3px; +} + +#content-related h4 { + font-size: 11px; +} + +#content-related .module h2 { + background: #eee url(../img/admin/nav-bg.gif) bottom left repeat-x; + color: #666; +} + diff --git a/bbb_django/bbb/static/css/forms.css b/bbb_django/bbb/static/css/forms.css new file mode 100644 index 0000000..35d0ed7 --- /dev/null +++ b/bbb_django/bbb/static/css/forms.css @@ -0,0 +1,354 @@ +@import url('widgets.css'); + +/* FORM ROWS */ + +.form-row { + overflow: hidden; + padding: 8px 12px; + font-size: 11px; + border-bottom: 1px solid #eee; +} + +.form-row img, .form-row input { + vertical-align: middle; +} + +form .form-row p { + padding-left: 0; + font-size: 11px; +} + +/* FORM LABELS */ + +form h4 { + margin: 0 !important; + padding: 0 !important; + border: none !important; +} + +label { + font-weight: normal !important; + color: #666; + font-size: 12px; +} + +.required label, label.required { + font-weight: bold !important; + color: #333 !important; +} + +/* RADIO BUTTONS */ + +form ul.radiolist li { + list-style-type: none; +} + +form ul.radiolist label { + float: none; + display: inline; +} + +form ul.inline { + margin-left: 0; + padding: 0; +} + +form ul.inline li { + float: left; + padding-right: 7px; +} + +/* ALIGNED FIELDSETS */ + +.aligned label { + display: block; + padding: 3px 10px 0 0; + float: left; + width: 8em; +} + +.aligned ul label { + display: inline; + float: none; + width: auto; +} + +.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField { + width: 350px; +} + +form .aligned p, form .aligned ul { + margin-left: 7em; + padding-left: 30px; +} + +form .aligned table p { + margin-left: 0; + padding-left: 0; +} + +form .aligned p.help { + padding-left: 38px; +} + +.aligned .vCheckboxLabel { + float: none !important; + display: inline; + padding-left: 4px; +} + +.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField { + width: 610px; +} + +.checkbox-row p.help { + margin-left: 0; + padding-left: 0 !important; +} + +fieldset .field-box { + float: left; + margin-right: 20px; +} + +/* WIDE FIELDSETS */ + +.wide label { + width: 15em !important; +} + +form .wide p { + margin-left: 15em; +} + +form .wide p.help { + padding-left: 38px; +} + +.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField { + width: 450px; +} + +/* COLLAPSED FIELDSETS */ + +fieldset.collapsed * { + display: none; +} + +fieldset.collapsed h2, fieldset.collapsed { + display: block !important; +} + +fieldset.collapsed h2 { + background-image: url(../img/admin/nav-bg.gif); + background-position: bottom left; + color: #999; +} + +fieldset.collapsed .collapse-toggle { + background: transparent; + display: inline !important; +} + +/* MONOSPACE TEXTAREAS */ + +fieldset.monospace textarea { + font-family: "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace; +} + +/* SUBMIT ROW */ + +.submit-row { + padding: 5px 7px; + text-align: right; + background: white url(../img/admin/nav-bg.gif) 0 100% repeat-x; + border: 1px solid #ccc; + margin: 5px 0; + overflow: hidden; +} + +.submit-row input { + margin: 0 0 0 5px; +} + +.submit-row p { + margin: 0.3em; +} + +.submit-row p.deletelink-box { + float: left; +} + +.submit-row .deletelink { + background: url(../img/admin/icon_deletelink.gif) 0 50% no-repeat; + padding-left: 14px; +} + +/* CUSTOM FORM FIELDS */ + +.vSelectMultipleField { + vertical-align: top !important; +} + +.vCheckboxField { + border: none; +} + +.vDateField, .vTimeField { + margin-right: 2px; +} + +.vURLField { + width: 30em; +} + +.vLargeTextField, .vXMLLargeTextField { + width: 48em; +} + +.flatpages-flatpage #id_content { + height: 40.2em; +} + +.module table .vPositiveSmallIntegerField { + width: 2.2em; +} + +.vTextField { + width: 20em; +} + +.vIntegerField { + width: 5em; +} + +.vForeignKeyRawIdAdminField { + width: 5em; +} + +/* INLINES */ + +.inline-group { + padding: 0; + border: 1px solid #ccc; + margin: 10px 0; +} + +.inline-group .aligned label { + width: 8em; +} + +.inline-related { + position: relative; +} + +.inline-related h3 { + margin: 0; + color: #666; + padding: 3px 5px; + font-size: 11px; + background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x; + border-bottom: 1px solid #ddd; +} + +.inline-related h3 span.delete { + float: right; +} + +.inline-related h3 span.delete label { + margin-left: 2px; + font-size: 11px; +} + +.inline-related fieldset { + margin: 0; + background: #fff; + border: none; +} + +.inline-related fieldset.module h3 { + margin: 0; + padding: 2px 5px 3px 5px; + font-size: 11px; + text-align: left; + font-weight: bold; + background: #bcd; + color: #fff; +} + +.inline-group .tabular fieldset.module { + border: none; + border-bottom: 1px solid #ddd; +} + +.inline-related.tabular fieldset.module table { + width: 100%; +} + +.last-related fieldset { + border: none; +} + +.inline-group .tabular tr.has_original td { + padding-top: 2em; +} + +.inline-group .tabular tr td.original { + padding: 2px 0 0 0; + width: 0; + _position: relative; +} + +.inline-group .tabular th.original { + width: 0px; + padding: 0; +} + +.inline-group .tabular td.original p { + position: absolute; + left: 0; + height: 1.1em; + padding: 2px 7px; + overflow: hidden; + font-size: 9px; + font-weight: bold; + color: #666; + _width: 700px; +} + +.inline-group ul.tools { + padding: 0; + margin: 0; + list-style: none; +} + +.inline-group ul.tools li { + display: inline; + padding: 0 5px; +} + +.inline-group div.add-row, +.inline-group .tabular tr.add-row td { + color: #666; + padding: 3px 5px; + border-bottom: 1px solid #ddd; + background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x; +} + +.inline-group .tabular tr.add-row td { + padding: 4px 5px 3px; + border-bottom: none; +} + +.inline-group ul.tools a.add, +.inline-group div.add-row a, +.inline-group .tabular tr.add-row td a { + background: url(../img/admin/icon_addlink.gif) 0 50% no-repeat; + padding-left: 14px; + font-size: 11px; + outline: 0; /* Remove dotted border around link */ +} + +.empty-form { + display: none; +} diff --git a/bbb_django/bbb/static/css/reset.css b/bbb_django/bbb/static/css/reset.css new file mode 100644 index 0000000..edee770 --- /dev/null +++ b/bbb_django/bbb/static/css/reset.css @@ -0,0 +1,7 @@ +/* +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.8.2r1 +*/ +html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;} \ No newline at end of file diff --git a/bbb_django/bbb/static/css/widgets.css b/bbb_django/bbb/static/css/widgets.css new file mode 100644 index 0000000..6e1a322 --- /dev/null +++ b/bbb_django/bbb/static/css/widgets.css @@ -0,0 +1,174 @@ +/* DATE AND TIME */ + +p.datetime { + line-height: 20px; + margin: 0; + padding: 0; + color: #666; + font-size: 11px; + font-weight: bold; +} + +.datetime span { + font-size: 11px; + color: #ccc; + font-weight: normal; + white-space: nowrap; +} + +table p.datetime { + font-size: 10px; + margin-left: 0; + padding-left: 0; +} + +/* CALENDARS & CLOCKS */ + +.calendarbox, .clockbox { + margin: 5px auto; + font-size: 11px; + width: 16em; + text-align: center; + background: white; + position: relative; +} + +.clockbox { + width: auto; +} + +.calendar { + margin: 0; + padding: 0; +} + +.calendar table { + margin: 0; + padding: 0; + border-collapse: collapse; + background: white; + width: 99%; +} + +.calendar caption, .calendarbox h2 { + margin: 0; + font-size: 11px; + text-align: center; + border-top: none; +} + +.calendar th { + font-size: 10px; + color: #666; + padding: 2px 3px; + text-align: center; + background: #e1e1e1 url(img/nav-bg.gif) 0 50% repeat-x; + border-bottom: 1px solid #ddd; +} + +.calendar td { + font-size: 11px; + text-align: center; + padding: 0; + border-top: 1px solid #eee; + border-bottom: none; +} + +.calendar td.selected a { + background: #C9DBED; +} + +.calendar td.nonday { + background: #efefef; +} + +.calendar td.today a { + background: #ffc; +} + +.calendar td a, .timelist a { + display: block; + font-weight: bold; + padding: 4px; + text-decoration: none; + color: #444; +} + +.calendar td a:hover, .timelist a:hover { + background: #5b80b2; + color: white; +} + +.calendar td a:active, .timelist a:active { + background: #036; + color: white; +} + +.calendarnav { + font-size: 10px; + text-align: center; + color: #ccc; + margin: 0; + padding: 1px 3px; +} + +.calendarnav a:link, #calendarnav a:visited, #calendarnav a:hover { + color: #999; +} + +.calendar-shortcuts { + background: white; + font-size: 10px; + line-height: 11px; + border-top: 1px solid #eee; + padding: 3px 0 4px; + color: #ccc; +} + +.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next { + display: block; + position: absolute; + font-weight: bold; + font-size: 12px; + background: #C9DBED url(img/default-bg.gif) bottom left repeat-x; + padding: 1px 4px 2px 4px; + color: white; +} + +.calendarnav-previous:hover, .calendarnav-next:hover { + background: #036; +} + +.calendarnav-previous { + top: 0; + left: 0; +} + +.calendarnav-next { + top: 0; + right: 0; +} + +.calendar-cancel { + margin: 0 !important; + padding: 0; + font-size: 10px; + background: #e1e1e1 url(img/nav-bg.gif) 0 50% repeat-x; + border-top: 1px solid #ddd; +} + +.calendar-cancel a { + padding: 2px; + color: #999; +} + +ul.timelist, .timelist li { + list-style-type: none; + margin: 0; + padding: 0; +} + +.timelist a { + padding: 2px; +} + diff --git a/bbb_django/bbb/static/img/default-bg.gif b/bbb_django/bbb/static/img/default-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..003aeca59fc46a07be968f351d75aa6e22e75f93 GIT binary patch literal 844 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 literal 0 HcmV?d00001 diff --git a/bbb_django/bbb/static/img/nav-bg.gif b/bbb_django/bbb/static/img/nav-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..f8402b809dc1efec80db6e466d789f88429a79a8 GIT binary patch literal 273 zcmZ?wbhEHb6l9QRIKsg2{rmT~Z{I$D{`}ReSAYNh{q^hDhYue(@t*9{u_A=gpfpKY#xG_U+rVXV3oq`}hC||i=Q&8(m$()z5YF*CjUEw001<4;4tjrTwH+&%FA_x~E6rskH`CdQ7= zuI>)uzWxal`(>Zw+Pr1!Chi?O LckSMx$Y2cs9wnNk literal 0 HcmV?d00001 diff --git a/bbb_django/bbb/static/js/DateTimeShortcuts.js b/bbb_django/bbb/static/js/DateTimeShortcuts.js new file mode 100644 index 0000000..8ddb84b --- /dev/null +++ b/bbb_django/bbb/static/js/DateTimeShortcuts.js @@ -0,0 +1,284 @@ +// Inserts shortcut buttons after all of the following: +// +// + +var DateTimeShortcuts = { + calendars: [], + calendarInputs: [], + clockInputs: [], + calendarDivName1: 'calendarbox', // name of calendar
that gets toggled + calendarDivName2: 'calendarin', // name of
that contains calendar + calendarLinkName: 'calendarlink',// name of the link that is used to toggle + clockDivName: 'clockbox', // name of clock
that gets toggled + clockLinkName: 'clocklink', // name of the link that is used to toggle + shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts + admin_media_prefix: '', + init: function() { + // Get admin_media_prefix by grabbing it off the window object. It's + // set in the admin/base.html template, so if it's not there, someone's + // overridden the template. In that case, we'll set a clearly-invalid + // value in the hopes that someone will examine HTTP requests and see it. + if (window.__admin_media_prefix__ != undefined) { + DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__; + } else { + DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/'; + } + + var inputs = document.getElementsByTagName('input'); + for (i=0; i + //

Choose a time

+ // + //

Cancel

+ //
+ + var clock_box = document.createElement('div'); + clock_box.style.display = 'none'; + clock_box.style.position = 'absolute'; + clock_box.className = 'clockbox module'; + clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num); + document.body.appendChild(clock_box); + addEvent(clock_box, 'click', DateTimeShortcuts.cancelEventPropagation); + + quickElement('h2', clock_box, gettext('Choose a time')); + time_list = quickElement('ul', clock_box, ''); + time_list.className = 'timelist'; + time_format = get_format('TIME_INPUT_FORMATS')[0]; + quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));"); + //quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));"); + //quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));"); + //quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));"); + + for(i=0;i<24;++i){ + for(j=0;j<60;j+=30){ + date_str = new Date(1970,1,1,i,j,0,0).strftime(time_format) + quickElement("a", quickElement("li", time_list, ""), date_str, "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ",'" + date_str + "');"); + } + } + + cancel_p = quickElement('p', clock_box, ''); + cancel_p.className = 'calendar-cancel'; + quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');'); + }, + openClock: function(num) { + var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num) + var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num) + + // Recalculate the clockbox position + // is it left-to-right or right-to-left layout ? + if (getStyle(document.body,'direction')!='rtl') { + clock_box.style.left = findPosX(clock_link) + 17 + 'px'; + } + else { + // since style's width is in em, it'd be tough to calculate + // px value of it. let's use an estimated px for now + // TODO: IE returns wrong value for findPosX when in rtl mode + // (it returns as it was left aligned), needs to be fixed. + clock_box.style.left = findPosX(clock_link) - 110 + 'px'; + } + clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px'; + clock_box.style.width = '150px'; + clock_box.style.height = '80px'; + clock_box.style.overflowY = 'auto'; + + // Show the clock box + clock_box.style.display = 'block'; + addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; }); + }, + dismissClock: function(num) { + document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none'; + window.document.onclick = null; + }, + handleClockQuicklink: function(num, val) { + DateTimeShortcuts.clockInputs[num].value = val; + DateTimeShortcuts.clockInputs[num].focus(); + DateTimeShortcuts.dismissClock(num); + }, + // Add calendar widget to a given field. + addCalendar: function(inp) { + var num = DateTimeShortcuts.calendars.length; + + DateTimeShortcuts.calendarInputs[num] = inp; + + // Shortcut links (calendar icon and "Today" link) + var shortcuts_span = document.createElement('span'); + shortcuts_span.className = DateTimeShortcuts.shortCutsClass; + inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling); + var today_link = document.createElement('a'); + today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);'); + today_link.appendChild(document.createTextNode(gettext('Today'))); + var cal_link = document.createElement('a'); + cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');'); + cal_link.id = DateTimeShortcuts.calendarLinkName + num; + quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_calendar.gif', 'alt', gettext('Calendar')); + shortcuts_span.appendChild(document.createTextNode('\240')); + shortcuts_span.appendChild(today_link); + shortcuts_span.appendChild(document.createTextNode('\240|\240')); + shortcuts_span.appendChild(cal_link); + + // Create calendarbox div. + // + // Markup looks like: + // + //
+ //

+ // + // February 2003 + //

+ //
+ // + //
+ //
+ // Yesterday | Today | Tomorrow + //
+ //

Cancel

+ //
+ var cal_box = document.createElement('div'); + cal_box.style.display = 'none'; + cal_box.style.position = 'absolute'; + cal_box.className = 'calendarbox module'; + cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num); + document.body.appendChild(cal_box); + addEvent(cal_box, 'click', DateTimeShortcuts.cancelEventPropagation); + + // next-prev links + var cal_nav = quickElement('div', cal_box, ''); + var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');'); + cal_nav_prev.className = 'calendarnav-previous'; + var cal_nav_next = quickElement('a', cal_nav, '>', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');'); + cal_nav_next.className = 'calendarnav-next'; + + // main box + var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num); + cal_main.className = 'calendar'; + DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num)); + DateTimeShortcuts.calendars[num].drawCurrent(); + + // calendar shortcuts + var shortcuts = quickElement('div', cal_box, ''); + shortcuts.className = 'calendar-shortcuts'; + quickElement('a', shortcuts, gettext('Yesterday'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);'); + shortcuts.appendChild(document.createTextNode('\240|\240')); + quickElement('a', shortcuts, gettext('Today'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);'); + shortcuts.appendChild(document.createTextNode('\240|\240')); + quickElement('a', shortcuts, gettext('Tomorrow'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);'); + + // cancel bar + var cancel_p = quickElement('p', cal_box, ''); + cancel_p.className = 'calendar-cancel'; + quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');'); + }, + openCalendar: function(num) { + var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num) + var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num) + var inp = DateTimeShortcuts.calendarInputs[num]; + + // Determine if the current value in the input has a valid date. + // If so, draw the calendar with that date's year and month. + if (inp.value) { + var date_parts = inp.value.split('-'); + var year = date_parts[0]; + var month = parseFloat(date_parts[1]); + if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) { + DateTimeShortcuts.calendars[num].drawDate(month, year); + } + } + + // Recalculate the clockbox position + // is it left-to-right or right-to-left layout ? + if (getStyle(document.body,'direction')!='rtl') { + cal_box.style.left = findPosX(cal_link) + 17 + 'px'; + } + else { + // since style's width is in em, it'd be tough to calculate + // px value of it. let's use an estimated px for now + // TODO: IE returns wrong value for findPosX when in rtl mode + // (it returns as it was left aligned), needs to be fixed. + cal_box.style.left = findPosX(cal_link) - 180 + 'px'; + } + cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px'; + + cal_box.style.display = 'block'; + addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; }); + }, + dismissCalendar: function(num) { + document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none'; + window.document.onclick = null; + }, + drawPrev: function(num) { + DateTimeShortcuts.calendars[num].drawPreviousMonth(); + }, + drawNext: function(num) { + DateTimeShortcuts.calendars[num].drawNextMonth(); + }, + handleCalendarCallback: function(num) { + format = get_format('DATE_INPUT_FORMATS')[0]; + // the format needs to be escaped a little + format = format.replace('\\', '\\\\'); + format = format.replace('\r', '\\r'); + format = format.replace('\n', '\\n'); + format = format.replace('\t', '\\t'); + format = format.replace("'", "\\'"); + return ["function(y, m, d) { DateTimeShortcuts.calendarInputs[", + num, + "].value = new Date(y, m-1, d).strftime('", + format, + "');DateTimeShortcuts.calendarInputs[", + num, + "].focus();document.getElementById(DateTimeShortcuts.calendarDivName1+", + num, + ").style.display='none';}"].join(''); + }, + handleCalendarQuickLink: function(num, offset) { + var d = new Date(); + d.setDate(d.getDate() + offset) + DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]); + DateTimeShortcuts.calendarInputs[num].focus(); + DateTimeShortcuts.dismissCalendar(num); + }, + cancelEventPropagation: function(e) { + if (!e) e = window.event; + e.cancelBubble = true; + if (e.stopPropagation) e.stopPropagation(); + } +} + +addEvent(window, 'load', DateTimeShortcuts.init); diff --git a/bbb_django/bbb/static/js/calendar.js b/bbb_django/bbb/static/js/calendar.js new file mode 100644 index 0000000..c95a95d --- /dev/null +++ b/bbb_django/bbb/static/js/calendar.js @@ -0,0 +1,156 @@ +/* +calendar.js - Calendar functions by Adrian Holovaty +*/ + +function removeChildren(a) { // "a" is reference to an object + while (a.hasChildNodes()) a.removeChild(a.lastChild); +} + +// quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]); +function quickElement() { + var obj = document.createElement(arguments[0]); + if (arguments[2] != '' && arguments[2] != null) { + var textNode = document.createTextNode(arguments[2]); + obj.appendChild(textNode); + } + var len = arguments.length; + for (var i = 3; i < len; i += 2) { + obj.setAttribute(arguments[i], arguments[i+1]); + } + arguments[1].appendChild(obj); + return obj; +} + +// CalendarNamespace -- Provides a collection of HTML calendar-related helper functions +var CalendarNamespace = { + monthsOfYear: gettext('January February March April May June July August September October November December').split(' '), + daysOfWeek: gettext('S M T W T F S').split(' '), + firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')), + isLeapYear: function(year) { + return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0)); + }, + getDaysInMonth: function(month,year) { + var days; + if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) { + days = 31; + } + else if (month==4 || month==6 || month==9 || month==11) { + days = 30; + } + else if (month==2 && CalendarNamespace.isLeapYear(year)) { + days = 29; + } + else { + days = 28; + } + return days; + }, + draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999 + var today = new Date(); + var todayDay = today.getDate(); + var todayMonth = today.getMonth()+1; + var todayYear = today.getFullYear(); + var todayClass = ''; + + month = parseInt(month); + year = parseInt(year); + var calDiv = document.getElementById(div_id); + removeChildren(calDiv); + var calTable = document.createElement('table'); + quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month-1] + ' ' + year); + var tableBody = quickElement('tbody', calTable); + + // Draw days-of-week header + var tableRow = quickElement('tr', tableBody); + for (var i = 0; i < 7; i++) { + quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]); + } + + var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay(); + var days = CalendarNamespace.getDaysInMonth(month, year); + + // Draw blanks before first of month + tableRow = quickElement('tr', tableBody); + for (var i = 0; i < startingPos; i++) { + var _cell = quickElement('td', tableRow, ' '); + _cell.style.backgroundColor = '#f3f3f3'; + } + + // Draw days of month + var currentDay = 1; + for (var i = startingPos; currentDay <= days; i++) { + if (i%7 == 0 && currentDay != 1) { + tableRow = quickElement('tr', tableBody); + } + if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) { + todayClass='today'; + } else { + todayClass=''; + } + var cell = quickElement('td', tableRow, '', 'class', todayClass); + + quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));'); + currentDay++; + } + + // Draw blanks after end of month (optional, but makes for valid code) + while (tableRow.childNodes.length < 7) { + var _cell = quickElement('td', tableRow, ' '); + _cell.style.backgroundColor = '#f3f3f3'; + } + + calDiv.appendChild(calTable); + } +} + +// Calendar -- A calendar instance +function Calendar(div_id, callback) { + // div_id (string) is the ID of the element in which the calendar will + // be displayed + // callback (string) is the name of a JavaScript function that will be + // called with the parameters (year, month, day) when a day in the + // calendar is clicked + this.div_id = div_id; + this.callback = callback; + this.today = new Date(); + this.currentMonth = this.today.getMonth() + 1; + this.currentYear = this.today.getFullYear(); +} +Calendar.prototype = { + drawCurrent: function() { + CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback); + }, + drawDate: function(month, year) { + this.currentMonth = month; + this.currentYear = year; + this.drawCurrent(); + }, + drawPreviousMonth: function() { + if (this.currentMonth == 1) { + this.currentMonth = 12; + this.currentYear--; + } + else { + this.currentMonth--; + } + this.drawCurrent(); + }, + drawNextMonth: function() { + if (this.currentMonth == 12) { + this.currentMonth = 1; + this.currentYear++; + } + else { + this.currentMonth++; + } + this.drawCurrent(); + }, + drawPreviousYear: function() { + this.currentYear--; + this.drawCurrent(); + }, + drawNextYear: function() { + this.currentYear++; + this.drawCurrent(); + } +} diff --git a/bbb_django/bbb/static/js/core.js b/bbb_django/bbb/static/js/core.js new file mode 100644 index 0000000..3ca8ad0 --- /dev/null +++ b/bbb_django/bbb/static/js/core.js @@ -0,0 +1,221 @@ +// Core javascript helper functions + +// basic browser identification & version +var isOpera = (navigator.userAgent.indexOf("Opera")>=0) && parseFloat(navigator.appVersion); +var isIE = ((document.all) && (!isOpera)) && parseFloat(navigator.appVersion.split("MSIE ")[1].split(";")[0]); + +// Cross-browser event handlers. +function addEvent(obj, evType, fn) { + if (obj.addEventListener) { + obj.addEventListener(evType, fn, false); + return true; + } else if (obj.attachEvent) { + var r = obj.attachEvent("on" + evType, fn); + return r; + } else { + return false; + } +} + +function removeEvent(obj, evType, fn) { + if (obj.removeEventListener) { + obj.removeEventListener(evType, fn, false); + return true; + } else if (obj.detachEvent) { + obj.detachEvent("on" + evType, fn); + return true; + } else { + return false; + } +} + +// quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]); +function quickElement() { + var obj = document.createElement(arguments[0]); + if (arguments[2] != '' && arguments[2] != null) { + var textNode = document.createTextNode(arguments[2]); + obj.appendChild(textNode); + } + var len = arguments.length; + for (var i = 3; i < len; i += 2) { + obj.setAttribute(arguments[i], arguments[i+1]); + } + arguments[1].appendChild(obj); + return obj; +} + +// ---------------------------------------------------------------------------- +// Cross-browser xmlhttp object +// from http://jibbering.com/2002/4/httprequest.html +// ---------------------------------------------------------------------------- +var xmlhttp; +/*@cc_on @*/ +/*@if (@_jscript_version >= 5) + try { + xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); + } catch (e) { + try { + xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); + } catch (E) { + xmlhttp = false; + } + } +@else + xmlhttp = false; +@end @*/ +if (!xmlhttp && typeof XMLHttpRequest != 'undefined') { + xmlhttp = new XMLHttpRequest(); +} + +// ---------------------------------------------------------------------------- +// Find-position functions by PPK +// See http://www.quirksmode.org/js/findpos.html +// ---------------------------------------------------------------------------- +function findPosX(obj) { + var curleft = 0; + if (obj.offsetParent) { + while (obj.offsetParent) { + curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft); + obj = obj.offsetParent; + } + // IE offsetParent does not include the top-level + if (isIE && obj.parentElement){ + curleft += obj.offsetLeft - obj.scrollLeft; + } + } else if (obj.x) { + curleft += obj.x; + } + return curleft; +} + +function findPosY(obj) { + var curtop = 0; + if (obj.offsetParent) { + while (obj.offsetParent) { + curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop); + obj = obj.offsetParent; + } + // IE offsetParent does not include the top-level + if (isIE && obj.parentElement){ + curtop += obj.offsetTop - obj.scrollTop; + } + } else if (obj.y) { + curtop += obj.y; + } + return curtop; +} + +//----------------------------------------------------------------------------- +// Date object extensions +// ---------------------------------------------------------------------------- +Date.prototype.getCorrectYear = function() { + // Date.getYear() is unreliable -- + // see http://www.quirksmode.org/js/introdate.html#year + var y = this.getYear() % 100; + return (y < 38) ? y + 2000 : y + 1900; +} + +Date.prototype.getTwelveHours = function() { + hours = this.getHours(); + if (hours == 0) { + return 12; + } + else { + return hours <= 12 ? hours : hours-12 + } +} + +Date.prototype.getTwoDigitMonth = function() { + return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1); +} + +Date.prototype.getTwoDigitDate = function() { + return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate(); +} + +Date.prototype.getTwoDigitTwelveHour = function() { + return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours(); +} + +Date.prototype.getTwoDigitHour = function() { + return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours(); +} + +Date.prototype.getTwoDigitMinute = function() { + return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes(); +} + +Date.prototype.getTwoDigitSecond = function() { + return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds(); +} + +Date.prototype.getISODate = function() { + return this.getCorrectYear() + '-' + this.getTwoDigitMonth() + '-' + this.getTwoDigitDate(); +} + +Date.prototype.getHourMinute = function() { + return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute(); +} + +Date.prototype.getHourMinuteSecond = function() { + return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond(); +} + +Date.prototype.strftime = function(format) { + var fields = { + c: this.toString(), + d: this.getTwoDigitDate(), + H: this.getTwoDigitHour(), + I: this.getTwoDigitTwelveHour(), + m: this.getTwoDigitMonth(), + M: this.getTwoDigitMinute(), + p: (this.getHours() >= 12) ? 'PM' : 'AM', + S: this.getTwoDigitSecond(), + w: '0' + this.getDay(), + x: this.toLocaleDateString(), + X: this.toLocaleTimeString(), + y: ('' + this.getFullYear()).substr(2, 4), + Y: '' + this.getFullYear(), + '%' : '%' + }; + var result = '', i = 0; + while (i < format.length) { + if (format.charAt(i) === '%') { + result = result + fields[format.charAt(i + 1)]; + ++i; + } + else { + result = result + format.charAt(i); + } + ++i; + } + return result; +} + +// ---------------------------------------------------------------------------- +// String object extensions +// ---------------------------------------------------------------------------- +String.prototype.pad_left = function(pad_length, pad_string) { + var new_string = this; + for (var i = 0; new_string.length < pad_length; i++) { + new_string = pad_string + new_string; + } + return new_string; +} + +// ---------------------------------------------------------------------------- +// Get the computed style for and element +// ---------------------------------------------------------------------------- +function getStyle(oElm, strCssRule){ + var strValue = ""; + if(document.defaultView && document.defaultView.getComputedStyle){ + strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); + } + else if(oElm.currentStyle){ + strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ + return p1.toUpperCase(); + }); + strValue = oElm.currentStyle[strCssRule]; + } + return strValue; +} diff --git a/bbb_django/bbb/templates/base.html b/bbb_django/bbb/templates/base.html index 2b1f842..68ada9f 100644 --- a/bbb_django/bbb/templates/base.html +++ b/bbb_django/bbb/templates/base.html @@ -4,9 +4,11 @@ {% block title %}{% endblock %} {% block extrahead %}{% endblock%} - + + @@ -80,7 +82,7 @@ h1 { text-indent: -9999px; display: block; - background: url(/static/logo.png) no-repeat top left; + background: url(/static/img/logo.png) no-repeat top left; height: 48px; width: 194px; margin: 0 auto; @@ -201,19 +203,20 @@ border-color: #ccc#ddd #eee#ddd; } -/* +/**/ tr { padding: 4px 0; - display: block; + //display: block; } + th { - min-width: 160px; + //min-width: 160px; text-align: right; line-height:1.6em; vertical-align: top; padding: 0.2em 0.4em 0.2em 0; } -*/ + diff --git a/bbb_django/bbb/templates/create.html b/bbb_django/bbb/templates/create.html index 7b2dedf..37873f1 100644 --- a/bbb_django/bbb/templates/create.html +++ b/bbb_django/bbb/templates/create.html @@ -5,11 +5,13 @@ {% block title %}{% trans "Create a meeting" %}{% endblock %} {% block extrahead %} + + - - - + + + {% endblock%} {% block content %} diff --git a/bbb_django/bbb/templates/meetings.html b/bbb_django/bbb/templates/meetings.html index 06a566a..01b0e5c 100644 --- a/bbb_django/bbb/templates/meetings.html +++ b/bbb_django/bbb/templates/meetings.html @@ -11,21 +11,23 @@

{% trans "Meetings" %}

{% for meeting in meetings %}
  • {{ meeting.name }} {% trans "view details" %} - {% if meeting.info.end_time == "0" %} - {% if meeting.running == 'true' %} - {% trans "Running" %} - {% else %} - {% trans "Not running" %} - {% endif %} -
    {% csrf_token %}
    - {% else %} - {% trans "Ended" %} - {% endif %} + {% if meeting.info.started %} + {% if meeting.running == 'true' %} + {% trans "Running" %} +
    {% csrf_token %}
    + {% else %} + {% trans "Ended" %} + {% endif %} + {% else %} + {% trans "Not started" %} +
    +
    + {% endif %}
    {% for item in meeting.info.items %} - {% endfor %} diff --git a/bbb_django/bbb/urls.py b/bbb_django/bbb/urls.py index 38ef60f..c66f7bc 100644 --- a/bbb_django/bbb/urls.py +++ b/bbb_django/bbb/urls.py @@ -1,6 +1,6 @@ from django.conf.urls.defaults import * from bbb.views.core import (home_page, create_meeting, begin_meeting, meetings, - join_meeting, delete_meeting) + join_meeting, delete_meeting, export_meeting) # Uncomment the next two lines to enable the admin: from django.contrib import admin @@ -19,14 +19,14 @@ def i18n_javascript(request): url(r'^logoff/$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='logout'), url('^create/$', create_meeting, name='create'), - url('^begin/$', begin_meeting, name='begin'), + #url('^begin/$', begin_meeting, name='begin'), url('^meetings/$', meetings, name='meetings'), - url('^meeting/(?P[a-zA-Z0-9 _-]+)/join$', join_meeting, - name='join'), + url('^meeting/(?P[a-zA-Z0-9 _-]+)/export$', export_meeting, name='export'), + url('^meeting/(?P[a-zA-Z0-9 _-]+)/join$', join_meeting, name='join'), url('^meeting/(?P[a-zA-Z0-9 _-]+)/(?P.*)/delete$', delete_meeting, name='delete'), url('^help.html$', 'django.views.generic.simple.redirect_to', { - 'url': 'http://www.bigbluebutton.org/content/videos' , + 'url': 'http://www.bbbforum.com' , }, name='help'), (r'^admin/jsi18n', i18n_javascript), diff --git a/bbb_django/bbb/views/core.py b/bbb_django/bbb/views/core.py index f83dd6a..8d54330 100644 --- a/bbb_django/bbb/views/core.py +++ b/bbb_django/bbb/views/core.py @@ -9,15 +9,38 @@ from django.contrib import messages from django.utils.translation import ugettext_lazy as _ +from icalendar import Calendar, Event +from datetime import timedelta import hashlib -from bbb.models import Meeting +from bbb.models import * def home_page(request): context = RequestContext(request, { }) return render_to_response('home.html', context) +def export_meeting(request, meeting_id): + meeting = Meeting.objects.get(id=meeting_id) + cal = Calendar() + cal.add('prodid', '-//commuxi Corporation//bbbforum release//') + cal.add('version', '2.0') + event = Event() + event.add('summary', meeting.name) + event.add('dtstart', meeting.start_time) + if meeting.duration == 0: + event.add('duration', timedelta(days=1))#unlimit is 1 day by default + else: + event.add('duration', timedelta(minutes=meeting.duration)) + #event.add('dtend', '') + event.add('location', 'http://www.commux.com') + event.add('description', 'please join the meeting via %s, the attendee password is "%s"'%\ + (request.build_absolute_uri(reverse('join',args=[meeting_id])), meeting.attendee_password)) + cal.add_component(event) + print cal.to_ical() + response = HttpResponse(cal.to_ical(), mimetype='text/calendar') + response['Content-Disposition'] = 'attachment; filename=%s.ics'%meeting.name + return response @login_required def begin_meeting(request): @@ -35,7 +58,32 @@ def begin_meeting(request): def meetings(request): #meetings = Meeting.objects.all() - meetings = Meeting.get_meetings() + existing = Meeting.objects.filter(user=request.user) + #meetings = Meeting.get_meetings() + started = Meeting.get_meetings() + + meetings = [] + for meeting in existing: + d = { + 'name': meeting.name, + 'meeting_id': meeting.id, + 'info': { + 'moderator_pw': meeting.moderator_password, + 'attendee_pw': meeting.attendee_password, + 'record': meeting.record, + 'duration': meeting.get_duration_display(), + 'start_time': meeting.start_time, + 'started': meeting.started, + }, + } + + detail = started.get('%d'%meeting.id) + print meeting.id, detail + if detail is not None: + d['running'] = detail['running'] + d['info'].update(detail['info']) + + meetings.append(d) context = RequestContext(request, { 'meetings': meetings, @@ -54,6 +102,14 @@ def join_meeting(request, meeting_id): name = data.get('name') password = data.get('password') + meeting = Meeting.objects.get(id=meeting_id) + if password == meeting.moderator_password: + #TODO: How to delete the records in db, lazy way? + #meeting.logout_url = request.build_absolute_uri(reverse('delete',args=[meeting_id, password])) + meeting.started = True + meeting.save() + url = meeting.start() + return HttpResponseRedirect(Meeting.join_url(meeting_id, name, password)) else: form = form_class() @@ -69,9 +125,10 @@ def join_meeting(request, meeting_id): @login_required def delete_meeting(request, meeting_id, password): - if request.method == "POST": - #meeting = Meeting.objects.filter(meeting_id=meeting_id) - #meeting.delete() + #if request.method == "POST": + if True: + meeting = Meeting.objects.filter(id=meeting_id) + meeting.delete() Meeting.end_meeting(meeting_id, password) msg = _('Successfully ended meeting %s') % meeting_id @@ -98,9 +155,14 @@ def create_meeting(request): meeting.moderator_password = data.get('moderator_password') #meeting.meeting_id = data.get('meeting_id') meeting.welcome = data.get('welcome') + meeting.record = data.get('record') + meeting.duration = data.get('duration') + meeting.start_time = data.get('start_time') + meeting.user = request.user meeting.save() - url = meeting.start() - msg = _('Successfully created meeting %s') % meeting.name + #url = meeting.start() + #msg = _('Successfully created meeting %s') % meeting.name + msg = _('Successfully schdulered meeting %s') % meeting.name messages.success(request, msg) return HttpResponseRedirect(reverse('meetings')) ''' diff --git a/bbb_django/migrate.sh b/bbb_django/migrate.sh new file mode 100755 index 0000000..e9df6da --- /dev/null +++ b/bbb_django/migrate.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +sudo /home/jeromy/dev/bbb_python/env/bin/python manage.py schemamigration bbb --auto +sudo /home/jeromy/dev/bbb_python/env/bin/python manage.py migrate bbb diff --git a/requirements.txt b/requirements.txt index 9efff1a..97d19e4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,5 @@ Django==1.3 flup South +pytz +calendar
    {{ item.0 }} + {{ item.0 }} {{ item.1 }}