Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.. include:: ../CHANGELOG.rst
Changelog
=========

Changes are tracked via `GitHub Releases <https://github.com/openedx/xblocks-contrib/releases>`_.
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
All configuration values have a default; values that are commented out
serve to show the default.
"""

import os
import re
import sys
Expand Down
2 changes: 1 addition & 1 deletion xblock_pdf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Init for PDFBlock."""

from .pdf import PDFBlock
from .pdf import PDFBlock as PDFBlock
75 changes: 38 additions & 37 deletions xblock_pdf/pdf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""pdfXBlock main Python class."""

import json

from django.utils.translation import gettext_noop as _
Expand All @@ -13,7 +14,7 @@
resource_loader = ResourceLoader(__name__)


@XBlock.needs('i18n')
@XBlock.needs("i18n")
class PDFBlock(XBlock):
"""PDF XBlock. Allows authors to embed PDFs in their courses."""

Expand All @@ -23,21 +24,21 @@ class PDFBlock(XBlock):
display_name=_("Display Name"),
default=_("PDF"),
scope=Scope.settings,
help=_("This name appears in the horizontal navigation at the top of the page.")
help=_("This name appears in the horizontal navigation at the top of the page."),
)

url = String(
display_name=_("PDF URL"),
default=_("https://tutorial.math.lamar.edu/pdf/Trig_Cheat_Sheet.pdf"),
scope=Scope.content,
help=_("The URL for your PDF.")
help=_("The URL for your PDF."),
)

allow_download = Boolean(
display_name=_("PDF Download Allowed"),
default=True,
scope=Scope.content,
help=_("Display a download button for this PDF.")
help=_("Display a download button for this PDF."),
)

source_text = String(
Expand All @@ -47,7 +48,7 @@ class PDFBlock(XBlock):
help=_(
"Add a download link for the source file of your PDF. "
"Use it for example to provide the PowerPoint file used to create this PDF."
)
),
)

source_url = String(
Expand All @@ -57,38 +58,38 @@ class PDFBlock(XBlock):
help=_(
"Add a download link for the source file of your PDF. "
"Use it for example to provide the PowerPoint file used to create this PDF."
)
),
)

@property
def raw_settings(self):
"""Get the raw settings of the XBlock as a dictionary."""
return {
'display_name': self.display_name,
'url': self.url,
'allow_download': self.allow_download,
'disable_all_download': is_all_download_disabled(),
'source_text': self.source_text,
'source_url': self.source_url,
"display_name": self.display_name,
"url": self.url,
"allow_download": self.allow_download,
"disable_all_download": is_all_download_disabled(),
"source_text": self.source_text,
"source_url": self.source_url,
}

def student_view(self, context=None): # pylint: disable=unused-argument
"""Primary view of the XBlock, shown to students when viewing courses."""
html = resource_loader.render_django_template(
'templates/html/pdf_view.html',
"templates/html/pdf_view.html",
context=self.raw_settings,
i18n_service=self.runtime.service(self, "i18n"),
)

event_type = 'edx.pdf.loaded'
event_type = "edx.pdf.loaded"
event_data = {
'url': self.url,
'source_url': self.source_url,
"url": self.url,
"source_url": self.source_url,
}
self.runtime.publish(self, event_type, event_data)
frag = Fragment(html)
frag.add_javascript(resource_loader.load_unicode("static/js/pdf_view.js"))
frag.initialize_js('pdfXBlockInitView')
frag.initialize_js("pdfXBlockInitView")
return frag

def studio_view(self, context=None):
Expand All @@ -98,49 +99,49 @@ def studio_view(self, context=None):
Shown to teachers when editing the XBlock.
"""
context = {
'display_name': self.display_name,
'url': self.url,
'allow_download': self.allow_download,
'disable_all_download': is_all_download_disabled(),
'source_text': self.source_text,
'source_url': self.source_url
"display_name": self.display_name,
"url": self.url,
"allow_download": self.allow_download,
"disable_all_download": is_all_download_disabled(),
"source_text": self.source_text,
"source_url": self.source_url,
}
html = resource_loader.render_django_template(
'templates/html/pdf_edit.html',
"templates/html/pdf_edit.html",
context=context,
i18n_service=self.runtime.service(self, "i18n"),
)
frag = Fragment(html)
frag.add_javascript(resource_loader.load_unicode("static/js/pdf_edit.js"))
frag.initialize_js('pdfXBlockInitEdit')
frag.initialize_js("pdfXBlockInitEdit")
return frag

