Markdown & code HL

parent 620c6047
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2018-06-10 17:30
# Generated by Django 1.11.20 on 2019-02-15 17:42
from __future__ import unicode_literals
from django.db import migrations, models
......@@ -12,7 +12,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('pages', '0004_auto_20180608_1504'),
('pages', '0005_auto_20190215_1841'),
]
operations = [
......@@ -30,9 +30,9 @@ class Migration(migrations.Migration):
('description_fr', models.CharField(blank=True, max_length=1000, null=True, verbose_name='Description')),
],
options={
'ordering': ('_order',),
'verbose_name': 'Media File',
'verbose_name_plural': 'Media Files',
'ordering': ('_order',),
},
),
migrations.CreateModel(
......@@ -42,9 +42,9 @@ class Migration(migrations.Migration):
('content', mezzanine.core.fields.RichTextField(verbose_name='Content')),
],
options={
'ordering': ('_order',),
'verbose_name': 'Media Library',
'verbose_name_plural': 'Media Libraries',
'ordering': ('_order',),
},
bases=('pages.page', models.Model),
),
......
from string import punctuation
from urllib import unquote
from urllib.parse import unquote
from django.db import models
from django.utils.translation import ugettext_lazy as _
......
......@@ -136,18 +136,20 @@ FILE_UPLOAD_PERMISSIONS = 0o644
DATABASES = {
"default": {
# Add "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
"ENGINE": "django.db.backends.postgresql_psycopg2",
# DB name or path to database file if using sqlite3.
"ENGINE": "django.db.backends.sqlite3",
"NAME": "sitepro",
# Not used with sqlite3.
"USER": "site-pro",
# Not used with sqlite3.
"PASSWORD": "totototo",
# Set to empty string for localhost. Not used with sqlite3.
"HOST": "localhost",
# Set to empty string for default. Not used with sqlite3.
"PORT": "5432",
# Add "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
# "ENGINE": "django.db.backends.postgresql_psycopg2",
# # DB name or path to database file if using sqlite3.
# "NAME": "sitepro",
# # Not used with sqlite3.
# "USER": "site-pro",
# # Not used with sqlite3.
# "PASSWORD": "totototo",
# # Set to empty string for localhost. Not used with sqlite3.
# "HOST": "localhost",
# # Set to empty string for default. Not used with sqlite3.
# "PORT": "5432",
}
}
......@@ -250,6 +252,7 @@ INSTALLED_APPS = (
# "mezzanine.mobile",
"mezzanine_file_collections",
"resume",
"mezzanine_pagedown",
)
SEARCH_MODEL_CHOICES = ('pages.Page', 'blog.BlogPost', 'resume.*')
......@@ -343,3 +346,8 @@ BLOG_USE_FEATURED_IMAGE = True
SITE_TITLE = "Alice Climent-Pommeret"
SITE_TAGLINE = ""
RICHTEXT_FILTER_LEVEL = 3
RICHTEXT_WIDGET_CLASS = 'mezzanine_pagedown.widgets.PageDownWidget'
RICHTEXT_FILTERS = ['mezzanine_pagedown.filters.custom']
PAGEDOWN_MARKDOWN_EXTENSIONS = ('extra','codehilite','toc')
......@@ -8,6 +8,7 @@ from django.views.i18n import set_language
from mezzanine.core.views import direct_to_template
from mezzanine.conf import settings
import mezzanine_pagedown.urls
#admin.autodiscover()
# Add the urlpatterns for any custom Django applications here.
......@@ -19,6 +20,7 @@ urlpatterns = i18n_patterns(
# admin interface, which would be marginally more secure.
url("^admin/", include(admin.site.urls)),
url("^$", direct_to_template, {"template": "index.html"}, name="home"),
url("^pagedown/", include(mezzanine_pagedown.urls)),
)
if settings.USE_MODELTRANSLATION:
......
......@@ -2,7 +2,7 @@ alabaster==0.7.10
Babel==2.6.0
beautifulsoup4==4.6.0
bleach==2.1.3
certifi
certifi==2018.11.29
chardet==3.0.4
dateutils==0.6.6
Django==1.11.20
......@@ -11,18 +11,19 @@ django-model-utils==3.1.2
django-modeltranslation==0.12.2
django-phonenumber-field==1.3.0
docutils==0.14
filebrowser-safe>=0.5.0
filebrowser-safe==0.5.0
future==0.16.0
grappelli-safe>=0.5.0
grappelli-safe==0.5.1
html5lib==1.0.1
idna==2.6
imagesize==1.0.0
Markdown==3.0.1
Mezzanine==4.3.1
mezzanine-pagedown==1.3
oauthlib==2.1.0
packaging==17.1
phonenumberslite==8.9.7
Pillow==5.2.0
#pkg-resources
psycopg2==2.7.4
pyparsing==2.2.0
python-dateutil==2.6.1
......
This diff is collapsed.
A javascript port of Markdown, as used on Stack Overflow
and the rest of Stack Exchange network.
Largely based on showdown.js by John Fraser (Attacklab).
Original Markdown Copyright (c) 2004-2005 John Gruber
<http://daringfireball.net/projects/markdown/>
Original Showdown code copyright (c) 2007 John Fraser
Modifications and bugfixes (c) 2009 Dana Robinson
Modifications and bugfixes (c) 2009-2014 Stack Exchange Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
This diff is collapsed.
This diff is collapsed.
(function () {
var output, Converter;
if (typeof exports === "object" && typeof require === "function") { // we're in a CommonJS (e.g. Node.js) module
output = exports;
Converter = require("./Markdown.Converter").Converter;
} else {
output = window.Markdown;
Converter = output.Converter;
}
output.getSanitizingConverter = function () {
var converter = new Converter();
converter.hooks.chain("postConversion", sanitizeHtml);
converter.hooks.chain("postConversion", balanceTags);
return converter;
}
function sanitizeHtml(html) {
return html.replace(/<[^>]*>?/gi, sanitizeTag);
}
// (tags that can be opened/closed) | (tags that stand alone)
var basic_tag_whitelist = /^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol(?: start="\d+")?|p|pre|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i;
// <a href="url..." optional title>|</a>
var a_white = /^(<a\shref="((https?|ftp):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)*[\]$]+"(\stitle="[^"<>]+")?\s?>|<\/a>)$/i;
// <img src="url..." optional width optional height optional alt optional title
var img_white = /^(<img\ssrc="(https?:\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)*[\]$]+"(\swidth="\d{1,3}")?(\sheight="\d{1,3}")?(\salt="[^"<>]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i;
function sanitizeTag(tag) {
if (tag.match(basic_tag_whitelist) || tag.match(a_white) || tag.match(img_white))
return tag;
else
return "";
}
/// <summary>
/// attempt to balance HTML tags in the html string
/// by removing any unmatched opening or closing tags
/// IMPORTANT: we *assume* HTML has *already* been
/// sanitized and is safe/sane before balancing!
///
/// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593
/// </summary>
function balanceTags(html) {
if (html == "")
return "";
var re = /<\/?\w+[^>]*(\s|$|>)/g;
// convert everything to lower case; this makes
// our case insensitive comparisons easier
var tags = html.toLowerCase().match(re);
// no HTML tags present? nothing to do; exit now
var tagcount = (tags || []).length;
if (tagcount == 0)
return html;
var tagname, tag;
var ignoredtags = "<p><img><br><li><hr>";
var match;
var tagpaired = [];
var tagremove = [];
var needsRemoval = false;
// loop through matched tags in forward order
for (var ctag = 0; ctag < tagcount; ctag++) {
tagname = tags[ctag].replace(/<\/?(\w+).*/, "$1");
// skip any already paired tags
// and skip tags in our ignore list; assume they're self-closed
if (tagpaired[ctag] || ignoredtags.search("<" + tagname + ">") > -1)
continue;
tag = tags[ctag];
match = -1;
if (!/^<\//.test(tag)) {
// this is an opening tag
// search forwards (next tags), look for closing tags
for (var ntag = ctag + 1; ntag < tagcount; ntag++) {
if (!tagpaired[ntag] && tags[ntag] == "</" + tagname + ">") {
match = ntag;
break;
}
}
}
if (match == -1)
needsRemoval = tagremove[ctag] = true; // mark for removal
else
tagpaired[match] = true; // mark paired
}
if (!needsRemoval)
return html;
// delete all orphaned tags from the string
var ctag = 0;
html = html.replace(re, function (match) {
var res = tagremove[ctag] ? "" : match;
ctag++;
return res;
});
return html;
}
})();
body
{
background-color: White;
font-family: sans-serif;
}
blockquote {
border-left: 2px dotted #888;
padding-left: 5px;
background: #d0f0ff;
}
.wmd-panel
{
margin-left: 25%;
margin-right: 25%;
width: 50%;
min-width: 500px;
}
.wmd-button-bar
{
width: 100%;
background-color: Silver;
}
.wmd-input
{
height: 300px;
width: 100%;
background-color: Gainsboro;
border: 1px solid DarkGray;
}
.wmd-preview
{
background-color: #c0e0ff;
}
.wmd-button-row
{
position: relative;
margin-left: 5px;
margin-right: 5px;
margin-bottom: 5px;
margin-top: 10px;
padding: 0px;
height: 20px;
}
.wmd-spacer
{
width: 1px;
height: 20px;
margin-left: 14px;
position: absolute;
background-color: Silver;
display: inline-block;
list-style: none;
}
.wmd-button {
width: 20px;
height: 20px;
padding-left: 2px;
padding-right: 3px;
position: absolute;
display: inline-block;
list-style: none;
cursor: pointer;
}
.wmd-button > span {
background-image: url(../../wmd-buttons.png);
background-repeat: no-repeat;
background-position: 0px 0px;
width: 20px;
height: 20px;
display: inline-block;
}
.wmd-spacer1
{
left: 50px;
}
.wmd-spacer2
{
left: 175px;
}
.wmd-spacer3
{
left: 300px;
}
.wmd-prompt-background
{
background-color: Black;
}
.wmd-prompt-dialog
{
border: 1px solid #999999;
background-color: #F5F5F5;
}
.wmd-prompt-dialog > div {
font-size: 0.8em;
font-family: arial, helvetica, sans-serif;
}
.wmd-prompt-dialog > form > input[type="text"] {
border: 1px solid #999999;
color: black;
}
.wmd-prompt-dialog > form > input[type="button"]{
border: 1px solid #888888;
font-family: trebuchet MS, helvetica, sans-serif;
font-size: 0.8em;
font-weight: bold;
}
<!DOCTYPE html>
<html>
<head>
<title>PageDown Demo Page</title>
<link rel="stylesheet" type="text/css" href="demo.css" />
<script type="text/javascript" src="../../Markdown.Converter.js"></script>
<script type="text/javascript" src="../../Markdown.Sanitizer.js"></script>
<script type="text/javascript" src="../../Markdown.Editor.js"></script>
</head>
<body>
<div class="wmd-panel">
<div id="wmd-button-bar"></div>
<textarea class="wmd-input" id="wmd-input">
This is the *first* editor.
------------------------------
Just plain **Markdown**, except that the input is sanitized:
<marquee>I'm the ghost from the past!</marquee>
and that it implements "fenced blockquotes" via a plugin:
"""
Do it like this:
1. Have idea.
2. ???
3. Profit!
"""
</textarea>
</div>
<div id="wmd-preview" class="wmd-panel wmd-preview"></div>
<br /> <br />
<div class="wmd-panel">
<div id="wmd-button-bar-second"></div>
<textarea class="wmd-input" id="wmd-input-second">
This is the *second* editor.
------------------------------
It has a plugin hook registered that surrounds all words starting with the
letter A with asterisks before doing the Markdown conversion. Another one gives bare links
a nicer link text. User input isn't sanitized here:
<marquee>I'm the ghost from the past!</marquee>
http://google.com
http://stackoverflow.com
It also includes a help button.
Finally, note that when you press Ctrl-Q or click the "Blockquote" button (without having a
selection), this editor creates an example text that's different from the first editor.
</textarea>
</div>
<div id="wmd-preview-second" class="wmd-panel wmd-preview"></div>
<script type="text/javascript">
(function () {
var converter1 = Markdown.getSanitizingConverter();
converter1.hooks.chain("preBlockGamut", function (text, rbg) {
return text.replace(/^ {0,3}""" *\n((?:.*?\n)+?) {0,3}""" *$/gm, function (whole, inner) {
return "<blockquote>" + rbg(inner) + "</blockquote>\n";
});
});
var editor1 = new Markdown.Editor(converter1);
editor1.run();
var converter2 = new Markdown.Converter();
converter2.hooks.chain("preConversion", function (text) {
return text.replace(/\b(a\w*)/gi, "*$1*");
});
converter2.hooks.chain("plainLinkText", function (url) {
return "This is a link to " + url.replace(/^https?:\/\//, "");
});
var help = function () { alert("Do you need help?"); }
var options = {
helpButton: { handler: help },
strings: { quoteexample: "whatever you're quoting, put it right here" }
};
var editor2 = new Markdown.Editor(converter2, "-second", options);
editor2.run();
})();
</script>
</body>
</html>
// NOTE: This is just a demo -- in a production environment,
// be sure to spend a few more thoughts on sanitizing user input.
// (also, you probably wouldn't use a get request)
var http = require("http"),
url = require("url"),
querystring = require("querystring"),
Converter = require("../../Markdown.Converter").Converter,
getSanitizingConverter = require("../../Markdown.Sanitizer").getSanitizingConverter,
conv = new Converter(),
saneConv = getSanitizingConverter();
http.createServer(function (req, res) {
var route = url.parse(req.url);
if (route.pathname !== "/") {
res.writeHead(404);
res.end("Page not found");
return;
}
var query = querystring.parse(route.query);
res.writeHead(200, { "Content-type": "text/html" });
res.write("<html><body>");
var markdown = query.md || "## Hello!\n\n<marquee>I'm walking</marquee>\n\nVisit [Stack Overflow](http://stackoverflow.com)\n\n<b><i>This is never closed!";
res.write("<h1>Your output, sanitized:</h1>\n" + saneConv.makeHtml(markdown))
res.write("<h1>Your output, unsanitized:</h1>\n" + conv.makeHtml(markdown))
res.write(
"<h1>Enter Markdown</h1>\n" +
"<form method='get' action='/'>" +
"<textarea cols=50 rows=10 name='md'>" +
markdown.replace(/</g, "&lt;") +
"</textarea><br>" +
"<input type='submit' value='Convert!'>" +
"</form>"
);
res.end("</body></html>");
}).listen(8000);
// Usage:
//
// var myConverter = new Markdown.Editor(myConverter, null, { strings: Markdown.local.fr });
(function () {
Markdown.local = Markdown.local || {};
Markdown.local.fr = {
bold: "Gras <strong> Ctrl+B",
boldexample: "texte en gras",
italic: "Italique <em> Ctrl+I",
italicexample: "texte en italique",
link: "Hyperlien <a> Ctrl+L",
linkdescription: "description de l'hyperlien",
linkdialog: "<p><b>Insérer un hyperlien</b></p><p>http://example.com/ \"titre optionnel\"</p>",
quote: "Citation <blockquote> Ctrl+Q",
quoteexample: "Citation",
code: "Extrait de code <pre><code> Ctrl+K",
codeexample: "votre extrait de code",
image: "Image <img> Ctrl+G",
imagedescription: "description de l'image",
imagedialog: "<p><b>Insérer une image</b></p><p>http://example.com/images/diagram.jpg \"titre optionnel\"<br><br><a href='http://www.google.com/search?q=free+image+hosting' target='_blank'>Vous chercher un hébergement d'image grauit ?</a></p>",
olist: "Liste numérotée <ol> Ctrl+O",
ulist: "Liste à point <ul> Ctrl+U",
litem: "Elément de liste",
heading: "Titre <h1>/<h2> Ctrl+H",
headingexample: "Titre",
hr: "Trait horizontal <hr> Ctrl+R",
undo: "Annuler - Ctrl+Z",
redo: "Refaire - Ctrl+Y",
redomac: "Refaire - Ctrl+Shift+Z",
help: "Aide sur Markdown"
};
})();
\ No newline at end of file
exports.Converter = require("./Markdown.Converter").Converter;
exports.getSanitizingConverter = require("./Markdown.Sanitizer").getSanitizingConverter;
{
"name": "pagedown",
"version": "1.1.0",
"description": "markdown converter, based on showdown",
"repository": { "type": "hg", "url": "https://code.google.com/p/pagedown/" },
"keywords": ["markdown"],
"license": "MIT",
"files": ["Markdown.Converter.js", "Markdown.Sanitizer.js", "node-pagedown.js"],
"main": "node-pagedown.js",
"bugs": "http://code.google.com/p/pagedown/issues/list",
"homepage": "http://code.google.com/p/pagedown/wiki/PageDown"
}
/* a11y-dark theme */
/* Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css */
/* @author: ericwbailey */
/* Comment */
.hljs-comment,
.hljs-quote {
color: #d4d0ab;
}
/* Red */
.hljs-variable,
.hljs-template-variable,
.hljs-tag,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class,
.hljs-regexp,
.hljs-deletion {
color: #ffa07a;
}
/* Orange */
.hljs-number,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params,
.hljs-meta,
.hljs-link {
color: #f5ab35;
}
/* Yellow */
.hljs-attribute {
color: #ffd700;
}
/* Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition {
color: #abe338;
}
/* Blue */