diff --git a/core/views/editor_helpers.py b/core/views/editor_helpers.py index 1fe30b0..f85d582 100644 --- a/core/views/editor_helpers.py +++ b/core/views/editor_helpers.py @@ -130,3 +130,6 @@ class NewWebImageForm(forms.Form): if full_path.exists(): raise forms.ValidationError("File already exists in %s" % rel_path) return self.cleaned_data['file_'] + +class HTMLarea(forms.Textarea): + template_name = "widgets/HTMLarea.html" diff --git a/core/views/expo.py b/core/views/expo.py index 91919a5..d84aa5f 100644 --- a/core/views/expo.py +++ b/core/views/expo.py @@ -23,6 +23,8 @@ import troggle.settings as settings from troggle.lib import version_control +from troggle.core.views.editor_helpers import HTMLarea + '''Formerly a separate package called 'flatpages' written by Martin Green 2011. This was NOT django.contrib.flatpages which stores HTML in the database, so the name was changed to expopages. @@ -380,5 +382,5 @@ class ExpoPageForm(forms.Form): '''The form used by the editexpopage function ''' title = forms.CharField(widget=forms.TextInput(attrs={'size':'60', 'placeholder': "Enter title (displayed in tab)"})) - html = forms.CharField(widget=forms.Textarea(attrs={"cols":80, "rows":20, 'placeholder': "Enter page content (using HTML)"})) + html = forms.CharField(widget=HTMLarea(attrs={"cols":80, "rows":20, 'placeholder': "Enter page content (using HTML)"})) change_message = forms.CharField(widget=forms.Textarea(attrs={"cols":80, "rows":3, 'placeholder': "Descibe the change made (for git)"})) diff --git a/settings.py b/settings.py index 7c05550..bcb972e 100644 --- a/settings.py +++ b/settings.py @@ -134,10 +134,13 @@ INSTALLED_APPS = ( 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.admindocs', + 'django.forms', #Required to customise widget templates # 'django.contrib.staticfiles', # We put our CSS etc explicitly in the right place so do not need this 'troggle.core', ) +FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' #Required to customise widget templates + # See the recommended order of these in https://docs.djangoproject.com/en/2.2/ref/middleware/ # Note that this is a radically different onion architecture from earlier versions though it looks the same, # see https://docs.djangoproject.com/en/2.0/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware diff --git a/templates/editexpopage.html b/templates/editexpopage.html index b71c91f..22c9106 100644 --- a/templates/editexpopage.html +++ b/templates/editexpopage.html @@ -2,243 +2,14 @@ {% block title %}Edit {{ path }}{% endblock %} {% block extrahead %} -<!--<script src="{{ settings.TINY_MCE_MEDIA_URL }}tiny_mce.js" type="text/javascript"></script>--> -<!-- <script type="text/javascript"> tinyMCE.init({ mode : "textareas" }); </script>--> - -<script src="{{ settings.MEDIA_URL }}admin/js/vendor/jquery/jquery.js" type="text/javascript"></script> - - <script src={{ settings.MEDIA_URL }}codemirror/codemirror.js></script> - <script src={{ settings.MEDIA_URL }}codemirror/xml.js></script> - <script src={{ settings.MEDIA_URL }}codemirror/javascript.js></script> - <script src={{ settings.MEDIA_URL }}codemirror/css.js></script> - <script src={{ settings.MEDIA_URL }}codemirror/htmlmixed.js></script> - <link rel=stylesheet href={{ settings.MEDIA_URL }}codemirror/codemirror.css> - <link rel=stylesheet href={{ settings.MEDIA_URL }}codemirror/docs.css> - <style type=text/css> - .CodeMirror { - float: left; - width: 50%; - border: 1px solid black; - height: 80%; - } - .CodeMirror-scroll{ - height: 99% - } - iframe { - width: 49%; - height: 80%; - float: left; - border: 1px solid black; - border-left: 0px; - } - body{max-width: none; - margin-left: 275px; - margin-right: 50px; - text-align: left; - } - #id_change_message{ - height: 5%; - } - </style> - <style type=text/css> - html { - font-family: "Helvetica Neue", sans-serif; - width: 100%; - color: #666666; - text-align: center; - } - - .popup-overlay { - /*Hides pop-up when there is no "active" class*/ - visibility: hidden; - position: absolute; - background: #ffffff; - border: 3px solid #666666; - width: 90%; - height: 80%; - overflow: scroll; - left: 5%; - z-index: 20; - } - - .popup-overlay.active { - /*displays pop-up when "active" class is present*/ - visibility: visible; - text-align: center; - } - - .popup-content { - /*Hides pop-up content when there is no "active" class */ - visibility: hidden; - } - - .popup-content.active { - /*Shows pop-up content when "active" class is present */ - visibility: visible; - } - - button { - display: inline-block; - vertical-align: middle; - border-radius: 30px; - margin: .20rem; - font-size: 1rem; - color: #666666; - background: #ffffff; - border: 1px solid #666666; - } - - button:hover { - border: 1px solid #666666; - background: #666666; - color: #ffffff; - } - </style> {% endblock %} {% block body %} <h1>Edit {{ path }}</h1> -<!--Creates the add image popup--> -<div class="add-image-popup popup-overlay"> - <div class="add-image-popup popup-content"> - <h2>Select Image</h2> - <p id="image_popup_content"> Loading ...</p> - <button onclick="new_image_popup()">Upload Image</button> - <button class="close" onclick="$('.add-image-popup').removeClass('active');">Close</button> - </div> -</div> - -<!--Creates the new image popup--> -<div class="new-image-popup popup-overlay"> - <div class="new-image-popup popup-content"> - <h2>New Image</h2> - <p id="new_image_popup_content"> Loading ...</p> - <button class="close" onclick="$('.new-image-popup').removeClass('active');">Close</button> - </div> -</div> <form action="" method="post">{% csrf_token %} -{{ form.non_field_errors }} -<div class="fieldWrapper"> - {{ form.title.errors }} - <label for="{{ form.title.id_for_label }}">Title:</label> - {{ form.title }} -</div> -<div class="fieldWrapper"> - {{ form.html.errors }} - <!--<label for="{{ form.title.id_for_label }}">HTML:</label> --> - {{ form.html }}<iframe id=preview></iframe> -</div> -<button type="button" onclick="addTag('i', '')">italic</button> -<button type="button" onclick="addTag('b', '')">bold</button> -<button type="button" onclick="addTag('h2', 'id="tophead"')">top heading</button> -<button type="button" onclick="addTag('h1', '')">heading 1</button> -<button type="button" onclick="addTag('h2', '')">heading 2</button> -<button type="button" onclick="addTag('h3', '')">heading 3</button> -<button type="button" onclick="addTag('h4', '')">heading 4</button> -<button type="button" onclick="addTag('a', 'href=""')">hyperlink</button> -<button type="button" onclick="addTag('p', '')">paragraph</button> -<button type="button" onclick="add_image_popup()">image</button> -<div class="fieldWrapper"> - {{ form.change_message.errors }} - <label for="{{ form.title.id_for_label }}">Git change message:</label> - {{ form.change_message }} -</div> -{% include "menu.html" %} +{{ form.as_p }} <p><input type="submit" value="Submit" /></p> </form> - - -<script> - - -function add_image_popup() { - $('.add-image-popup').addClass('active'); - $('#image_popup_content').load("{% url 'image_selector' path %}", function() { - $('.thumbnail').click(function(){ - $(".add-image-popup").removeClass("active"); - addStr($( this ).attr("data-html")) - }); - }) - } -function new_image_popup() { - $('.add-image-popup').removeClass('active'); - $('.new-image-popup').addClass('active'); - $.ajax({ - type : "GET", - dataType: "json", - url: "{% url 'new_image_form' path %}", - success: function(data){handle_new_image(data)} - }); - } - -function handle_new_image(data) { - if (data.hasOwnProperty('form')) { - $('#new_image_popup_content').html(data.form); - $('#new_image_form').on('submit', function(e){ - e.preventDefault(); - data = $('#new_image_form').serialize(); - - $.ajax({ - type : "POST", - dataType: "json", - url: "{% url 'new_image_form' path %}", - data: new FormData($('#new_image_form')[0]), - processData: false, - contentType: false, - success: function(data){ - handle_new_image(data); - } - }); - }); - } - else if (data.hasOwnProperty('html')) { - $('.new-image-popup').removeClass('active'); - addStr(data.html); - } - else { - alert(data.error); - } - } -</script> - <script> - var delay; - // Initialize CodeMirror editor with a nice html5 canvas demo. - var editor = CodeMirror.fromTextArea(document.getElementById('id_html'), { - mode: 'text/html', - tabMode: 'indent', - onChange: function() { - clearTimeout(delay); - delay = setTimeout(updatePreview, 300); - } - }); - - function updatePreview() { - var previewFrame = document.getElementById('preview'); - var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; - preview.open(); - preview.write("<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /><link rel='stylesheet' type='text/css' href='/css/main2.css' /> <style type=text/css>body{max-width: none;margin-left: 15px;margin-right: 15px;}</style></head><body>"); - preview.write(editor.getValue()); - preview.write("</body></html>"); - preview.close(); - } - setTimeout(updatePreview, 300); - - function addTag(tag, attr){ - // For codemirror & center cursor - var from = editor.getCursor(true); - var to = editor.getCursor(false); - editor.replaceRange("</"+tag+">", to); - if (attr.length > 0) {attr = " " + attr;} - editor.replaceRange("<"+tag+attr+">", from); - editor.focus(); - editor.setCursor({line: to.line , ch : to.ch + 2 + tag.length + attr.length }); - } - - function addStr(x){ - var to = editor.getCursor(false); - editor.replaceRange(x, to); - editor.focus(); - editor.setCursor({line: to.line , ch : to.ch + x.length }); - } - </script> +{% include "menu.html" %} +{% include 'html_editor_scripts_css.html' %} {% endblock %} diff --git a/templates/html_editor_scripts_css.html b/templates/html_editor_scripts_css.html new file mode 100644 index 0000000..5bd56d4 --- /dev/null +++ b/templates/html_editor_scripts_css.html @@ -0,0 +1,166 @@ +<script src="{{ settings.MEDIA_URL }}admin/js/vendor/jquery/jquery.js" type="text/javascript"></script> + + <script src={{ settings.MEDIA_URL }}codemirror/codemirror.js></script> + <script src={{ settings.MEDIA_URL }}codemirror/xml.js></script> + <script src={{ settings.MEDIA_URL }}codemirror/javascript.js></script> + <script src={{ settings.MEDIA_URL }}codemirror/css.js></script> + <script src={{ settings.MEDIA_URL }}codemirror/htmlmixed.js></script> + <link rel=stylesheet href={{ settings.MEDIA_URL }}codemirror/codemirror.css> + <link rel=stylesheet href={{ settings.MEDIA_URL }}codemirror/docs.css> + <style type=text/css> + .CodeMirror { + float: left; + width: 50%; + border: 1px solid black; + height: 80%; + } + .CodeMirror-scroll{ + height: 99% + } + iframe { + width: 49%; + height: 80%; + float: left; + border: 1px solid black; + border-left: 0px; + } + body{max-width: none; + margin-left: 275px; + margin-right: 50px; + text-align: left; + } + #id_change_message{ + height: 5%; + } + </style> + <style type=text/css> + html { + font-family: "Helvetica Neue", sans-serif; + width: 100%; + color: #666666; + text-align: center; + } + + .popup-overlay { + /*Hides pop-up when there is no "active" class*/ + visibility: hidden; + position: absolute; + background: #ffffff; + border: 3px solid #666666; + width: 90%; + height: 80%; + overflow: scroll; + left: 5%; + z-index: 20; + } + + .popup-overlay.active { + /*displays pop-up when "active" class is present*/ + visibility: visible; + text-align: center; + } + + .popup-content { + /*Hides pop-up content when there is no "active" class */ + visibility: hidden; + } + + .popup-content.active { + /*Shows pop-up content when "active" class is present */ + visibility: visible; + } + + + </style> +<script> +function add_image_popup() { + $('.add-image-popup').addClass('active'); + $('#image_popup_content').load("{% url 'image_selector' path %}", function() { + $('.thumbnail').click(function(){ + $(".add-image-popup").removeClass("active"); + addStr($( this ).attr("data-html")) + }); + }) + } +function new_image_popup() { + $('.add-image-popup').removeClass('active'); + $('.new-image-popup').addClass('active'); + $.ajax({ + type : "GET", + dataType: "json", + url: "{% url 'new_image_form' path %}", + success: function(data){handle_new_image(data)} + }); + } + +function handle_new_image(data) { + if (data.hasOwnProperty('form')) { + $('#new_image_popup_content').html(data.form); + $('#new_image_form').on('submit', function(e){ + e.preventDefault(); + data = $('#new_image_form').serialize(); + + $.ajax({ + type : "POST", + dataType: "json", + url: "{% url 'new_image_form' path %}", + data: new FormData($('#new_image_form')[0]), + processData: false, + contentType: false, + success: function(data){ + handle_new_image(data); + } + }); + }); + } + else if (data.hasOwnProperty('html')) { + $('.new-image-popup').removeClass('active'); + addStr(data.html); + } + else { + alert(data.error); + } + } +</script> + <script> + var delay; + // Initialize CodeMirror editor with a nice html5 canvas demo. + var editor = CodeMirror.fromTextArea(document.getElementById('id_html'), { + mode: 'text/html', + tabMode: 'indent', + onChange: function() { + clearTimeout(delay); + delay = setTimeout(updatePreview, 300); + } + }); + + function updatePreview() { + var previewFrame = document.getElementById('preview'); + var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; + preview.open(); + preview.write("<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /><link rel='stylesheet' type='text/css' href='/css/main2.css' /> <style type=text/css>body{max-width: none;margin-left: 15px;margin-right: 15px;}</style></head><body>"); + preview.write(editor.getValue()); + preview.write("</body></html>"); + preview.close(); + } + setTimeout(updatePreview, 300); + + function addTag(tag, attr){ + // For codemirror & center cursor + var from = editor.getCursor(true); + var to = editor.getCursor(false); + editor.replaceRange("</"+tag+">", to); + if (attr.length > 0) {attr = " " + attr;} + editor.replaceRange("<"+tag+attr+">", from); + editor.focus(); + editor.setCursor({line: to.line , ch : to.ch + 2 + tag.length + attr.length }); + } + + function addStr(x){ + var to = editor.getCursor(false); + editor.replaceRange(x, to); + editor.focus(); + editor.setCursor({line: to.line , ch : to.ch + x.length }); + } + </script> + diff --git a/templates/widgets/HTMLarea.html b/templates/widgets/HTMLarea.html new file mode 100644 index 0000000..9837327 --- /dev/null +++ b/templates/widgets/HTMLarea.html @@ -0,0 +1,32 @@ +<!--Creates the add image popup--> +<div><div class="add-image-popup popup-overlay"> + <div class="add-image-popup popup-content"> + <h2>Select Image</h2> + <p id="image_popup_content"> Loading ...</p> + <button onclick="new_image_popup()">Upload Image</button> + <button class="close" onclick="$('.add-image-popup').removeClass('active');">Close</button> + </div> +</div> + +<!--Creates the new image popup--> +<div class="new-image-popup popup-overlay"> + <div class="new-image-popup popup-content"> + <h2>New Image</h2> + <p id="new_image_popup_content"> Loading ...</p> + <button class="close" onclick="$('.new-image-popup').removeClass('active');">Close</button> + </div> +</div> +{% include "django/forms/widgets/textarea.html" %} +<iframe id=preview></iframe> +<button type="button" onclick="addTag('i', '')">italic</button> +<button type="button" onclick="addTag('b', '')">bold</button> +<button type="button" onclick="addTag('h2', 'id="tophead"')">top heading</button> +<button type="button" onclick="addTag('h1', '')">heading 1</button> +<button type="button" onclick="addTag('h2', '')">heading 2</button> +<button type="button" onclick="addTag('h3', '')">heading 3</button> +<button type="button" onclick="addTag('h4', '')">heading 4</button> +<button type="button" onclick="addTag('a', 'href=""')">hyperlink</button> +<button type="button" onclick="addTag('p', '')">paragraph</button> +<button type="button" onclick="add_image_popup()">image</button> +<button type="button" onclick="addStr('<hr/>')">horizontal line</button> +</div>