@XBlock.json_handler
def on_download(self, data, suffix=''): # pylint: disable=unused-argument
def on_download(self, data, suffix=""): # pylint: disable=unused-argument
"""Download file event handler."""
event_type = 'edx.pdf.downloaded'
event_type = "edx.pdf.downloaded"
event_data = {
'url': self.url,
'source_url': self.source_url,
"url": self.url,
"source_url": self.source_url,
}
self.runtime.publish(self, event_type, event_data)

@XBlock.handler
def load_pdf(self, *_args, **_kwargs):
"""Get the PDF block's settings in JSON format."""
return Response(json.dumps(self.raw_settings), content_type='application/json', charset='utf8')
return Response(json.dumps(self.raw_settings), content_type="application/json", charset="utf8")

@XBlock.json_handler
def save_pdf(self, data, suffix=''): # pylint: disable=unused-argument
def save_pdf(self, data, suffix=""): # pylint: disable=unused-argument
"""Save handler."""
self.display_name = data['display_name']
self.url = data['url']
self.display_name = data["display_name"]
self.url = data["url"]

if not is_all_download_disabled():
self.allow_download = bool_from_str(data['allow_download'])
self.source_text = data['source_text']
self.source_url = data['source_url']
self.allow_download = bool_from_str(data["allow_download"])
self.source_text = data["source_text"]
self.source_url = data["source_url"]

return {
'result': 'success',
"result": "success",
}
56 changes: 30 additions & 26 deletions xblock_pdf/tests/test_pdf.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Tests for the PDF Block"""

import json
from typing import Any, Optional
from unittest.mock import MagicMock, patch

from django.test import TestCase, override_settings
from django.test import override_settings
from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds
from xblock.test.toy_runtime import ToyRuntime
Expand Down Expand Up @@ -41,7 +42,6 @@ def mock_handle_request(data: Optional[dict[str, Any]] = None, method: str = "PO

def test_defaults_render():
"""Test the basic view loads."""
scope_ids = ScopeIds("1", "2", "3", "4")
block = make_block()
content = get_student_content(block)
assert '<iframe src="https://tutorial.math.lamar.edu/pdf/Trig_Cheat_Sheet.pdf"' in content
Expand All @@ -52,10 +52,10 @@ def test_download_button():
block = make_block(allow_download=True)
get_student_content(block)
content = get_student_content(block)
assert 'Download the PDF' in content
assert "Download the PDF" in content
block.allow_download = False
content = get_student_content(block)
assert 'Download the PDF' not in content
assert "Download the PDF" not in content


def test_source_url():
Expand All @@ -80,13 +80,15 @@ def test_studio_view_renders():
def test_saves_settings():
"""Test that PDF settings are saved."""
block = make_block()
request = mock_handle_request({
"display_name": "Novel application of theory",
"url": "https://example.com/nature_article.pdf",
"allow_download": "false",
"source_text": "Get educated",
"source_url": "https://example.com/nature_article.tex",
})
request = mock_handle_request(
{
"display_name": "Novel application of theory",
"url": "https://example.com/nature_article.pdf",
"allow_download": "false",
"source_text": "Get educated",
"source_url": "https://example.com/nature_article.tex",
}
)
block.save_pdf(request)
assert block.display_name == "Novel application of theory"
assert block.url == "https://example.com/nature_article.pdf"
Expand All @@ -102,20 +104,22 @@ def test_saves_settings_omits_on_download_disabled_flag():
downloads disabled flag is set.
"""
block = make_block()
request = mock_handle_request({
"display_name": "Novel application of theory",
"url": "https://example.com/nature_article.pdf",
# These fields shouldn't be visible on the front end,
# but should be dropped if they somehow are.
#
# Potential future improvement would be saving these
# but ignoring them when rendering. This is not currently
# the case since the fields are entirely absent from the studio
# render, and so would send blank data which would error out.
"allow_download": "false",
"source_text": "Get educated",
"source_url": "https://example.com/nature_article.tex",
})
request = mock_handle_request(
{
"display_name": "Novel application of theory",
"url": "https://example.com/nature_article.pdf",
# These fields shouldn't be visible on the front end,
# but should be dropped if they somehow are.
#
# Potential future improvement would be saving these
# but ignoring them when rendering. This is not currently
# the case since the fields are entirely absent from the studio
# render, and so would send blank data which would error out.
"allow_download": "false",
"source_text": "Get educated",
"source_url": "https://example.com/nature_article.tex",
}
)
block.save_pdf(request)
assert block.display_name == "Novel application of theory"
assert block.url == "https://example.com/nature_article.pdf"
Expand All @@ -138,7 +142,7 @@ def test_download_event_fires(mock_publish):
{
"url": "https://tutorial.math.lamar.edu/pdf/Trig_Cheat_Sheet.pdf",
"source_url": "",
}
},
)


