Commit 179db2d0 by swdanielli

delete resource, doc string, code cleaning

parent 6b18c0f8
......@@ -5,12 +5,12 @@ import json, string, random, re
import pkg_resources
try:
from eventtracking import tracker
from eventtracking import tracker
except:
class tracker:
@staticmethod
def emit(a,b):
pass
class tracker:
@staticmethod
def emit(a,b):
pass
from mako.template import Template
from mako.lookup import TemplateLookup
......@@ -24,252 +24,372 @@ from webob.response import Response
aws_access_key_env='AKIAIRDHSV6YZJZ4RFGA'
aws_secret_key_env='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'
class HelpResource(dict):
def __str__(self):
return json.dumps(self)
def __init__(s):
if isinstance(s, str):
self.update(json.loads(s))
elif isinstance(s, dict):
self.update(s)
else:
raise TypeError("Inappropriate type "+str(type(s))+" initializing HelpResource. Should be str or dict")
for e,t in (('id', int), ('title', str), ('url', str), ('upvotes', int), ('downvotes', int), ('description', str)):
if e not in self:
raise TypeError("Insufficient fields initializing HelpResource. "+e+" required.")
if not isinstance(self["e"], t):
raise TypeError("Incorrect field type initializing HelpResource. "+e+" should be "+str(t)+". It is "+type(self[e]))
@property
def title(self):
return self["title"]
@title.setter
def title(self, newtitle):
self["title"] = newtitle
@property
def url(self):
return self["url"]
@url.setter
def url(self, newurl):
self["url"] = newurl
@property
def upvotes(self):
return self["upvotes"]
@upvotes.setter
def upvotes(self, newupvotes):
self["upvotes"] = newupvotes
@property
def downvotes(self):
return self["downvotes"]
@downvotes.setter
def downvotes(self, newdownvotes):
self["downvotes"] = newdownvotes
bucketName='danielswli'
uploadedFileDir = 'uploads/'
class RecommenderXBlock(XBlock):
"""
This XBlock will show a set of recommended resources
This XBlock will show a set of recommended resources provided by students.
"""
# Scope-wide. List of JSON objects corresponding to recommendations combine XML and user.
default_recommendations = List(help="List of help resources", default=[], scope=Scope.content)
# Scope-wide. List of JSON objects corresponding to recommendations as defined in XML.
recommendations = List(help="List of help resources", default=[], scope=Scope.user_state_summary)
# Upvotes for this particular user
upvotes = List(help="List of items user gave upvote to", default=[], scope=Scope.user_state)
# Downvotes for this particular user
downvotes = List(help="List of items user gave downvote to", default=[], scope=Scope.user_state)
# Flag resource id (for problematic resource) for this particular user
flagId = List(help="List of items user flagged to", default=[], scope=Scope.user_state)
# Flag resource reason (for problematic resource) for this particular user
flagReason = List(help="List of reasons of items user flagged to", default=[], scope=Scope.user_state)
# List of deleted recommendation ID.
deletedRecommendationIds = List(help="List of help resources", default=[], scope=Scope.user_state_summary)
# Ids of upvoted resources for this particular user
upvotedIds = List(help="List of items user gave upvote to", default=[], scope=Scope.user_state)
# Ids of downvoted resources for this particular user
downvotedIds = List(help="List of items user gave downvote to", default=[], scope=Scope.user_state)
# Ids of flagged resource (problematic resource) for this particular user
flaggedIds = List(help="List of items user flagged to", default=[], scope=Scope.user_state)
# Reasons of flagged resource (problematic resource) for this particular user
flaggedReasons = List(help="List of reasons of items user flagged to", default=[], scope=Scope.user_state)
template_lookup = None
resource_content_fields = ['url', 'title', 'description', 'descriptionText']
def resource_string(self, path):
"""Handy helper for getting resources from our kit."""
"""Handy helper for getting static file resources from our Python package."""
data = pkg_resources.resource_string(__name__, path)
return data.decode("utf8")
def getResourceNewId(self):
recoms = self.recommendations
if not recoms:
recoms = self.default_recommendations
"""
Generate a unique Id for each resource.
"""
recommendations = self.recommendations
if not recommendations:
recommendations = self.default_recommendations
resourceId = -1
for recom in recoms:
if recom['id'] > resourceId:
resourceId = recom['id']
for recommendation in recommendations:
if recommendation['id'] > resourceId:
resourceId = recommendation['id']
for deletedRecommendationId in self.deletedRecommendationIds:
if deletedRecommendationId > resourceId:
resourceId = deletedRecommendationId
return resourceId + 1
def getEntryIndex(self, entryId, entryList):
"""
Get the element index in a list based on its ID.
"""
for idx in range(0, len(entryList)):
if entryList[idx]['id'] == entryId:
return idx
return -1
@XBlock.json_handler
def delete_resource(self, data, suffix=''):
"""
Delete an entry of resource.
Args:
data: dict in JSON format
data['id']: the ID of the resouce to be deleted
Returns:
result: dict in JSON format
result['Success']: the boolean indicator for whether the deletion is complete
result['error']: the error message generated when the deletion fails
result[resource_content_field]: the content of the deleted resource
"""
resourceId = data['id']
result = {}
result['id'] = resourceId
idx = self.getEntryIndex(resourceId, self.recommendations)
if idx not in range(0, len(self.recommendations)):
result['error'] = 'bad id';
tracker.emit('delete_resource', result)
result['Success'] = False
return result
result['upvotes'] = self.recommendations[idx]['upvotes']
result['downvotes'] = self.recommendations[idx]['downvotes']
for field in self.resource_content_fields:
result[field] = self.recommendations[idx][field]
tracker.emit('delete_resource', result)
self.deletedRecommendationIds.append(resourceId)
del self.recommendations[idx]
result['Success'] = True
return result
@XBlock.json_handler
def handle_upvote(self, data, suffix=''):
''' untested '''
resource = data['resource']
idx = self.getEntryIndex(resource, self.recommendations)
oldVotes = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
if resource in self.upvotes:
del self.upvotes[self.upvotes.index(resource)]
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!"
return {"Success": True}
"""
Add one vote to an entry of resource.
Args:
data: dict in JSON format
data['id']: the ID of the resouce which was upvoted
Returns:
result: dict in JSON format
result['Success']: the boolean indicator for whether the process of this voting action is complete
result['error']: the error message generated when the process fails
result['oldVotes']: the original votes
result['newVotes']: the votes after this action
result['toggle']: the boolean indicator for whether the resource was switched from downvoted to upvoted
"""
resourceId = data['id']
idx = self.getEntryIndex(resourceId, self.recommendations)
result = {}
result['id'] = resourceId
if idx not in range(0, len(self.recommendations)):
result['error'] = 'bad id';
tracker.emit('recommender_upvote', result)
result['Success'] = False
return result
result['oldVotes'] = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
if resourceId in self.upvotedIds:
del self.upvotedIds[self.upvotedIds.index(resourceId)]
self.recommendations[idx]['upvotes'] -= 1
result['newVotes'] = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
tracker.emit('recommender_upvote', result)
result['Success'] = True
return result
if resource in self.downvotes:
del self.downvotes[self.downvotes.index(resource)]
self.recommendations[idx]['downvotes'] -= 1
self.upvotes.append(resource)
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!"
return {"Success": True}
if resourceId in self.downvotedIds:
del self.downvotedIds[self.downvotedIds.index(resourceId)]
self.recommendations[idx]['downvotes'] -= 1
result['toggle'] = True
self.upvotedIds.append(resourceId)
self.recommendations[idx]['upvotes'] += 1
result['newVotes'] = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
tracker.emit('recommender_upvote', result)
result['Success'] = True
return result
@XBlock.json_handler
def handle_downvote(self, data, suffix=''):
''' untested '''
resource = data['resource']
idx = self.getEntryIndex(resource, self.recommendations)
oldVotes = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
if resource in self.downvotes:
del self.downvotes[self.downvotes.index(resource)]
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!"
return {"Success": True}
"""
Subtract one vote from an entry of resource.
Args:
data: dict in JSON format
data['id']: the ID of the resouce which was downvoted
Returns:
result: dict in JSON format
result['Success']: the boolean indicator for whether the process of this voting action is complete
result['error']: the error message generated when the process fails
result['oldVotes']: the original votes
result['newVotes']: the votes after this action
result['toggle']: the boolean indicator for whether the resource was switched from upvoted to downvoted
"""
resourceId = data['id']
idx = self.getEntryIndex(resourceId, self.recommendations)
result = {}
result['id'] = resourceId
if idx not in range(0, len(self.recommendations)):
result['error'] = 'bad id';
tracker.emit('recommender_downvote', result)
result['Success'] = False
return result
result['oldVotes'] = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
if resourceId in self.downvotedIds:
del self.downvotedIds[self.downvotedIds.index(resourceId)]
self.recommendations[idx]['downvotes'] -= 1
result['newVotes'] = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
tracker.emit('recommender_downvote', result)
result['Success'] = True
return result
if resource in self.upvotes:
del self.upvotes[self.upvotes.index(resource)]
self.recommendations[idx]['upvotes'] -= 1
self.downvotes.append(resource)
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!"
return {"Success": True}
if resourceId in self.upvotedIds:
del self.upvotedIds[self.upvotedIds.index(resourceId)]
self.recommendations[idx]['upvotes'] -= 1
result['toggle'] = True
self.downvotedIds.append(resourceId)
self.recommendations[idx]['downvotes'] += 1
result['newVotes'] = self.recommendations[idx]['upvotes'] - self.recommendations[idx]['downvotes']
tracker.emit('recommender_downvote', result)
result['Success'] = True
return result
@XBlock.handler
def upload_screenshot(self, request, suffix=''):
"""
Upload a screenshot for an entry of resource as a preview, to S3.
Args:
request: HTTP POST request
request.POST['file'].file: the file to be uploaded
Returns:
response: HTTP response
response.body (response.responseText): name of the uploaded file
Env variables:
aws_access_key_env: s3 access key
aws_secret_key_env: s3 secret key
bucket: name of the s3 bucket
"""
chars=string.ascii_uppercase + string.digits
size=11
fileDir = 'uploads/'
if re.search('.png$', str(request.POST['file'].file)):
filetype = '.png'
elif re.search('.jpg$', str(request.POST['file'].file)):
filetype = '.jpg'
else:
fileNameLength=11
fileType = ''
allowedTypes = ['.png', '.jpg', '.gif']
for allowedType in allowedTypes:
if str(request.POST['file'].file).endswith(allowedType):
fileType = allowedType
if fileType == '':
tracker.emit('upload_screenshot', {'uploadedFileName': 'FILETYPEERROR'})
print "Wrong file type"
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)
S3FS_handler = S3FS(bucketName, aws_access_key=aws_access_key_env, aws_secret_key=aws_secret_key_env)
while True:
file_id = ''.join(random.choice(chars) for _ in range(size))
filename = fileDir + file_id + filetype
if not myS3FS.exists(filename):
fileId = ''.join(random.choice(chars) for _ in range(fileNameLength))
fileName = uploadedFileDir + fileId + fileType
if not S3FS_handler.exists(fileName):
break
content = request.POST['file'].file.read()
fhwrite = myS3FS.open(filename, 'wb')
fhwrite = S3FS_handler.open(fileName, 'wb')
fhwrite.write(content)
fhwrite.close()
myS3FS.makepublic(filename)
S3FS_handler.makepublic(fileName)
response = Response()
response.body = filename
response.body = fileName
response.headers['Content-Type'] = 'text/plain'
tracker.emit('upload_screenshot', {'uploadedFileName': filename})
tracker.emit('upload_screenshot', {'uploadedFileName': fileName})
return response
@XBlock.json_handler
def add_resource(self, data, suffix=''):
resource = data['resource']
"""
Add an entry of new resource.
Args:
data: dict in JSON format
data[resource_content_field]: the content of the resource to be added
Returns:
result: dict in JSON format
result['Success']: the boolean indicator for whether the addition is complete
result['error']: the error message generated when the addition fails
result[resource_content_field]: the content of the added resource
"""
# Construct new resource
valid_fields = ['url', 'title', 'description', 'descriptionText']
new_resource = {}
for field in valid_fields:
new_resource[field] = data['resource'][field]
result = {}
for field in self.resource_content_fields:
result[field] = data[field]
# check url for redundancy
for recom in self.recommendations:
if recom['url'] == data['resource']['url']:
print "add redundant resource"
new_resource['error'] = 'redundant resource'
tracker.emit('add_resource', new_resource)
return {"Success": False}
new_resource['id'] = self.getResourceNewId()
tracker.emit('add_resource', new_resource)
new_resource['upvotes'] = 0
new_resource['downvotes'] = 0
#new_resource['isProblematic'] = "notProblematic"
#new_resource['problematicReason'] = ""
self.recommendations.append(new_resource)
return {"Success": True, "id": new_resource['id']}
for recommendation in self.recommendations:
if recommendation['url'] == data['url']:
result['error'] = 'redundant resource'
tracker.emit('add_resource', result)
result['Success'] = False
return result
result['id'] = self.getResourceNewId()
tracker.emit('add_resource', result)
result['upvotes'] = 0
result['downvotes'] = 0
self.recommendations.append(dict(result))
result['Success'] = True
return result
@XBlock.json_handler
def edit_resource(self, data, suffix=''):
resource = data['resource']
idx = self.getEntryIndex(resource, self.recommendations)
"""
Edit an entry of existing resource.
Args:
data: dict in JSON format
data['id']: the ID of the edited resouce
data[resource_content_field]: the content of the resource to be edited
Returns:
result: dict in JSON format
result['Success']: the boolean indicator for whether the edit is complete
result['error']: the error message generated when the edit fails
result[old_resource_content_field]: the content of the resource before edited
result[resource_content_field]: the content of the resource after edited
"""
resourceId = data['id']
result = {}
result['id'] = resourceId
idx = self.getEntryIndex(resourceId, self.recommendations)
if idx not in range(0, len(self.recommendations)):
print "Edit failed!"
tracker.emit('edit_resource', {'id': resource, 'error': 'bad id'})
return {"Success": False}
valid_fields = ['url', 'title', 'description', 'descriptionText']
resource_log = {}
resource_log['id'] = resource
for field in valid_fields:
resource_log['old_' + field] = self.recommendations[idx][field]
resource_log[field] = data[field]
result['error'] = 'bad id';
tracker.emit('edit_resource', result)
result['Success'] = False
return result
for field in self.resource_content_fields:
result['old_' + field] = self.recommendations[idx][field]
result[field] = data[field]
# check url for redundancy
if self.recommendations[idx]['url'] != data['url']:
for recom in self.recommendations:
if recom['url'] == data['url']:
resource_log['error'] = 'existing url'
for field in valid_fields:
resource_log['dup_' + field] = self.recommendations[self.recommendations.index(recom)][field]
resource_log['dup_id'] = self.recommendations[self.recommendations.index(recom)]['id']
print "provided url is existing"
tracker.emit('edit_resource', resource_log)
return {"Success": False}
for key in data:
if key == 'resource':
continue
if data[key] == "":
continue
self.recommendations[idx][key] = data[key]
tracker.emit('edit_resource', resource_log)
return {"Success": True}
for recommendation in self.recommendations:
if recommendation['url'] == data['url']:
result['error'] = 'existing url'
for field in self.resource_content_fields:
result['dup_' + field] = self.recommendations[self.recommendations.index(recommendation)][field]
result['dup_id'] = self.recommendations[self.recommendations.index(recommendation)]['id']
tracker.emit('edit_resource', result)
result['Success'] = False
return result
for field in data:
if field == 'id':
continue
if data[field] == "":
continue
self.recommendations[idx][field] = data[field]
tracker.emit('edit_resource', result)
result['Success'] = True
return result
@XBlock.json_handler
def flag_resource(self, data, suffix=''):
flagLog = {}
flagLog['id'] = data['resource']
flagLog['isProblematic'] = data['isProblematic']
flagLog['reason'] = data['reason']
"""
Flag (or unflag) an entry of problematic resource and give the reason.
Args:
data: dict in JSON format
data['id']: the ID of the problematic resouce
data['isProblematic']: the boolean indicator for whether the resource is problematic
data['reason']: the reason why the user believes the resource is problematic
Returns:
result: dict in JSON format
result['Success']: the boolean indicator for whether the edit is complete
result['reason']: the new reason
result['oldReason']: the old reason
result['id']: the ID of the problematic resouce
result['isProblematic']: the boolean indicator for whether the resource is problematic
"""
result = {}
result['id'] = data['id']
result['isProblematic'] = data['isProblematic']
result['reason'] = data['reason']
if data['isProblematic'] == True:
if data['resource'] in self.flagId:
flagLog['oldReason'] = self.flagReason[self.flagId.index(data['resource'])]
self.flagReason[self.flagId.index(data['resource'])] = data['reason']
if data['id'] in self.flaggedIds:
result['oldReason'] = self.flaggedReasons[self.flaggedIds.index(data['id'])]
self.flaggedReasons[self.flaggedIds.index(data['id'])] = data['reason']
else:
self.flagId.append(data['resource'])
self.flagReason.append(data['reason'])
self.flaggedIds.append(data['id'])
self.flaggedReasons.append(data['reason'])
else:
if data['resource'] in self.flagId:
flagLog['oldReason'] = self.flagReason[self.flagId.index(data['resource'])]
idx = self.flagId.index(data['resource'])
del self.flagId[idx]
del self.flagReason[idx]
tracker.emit('flag_resource', flagLog)
return {"Success": True}
if data['id'] in self.flaggedIds:
result['oldReason'] = self.flaggedReasons[self.flaggedIds.index(data['id'])]
result['reason'] = ''
idx = self.flaggedIds.index(data['id'])
del self.flaggedIds[idx]
del self.flaggedReasons[idx]
tracker.emit('flag_resource', result)
result['Success'] = True
return result
@XBlock.json_handler
def is_user_staff(self, data, suffix=''):
"""
Return whether the user is staff.
Returns:
is_user_staff: indicator for whether the user is staff
"""
return {'is_user_staff': self.xmodule_runtime.user_is_staff}
# TO-DO: change this view to display your data your own way.
def student_view(self, context=None):
......@@ -277,7 +397,7 @@ class RecommenderXBlock(XBlock):
The primary view of the RecommenderXBlock, shown to students
when viewing courses.
"""
print "entered"
if not self.recommendations:
self.recommendations = self.default_recommendations
if not self.recommendations:
......@@ -293,7 +413,7 @@ class RecommenderXBlock(XBlock):
resources = [{'id' : r['id'], 'title' : r['title'], "votes" : r['upvotes'] - r['downvotes'], 'url' : r['url'], 'description' : r['description'], 'descriptionText' : r['descriptionText']} for r in self.recommendations]
resources = sorted(resources, key = lambda r: r['votes'], reverse=True)
frag = Fragment(self.template_lookup.get_template("recommender.html").render(resources = resources, upvotes = self.upvotes, downvotes = self.downvotes, flagId = self.flagId, flagReason = self.flagReason))
frag = Fragment(self.template_lookup.get_template("recommender.html").render(resources = resources, upvotedIds = self.upvotedIds, downvotedIds = self.downvotedIds, flaggedIds = self.flaggedIds, flaggedReasons = self.flaggedReasons))
frag.add_css_url("//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/smoothness/jquery-ui.css")
frag.add_javascript_url("//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js")
frag.add_css(self.resource_string("static/css/recommender.css"))
......@@ -337,7 +457,6 @@ class RecommenderXBlock(XBlock):
for line in node.text.split('\n'):
line = line.strip()
if len(line) > 2:
print "Decoding", line
lines.append(json.loads(line))
block.default_recommendations = lines
......
......@@ -22,14 +22,17 @@
.recommender_row_top, .recommender_modify_title_container {
/* overflow:scroll;
max-height:200px;*/
font-size: 20px;
font-size: 14px;
padding-top: 0.5em;
padding-left: 0.5em;
background-color: #f5efef;
height: 40px;
height: 28px;
color: #948f8f;
}
.recommender_row_top:before { content: 'Show'; }
.recommender_row_top.resource_list_expanded:before { content: 'Hide'; }
.recommender_row {
height: 380px;
}
......@@ -131,9 +134,9 @@
.recommender_blurb {
display:inline-block;
vertical-align: middle;
width:525px;
width:585px;
overflow:hidden;
text-overflow:ellipsis;
word-wrap: break-word;
}
.recommender_entryId, .recommender_descriptionImg, .recommender_problematicReason, .recommender_descriptionText {
......@@ -157,7 +160,7 @@
color: red;
}
.recommender_vote_arrow_down, .recommender_vote_arrow_up, .recommender_title, .resource_edit_button, .flagResource, .resource_add_button, .hide-show, .paginationCell, .backToViewButton { cursor: pointer; }
.recommender_vote_arrow_down, .recommender_vote_arrow_up, .recommender_title, .resource_edit_button, .flagResource, .staffEdition, .resource_add_button, .hide-show, .paginationCell, .backToViewButton { cursor: pointer; }
.resource_add_button { text-align: right; font-weight: bold; }
.resource_edit_button { float: left; }
......@@ -178,6 +181,10 @@
.backToViewButton { color: #1d9dd9; float: left; }
.flag_reason_submit, .edit_submit, .add_submit { margin-top: 0.5em; }
.delete_resource, .endorse_resource, .deendorse_resource { margin-top: 0.5em; float:left; }
.ui-icon.problematic {
background-image: url(http://download.jqueryui.com/themeroller/images/ui-icons_ff0000_256x240.png);
}
......@@ -197,9 +204,9 @@
.recommender_modify_title { float: right; padding-right: 2em; overflow: auto; }
.nonevoting { color: #948f8f; }
.hide-show-icon { margin-left: 0.5em; }
.recommender_vote_score, .recommender_vote_arrow_up, .recommender_vote_arrow_down { color: #948f8f; }
.recommender_vote_score.upvoting, .recommender_vote_arrow_up.upvoting { color: rgb(69, 194, 10); }
.recommender_vote_score.downvoting, .recommender_vote_arrow_down.downvoting { color: red; }
.recommender_vote_arrow_up.downvoting, .recommender_vote_arrow_down.upvoting { color: #948f8f; }
......@@ -213,6 +220,5 @@
.moreIcon:before { content: '...'; }
.lightgreyBg { background-color: #948f8f; }
a { word-break: break-all; }
a:link, a:visited { color: black; }
form { margin: 0em; }
......@@ -5,7 +5,7 @@
<div class="recommender_recommendations">
<div class="recommender_content">
<div class="recommender_row_top hide-show resource_list_expanded">
Related resources<div class='recommender_panel'> <span class='hide-show-icon upArrowIcon'></span> </div>
Related Resources<div class='recommender_panel'> <span class='hide-show-icon upArrowIcon'></span> </div>
</div>
<div class='recommender_row_inner'>
<div class="recommender_row">
......@@ -17,16 +17,16 @@
</div>
% for elem in resources:
<%
if elem['id'] in downvotes:
if elem['id'] in downvotedIds:
voteMode = "downvoting"
elif elem['id'] in upvotes:
elif elem['id'] in upvotedIds:
voteMode = 'upvoting'
else:
voteMode = 'nonevoting'
voteMode = ''
if elem['id'] in flagId:
if elem['id'] in flaggedIds:
flagMode = 'problematic'
reason = flagReason[flagId.index(elem['id'])]
reason = flaggedReasons[flaggedIds.index(elem['id'])]
else:
flagMode = ''
reason = ''
......@@ -77,13 +77,19 @@
</div>
<div class='recommender_modify'>
<div class='recommender_modify_title_container'><div class='backToViewButton'>&lt; Related resources</div><div class='recommender_modify_title'></div></div>
<div class="staffEditionBlock">
<div class="staffEditionBlockTitle">Delete, endorse or de-endorse a resource.</div>
<input type="button" value="Delete resource" class="delete_resource">
<input type="button" value="Endorse resource" class="endorse_resource">
<input type="button" value="De-endorse resource" class="deendorse_resource">
</div>
<div class="flagSourceBlock">
<div class="flagSourceBlockTitle">Why would you like to flag this resource?</div>
<input type="text"
class="flag_reason"
name="flag_rationale"
placeholder="Reason for why this resource should be removed"/><br/>
<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">
</div>
<div class="editSourceBlock">
<div class="editSourceBlockTitle">Edit the description, hypelink, and previewing screenshot for the selected resource.</div>
......@@ -101,7 +107,7 @@
placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><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 >
<input type="button" value="Save change" class="edit_submit" disabled >
</form>
</div>
<div class="recommender_add">
......@@ -120,7 +126,7 @@
placeholder="Provide a meaningful description so other students know whether this is useful to them"></textarea><br/>
<form method="post" id="addResourceForm">
Previewing screenshot: <input type="file" name="file"><br/>
<input type="button" value="Add resource" class="add_submit" style="margin-top: 0.5em" disabled >
<input type="button" value="Add resource" class="add_submit" disabled >
</form>
</div>
</div>
......
if (typeof Logger == 'undefined') {
var Logger = {
log: function(a) { return; }
log: function(a) { return; }
}
}
function RecommenderXBlock(runtime, element) {
var handleUpvoteUrl = runtime.handlerUrl(element, 'handle_upvote');
var handleDownvoteUrl = runtime.handlerUrl(element, 'handle_downvote');
var addResourceUrl = runtime.handlerUrl(element, 'add_resource');
var editResourceUrl = runtime.handlerUrl(element, 'edit_resource');
var flagResourceUrl = runtime.handlerUrl(element, 'flag_resource');
var uploadScreenshotUrl = runtime.handlerUrl(element, 'upload_screenshot');
var baseUrl = 'http://s3-us-west-2.amazonaws.com/danielswli/';
var currentPage = 1;
var entriesPerPage = 5;
var pageSpan = 2;
var handleUpvoteUrl = runtime.handlerUrl(element, 'handle_upvote');
var handleDownvoteUrl = runtime.handlerUrl(element, 'handle_downvote');
var addResourceUrl = runtime.handlerUrl(element, 'add_resource');
var editResourceUrl = runtime.handlerUrl(element, 'edit_resource');
var flagResourceUrl = runtime.handlerUrl(element, 'flag_resource');
var uploadScreenshotUrl = runtime.handlerUrl(element, 'upload_screenshot');
var isUserStaffUrl = runtime.handlerUrl(element, 'is_user_staff');
var deleteResourceUrl = runtime.handlerUrl(element, 'delete_resource');
var baseUrl = 'http://s3-us-west-2.amazonaws.com/danielswli/';
var currentPage = 1;
var entriesPerPage = 5;
var pageSpan = 2;
/* resource list collapse or expansion */
$(".hide-show").click(function () {
if ($(this).hasClass('resource_list_expanded')) {
/* Initiate at least once for every session */
Logger.log('hide-show.click.event', {
'status': 'hide'
$(".hide-show").click(function () {
if ($(this).hasClass('resource_list_expanded')) {
/* Initiate at least once for every session */
Logger.log('hide-show.click.event', {
'status': 'hide'
});
$(".recommender_row_inner", element).slideUp('fast');
$(this).css('cursor', 's-resize');
}
else {
Logger.log('hide-show.click.event', {
'status': 'show'
});
$(".recommender_row_inner", element).slideDown('fast');
$(this).css('cursor', 'n-resize');
}
$(this).find('.hide-show-icon').toggleClass('upArrowIcon').toggleClass('downArrowIcon');
$(this).toggleClass('resource_list_expanded');
addTooltip();
});
/* show content/icon for different page */
function pagination() {
/* show resource for each page */
$('.recommender_resource').each(function(index, element) {
if (index < (currentPage-1)*entriesPerPage || index >= currentPage*entriesPerPage) { $(element).hide(); }
else { $(element).show(); }
});
$(".recommender_row_inner", element).slideUp('fast');
//$('.resource_add_button').css('visibility', 'hidden');
$(this).css('cursor', 's-resize');
}
else {
Logger.log('hide-show.click.event', {
'status': 'show'
/* change icon for each page */
$('.paginationRow').each(function(index, element) {
if (index + 1 == currentPage) { $(element).show(); }
else { $(element).hide(); }
});
$(".recommender_row_inner", element).slideDown('fast');
//$('.resource_add_button').css('visibility', 'visible');
$(this).css('cursor', 'n-resize');
}
$(this).find('.hide-show-icon').toggleClass('upArrowIcon').toggleClass('downArrowIcon');
$(this).toggleClass('resource_list_expanded');
addTooltip();
});
/* show content/icon for different page */
function pagination() {
/* show resource for each page */
$('.recommender_resource').each(function(index, element) {
if (index < (currentPage-1)*entriesPerPage || index >= currentPage*entriesPerPage) { $(element).hide(); }
else { $(element).show(); }
});
/* change icon for each page */
$('.paginationRow').each(function(index, element) {
if (index + 1 == currentPage) { $(element).show(); }
else { $(element).hide(); }
});
}
/* creating pagination (icon and page-change event) for each page of resource list */
function paginationRow() {
var totalPage = Math.ceil($('.recommender_resource').length/entriesPerPage);
if (totalPage == 1) { return; }
$('.paginationRow').remove();
$('.paginationCell').unbind();
/* each paginationRow correspond to each page of resource list */
for (var pageIdx = 1; pageIdx <= totalPage; pageIdx++) {
var paginationRowDiv = $('.paginationRowTemplate').clone().removeClass('hidden').removeClass('paginationRowTemplate').addClass('paginationRow');
/* no previous page if current page = 1 */
if (pageIdx == 1) { paginationRowDiv.find('.leftArrowIcon').css("visibility", "hidden"); }
if (pageIdx - pageSpan <= 1) { paginationRowDiv.find('.leftMoreIcon').css("visibility", "hidden"); }
function paginationRow() {
var totalPage = Math.ceil($('.recommender_resource').length/entriesPerPage);
$('.paginationRow').remove();
$('.paginationCell').unbind();
if (totalPage == 1) { return; }
/* each paginationRow correspond to each page of resource list */
for (var pageIdx = 1; pageIdx <= totalPage; pageIdx++) {
var paginationRowDiv = $('.paginationRowTemplate').clone().removeClass('hidden').removeClass('paginationRowTemplate').addClass('paginationRow');
/* no previous page if current page = 1 */
if (pageIdx == 1) { paginationRowDiv.find('.leftArrowIcon').css("visibility", "hidden"); }
if (pageIdx - pageSpan <= 1) { paginationRowDiv.find('.leftMoreIcon').css("visibility", "hidden"); }
for (var i = pageIdx - pageSpan; i <= pageIdx + pageSpan; i++) {
var currentCellDiv = paginationRowDiv.find('.lightgreyBg');
if (i == pageIdx) { currentCellDiv.text(i.toString()); }
else {
var cellDiv = currentCellDiv.clone().removeClass('lightgreyBg').text(i.toString());
if (i <= 0 || i > totalPage) { cellDiv.css("visibility", "hidden"); }
if (i > pageIdx) { paginationRowDiv.find('.rightMoreIcon').before(cellDiv); }
else { currentCellDiv.before(cellDiv); }
for (var i = pageIdx - pageSpan; i <= pageIdx + pageSpan; i++) {
var currentCellDiv = paginationRowDiv.find('.lightgreyBg');
if (i == pageIdx) { currentCellDiv.text(i.toString()); }
else {
var cellDiv = currentCellDiv.clone().removeClass('lightgreyBg').text(i.toString());
if (i <= 0 || i > totalPage) { cellDiv.css("visibility", "hidden"); }
if (i > pageIdx) { paginationRowDiv.find('.rightMoreIcon').before(cellDiv); }
else { currentCellDiv.before(cellDiv); }
}
}
if (pageIdx + pageSpan >= totalPage) { paginationRowDiv.find('.rightMoreIcon').css("visibility", "hidden"); }
/* no next page if current page is last page */
if (pageIdx == totalPage) { paginationRowDiv.find('.rightArrowIcon').css("visibility", "hidden"); }
$('.pagination').append(paginationRowDiv);
}
if (pageIdx + pageSpan >= totalPage) { paginationRowDiv.find('.rightMoreIcon').css("visibility", "hidden"); }
/* no next page if current page is last page */
if (pageIdx == totalPage) { paginationRowDiv.find('.rightArrowIcon').css("visibility", "hidden"); }
$('.pagination').append(paginationRowDiv);
}
/* page change */
$('.paginationCell').click(function () {
var logStr = 'From page ' + currentPage.toString();
if ($(this).hasClass('moreIcon')) {
Logger.log('pagination.click.event', {
'status': 'moreIcon'
});
return;
}
else if ($(this).hasClass('leftArrowIcon')) {
currentPage -= 1;
/* page change */
$('.paginationCell').click(function () {
var logStr = 'From page ' + currentPage.toString();
if ($(this).hasClass('moreIcon')) {
Logger.log('pagination.click.event', {
'status': 'moreIcon'
});
return;
}
else if ($(this).hasClass('leftArrowIcon')) {
currentPage -= 1;
}
else if ($(this).hasClass('rightArrowIcon')) { currentPage += 1; }
else { currentPage = parseInt($(this).text()); }
logStr += ' To page ' + currentPage.toString();
Logger.log('pagination.click.event', {
'status': logStr
});
pagination();
});
}
/* change between different mode (resource list or add/edit mode) */
function backToView() {
$('.recommender_modify').hide();
$('.flagSourceBlock').hide();
$('.editSourceBlock').hide();
$('.recommender_add').hide();
$('.staffEditionBlock').hide();
if ($('.recommender_resource').length == 0) {
$('.noResourceIntro').removeClass('hidden');
}
else if ($(this).hasClass('rightArrowIcon')) { currentPage += 1; }
else { currentPage = parseInt($(this).text()); }
logStr += ' To page ' + currentPage.toString();
Logger.log('pagination.click.event', {
'status': logStr
$('.recommender_resource').removeClass('resource_hovered');
$('.previewingImg').addClass('hidden');
$('.descriptionText').hide();
$('.recommender_content').show();
}
$('.backToViewButton').click(function() {
Logger.log('backToView.click.event', {
'status': 'Back to resource list mode'
});
pagination();
});
}
/* button for adding new resource */
$('.resource_add_button').click(function() {
Logger.log('addResource.click.event', {
'status': 'Entering add resource mode'
});
backToView();
});
addResourceReset();
$('.recommender_add').show();
$('.recommender_content').hide();
$('.recommender_modify').show();
$('.recommender_modify_title').text('Suggest resource');
/* button for adding new resource */
$('.resource_add_button').click(function() {
Logger.log('addResource.click.event', {
'status': 'Entering add resource mode'
});
/* Don't trigger event bound to parent div */
/*
(function(e) {
var e = window.event || e;
if (e.stopPropagation) e.stopPropagation();
else e.cancelBubble = true;
})(event);
*/
});
/* change between different mode (resource list or add/edit mode) */
function backToView() {
$('.recommender_modify').hide();
$('.flagSourceBlock').hide();
$('.editSourceBlock').hide();
$('.recommender_add').hide();
$('.recommender_content').show();
//if ($('.recommender_row_top').css('cursor') == 's-resize') { $(".hide-show").click(); }
}
$('.backToViewButton').click(function() {
Logger.log('backToView.click.event', {
'status': 'Back to resource list mode'
});
backToView();
});
/* initialize add resource mode */
function addResourceReset() {
$('.in_title').val('');
$('.in_url').val('');
$('.in_descriptionText').val('');
$('#addResourceForm').find("input[name='file']").val('');
$('.add_submit').attr('disabled', true);
}
/* check whether enough information (title/url) is provided for recommending a resource, if yes, enable summission button */
function enableAddSubmit(divPtr) {
if ($('.in_title').val() == '' || $('.in_url').val() == '') {
addResourceReset();
$('.recommender_add').show();
$('.recommender_content').hide();
$('.recommender_modify').show();
$('.recommender_modify_title').text('Suggest resource');
});
/* initialize add resource mode */
function addResourceReset() {
$('.in_title').val('');
$('.in_url').val('');
$('.in_descriptionText').val('');
$('#addResourceForm').find("input[name='file']").val('');
$('.add_submit').attr('disabled', true);
return;
}
$('.add_submit').attr('disabled', false);
}
/* check whether the input text area is changed, if yes, check whether student can submit the resource */
$('.in_title').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']['descriptionText'] = $('.in_descriptionText').val();
data['resource']['description'] = '';
var formDiv = $('#addResourceForm');
}
/* check whether enough information (title/url) is provided for recommending a resource, if yes, enable summission button */
function enableAddSubmit(divPtr) {
if ($('.in_title').val() == '' || $('.in_url').val() == '') {
$('.add_submit').attr('disabled', true);
return;
}
$('.add_submit').attr('disabled', false);
}
/* check whether the input text area is changed, if yes, check whether student can submit the resource */
$('.in_title').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['url'] = $('.in_url').val();
data['title'] = $('.in_title').val();
data['descriptionText'] = $('.in_descriptionText').val();
data['description'] = '';
var formDiv = $('#addResourceForm');
var file = new FormData($(formDiv)[0]);
Logger.log('addResource.click.event', {
'status': 'Add new resource',
'title': data['resource']['title'],
'url': data['resource']['url'],
'description': $(formDiv).find("input[name='file']").val(),
'descriptionText': data['resource']['descriptionText']
Logger.log('addResource.click.event', {
'status': 'Add new resource',
'title': data['title'],
'url': data['url'],
'description': $(formDiv).find("input[name='file']").val(),
'descriptionText': data['descriptionText']
});
if ($(formDiv).find("input[name='file']").val() == '') { addResource(data); }
else {
/* upload once student select a file */
$.ajax({
type: 'POST',
url: uploadScreenshotUrl,
data: file,
contentType: false,
cache: false,
processData: false,
async: false,
/* 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(result) {
if (result.responseText == 'FILETYPEERROR') {
alert('Please upload an image');
$(formDiv).find("input[name='file']").val('');
}
else {
/* update new entry */
data['resource']['description'] = baseUrl + result.responseText;
addResource(data);
}
},
});
}
});
function addResource(data) {
$.ajax({
type: "POST",
url: addResourceUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
/* decide the rigth place for the added resource (pos), based on sorting the votes */
var pos = -1;
$('.recommender_vote_score').each(function(idx, ele){
if (parseInt($(ele).text()) < 0) {
pos = idx;
return false;
}
});
/* show the added resource at right place (pos), based on sorting the votes, and lead student to that page */
if ($('.recommender_resource').length == 0) {
$('.noResourceIntro').addClass('hidden');
$('.descriptionText').show();
currentPage = 1;
var newDiv = $('.recommender_resourceTemplate').clone().removeClass('hidden').removeClass('recommender_resourceTemplate').addClass('recommender_resource');
}
else {
if (pos == -1) {
var toDiv = $('.recommender_resource:last');
currentPage = Math.ceil(($('.recommender_resource').length+1)/entriesPerPage);
}
else {
var toDiv = $('.recommender_resource:eq(' + pos.toString() + ')');
currentPage = Math.ceil((pos + 1)/entriesPerPage);
}
var newDiv = $(toDiv).clone();
}
/* div for the new resource */
$(newDiv).find('.recommender_vote_arrow_up,.recommender_vote_score,.recommender_vote_arrow_down')
.removeClass('downvoting').removeClass('upvoting').addClass('nonevoting');
$(newDiv).find('.recommender_vote_score').text('0');
$(newDiv).find('a').attr('href', data['resource']['url']);
$(newDiv).find('a').text(data['resource']['title']);
$(newDiv).find('.recommender_descriptionImg').text(data['resource']['description']);
$(newDiv).find('.recommender_descriptionText').text(data['resource']['descriptionText']);
$(newDiv).find('.recommender_entryId').text(result['id']);
$(newDiv).find('.recommender_problematicReason').text('');
$(newDiv).find('.flagResource').removeClass('problematic');
if ($('.recommender_resource').length == 0) { $('.recommender_resourceTemplate').before(newDiv); }
else {
if (pos == -1) { $(toDiv).after(newDiv); }
else { $(toDiv).before(newDiv); }
}
addResourceReset();
unbindEvent();
bindEvent();
paginationRow();
pagination();
backToView();
}
else { alert('add redundant resource'); }
}
});
}
if ($(formDiv).find("input[name='file']").val() == '') { addResource(data); }
else {
/* upload once student select a file */
$.ajax({
type: 'POST',
url: uploadScreenshotUrl,
data: file,
contentType: false,
cache: false,
processData: false,
async: false,
/* 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(result) {
if (result.responseText == 'FILETYPEERROR') {
alert('Please upload an image');
$(formDiv).find("input[name='file']").val('');
}
else {
/* update new entry */
data['description'] = baseUrl + result.responseText;
addResource(data);
}
},
});
}
});
function addResource(data) {
$.ajax({
type: "POST",
url: addResourceUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
/* decide the rigth place for the added resource (pos), based on sorting the votes */
var pos = -1;
$('.recommender_vote_score').each(function(idx, ele){
if (parseInt($(ele).text()) < 0) {
pos = idx;
return false;
}
});
/* show the added resource at right place (pos), based on sorting the votes, and lead student to that page */
if ($('.recommender_resource').length == 0) {
$('.noResourceIntro').addClass('hidden');
$('.descriptionText').show();
currentPage = 1;
var newDiv = $('.recommender_resourceTemplate').clone(true, true).removeClass('hidden').removeClass('recommender_resourceTemplate').addClass('recommender_resource');
}
else {
if (pos == -1) {
var toDiv = $('.recommender_resource:last');
currentPage = Math.ceil(($('.recommender_resource').length+1)/entriesPerPage);
}
else {
var toDiv = $('.recommender_resource:eq(' + pos.toString() + ')');
currentPage = Math.ceil((pos + 1)/entriesPerPage);
}
var newDiv = $(toDiv).clone(true, true);
}
/* div for the new resource */
$(newDiv).find('.recommender_vote_arrow_up,.recommender_vote_score,.recommender_vote_arrow_down')
.removeClass('downvoting').removeClass('upvoting');
$(newDiv).find('.recommender_vote_score').text('0');
$(newDiv).find('a').attr('href', result['url']);
$(newDiv).find('a').text(result['title']);
$(newDiv).find('.recommender_descriptionImg').text(result['description']);
$(newDiv).find('.recommender_descriptionText').text(result['descriptionText']);
$(newDiv).find('.recommender_entryId').text(result['id']);
$(newDiv).find('.recommender_problematicReason').text('');
$(newDiv).find('.flagResource').removeClass('problematic');
if ($('.recommender_resource').length == 0) {
$('.recommender_resourceTemplate').before(newDiv);
unbindEvent();
bindEvent();
}
else {
if (pos == -1) { $(toDiv).after(newDiv); }
else { $(toDiv).before(newDiv); }
}
addResourceReset();
//unbindEvent();
//bindEvent();
paginationRow();
pagination();
backToView();
}
else { alert('add redundant resource'); }
}
});
}
/* unbind event for each entry of resources */
function unbindEvent() {
$('.recommender_vote_arrow_up').unbind();
$('.recommender_vote_arrow_down').unbind();
$('.recommender_resource').unbind();
$('.resource_edit_button').unbind();
$('.flagResource').unbind();
}
/* bind event for each entry of resources */
function bindEvent() {
/* upvoting event */
$('.recommender_vote_arrow_up').click(function() {
var data = {};
data['resource'] = parseInt($(this).parent().parent().find('.recommender_entryId').text());
if (data['resource'] == -1) { return; }
Logger.log('arrowUp.click.event', {
'status': 'Arrow up',
'id': data['resource']
function unbindEvent() {
$('.recommender_vote_arrow_up').unbind();
$('.recommender_vote_arrow_down').unbind();
$('.recommender_resource').unbind();
$('.resource_edit_button').unbind();
$('.flagResource').unbind();
}
/* bind event for each entry of resources */
function bindEvent() {
/* upvoting event */
$('.recommender_vote_arrow_up').click(function() {
var data = {};
data['id'] = parseInt($(this).parent().parent().find('.recommender_entryId').text());
if (data['id'] == -1) { return; }
Logger.log('arrowUp.click.event', {
'status': 'Arrow up',
'id': data['id']
});
var divArrowUp = this;
$.ajax({
type: "POST",
url: handleUpvoteUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
var scoreDiv = $(divArrowUp).parent().find('.recommender_vote_score');
/* change downvoting to upvoting */
if ($(divArrowUp).hasClass('downvoting')) {
$(divArrowUp).parent().find('.downvoting').removeClass('downvoting').addClass('upvoting');
scoreDiv.html((parseInt(scoreDiv.text()) + 2).toString());
}
/* upvoting */
else if ($(divArrowUp).hasClass('nonevoting')) {
$(divArrowUp).parent().find('.nonevoting').removeClass('nonevoting').addClass('upvoting');
scoreDiv.html((parseInt(scoreDiv.text()) + 1).toString());
}
/* undo upvoting */
else if ($(divArrowUp).hasClass('upvoting')) {
$(divArrowUp).parent().find('.upvoting').removeClass('upvoting').addClass('nonevoting');
scoreDiv.html((parseInt(scoreDiv.text()) - 1).toString());
}
}
}
});
});
$.ajax({
type: "POST",
url: handleUpvoteUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
var divArrowUp = $('.recommender_resource:eq(' + findResourceDiv(result['id']).toString() + ')');
$(divArrowUp)
.find('.recommender_vote_arrow_up, .recommender_vote_arrow_down, .recommender_vote_score')
.toggleClass('upvoting');
if ('toggle' in result) {
$(divArrowUp)
.find('.recommender_vote_arrow_up, .recommender_vote_arrow_down, .recommender_vote_score')
.toggleClass('downvoting');
}
$(divArrowUp).find('.recommender_vote_score').html(result['newVotes'].toString());
}
}
});
});
/* downvoting event */
$('.recommender_vote_arrow_down').click(function() {
var data = {};
data['resource'] = parseInt($(this).parent().parent().find('.recommender_entryId').text());
if (data['resource'] == -1) { return; }
Logger.log('arrowDown.click.event', {
'status': 'Arrow down',
'id': data['resource']
$('.recommender_vote_arrow_down').click(function() {
var data = {};
data['id'] = parseInt($(this).parent().parent().find('.recommender_entryId').text());
if (data['id'] == -1) { return; }
Logger.log('arrowDown.click.event', {
'status': 'Arrow down',
'id': data['id']
});
var divArrowDown = this;
$.ajax({
type: "POST",
url: handleDownvoteUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
var scoreDiv = $(divArrowDown).parent().find('.recommender_vote_score');
/* undo downvoting */
if ($(divArrowDown).hasClass('downvoting')) {
$(divArrowDown).parent().find('.downvoting').removeClass('downvoting').addClass('nonevoting');
scoreDiv.html((parseInt(scoreDiv.text()) + 1).toString());
}
/* downvoting */
else if ($(divArrowDown).hasClass('nonevoting')) {
$(divArrowDown).parent().find('.nonevoting').removeClass('nonevoting').addClass('downvoting');
scoreDiv.html((parseInt(scoreDiv.text()) - 1).toString());
}
/* change voting to downvoting */
else if ($(divArrowDown).hasClass('upvoting')) {
$(divArrowDown).parent().find('.upvoting').removeClass('upvoting').addClass('downvoting');
scoreDiv.html((parseInt(scoreDiv.text()) - 2).toString());
}
}
}
});
});
$.ajax({
type: "POST",
url: handleDownvoteUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
var divArrowDown = $('.recommender_resource:eq(' + findResourceDiv(result['id']).toString() + ')');
$(divArrowDown)
.find('.recommender_vote_arrow_up, .recommender_vote_arrow_down, .recommender_vote_score')
.toggleClass('downvoting');
if ('toggle' in result) {
$(divArrowDown)
.find('.recommender_vote_arrow_up, .recommender_vote_arrow_down, .recommender_vote_score')
.toggleClass('upvoting');
}
$(divArrowDown).find('.recommender_vote_score').html(result['newVotes'].toString());
}
}
});
});
/* show preview when hover a entry of resource*/
$('.recommender_resource').hover(
function() {
$('.recommender_resource').removeClass('resource_hovered');
$(this).addClass('resource_hovered');
// $('.descriptionText').hide();
$('.previewingImg').removeClass('hidden');
$('.previewingImg').attr('src', $(this).find('.recommender_descriptionImg').text());
$('.descriptionText').text($(this).find('.recommender_descriptionText').text());
Logger.log('resource.hover.event', {
'status': 'Hovering resource',
'id': $(this).find('.recommender_entryId').text()
});
}, function() {
}
);
$('.recommender_resource').hover(
function() {
$('.recommender_resource').removeClass('resource_hovered');
$('.descriptionText').hide();
$('.previewingImg').addClass('hidden');
$(this).addClass('resource_hovered');
$('.previewingImg').attr('src', $(this).find('.recommender_descriptionImg').text());
$('.descriptionText').text($(this).find('.recommender_descriptionText').text());
if ($('.descriptionText').text() != '') { $('.descriptionText').show(); }
$(".previewingImg").load(function() { $('.previewingImg').removeClass('hidden'); });
Logger.log('resource.hover.event', {
'status': 'Hovering resource',
'id': $(this).find('.recommender_entryId').text()
});
}, function() {
}
);
/* edit existing resource */
$('.resource_edit_button').click(function() {
$('.editSourceBlock').show();
$('.recommender_content').hide();
$('.recommender_modify').show();
$('.recommender_modify_title').text('Edit existing resource');
/* initialize the text area */
$('.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_descriptionText').val($(this).parent().parent().find('.recommender_descriptionText').text());
$('#editResourceForm').find("input[name='file']").val('');
$('.edit_submit').attr('disabled', true);
var divEdit = this;
$('.resource_edit_button').click(function() {
$('.editSourceBlock').show();
$('.recommender_content').hide();
$('.recommender_modify').show();
$('.recommender_modify_title').text('Edit existing resource');
var resourceDiv = $(this).parent().parent();
Logger.log('editResource.click.event', {
'status': 'Entering edit resource mode',
'id': $(this).parent().parent().find('.recommender_entryId').text()
});
/* check whether enough information (title/url) is provided for editing a resource, if yes, enable summission button */
function enableEditSubmit() {
if ($('.edit_title').val() == '' || $('.edit_url').val() == '') {
$('.edit_submit').attr('disabled', true);
return;
}
$('.edit_submit').attr('disabled', false);
}
/* check whether the input text area is changed, if yes, check whether student can submit the resource */
$('.edit_title,.edit_url,.edit_descriptionText').unbind();
$('.edit_title,.edit_url,.edit_descriptionText').bind('input propertychange', function() { enableEditSubmit(); });
$('#editResourceForm').find("input[name='file']").unbind();
$('#editResourceForm').find("input[name='file']").change(function() {
if ($(this).val() != '') { 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['descriptionText'] = $('.edit_descriptionText').val();
data['description'] = ''
if (data['url'] == '' || data['title'] == '') { return; }
var formDiv = $('#editResourceForm');
var file = new FormData($(formDiv)[0]);
Logger.log('editResource.click.event', {
'status': 'Edit existing resource',
'title': data['title'],
'url': data['url'],
'descriptionText': data['descriptionText'],
'description': $(formDiv).find("input[name='file']").val(),
'id': $(divEdit).parent().parent().find('.recommender_entryId').text()
var data = {};
data['id'] = parseInt(resourceDiv.find('.recommender_entryId').text());
/* initialize the text area */
$('.edit_title').val(resourceDiv.find('.recommender_title').find('a').text());
$('.edit_url').val(resourceDiv.find('.recommender_title').find('a').attr('href'));
$('.edit_descriptionText').val(resourceDiv.find('.recommender_descriptionText').text());
$('#editResourceForm').find("input[name='file']").val('');
$('.edit_submit').attr('disabled', true);
Logger.log('editResource.click.event', {
'status': 'Entering edit resource mode',
'id': data['id']
});
if ($(formDiv).find("input[name='file']").val() == '') { editResource(data); }
else {
/* upload once student select a file */
$.ajax({
type: 'POST',
url: uploadScreenshotUrl,
data: file,
contentType: false,
cache: false,
processData: false,
async: false,
/* 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(result) {
if (result.responseText == 'FILETYPEERROR') {
alert('Please upload an image');
$(formDiv).find("input[name='file']").val('');
}
else {
/* update new entry */
data['description'] = baseUrl + result.responseText;
editResource(data);
}
},
});
}
/* check whether enough information (title/url) is provided for editing a resource, if yes, enable summission button */
function enableEditSubmit() {
if ($('.edit_title').val() == '' || $('.edit_url').val() == '') {
$('.edit_submit').attr('disabled', true);
return;
}
$('.edit_submit').attr('disabled', false);
}
/* check whether the input text area is changed, if yes, check whether student can submit the resource */
$('.edit_title,.edit_url,.edit_descriptionText').unbind();
$('.edit_title,.edit_url,.edit_descriptionText').bind('input propertychange', function() { enableEditSubmit(); });
$('#editResourceForm').find("input[name='file']").unbind();
$('#editResourceForm').find("input[name='file']").change(function() {
if ($(this).val() != '') { 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 */
data['url'] = $('.edit_url').val();
data['title'] = $('.edit_title').val();
data['descriptionText'] = $('.edit_descriptionText').val();
data['description'] = ''
if (data['url'] == '' || data['title'] == '') { return; }
var formDiv = $('#editResourceForm');
var file = new FormData($(formDiv)[0]);
Logger.log('editResource.click.event', {
'status': 'Edit existing resource',
'title': data['title'],
'url': data['url'],
'descriptionText': data['descriptionText'],
'description': $(formDiv).find("input[name='file']").val(),
'id': data['id']
});
if ($(formDiv).find("input[name='file']").val() == '') { editResource(data); }
else {
/* upload once student select a file */
$.ajax({
type: 'POST',
url: uploadScreenshotUrl,
data: file,
contentType: false,
cache: false,
processData: false,
async: false,
/* 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(result) {
if (result.responseText == 'FILETYPEERROR') {
alert('Please upload an image');
$(formDiv).find("input[name='file']").val('');
}
else {
/* update new entry */
data['description'] = baseUrl + result.responseText;
editResource(data);
}
},
});
}
function editResource (data) {
$.ajax({
type: "POST",
url: editResourceUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
/* show the edited resource */
$(divEdit).parent().parent().find('.recommender_title').find('a').text(data['title']);
$(divEdit).parent().parent().find('.recommender_title').find('a').attr('href', data['url']);
if (data["description"] != "") { $(divEdit).parent().parent().find('.recommender_descriptionImg').text(data['description']); }
if (data["descriptionText"] != "") { $(divEdit).parent().parent().find('.recommender_descriptionText').text(data['descriptionText']); }
backToView();
}
else { alert('The url you entered has been already provided by your fellows'); }
}
});
}
});
});
function editResource (data) {
$.ajax({
type: "POST",
url: editResourceUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success'] == true) {
var resourceDiv = $('.recommender_resource:eq(' + findResourceDiv(result['id']).toString() + ')');
/* show the edited resource */
resourceDiv.find('.recommender_title').find('a').text(result['title']);
resourceDiv.find('.recommender_title').find('a').attr('href', result['url']);
if (data["description"] != "") { resourceDiv.find('.recommender_descriptionImg').text(result['description']); }
if (data["descriptionText"] != "") { resourceDiv.find('.recommender_descriptionText').text(result['descriptionText']); }
backToView();
}
else { alert('The url you entered has been already provided by your fellows'); }
}
});
}
});
});
/* flag problematic resource */
$('.flagResource').click(function() {
$('.flagSourceBlock').show();
$('.recommender_content').hide();
$('.recommender_modify').show();
$('.recommender_modify_title').text('Flag Resource');
var flagDiv = $(this);
var flaggedResourceDiv = $(this).parent().parent();
$('.flag_reason').val($(flaggedResourceDiv).find('.recommender_problematicReason').text());
Logger.log('flagResource.click.event', {
'status': 'Entering flag resource mode',
'id': $(flaggedResourceDiv).find('.recommender_entryId').text()
});
/* record the flagging once user click on the flag button */
/*
if (!$(this).hasClass('problematic')) {
data = {};
data['resource'] = parseInt($(flaggedResourceDiv).find('.recommender_entryId').text());
data['reason'] = '';
data['isProblematic'] = true;
$.ajax({
type: "POST",
url: flagResourceUrl,
data: JSON.stringify(data),
success: function(result) {
$(flagDiv).addClass('problematic');
addTooltip();
}
});
}
*/
$('.flag_reason_submit').unbind();
$('.unflag_button').unbind();
/* record the reason for problematic resource */
$('.flag_reason_submit').click(function() {
data = {};
data['resource'] = parseInt($(flaggedResourceDiv).find('.recommender_entryId').text());
data['reason'] = $('.flag_reason').val();
data['isProblematic'] = true;
Logger.log('flagResource.click.event', {
'status': 'Flagging resource',
'id': $(flaggedResourceDiv).find('.recommender_entryId').text(),
'reason': data['reason'],
'isProblematic': true
$('.flagResource').click(function() {
$('.flagSourceBlock').show();
$('.recommender_content').hide();
$('.recommender_modify').show();
$('.recommender_modify_title').text('Flag Resource');
var flagDiv = $(this);
var flaggedResourceDiv = $(this).parent().parent();
$('.flag_reason').val($(flaggedResourceDiv).find('.recommender_problematicReason').text());
data = {};
data['id'] = parseInt($(flaggedResourceDiv).find('.recommender_entryId').text());
Logger.log('flagResource.click.event', {
'status': 'Entering flag resource mode',
'id': data['id']
});
$.ajax({
type: "POST",
url: flagResourceUrl,
data: JSON.stringify(data),
success: function(result) {
$(flaggedResourceDiv).find('.recommender_problematicReason').text(data['reason']);
backToView();
}
});
});
$('.flag_reason_submit').unbind();
$('.unflag_button').unbind();
/* record the reason for problematic resource */
$('.flag_reason_submit').click(function() {
data['reason'] = $('.flag_reason').val();
data['isProblematic'] = true;
Logger.log('flagResource.click.event', {
'status': 'Flagging resource',
'id': data['id'],
'reason': data['reason'],
'isProblematic': data['isProblematic']
});
$.ajax({
type: "POST",
url: flagResourceUrl,
data: JSON.stringify(data),
success: function(result) {
var flaggedResourceDiv = $('.recommender_resource:eq(' + findResourceDiv(result['id']).toString() + ')');
var flagDiv = $('.flagResource:eq(' + findResourceDiv(result['id']).toString() + ')');
/* unflag the resource */
$('.unflag_button').click(function() {
data = {};
data['resource'] = parseInt($(flaggedResourceDiv).find('.recommender_entryId').text());
data['isProblematic'] = false;
Logger.log('flagResource.click.event', {
'status': 'Unflagging resource',
'id': $(flaggedResourceDiv).find('.recommender_entryId').text(),
'isProblematic': false
$(flaggedResourceDiv).find('.recommender_problematicReason').text(result['reason']);
if (result['isProblematic']) { $(flagDiv).addClass('problematic'); }
else { $(flagDiv).removeClass('problematic'); }
addTooltip();
backToView();
}
});
});
/* unflag the resource */
$('.unflag_button').click(function() {
data['isProblematic'] = false;
Logger.log('flagResource.click.event', {
'status': 'Unflagging resource',
'id': data['id'],
'isProblematic': data['isProblematic']
});
$.ajax({
type: "POST",
url: flagResourceUrl,
data: JSON.stringify(data),
success: function(result) {
$(flagDiv).removeClass('problematic');
$(flaggedResourceDiv).find('.recommender_problematicReason').text('');
backToView();
}
});
});
$.ajax({
type: "POST",
url: flagResourceUrl,
data: JSON.stringify(data),
success: function(result) {
var flaggedResourceDiv = $('.recommender_resource:eq(' + findResourceDiv(result['id']).toString() + ')');
var flagDiv = $('.flagResource:eq(' + findResourceDiv(result['id']).toString() + ')');
$(flaggedResourceDiv).find('.recommender_problematicReason').text(result['reason']);
if (result['isProblematic']) { $(flagDiv).addClass('problematic'); }
else { $(flagDiv).removeClass('problematic'); }
addTooltip();
backToView();
}
});
});
});
addTooltip();
}
function addTooltip() {
tooltipsCats.forEach(function(ele, ind) {
$(ele).attr('title', tooltipsCatsText[ele]);
});
}
function initial() {
$(".hide-show").click();
$('.recommender_modify').hide();
$('.flagSourceBlock').hide();
$('.editSourceBlock').hide();
$('.recommender_add').hide();
paginationRow();
pagination();
addResourceReset();
bindEvent();
addTooltip();
}
function addTooltip() {
tooltipsCats.forEach(function(ele, ind) {
$(ele).attr('title', tooltipsCatsText[ele]);
});
}
function findResourceDiv(resourceId) {
index = -1;
$('.recommender_entryId').each(function(idx, ele){
if (parseInt($(ele).text()) == resourceId) {
index = idx;
return false;
}
});
return index;
}
function addFunctionsForStaff() {
$.ajax({
type: "POST",
url: isUserStaffUrl,
data: JSON.stringify({}),
success: function(result) {
if (result['is_user_staff']) {
$('.recommender_edit').append('<span class="ui-icon ui-icon-gear staffEdition"></span>');
$('.staffEdition').click(function() {
$('.staffEditionBlock').show();
$('.recommender_content').hide();
$('.recommender_modify').show();
$('.recommender_modify_title').text('Staff manipulation');
var data = {};
data['id'] = parseInt($(this).parent().parent().find('.recommender_entryId').text());
$('.delete_resource').unbind();
$('.delete_resource').click(function() {
$.ajax({
type: "POST",
url: deleteResourceUrl,
data: JSON.stringify(data),
success: function(result) {
if (result['Success']) {
var deletedResourceIdx = findResourceDiv(result['id']);
$('.recommender_resource:eq(' + deletedResourceIdx.toString() + ')').remove();
/* Delete last resource */
if ($('.recommender_resource').length == deletedResourceIdx) { deletedResourceIdx--; }
currentPage = Math.ceil((deletedResourceIdx + 1)/entriesPerPage);
paginationRow();
pagination();
backToView();
}
else { alert(result['error']); }
}
});
});
});
}
}
});
}
function initial() {
backToView();
$(".hide-show").click();
addFunctionsForStaff();
paginationRow();
pagination();
addResourceReset();
bindEvent();
if ($('.recommender_resource').length == 0) {
$('.noResourceIntro').removeClass('hidden');
$('.descriptionText').hide();
}
}
initial();
if ($('.recommender_resource').length == 0) {
$('.noResourceIntro').removeClass('hidden');
$('.descriptionText').hide();
}
}
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