Commit 17765d8c by swdanielli

server side event, some code clean up and corner case handling

parent 087e0074
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
import json, string, random, re import json, string, random, re
import pkg_resources import pkg_resources
from eventtracking import tracker
from mako.template import Template from mako.template import Template
from mako.lookup import TemplateLookup from mako.lookup import TemplateLookup
...@@ -102,9 +105,11 @@ class RecommenderXBlock(XBlock): ...@@ -102,9 +105,11 @@ class RecommenderXBlock(XBlock):
''' untested ''' ''' untested '''
resource = data['resource'] resource = data['resource']
idx = self.getEntryIndex(resource, self.recommendations) idx = self.getEntryIndex(resource, self.recommendations)
oldVotes = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
if resource in self.upvotes: if resource in self.upvotes:
del self.upvotes[self.upvotes.index(resource)] del self.upvotes[self.upvotes.index(resource)]
self.recommendations[idx]['upvotes'] -= 1 self.recommendations[idx]['upvotes'] -= 1
tracker.emit('recommender_upvote', {'id': resource, 'oldVotes': oldVotes, 'newVotes': self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']})
print "Upvote unclicked!" print "Upvote unclicked!"
return {"Success": True} return {"Success": True}
...@@ -113,6 +118,7 @@ class RecommenderXBlock(XBlock): ...@@ -113,6 +118,7 @@ class RecommenderXBlock(XBlock):
self.recommendations[idx]['downvotes'] -= 1 self.recommendations[idx]['downvotes'] -= 1
self.upvotes.append(resource) self.upvotes.append(resource)
self.recommendations[idx]['upvotes'] += 1 self.recommendations[idx]['upvotes'] += 1
tracker.emit('recommender_upvote', {'id': resource, 'oldVotes': oldVotes, 'newVotes': self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']})
print "Upvote clicked!" print "Upvote clicked!"
return {"Success": True} return {"Success": True}
...@@ -121,9 +127,11 @@ class RecommenderXBlock(XBlock): ...@@ -121,9 +127,11 @@ class RecommenderXBlock(XBlock):
''' untested ''' ''' untested '''
resource = data['resource'] resource = data['resource']
idx = self.getEntryIndex(resource, self.recommendations) idx = self.getEntryIndex(resource, self.recommendations)
oldVotes = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
if resource in self.downvotes: if resource in self.downvotes:
del self.downvotes[self.downvotes.index(resource)] del self.downvotes[self.downvotes.index(resource)]
self.recommendations[idx]['downvotes'] -= 1 self.recommendations[idx]['downvotes'] -= 1
tracker.emit('recommender_downvote', {'id': resource, 'oldVotes': oldVotes, 'newVotes': self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']})
print "Downvote unclicked!" print "Downvote unclicked!"
return {"Success": True} return {"Success": True}
...@@ -132,6 +140,7 @@ class RecommenderXBlock(XBlock): ...@@ -132,6 +140,7 @@ class RecommenderXBlock(XBlock):
self.recommendations[idx]['upvotes'] -= 1 self.recommendations[idx]['upvotes'] -= 1
self.downvotes.append(resource) self.downvotes.append(resource)
self.recommendations[idx]['downvotes'] += 1 self.recommendations[idx]['downvotes'] += 1
tracker.emit('recommender_downvote', {'id': resource, 'oldVotes': oldVotes, 'newVotes': self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']})
print "Downvote clicked!" print "Downvote clicked!"
return {"Success": True} return {"Success": True}
...@@ -146,8 +155,13 @@ class RecommenderXBlock(XBlock): ...@@ -146,8 +155,13 @@ class RecommenderXBlock(XBlock):
elif re.search('.jpg$', str(request.POST['file'].file)): elif re.search('.jpg$', str(request.POST['file'].file)):
filetype = '.jpg' filetype = '.jpg'
else: else:
tracker.emit('upload_screenshot', {'uploadedFileName': 'FILETYPEERROR'})
print "Wrong file type" print "Wrong file type"
return {"Success": False} response = Response()
response.body = 'FILETYPEERROR'
response.headers['Content-Type'] = 'text/plain'
return response
myS3FS = S3FS('danielswli', aws_access_key=aws_access_key_env, aws_secret_key=aws_secret_key_env) myS3FS = S3FS('danielswli', aws_access_key=aws_access_key_env, aws_secret_key=aws_secret_key_env)
while True: while True:
file_id = ''.join(random.choice(chars) for _ in range(size)) file_id = ''.join(random.choice(chars) for _ in range(size))
...@@ -163,30 +177,34 @@ class RecommenderXBlock(XBlock): ...@@ -163,30 +177,34 @@ class RecommenderXBlock(XBlock):
response = Response() response = Response()
response.body = filename response.body = filename
response.headers['Content-Type'] = 'text/plain' response.headers['Content-Type'] = 'text/plain'
print 'before return' tracker.emit('upload_screenshot', {'uploadedFileName': filename})
return response return response
@XBlock.json_handler @XBlock.json_handler
def add_resource(self, data, suffix=''): def add_resource(self, data, suffix=''):
''' untested ''' ''' untested '''
resource = data['resource'] resource = data['resource']
# Construct new resource
valid_fields = ['url', 'title', 'description']
new_resource = {}
for field in valid_fields:
new_resource[field] = data['resource'][field]
# check url for redundancy # check url for redundancy
for recom in self.recommendations: for recom in self.recommendations:
if recom['url'] == data['resource']['url']: if recom['url'] == data['resource']['url']:
print "add redundant resource" print "add redundant resource"
new_resource['error'] = 'redundant resource'
tracker.emit('add_resource', new_resource)
return {"Success": False} return {"Success": False}
# Construct new resource new_resource['id'] = self.getResourceNewId()
valid_fields = ['url', 'title', 'description'] tracker.emit('add_resource', new_resource)
new_resource = {}
for field in valid_fields:
new_resource[field] = data['resource'][field]
new_resource['upvotes'] = 0 new_resource['upvotes'] = 0
new_resource['downvotes'] = 0 new_resource['downvotes'] = 0
new_resource['id'] = self.getResourceNewId()
new_resource['isProblematic'] = "notProblematic" new_resource['isProblematic'] = "notProblematic"
new_resource['problematicReason'] = "" new_resource['problematicReason'] = ""
# self.resources.append(new_resource)
self.recommendations.append(new_resource) self.recommendations.append(new_resource)
return {"Success": True, "id": new_resource['id']} return {"Success": True, "id": new_resource['id']}
...@@ -196,36 +214,54 @@ class RecommenderXBlock(XBlock): ...@@ -196,36 +214,54 @@ class RecommenderXBlock(XBlock):
idx = self.getEntryIndex(resource, self.recommendations) idx = self.getEntryIndex(resource, self.recommendations)
if idx not in range(0, len(self.recommendations)): if idx not in range(0, len(self.recommendations)):
print "Edit failed!" print "Edit failed!"
tracker.emit('edit_resource', {'id': resource, 'error': 'bad id'})
return {"Success": False} return {"Success": False}
valid_fields = ['url', 'title', 'description']
resource_log = {}
resource_log['id'] = resource
for field in valid_fields:
resource_log['old_' + field] = self.recommendations[idx][field]
resource_log[field] = data[field]
# check url for redundancy # check url for redundancy
for recom in self.recommendations: for recom in self.recommendations:
if self.recommendations[idx]['url'] == data['url']: if self.recommendations[idx]['url'] == data['url']:
continue continue
if recom['url'] == data['url']: if recom['url'] == data['url']:
resource_log['error'] = 'existing url'
print "provided url is existing" print "provided url is existing"
tracker.emit('edit_resource', resource_log)
return {"Success": False} return {"Success": False}
for key in data: for key in data:
if key == 'resource': if key == 'resource':
next continue
if data[key] == '': if data[key] == "":
next continue
self.recommendations[idx][key] = data[key] self.recommendations[idx][key] = data[key]
tracker.emit('edit_resource', resource_log)
return {"Success": True} return {"Success": True}
@XBlock.json_handler @XBlock.json_handler
def flag_resource(self, data, suffix=''): def flag_resource(self, data, suffix=''):
flagLog = {}
flagLog['id'] = data['resource']
flagLog['isProblematic'] = data['isProblematic']
flagLog['reason'] = data['reason']
if data['isProblematic'] == True: if data['isProblematic'] == True:
if data['resource'] in self.flagId: if data['resource'] in self.flagId:
flagLog['oldReason'] = self.flagReason[self.flagId.index(data['resource'])]
self.flagReason[self.flagId.index(data['resource'])] = data['reason'] self.flagReason[self.flagId.index(data['resource'])] = data['reason']
else: else:
self.flagId.append(data['resource']) self.flagId.append(data['resource'])
self.flagReason.append(data['reason']) self.flagReason.append(data['reason'])
else: else:
if data['resource'] in self.flagId: if data['resource'] in self.flagId:
flagLog['oldReason'] = self.flagReason[self.flagId.index(data['resource'])]
idx = self.flagId.index(data['resource']) idx = self.flagId.index(data['resource'])
del self.flagId[idx] del self.flagId[idx]
del self.flagReason[idx] del self.flagReason[idx]
tracker.emit('flag_resource', flagLog)
return {"Success": True} return {"Success": True}
# TO-DO: change this view to display your data your own way. # TO-DO: change this view to display your data your own way.
......
...@@ -9,6 +9,12 @@ ...@@ -9,6 +9,12 @@
</div> </div>
<div class='recommender_row_inner'> <div class='recommender_row_inner'>
<div class="recommender_row"> <div class="recommender_row">
<div class="noResourceIntro hidden">
This will be a list of resources your fellow students
thought might be helpful, but it is empty currently.
If you find useful resources, either on edx.org or
elsewhere, please add it.
</div>
% for elem in resources: % for elem in resources:
<% <%
if elem['id'] in downvotes: if elem['id'] in downvotes:
...@@ -26,7 +32,6 @@ ...@@ -26,7 +32,6 @@
reason = '' reason = ''
%> %>
<%include file="resourcebox.html" args="id=elem['id'],title=elem['title'],votes=elem['votes'],url=elem['url'],description=elem['description'],isProblematic=flagMode,problematicReason=reason,voteMode=voteMode" /> <%include file="resourcebox.html" args="id=elem['id'],title=elem['title'],votes=elem['votes'],url=elem['url'],description=elem['description'],isProblematic=flagMode,problematicReason=reason,voteMode=voteMode" />
% endfor % endfor
</div> </div>
<div class='pagination'> </div> <div class='pagination'> </div>
...@@ -56,20 +61,30 @@ ...@@ -56,20 +61,30 @@
<input type="button" value="Flag resource" class="flag_reason_submit" style="margin-top: 0.5em;"> <input type="button" value="Flag resource" class="flag_reason_submit" style="margin-top: 0.5em;">
</div> </div>
<div class="editSourceBlock"> <div class="editSourceBlock">
<div class="editSourceBlockTitle">Edit the description, hypelink, and previewing screenshot for the selected resource.</div>
<div style="float: left">Description&nbsp;</div><div class = 'redTxt'>*</div>
<textarea
class="edit_title"
placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><br/>
<div style="float: left">Location&nbsp;</div><div class = 'redTxt'>*</div>
<input type="text"
class="edit_url"
placeholder="http://en.wikipedia.org/wiki/Edx"/><br/>
<form method="post" id="editResourceForm">
Previewing screenshot: <input type="file" name="file"><br/>
<input type="button" value="Save change" class="edit_submit" style="margin-top: 0.5em" disabled >
</form>
</div> </div>
<div class="recommender_add"> <div class="recommender_add">
<div class="addSourceBlockTitle">Suggest a resource which can help other students with this problem.</div> <div class="addSourceBlockTitle">Suggest a resource which can help other students with this problem.</div>
Description <input type="text" <div style="float: left">Description&nbsp;</div><div class = 'redTxt'>*</div><textarea
class="in_title" class="in_title"
style="height: 25px; position: relative; left: 10px; width:100%" placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><br/>
placeholder="Provide a meaningful description so other students know whether this is useful to them"/><br/> <div style="float: left">Location&nbsp;</div><div class = 'redTxt'>*</div> <input type="text"
Location <input type="text"
class="in_url" class="in_url"
style="height: 25px; position: relative; left: 22px; width:100%"
placeholder="http://en.wikipedia.org/wiki/Edx"/><br/> placeholder="http://en.wikipedia.org/wiki/Edx"/><br/>
<form method="post" id="addResourceForm"> <form method="post" id="addResourceForm">
Previewing screenshot: <input type="file" name="file"><br/> Previewing screenshot: <input type="file" name="file"><br/>
<div class='in_filename hidden'></div>
<input type="button" value="Add resource" class="add_submit" style="margin-top: 0.5em" disabled > <input type="button" value="Add resource" class="add_submit" style="margin-top: 0.5em" disabled >
</form> </form>
</div> </div>
......
...@@ -14,13 +14,11 @@ function RecommenderXBlock(runtime, element) { ...@@ -14,13 +14,11 @@ function RecommenderXBlock(runtime, element) {
/* resource list collapse or expansion */ /* resource list collapse or expansion */
$(".hide-show").click(function () { $(".hide-show").click(function () {
if ($(this).hasClass('resource_list_expanded')) { if ($(this).hasClass('resource_list_expanded')) {
//$(this).find('.hide-show-icon').removeClass('upArrowIcon').addClass('downArrowIcon');
$(".recommender_row_inner", element).slideUp('fast'); $(".recommender_row_inner", element).slideUp('fast');
$('.resource_add_button').css('visibility', 'hidden'); $('.resource_add_button').css('visibility', 'hidden');
$(this).css('cursor', 's-resize'); $(this).css('cursor', 's-resize');
} }
else { else {
//$(this).find('.hide-show-icon').removeClass('downArrowIcon').addClass('upArrowIcon');
$(".recommender_row_inner", element).slideDown('fast'); $(".recommender_row_inner", element).slideDown('fast');
$('.resource_add_button').css('visibility', 'visible'); $('.resource_add_button').css('visibility', 'visible');
$(this).css('cursor', 'n-resize'); $(this).css('cursor', 'n-resize');
...@@ -112,7 +110,6 @@ function RecommenderXBlock(runtime, element) { ...@@ -112,7 +110,6 @@ function RecommenderXBlock(runtime, element) {
$('.in_title').val(''); $('.in_title').val('');
$('.in_url').val(''); $('.in_url').val('');
$('#addResourceForm').find("input[name='file']").val(''); $('#addResourceForm').find("input[name='file']").val('');
$('.in_filename').text('');
$('.add_submit').attr('disabled', true); $('.add_submit').attr('disabled', true);
} }
...@@ -129,37 +126,45 @@ function RecommenderXBlock(runtime, element) { ...@@ -129,37 +126,45 @@ function RecommenderXBlock(runtime, element) {
$('.in_title').bind('input propertychange', function() { enableAddSubmit(); }); $('.in_title').bind('input propertychange', function() { enableAddSubmit(); });
$('.in_url').bind('input propertychange', function() { enableAddSubmit(); }); $('.in_url').bind('input propertychange', function() { enableAddSubmit(); });
/* upload screenshot, submit the resource, save to database, update the current view */
$('.add_submit').click(function() {
/* data: parameter passed to database */
var data = {};
data['resource'] = {};
data['resource']['url'] = $('.in_url').val();
data['resource']['title'] = $('.in_title').val();
data['resource']['description'] = '';
var formDiv = $('#addResourceForm');
var file = new FormData($(formDiv)[0]);
if ($(formDiv).find("input[name='file']").val() == '') { addResource(data); }
else {
/* upload once student select a file */ /* upload once student select a file */
$('#addResourceForm').find("input[name='file']").change(function (){
if ($(this).val() == '') { return false; }
var formDiv = $(this).parent();
var data = new FormData($(formDiv)[0]);
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: uploadScreenshotUrl, url: uploadScreenshotUrl,
data: data, data: file,
contentType: false, contentType: false,
cache: false, cache: false,
processData: false, processData: false,
async: false, async: false,
/* WANRING: I DON'T KNOW WHY IT ALWAYS ACTIVATE ERROR (COMPLETE) EVENT, INSTEAD OF SUCCESS, ALTHOUGH IT ACTIVATES SUCCESS CORRECTLY IN XBLOCK-SDK */ /* WANRING: I DON'T KNOW WHY IT ALWAYS ACTIVATES ERROR (COMPLETE) EVENT, INSTEAD OF SUCCESS, ALTHOUGH IT ACTIVATES SUCCESS CORRECTLY IN XBLOCK-SDK */
complete: function(data) { complete: function(result) {
/* save filename in hidden div */ if (result.responseText == 'FILETYPEERROR') {
$('.in_filename').text(data.responseText); alert('Please upload an image');
enableAddSubmit(); $(formDiv).find("input[name='file']").val('');
}
else {
/* update new entry */
data['resource']['description'] = baseUrl + result.responseText;
addResource(data);
}
}, },
}); });
}
}); });
/* submit the resource, save to database, update the current view */ function addResource(data) {
$('.add_submit').click(function() {
/* data: parameter passed to database */
var data = {};
data['resource'] = {};
data['resource']['url'] = $('.in_url').val();
data['resource']['title'] = $('.in_title').val();
data['resource']['description'] = baseUrl + $(this).parent().find('.in_filename').text();
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: addResourceUrl, url: addResourceUrl,
...@@ -213,7 +218,7 @@ function RecommenderXBlock(runtime, element) { ...@@ -213,7 +218,7 @@ function RecommenderXBlock(runtime, element) {
else { alert('add redundant resource'); } else { alert('add redundant resource'); }
} }
}); });
}); }
/* unbind event for each entry of resources */ /* unbind event for each entry of resources */
function unbindEvent() { function unbindEvent() {
...@@ -311,22 +316,11 @@ function RecommenderXBlock(runtime, element) { ...@@ -311,22 +316,11 @@ function RecommenderXBlock(runtime, element) {
$('.recommender_modify').show(); $('.recommender_modify').show();
$('.recommender_modify_title').text('Edit existing resource'); $('.recommender_modify_title').text('Edit existing resource');
$('.editSourceBlock').empty();
var uploadForm = '<form id="editResourceForm" method="post">'
+ 'Previewing screenshot: <input type="file" name="file"><br/>'
+ '<div class="edit_filename hidden"></div>'
+ '<input type="button" value="Save change" class="edit_submit" style="margin-top: 0.5em" disabled></form>';
$('.editSourceBlock').append(
'<div class="editSourceBlockTitle">Edit the description, hypelink, and previewing screenshot for the selected resource</div>' +
'Description: ' + '<input type="text" class="edit_title" style="height: 25px; position: relative; left: 10px;"><br/>' +
'HyperLink: <input type="text" class="edit_url" style="height: 25px; position: relative; left: 22px;"><br/>' + uploadForm);
/* initialize the text area */ /* initialize the text area */
$('.edit_title').val($(this).parent().parent().find('.recommender_title').find('a').text()); $('.edit_title').val($(this).parent().parent().find('.recommender_title').find('a').text());
$('.edit_url').val($(this).parent().parent().find('.recommender_title').find('a').attr('href')); $('.edit_url').val($(this).parent().parent().find('.recommender_title').find('a').attr('href'));
$('#editResourceForm').find("input[name='file']").val('');
addTooltip(); $('.edit_submit').attr('disabled', true);
var divEdit = this; var divEdit = this;
/* check whether enough information (title/url) is provided for editing a resource, if yes, enable summission button */ /* check whether enough information (title/url) is provided for editing a resource, if yes, enable summission button */
...@@ -339,41 +333,52 @@ function RecommenderXBlock(runtime, element) { ...@@ -339,41 +333,52 @@ function RecommenderXBlock(runtime, element) {
} }
/* check whether the input text area is changed, if yes, check whether student can submit the resource */ /* check whether the input text area is changed, if yes, check whether student can submit the resource */
$('.edit_title').unbind();
$('.edit_url').unbind();
$('.edit_title').bind('input propertychange', function() { enableEditSubmit(); }); $('.edit_title').bind('input propertychange', function() { enableEditSubmit(); });
$('.edit_url').bind('input propertychange', function() { enableEditSubmit(); }); $('.edit_url').bind('input propertychange', function() { enableEditSubmit(); });
/* upload the screen shot, submit the edited resource, save to database, update the current view */
$('.edit_submit').unbind();
$('.edit_submit').click(function() {
/* data: parameter passed to database */
var data = {};
data['resource'] = parseInt($(divEdit).parent().parent().find('.recommender_entryId').text());
data['url'] = $('.edit_url').val();
data['title'] = $('.edit_title').val();
data['description'] = ''
if (data['url'] == '' || data['title'] == '') { return; }
var formDiv = $('#editResourceForm');
var file = new FormData($(formDiv)[0]);
if ($(formDiv).find("input[name='file']").val() == '') { editResource(data); }
else {
/* upload once student select a file */ /* upload once student select a file */
$('#editResourceForm').find("input[name='file']").change(function (){
if ($(this).val() == '') { return false; }
var formDiv = $(this).parent();
var data = new FormData($(formDiv)[0]);
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: uploadScreenshotUrl, url: uploadScreenshotUrl,
data: data, data: file,
contentType: false, contentType: false,
cache: false, cache: false,
processData: false, processData: false,
async: false, async: false,
/* WANRING: I DON'T KNOW WHY IT ALWAYS ACTIVATE ERROR (COMPLETE) EVENT, INSTEAD OF SUCCESS, ALTHOUGH IT ACTIVATES SUCCESS CORRECTLY IN XBLOCK-SDK */ /* WANRING: I DON'T KNOW WHY IT ALWAYS ACTIVATES ERROR (COMPLETE) EVENT, INSTEAD OF SUCCESS, ALTHOUGH IT ACTIVATES SUCCESS CORRECTLY IN XBLOCK-SDK */
complete: function(data) { complete: function(result) {
/* save filename in hidden div */ if (result.responseText == 'FILETYPEERROR') {
$('.edit_filename').text(data.responseText); alert('Please upload an image');
enableEditSubmit(); $(formDiv).find("input[name='file']").val('');
}
else {
/* update new entry */
data['description'] = baseUrl + result.responseText;
editResource(data);
}
}, },
}); });
}); }
/* submit the edited resource, save to database, update the current view */
$('.edit_submit').click(function() {
/* data: parameter passed to database */
var data = {};
data['resource'] = parseInt($(divEdit).parent().parent().find('.recommender_entryId').text());
data['url'] = $('.edit_url').val();
data['title'] = $('.edit_title').val();
if (data['url'] == '' || data['title'] == '') { return; }
if ($(this).parent().find('.edit_filename').text() != '') { data['description'] = baseUrl + $(this).parent().find('.edit_filename').text(); }
function editResource (data) {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: editResourceUrl, url: editResourceUrl,
...@@ -383,13 +388,13 @@ function RecommenderXBlock(runtime, element) { ...@@ -383,13 +388,13 @@ function RecommenderXBlock(runtime, element) {
/* show the edited resource */ /* show the edited resource */
$(divEdit).parent().parent().find('.recommender_title').find('a').text(data['title']); $(divEdit).parent().parent().find('.recommender_title').find('a').text(data['title']);
$(divEdit).parent().parent().find('.recommender_title').find('a').attr('href', data['url']); $(divEdit).parent().parent().find('.recommender_title').find('a').attr('href', data['url']);
if ("description" in data) { $(divEdit).parent().parent().find('.recommender_descriptionSlot').text(data['description']); } if (data["description"] != "") { $(divEdit).parent().parent().find('.recommender_descriptionSlot').text(data['description']); }
$('.editSourceBlock').empty();
backToView(); backToView();
} }
else { alert('add redundant resource'); } else { alert('add redundant resource'); }
} }
}); });
}
}); });
}); });
...@@ -493,6 +498,10 @@ function RecommenderXBlock(runtime, element) { ...@@ -493,6 +498,10 @@ function RecommenderXBlock(runtime, element) {
pagination(); pagination();
addResourceReset(); addResourceReset();
bindEvent(); bindEvent();
if ($('.recommender_resource').length == 0) {
$('.noResourceIntro').show();
}
} }
initial(); initial();
} }
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