Expand Down
4 changes: 2 additions & 2 deletions xblock_pdf/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

def bool_from_str(str_value):
"""Convert string from submitted form to boolean."""
return str_value.strip().lower() == 'true'
return str_value.strip().lower() == "true"


def is_all_download_disabled():
"""Check if all downloads are disabled or not."""
return getattr(settings, 'PDFXBLOCK_DISABLE_ALL_DOWNLOAD', False)
return getattr(settings, "PDFXBLOCK_DISABLE_ALL_DOWNLOAD", False)
16 changes: 8 additions & 8 deletions xblocks_contrib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Init for the xblocks_contrib package."""

from .annotatable import AnnotatableBlock
from .discussion import DiscussionXBlock
from .html import HtmlBlock
from .lti import LTIBlock
from .poll import PollBlock
from .problem import ProblemBlock
from .video import VideoBlock
from .word_cloud import WordCloudBlock
from .annotatable import AnnotatableBlock as AnnotatableBlock
from .discussion import DiscussionXBlock as DiscussionXBlock
from .html import HtmlBlock as HtmlBlock
from .lti import LTIBlock as LTIBlock
from .poll import PollBlock as PollBlock
from .problem import ProblemBlock as ProblemBlock
from .video import VideoBlock as VideoBlock
from .word_cloud import WordCloudBlock as WordCloudBlock

__version__ = "0.17.0"
2 changes: 1 addition & 1 deletion xblocks_contrib/annotatable/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Init for the AnnotatableBlock.
"""

from .annotatable import AnnotatableBlock
from .annotatable import AnnotatableBlock as AnnotatableBlock
14 changes: 6 additions & 8 deletions xblocks_contrib/annotatable/annotatable.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class SerializationError(Exception):
"""
Thrown when a module cannot be exported to XML
"""

def __init__(self, location, msg):
super().__init__(msg)
self.location = location
Expand Down Expand Up @@ -249,8 +250,8 @@ def workbench_scenarios():
@classmethod
def definition_from_xml(cls, xml_object, system):
if len(xml_object) == 0 and len(list(xml_object.items())) == 0:
return {'data': ''}, []
return {'data': etree.tostring(xml_object, pretty_print=True, encoding='unicode')}, []
return {"data": ""}, []
return {"data": etree.tostring(xml_object, pretty_print=True, encoding="unicode")}, []

def definition_to_xml(self, resource_fs):
"""
Expand All @@ -277,13 +278,10 @@ def definition_to_xml(self, resource_fs):
except etree.XMLSyntaxError as err:
# Can't recover here, so just add some info and
# re-raise
lines = self.data.split('\n')
lines = self.data.split("\n")
line, offset = err.position # lint-amnesty, pylint: disable=unpacking-non-sequence
msg = (
"Unable to create xml for block {loc}. "
"Context: '{context}'"
).format(
context=lines[line - 1][offset - 40:offset + 40],
msg = ("Unable to create xml for block {loc}. Context: '{context}'").format(
context=lines[line - 1][offset - 40 : offset + 40],
loc=self.usage_key,
)
raise SerializationError(self.usage_key, msg) from err
2 changes: 1 addition & 1 deletion xblocks_contrib/discussion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Init for the DiscussionXBlock.
"""

from .discussion import DiscussionXBlock
from .discussion import DiscussionXBlock as DiscussionXBlock
Loading
Loading