Commit 38c7535e by Martyn James

Merge pull request #5 from edx-solutions/mjevtic/validation

Validation
parents 258ad437 3cce68c5
......@@ -88,7 +88,12 @@ class GoogleCalendarBlock(XBlock):
}))
fragment.add_javascript(load_resource('public/js/google_calendar_edit.js'))
fragment.initialize_js('GoogleCalendarEditBlock')
defaults = {
'defaultName': self.fields['display_name']._default,
'defaultID': self.fields['calendar_id']._default
}
fragment.initialize_js('GoogleCalendarEditBlock', defaults)
return fragment
......
......@@ -10,6 +10,7 @@ import textwrap
#import webob
#from lxml import etree
#from xml.etree import ElementTree as ET
import requests
from xblock.core import XBlock
from xblock.fields import Scope, String
......@@ -81,7 +82,7 @@ class GoogleDocumentBlock(XBlock):
}))
fragment.add_javascript(load_resource('public/js/google_docs_edit.js'))
fragment.initialize_js('GoogleDocumentEditBlock')
fragment.initialize_js('GoogleDocumentEditBlock', {'defaultName': self.fields['display_name']._default})
return fragment
......@@ -112,3 +113,17 @@ class GoogleDocumentBlock(XBlock):
return {
'result': 'success',
}
@XBlock.json_handler
def check_url(self, data, suffix=''):
try:
r = requests.head(data['url'])
except:
return {
'status_code': 404,
}
return {
'status_code': r.status_code,
}
......@@ -12,3 +12,100 @@
width: 100%;
height: 100%;
}
#validation_alert{
width: 100%;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
background-color: #323232;
border-bottom: 3px solid rgb(192, 172, 0);
padding: 10px;
position: absolute;
top:0;
z-index: 10;
max-height: 200px;
}
.alert_icon{
position: absolute;
top: 35%;
font-size: 200%;
left: 3%;
}
.alert_icon:before{
font-family: "FontAwesome";
content: "\f071";
display: inline-block;
color: rgb(192, 172, 0);
float: left;
height: 0;
width: 0;
}
.alert_header {
width: 85%;
margin: 0 5% 0 10%;
}
.alert_title {
width: auto;
color: white;
}
.alert_message {
font-size: 80%;
color: darkgray;
}
[class^="icon"] {
width: auto;
margin: 0;
padding: 2px;
}
&:hover {
color: rgb(128, 169, 204);
}
.is--hidden {
height: 0;
width: 0;
padding: 0;
margin: 0;
pointer-events: none;
outline: none;
}
.covered, .is--hidden {
display: none;
visibility: hidden;
}
.error {
outline: none !important;
border:2px solid red !important;
box-shadow: 0 0 10px #719ECE !important;
}
#save-button.disabled {
background-color: gray;
cursor: auto;
}
#google-docs-actions{
position: absolute;
bottom: 0;
}
.editor_content_wrapper {
width: 100%;
overflow-y: scroll;
position: absolute;
bottom: 0;
top: 0;
}
#xblock-inputs.alerted {
top: 69px;
}
......@@ -12,3 +12,100 @@
width: 100%;
height: 100%;
}
#validation_alert{
width: 100%;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
background-color: #323232;
border-bottom: 3px solid rgb(192, 172, 0);
padding: 10px;
position: absolute;
top:0;
z-index: 10;
max-height: 200px;
}
.alert_icon{
position: absolute;
top: 35%;
font-size: 200%;
left: 3%;
}
.alert_icon:before{
font-family: "FontAwesome";
content: "\f071";
display: inline-block;
color: rgb(192, 172, 0);
float: left;
height: 0;
width: 0;
}
.alert_header {
width: 85%;
margin: 0 5% 0 10%;
}
.alert_title {
width: auto;
color: white;
}
.alert_message {
font-size: 80%;
color: darkgray;
}
[class^="icon"] {
width: auto;
margin: 0;
padding: 2px;
}
&:hover {
color: rgb(128, 169, 204);
}
.is--hidden {
height: 0;
width: 0;
padding: 0;
margin: 0;
pointer-events: none;
outline: none;
}
.covered, .is--hidden {
display: none;
visibility: hidden;
}
.error {
outline: none !important;
border:2px solid red;
box-shadow: 0 0 10px #719ECE;
}
#save-button.disabled {
background-color: gray;
cursor: auto;
}
#google-docs-actions{
position: absolute;
bottom: 0;
}
.editor_content_wrapper {
width: 100%;
overflow-y: scroll;
position: absolute;
bottom: 0;
top: 0;
}
#xblock-inputs.alerted {
top: 69px;
}
function GoogleCalendarEditBlock(runtime, element) {
$('.save-button', element).bind('click', function() {
function GoogleCalendarEditBlock(runtime, element, defaults) {
var clear_name_button = $('.clear-display-name', element);
var clear_id_button = $('.clear-calendar-id', element);
var save_button = $('.save-button', element);
var validation_alert = $('#validation_alert', element);
var xblock_inputs_wrapper = $('#xblock-inputs', element);
ToggleClearDefaultName();
ToggleClearCalendarID();
$('.clear-display-name', element).bind('click', function() {
$(this).addClass('inactive');
$('#edit_display_name', element).val(defaults.defaultName);
});
$('#edit_display_name', element).bind('keyup', function(){
ToggleClearDefaultName();
});
$('.clear-calendar-id', element).bind('click', function() {
$(this).addClass('inactive');
$('#edit_calendar_id', element).val(defaults.defaultID);
save_button.unbind('click').bind('click', SaveEditing);
});
$('#edit_calendar_id', element).bind('keyup', function(){
ToggleClearCalendarID();
var inputVal = $(this).val();
var calendarIDReg = /[\w-\.]+@+[\w-\.]/;
if(!calendarIDReg.test(inputVal)) {
save_button.addClass('disabled').unbind('click');
validation_alert.removeClass('covered');
$(this).addClass('error');
xblock_inputs_wrapper.addClass('alerted');
} else {
validation_alert.addClass('covered');
save_button.removeClass('disabled');
$(this).removeClass('error');
xblock_inputs_wrapper.removeClass('alerted');
save_button.bind('click', SaveEditing);
}
});
$('.cancel-button', element).bind('click', function() {
runtime.notify('cancel', {});
});
function ToggleClearDefaultName(){
if ($('#edit_display_name').val() == defaults.defaultName){
if (!clear_name_button.hasClass('inactive')){
clear_name_button.addClass('inactive');
}
}
else {
clear_name_button.removeClass('inactive');
}
}
function ToggleClearCalendarID(){
if ($('#edit_calendar_id').val() == defaults.defaultID){
if (!clear_id_button.hasClass('inactive')){
clear_id_button.addClass('inactive');
}
}
else {
clear_id_button.removeClass('inactive');
}
}
function SaveEditing(){
var data = {
'display_name': $('.edit-display-name', element).val(),
'calendar_id': $('.edit-calendar-id', element).val(),
......@@ -17,9 +88,5 @@ function GoogleCalendarEditBlock(runtime, element) {
$('.xblock-editor-error-message', element).css('display', 'block');
}
});
});
$('.cancel-button', element).bind('click', function() {
runtime.notify('cancel', {});
});
}
}
function GoogleDocumentEditBlock(runtime, element) {
$('.save-button', element).bind('click', function() {
function GoogleDocumentEditBlock(runtime, element, defaults) {
var clear_name_button = $('.clear-display-name', element);
var save_button = $('.save-button', element);
var validation_alert = $('#validation_alert', element);
var embed_code_textbox = $('#edit_embed_code', element);
var xblock_inputs_wrapper = $('#xblock-inputs', element);
ToggleClearDefaultName();
IsUrlValid();
$('.clear-display-name', element).bind('click', function() {
$(this).addClass('inactive');
$('#edit_display_name', element).val(defaults.defaultName);
});
$('#edit_display_name', element).bind('keyup', function(){
ToggleClearDefaultName();
});
$('#edit_embed_code', element).bind('keyup', function(){
IsUrlValid();
});
$('.cancel-button', element).bind('click', function() {
runtime.notify('cancel', {});
});
function ToggleClearDefaultName(name, button){
if ($('#edit_display_name').val() == defaults.defaultName){
if (!clear_name_button.hasClass('inactive')){
clear_name_button.addClass('inactive');
}
}
else {
clear_name_button.removeClass('inactive');
}
}
function SaveEditing(){
var data = {
'display_name': $('.edit-display-name', element).val(),
'embed_code': $('.edit-embed-code', element).val(),
......@@ -16,9 +55,42 @@ function GoogleDocumentEditBlock(runtime, element) {
$('.xblock-editor-error-message', element).css('display', 'block');
}
});
});
}
$('.cancel-button', element).bind('click', function() {
runtime.notify('cancel', {});
});
function IsUrlValid(){
var embed_html = $("#edit_embed_code", element).val();
var google_doc = $(embed_html);
$('#edit_embed_code', element).css({'cursor':'wait'});
save_button.addClass('disabled').unbind('click');
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'check_url'),
data: JSON.stringify({url: google_doc.attr("src")}),
success: function(result) {
if (result.status_code >= 400){
validation_alert.removeClass('covered');
embed_code_textbox.addClass('error');
xblock_inputs_wrapper.addClass('alerted');
} else {
validation_alert.addClass('covered');
save_button.removeClass('disabled');
embed_code_textbox.removeClass('error');
xblock_inputs_wrapper.removeClass('alerted');
save_button.bind('click', SaveEditing);
}
},
error: function(result) {
validation_alert.removeClass('covered');
save_button.addClass('disabled').unbind('click');
embed_code_textbox.addClass('error');
xblock_inputs_wrapper.addClass('alerted');
},
complete: function() {
$('#edit_embed_code', element).css({'cursor':'auto'});
}
});
}
}
......@@ -2,49 +2,66 @@
<!-- TODO: Replace by default edit view once available in Studio -->
<div class="wrapper-comp-settings is-active editor-with-buttons " id="settings-tab">
<ul class="list-input settings-list">
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_display_name">{% trans "Display Name" %}</label>
<input class="input setting-input edit-display-name" id="edit_display_name" value="{{ self.display_name }}" type="text">
</div>
<span class="tip setting-help">{% trans "This name appears in the horizontal navigation at the top of the page." %}</span>
</li>
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_calendar_id">{% trans "Public Calendar ID" %}</label>
<textarea rows="3" cols="60" class="input setting-input edit-calendar-id" id="edit_calendar_id">
{{ self.calendar_id }}
</textarea>
</div>
<span class="tip setting-help">{% trans "Google provides an ID for publicly available calendars. In the Google Calendar, open Settings and copy the ID from the Calendar Address section into this field. You can " %}
<a href="https://support.google.com/calendar/answer/34578?ctx=tltp" target="_blank">learn more here</a>.
</span>
</li>
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_default_view">{% trans "Default View" %}</label>
<select class="input setting-input edit-label_type" id="edit_label_type" value="{{self.default_view}}">
{% for view in self.views %}
<option value="{{forloop.counter0}}" {% if forloop.counter0 == self.default_view %}selected="selected"{% endif %}>
{{view}}
</option>
{% endfor %}
</select>
</div>
<span class="tip setting-help">{% trans "The calendar view that students see by default. A student can change this view." %}</span>
</li>
<li class="field comp-setting-entry is-set"></li>
</ul>
<div class="xblock-actions">
<div class="user-inputs-and-validation">
<div id="validation_alert" class="covered">
<i class="alert_icon"></i>
<div class="alert_header">
<h2 class="alert_title">{% trans "Invalid Google Document" %}</h2>
<p class="alert_message">{% trans "Please correct the outlined fields." %}</p>
</div>
</div>
<div id="xblock-inputs" class="editor_content_wrapper">
<ul class="list-input settings-list">
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_display_name">{% trans "Display Name" %}</label>
<input class="input setting-input edit-display-name" id="edit_display_name" value="{{ self.display_name }}" type="text">
<button class="action setting-clear clear-display-name" type="button" name="setting-clear">
<i class="icon-undo"></i>
<span class="sr">"<%= gettext("Clear Value") %>"</span>
</button>
</div>
<span class="tip setting-help">{% trans "This name appears in the horizontal navigation at the top of the page." %}</span>
</li>
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_calendar_id">{% trans "Public Calendar ID" %}</label>
<input class="input setting-input edit-calendar-id" id="edit_calendar_id" value="{{ self.calendar_id }}" type="text">
<button class="action setting-clear clear-calendar-id" type="button" name="setting-clear">
<i class="icon-undo"></i>
<span class="sr">"<%= gettext("Clear Value") %>"</span>
</button>
</div>
<span class="tip setting-help">{% trans "Google provides an ID for publicly available calendars. In the Google Calendar, open Settings and copy the ID from the Calendar Address section into this field. You can " %}
<a href="https://support.google.com/calendar/answer/34578?ctx=tltp" target="_blank">learn more here</a>.
</span>
</li>
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_default_view">{% trans "Default View" %}</label>
<select class="input setting-input edit-label_type" id="edit_label_type" value="{{self.default_view}}">
{% for view in self.views %}
<option value="{{forloop.counter0}}" {% if forloop.counter0 == self.default_view %}selected="selected"{% endif %}>
{{view}}
</option>
{% endfor %}
</select>
</div>
<span class="tip setting-help">{% trans "The calendar view that students see by default. A student can change this view." %}</span>
</li>
<li class="field comp-setting-entry is-set"></li>
</ul>
</div>
</div>
<div class="xblock-actions" id="google-calendar-actions">
<span class="xblock-editor-error-message"></span>
<ul>
<li class="action-item">
<a href="#" class="button action-primary save-button">{% trans "Save" %}</a>
<a href="#" class="button action-primary save-button" id="save_button">{% trans "Save" %}</a>
</li>
<li class="action-item">
<a href="#" class="button cancel-button">{% trans "Cancel" %}</a>
<a href="#" class="button cancel-button" id="cancel_button">{% trans "Cancel" %}</a>
</li>
</ul>
</div>
......
......@@ -2,34 +2,46 @@
<!-- TODO: Replace by default edit view once available in Studio -->
<div class="wrapper-comp-settings is-active editor-with-buttons " id="settings-tab">
<ul class="list-input settings-list">
<li class="field comp-setting-entry is-set">
<div class="user-inputs-and-validation">
<div id="validation_alert" class="covered">
<i class="alert_icon"></i>
<div class="alert_header">
<h2 class="alert_title">{% trans "Invalid Google Document" %}</h2>
<p class="alert_message">{% trans "Please correct the outlined fields." %}</p>
</div>
</div>
<div id="xblock-inputs" class="editor_content_wrapper">
<ul class="list-input settings-list">
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_display_name">{% trans "Display Name" %}</label>
<input class="input setting-input edit-display-name" id="edit_display_name" value="{{ self.display_name }}" type="text">
<button class="action setting-clear clear-display-name" type="button" name="setting-clear">
<i class="icon-undo"></i>
<span class="sr">"<%= gettext("Clear Value") %>"</span>
</button>
</div>
<span class="tip setting-help">{% trans "This name appears in the horizontal navigation at the top of the page." %}</span>
</li>
<li class="field comp-setting-entry is-set">
</li>
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="edit_embed_code">{% trans "Embed Code" %}</label>
<textarea rows="3" cols="60" class="input setting-input edit-embed-code" id="edit_embed_code">
{{ self.embed_code }}
</textarea>
<textarea rows="3" cols="60" class="input setting-input edit-embed-code" id="edit_embed_code">{{ self.embed_code }}</textarea>
</div>
<span class="tip setting-help">{% trans "Google provides an embed code for Drive documents. In the Google Drive document, from the File menu, select Publish to the Web. Modify settings as needed, click Publish, and copy the embed code into this field." %}</span>
</li>
<li class="field comp-setting-entry is-set"></li>
</ul>
<div class="xblock-actions">
</li>
</ul>
</div>
</div>
<div class="xblock-actions" id="google-docs-actions">
<span class="xblock-editor-error-message"></span>
<ul>
<li class="action-item">
<a href="#" class="button action-primary save-button">{% trans "Save" %}</a>
<a href="#" class="button action-primary save-button" id="save_button">{% trans "Save" %}</a>
</li>
<li class="action-item">
<a href="#" class="button cancel-button">{% trans "Cancel" %}</a>
<a href="#" class="button cancel-button" id="cancel_button">{% trans "Cancel" %}</a>
</li>
</ul>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment