diff --git a/runtests.py b/runtests.py index b8f81f20d5..4dd0476706 100755 --- a/runtests.py +++ b/runtests.py @@ -82,6 +82,9 @@ if not settings.configured: 'wagtail.wagtailredirects', 'wagtail.tests', ], + PASSWORD_HASHERS=( + 'django.contrib.auth.hashers.MD5PasswordHasher', # don't use the intentionally slow default password hasher + ), WAGTAILSEARCH_BACKENDS=WAGTAILSEARCH_BACKENDS, WAGTAIL_SITE_NAME='Test Site' ) diff --git a/scripts/install/debian.sh b/scripts/install/debian.sh new file mode 100644 index 0000000000..2b1c04326e --- /dev/null +++ b/scripts/install/debian.sh @@ -0,0 +1,129 @@ +# Production-configured Wagtail installation +# (secure services/account for full production use). +# Tested on Debian 7.0. +# Tom Dyson and Neal Todd + +# NB: Ensure the system locale is okay before running (dpkg-reconfigure locales). + +PROJECT=mywagtail +PROJECT_ROOT=/usr/local/django + +echo "This script overwrites key files, and should only be run on a new box." +read -p "Type 'yes' to confirm: " CONFIRM +[ “$CONFIRM” == “yes” ] || exit + +read -p "Enter a name for your project [$PROJECT]: " U_PROJECT +if [ ! -z "$U_PROJECT" ]; then + PROJECT=$U_PROJECT +fi + +read -p "Enter the root of your project, without trailing slash [$PROJECT_ROOT]: " U_PROJECT_ROOT +if [ ! -z "$U_PROJECT_ROOT" ]; then + PROJECT_ROOT=$U_PROJECT_ROOT +fi + +if [ ! -z "$PROJECT_ROOT" ]; then + mkdir -p $PROJECT_ROOT || exit +fi + +echo -e "\nPlease come back in a few minutes, when we'll need you to create an admin account." +sleep 5 + +SERVER_IP=`ifconfig eth0 |grep "inet addr" | cut -d: -f2 | cut -d" " -f1` + +aptitude update +aptitude -y install git python-pip nginx postgresql redis-server +aptitude -y install postgresql-server-dev-all python-dev libxml2-dev libxslt-dev libjpeg62-dev + +wget -nv http://nodejs.org/dist/v0.10.20/node-v0.10.20.tar.gz +tar xzf node-v0.10.20.tar.gz +cd node-v0.10.20 +./configure && make -s && make -s install +cd .. +rm -r node-v0.10.20 node-v0.10.20.tar.gz +npm install -g less + +perl -pi -e "s/^(local\s+all\s+postgres\s+)peer$/\1trust/" /etc/postgresql/9.1/main/pg_hba.conf +service postgresql reload + +aptitude -y install openjdk-7-jre-headless +curl -O https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.0.0.deb +dpkg -i elasticsearch-1.0.0.deb +rm elasticsearch-1.0.0.deb +update-rc.d elasticsearch defaults 95 10 +service elasticsearch start + +cd $PROJECT_ROOT +git clone https://github.com/torchbox/wagtaildemo.git $PROJECT +cd $PROJECT +mv wagtaildemo $PROJECT +perl -pi -e"s/wagtaildemo/$PROJECT/" manage.py $PROJECT/wsgi.py $PROJECT/settings/*.py +rm -r etc README.md Vagrantfile* .git .gitignore + +dd if=/dev/zero of=/tmpswap bs=1024 count=524288 +mkswap /tmpswap +swapon /tmpswap +pip install -r requirements/production.txt +swapoff -v /tmpswap +rm /tmpswap + +echo SECRET_KEY = \"`python -c 'import random; print "".join([random.SystemRandom().choice("abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)") for i in range(50)])'`\" > $PROJECT/settings.local.py +echo ALLOWED_HOSTS = [\'$SERVER_IP\',] >> $PROJECT/settings/local.py +createdb -Upostgres $PROJECT +./manage.py syncdb --settings=$PROJECT.settings.production +./manage.py migrate --settings=$PROJECT.settings.production +./manage.py update_index --settings=$PROJECT.settings.production +./manage.py collectstatic --settings=$PROJECT.settings.production --noinput + +pip install uwsgi +cp $PROJECT/wsgi.py $PROJECT/wsgi_production.py +perl -pi -e"s/($PROJECT.settings)/\1.production/" $PROJECT/wsgi_production.py + +curl -O https://raw2.github.com/nginx/nginx/master/conf/uwsgi_params +cat << EOF > /etc/nginx/sites-enabled/default +upstream django { + server unix://$PROJECT_ROOT/$PROJECT/uwsgi.sock; +} +server { + listen 80; + charset utf-8; + client_max_body_size 75M; # max upload size + location /media { + alias $PROJECT_ROOT/$PROJECT/media; + } + location /static { + alias $PROJECT_ROOT/$PROJECT/static; + } + location / { + uwsgi_pass django; + include $PROJECT_ROOT/$PROJECT/uwsgi_params; + } +} +EOF + +cat << EOF > $PROJECT_ROOT/$PROJECT/uwsgi_conf.ini +[uwsgi] +chdir = $PROJECT_ROOT/$PROJECT +module = $PROJECT.wsgi_production +master = true +processes = 10 +socket = $PROJECT_ROOT/$PROJECT/uwsgi.sock +chmod-socket = 666 +vacuum = true +EOF + +mkdir -p /etc/uwsgi/vassals/ +ln -s $PROJECT_ROOT/$PROJECT/uwsgi_conf.ini /etc/uwsgi/vassals/ + +curl -o /etc/init.d/uwsgi https://raw.github.com/torchbox/wagtail/master/scripts/install/uwsgi-init.d +mkdir /var/log/uwsgi +chmod 755 /etc/init.d/uwsgi +update-rc.d uwsgi defaults + +service uwsgi start +service nginx restart + +URL="http://$SERVER_IP" +echo -e "\n\nWagtail lives!\n\n" +echo "The public site is at $URL/" +echo "and the admin interface is at $URL/admin/" diff --git a/scripts/install/ubuntu.sh b/scripts/install/ubuntu.sh new file mode 100644 index 0000000000..ac38d59106 --- /dev/null +++ b/scripts/install/ubuntu.sh @@ -0,0 +1,125 @@ +# Production-configured Wagtail installation +# (secure services/account for full production use). +# Tested on Ubuntu 13.10. +# Tom Dyson and Neal Todd + +PROJECT=mywagtail +PROJECT_ROOT=/usr/local/django + +echo "This script overwrites key files, and should only be run on a new box." +read -p "Type 'yes' to confirm: " CONFIRM +[ “$CONFIRM” == “yes” ] || exit + +read -p "Enter a name for your project [$PROJECT]: " U_PROJECT +if [ ! -z "$U_PROJECT" ]; then + PROJECT=$U_PROJECT +fi + +read -p "Enter the root of your project, without trailing slash [$PROJECT_ROOT]: " U_PROJECT_ROOT +if [ ! -z "$U_PROJECT_ROOT" ]; then + PROJECT_ROOT=$U_PROJECT_ROOT +fi + +if [ ! -z "$PROJECT_ROOT" ]; then + mkdir -p $PROJECT_ROOT || exit +fi + +echo -e "\nPlease come back in a few minutes, when we'll need you to create an admin account." +sleep 5 + +SERVER_IP=`ifconfig eth0 |grep "inet addr" | cut -d: -f2 | cut -d" " -f1` + +aptitude update +aptitude -y install git python-pip nginx postgresql redis-server +aptitude -y install postgresql-server-dev-all python-dev libxml2-dev libxslt-dev libjpeg62-dev + +aptitude -y install npm +ln -s /usr/bin/nodejs /usr/bin/node +npm install -g less + +perl -pi -e "s/^(local\s+all\s+postgres\s+)peer$/\1trust/" /etc/postgresql/9.1/main/pg_hba.conf +service postgresql reload + +aptitude -y install openjdk-7-jre-headless +curl -O https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.0.0.deb +dpkg -i elasticsearch-1.0.0.deb +rm elasticsearch-1.0.0.deb +update-rc.d elasticsearch defaults 95 10 +service elasticsearch start + +cd $PROJECT_ROOT +git clone https://github.com/torchbox/wagtaildemo.git $PROJECT +cd $PROJECT +mv wagtaildemo $PROJECT +perl -pi -e"s/wagtaildemo/$PROJECT/" manage.py $PROJECT/wsgi.py $PROJECT/settings/*.py +rm -r etc README.md Vagrantfile* .git .gitignore + +dd if=/dev/zero of=/tmpswap bs=1024 count=524288 +mkswap /tmpswap +swapon /tmpswap +pip install -r requirements/production.txt +swapoff -v /tmpswap +rm /tmpswap + +echo SECRET_KEY = \"`python -c 'import random; print "".join([random.SystemRandom().choice("abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)") for i in range(50)])'`\" > $PROJECT/settings.local.py +echo ALLOWED_HOSTS = [\'$SERVER_IP\',] >> $PROJECT/settings/local.py +createdb -Upostgres $PROJECT +./manage.py syncdb --settings=$PROJECT.settings.production +./manage.py migrate --settings=$PROJECT.settings.production +./manage.py update_index --settings=$PROJECT.settings.production +./manage.py collectstatic --settings=$PROJECT.settings.production --noinput + +pip install uwsgi +cp $PROJECT/wsgi.py $PROJECT/wsgi_production.py +perl -pi -e"s/($PROJECT.settings)/\1.production/" $PROJECT/wsgi_production.py + +curl -O https://raw2.github.com/nginx/nginx/master/conf/uwsgi_params +cat << EOF > /etc/nginx/sites-enabled/default +upstream django { + server unix://$PROJECT_ROOT/$PROJECT/uwsgi.sock; +} +server { + listen 80; + charset utf-8; + client_max_body_size 75M; # max upload size + location /media { + alias $PROJECT_ROOT/$PROJECT/media; + } + location /static { + alias $PROJECT_ROOT/$PROJECT/static; + } + location / { + uwsgi_pass django; + include $PROJECT_ROOT/$PROJECT/uwsgi_params; + } +} +EOF + +cat << EOF > $PROJECT_ROOT/$PROJECT/uwsgi_conf.ini +[uwsgi] +chdir = $PROJECT_ROOT/$PROJECT +module = $PROJECT.wsgi_production +master = true +processes = 10 +socket = $PROJECT_ROOT/$PROJECT/uwsgi.sock +chmod-socket = 666 +vacuum = true +EOF + +mkdir -p /etc/uwsgi/vassals/ +ln -s $PROJECT_ROOT/$PROJECT/uwsgi_conf.ini /etc/uwsgi/vassals/ + +cat << EOF > /etc/init/uwsgi.conf +description "uwsgi for wagtail" +start on runlevel [2345] +stop on runlevel [06] +exec uwsgi --emperor /etc/uwsgi/vassals +EOF + +service uwsgi start +service nginx restart + +URL="http://$SERVER_IP" +echo -e "\n\nWagtail lives!\n\n" +echo "The public site is at $URL/" +echo "and the admin interface is at $URL/admin/" diff --git a/scripts/install/uwsgi-init.d b/scripts/install/uwsgi-init.d new file mode 100644 index 0000000000..43f0bdb130 --- /dev/null +++ b/scripts/install/uwsgi-init.d @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +### BEGIN INIT INFO +# Provides: emperor +# Required-Start: $all +# Required-Stop: $all +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: uwsgi for wagtail +# Description: uwsgi for wagtail +### END INIT INFO +set -e + + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin +DAEMON=/usr/local/bin/uwsgi +RUN=/var/run/uwsgi +CONFIG_DIR=/etc/uwsgi/vassals +NAME=uwsgi +DESC=emperor +OWNER=root +GROUP=root +OP=$1 + +[[ -x $DAEMON ]] || exit 0 +[[ -d $RUN ]] || mkdir $RUN && chown $OWNER.$GROUP $RUN + + +do_pid_check() +{ + local PIDFILE=$1 + [[ -f $PIDFILE ]] || return 0 + local PID=$(cat $PIDFILE) + for p in $(pgrep $NAME); do + [[ $p == $PID ]] && return 1 + done + return 0 +} + + +do_start() +{ + local PIDFILE=$RUN/$NAME.pid + local START_OPTS=" \ + --emperor $CONFIG_DIR \ + --pidfile $PIDFILE \ + --uid $OWNER --gid $GROUP \ + --daemonize /var/log/$NAME/uwsgi-emperor.log" + if do_pid_check $PIDFILE; then + $NAME $START_OPTS + else + echo "Already running!" + fi +} + +send_sig() +{ + local PIDFILE=$RUN/$NAME.pid + set +e + [[ -f $PIDFILE ]] && kill $1 $(cat $PIDFILE) > /dev/null 2>&1 + set -e +} + +wait_and_clean_pidfile() +{ + local PIDFILE=$RUN/uwsgi.pid + until do_pid_check $PIDFILE; do + echo -n ""; + done + rm -f $PIDFILE +} + +do_stop() +{ + send_sig -3 + wait_and_clean_pidfile +} + +do_reload() +{ + send_sig -1 +} + +case "$OP" in + start) + echo "Starting $DESC: " + do_start + echo "$NAME." + ;; + stop) + echo -n "Stopping $DESC: " + do_stop + echo "$NAME." + ;; + reload) + echo -n "Reloading $DESC: " + do_reload + echo "$NAME." + ;; + restart) + echo "Restarting $DESC: " + do_stop + sleep 1 + do_start + echo "$NAME." + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|restart|reload}">&2 + exit 1 + ;; +esac +exit 0 \ No newline at end of file diff --git a/wagtail/tests/fixtures/test.json b/wagtail/tests/fixtures/test.json index 98db24215a..99296f7b27 100644 --- a/wagtail/tests/fixtures/test.json +++ b/wagtail/tests/fixtures/test.json @@ -39,7 +39,7 @@ "model": "wagtailcore.page", "fields": { "title": "Events", - "numchild": 1, + "numchild": 2, "show_in_menus": true, "live": true, "depth": 3, @@ -69,7 +69,8 @@ "content_type": ["tests", "eventpage"], "path": "0001000100010001", "url_path": "/home/events/christmas/", - "slug": "christmas" + "slug": "christmas", + "owner": 2 } }, { @@ -84,6 +85,62 @@ } }, +{ + "pk": 5, + "model": "wagtailcore.page", + "fields": { + "title": "Tentative Unpublished Event", + "numchild": 1, + "show_in_menus": true, + "live": false, + "depth": 4, + "content_type": ["tests", "eventpage"], + "path": "0001000100010002", + "url_path": "/home/events/tentative-unpublished-event/", + "slug": "tentative-unpublished-event", + "owner": 2 + } +}, +{ + "pk": 5, + "model": "tests.eventpage", + "fields": { + "date_from": "2015-07-04", + "audience": "public", + "location": "The moon", + "body": "

I haven't worked out the details yet, but it's going to have cake and ponies

", + "cost": "Free" + } +}, + +{ + "pk": 6, + "model": "wagtailcore.page", + "fields": { + "title": "Someone Else's Event", + "numchild": 1, + "show_in_menus": true, + "live": false, + "depth": 4, + "content_type": ["tests", "eventpage"], + "path": "0001000100010003", + "url_path": "/home/events/someone-elses-event/", + "slug": "someone-elses-event", + "owner": 3 + } +}, +{ + "pk": 6, + "model": "tests.eventpage", + "fields": { + "date_from": "2015-07-04", + "audience": "private", + "location": "The moon", + "body": "

your name's not down, you're not coming in

", + "cost": "Free (but not for you)" + } +}, + { "pk": 1, "model": "wagtailcore.site", @@ -93,5 +150,140 @@ "port": 80, "is_default_site": true } +}, + +{ + "pk": 3, + "model": "auth.group", + "fields": { + "name": "Event editors", + "permissions": [ + ["access_admin", "wagtailadmin", "admin"], + ["add_image", "wagtailimages", "image"], + ["change_image", "wagtailimages", "image"], + ["delete_image", "wagtailimages", "image"] + ] + } +}, +{ + "pk": 4, + "model": "auth.group", + "fields": { + "name": "Event moderators", + "permissions": [ + ["access_admin", "wagtailadmin", "admin"], + ["add_image", "wagtailimages", "image"], + ["change_image", "wagtailimages", "image"], + ["delete_image", "wagtailimages", "image"] + ] + } +}, +{ + "pk": 1, + "model": "wagtailcore.grouppagepermission", + "fields": { + "group": ["Event editors"], + "page": 3, + "permission_type": "add" + } +}, +{ + "pk": 2, + "model": "wagtailcore.grouppagepermission", + "fields": { + "group": ["Event moderators"], + "page": 3, + "permission_type": "add" + } +}, +{ + "pk": 3, + "model": "wagtailcore.grouppagepermission", + "fields": { + "group": ["Event moderators"], + "page": 3, + "permission_type": "edit" + } +}, +{ + "pk": 4, + "model": "wagtailcore.grouppagepermission", + "fields": { + "group": ["Event moderators"], + "page": 3, + "permission_type": "publish" + } +}, + +{ + "pk": 1, + "model": "auth.user", + "fields": { + "username": "superuser", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": true, + "is_staff": true, + "groups": [ + ], + "user_permissions": [], + "password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22", + "email": "superuser@example.com" + } +}, +{ + "pk": 2, + "model": "auth.user", + "fields": { + "username": "eventeditor", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": false, + "is_staff": false, + "groups": [ + ["Event editors"] + ], + "user_permissions": [], + "password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22", + "email": "eventeditor@example.com" + } +}, +{ + "pk": 3, + "model": "auth.user", + "fields": { + "username": "eventmoderator", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": false, + "is_staff": false, + "groups": [ + ["Event moderators"] + ], + "user_permissions": [], + "password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22", + "email": "eventmoderator@example.com" + } +}, +{ + "pk": 4, + "model": "auth.user", + "fields": { + "username": "inactiveuser", + "first_name": "", + "last_name": "", + "is_active": false, + "is_superuser": false, + "is_staff": false, + "groups": [ + ["Event moderators"] + ], + "user_permissions": [], + "password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22", + "email": "inactiveuser@example.com" + } } ] diff --git a/wagtail/tests/templates/tests/event_page.html b/wagtail/tests/templates/tests/event_page.html new file mode 100644 index 0000000000..917c835010 --- /dev/null +++ b/wagtail/tests/templates/tests/event_page.html @@ -0,0 +1,10 @@ + + + + Event: {{ self.title }} + + +

{{ self.title }}

+

Event

+ + diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-hr.coffee b/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-hr.coffee deleted file mode 100644 index 76eb5a04cf..0000000000 --- a/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-hr.coffee +++ /dev/null @@ -1,28 +0,0 @@ -# Hallo - a rich text editing jQuery UI widget -# (c) 2011 Henri Bergius, IKS Consortium -# Hallo may be freely distributed under the MIT license -((jQuery) -> - jQuery.widget "IKS.hallohr", - options: - editable: null - toolbar: null - uuid: '' - buttonCssClass: null - - populateToolbar: (toolbar) -> - buttonset = jQuery "" - - buttonElement = jQuery '' - buttonElement.hallobutton - uuid: @options.uuid - editable: @options.editable - label: "Horizontal rule" - command: "insertHorizontalRule" - icon: "icon-horizontalrule" - cssClass: @options.buttonCssClass - buttonset.append buttonElement - - buttonset.hallobuttonset() - toolbar.append buttonset - -)(jQuery) diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-hr.js b/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-hr.js new file mode 100644 index 0000000000..0989d1fc66 --- /dev/null +++ b/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-hr.js @@ -0,0 +1,31 @@ +// Generated by CoffeeScript 1.6.2 +(function() { + (function(jQuery) { + return jQuery.widget("IKS.hallohr", { + options: { + editable: null, + toolbar: null, + uuid: '', + buttonCssClass: null + }, + populateToolbar: function(toolbar) { + var buttonElement, buttonset; + + buttonset = jQuery(""); + buttonElement = jQuery(''); + buttonElement.hallobutton({ + uuid: this.options.uuid, + editable: this.options.editable, + label: "Horizontal rule", + command: "insertHorizontalRule", + icon: "icon-horizontalrule", + cssClass: this.options.buttonCssClass + }); + buttonset.append(buttonElement); + buttonset.hallobuttonset(); + return toolbar.append(buttonset); + } + }); + })(jQuery); + +}).call(this); \ No newline at end of file diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-wagtaillink.coffee b/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-wagtaillink.coffee deleted file mode 100644 index fbc405f8a2..0000000000 --- a/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-wagtaillink.coffee +++ /dev/null @@ -1,68 +0,0 @@ -# plugin for hallo.js to allow inserting links using Wagtail's page chooser - -(($) -> - $.widget "IKS.hallowagtaillink", - options: - uuid: '' - editable: null - - populateToolbar: (toolbar) -> - widget = this - - getEnclosingLink = () -> - # if cursor is currently within a link element, return it, otherwise return null - node = widget.options.editable.getSelection().commonAncestorContainer - return $(node).parents('a').get(0) - - # Create an element for holding the button - button = $('') - button.hallobutton - uuid: @options.uuid - editable: @options.editable - label: 'Links' - icon: 'icon-link' - command: null - queryState: (event) -> - button.hallobutton('checked', !!getEnclosingLink()) - - # Append the button to toolbar - toolbar.append button - - button.on "click", (event) -> - enclosingLink = getEnclosingLink() - if enclosingLink - # remove existing link - $(enclosingLink).replaceWith(enclosingLink.innerHTML) - button.hallobutton('checked', false) - widget.options.editable.element.trigger('change') - else - # commence workflow to add a link - lastSelection = widget.options.editable.getSelection() - - if lastSelection.collapsed - # TODO: don't hard-code this, as it may be changed in urls.py - url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true&prompt_for_link_text=true' - else - url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true' - - ModalWorkflow - url: url - responses: - pageChosen: (pageData) -> - a = document.createElement('a') - a.setAttribute('href', pageData.url) - if pageData.id - a.setAttribute('data-id', pageData.id) - a.setAttribute('data-linktype', 'page') - - if (not lastSelection.collapsed) and lastSelection.canSurroundContents() - # use the selected content as the link text - lastSelection.surroundContents(a) - else - # no text is selected, so use the page title as link text - a.appendChild(document.createTextNode pageData.title) - lastSelection.insertNode(a) - - widget.options.editable.element.trigger('change') - -)(jQuery) diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-wagtaillink.js b/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-wagtaillink.js new file mode 100644 index 0000000000..03732110d1 --- /dev/null +++ b/wagtail/wagtailadmin/static/wagtailadmin/js/hallo-plugins/hallo-wagtaillink.js @@ -0,0 +1,74 @@ +// Generated by CoffeeScript 1.6.2 +(function() { + (function($) { + return $.widget("IKS.hallowagtaillink", { + options: { + uuid: '', + editable: null + }, + populateToolbar: function(toolbar) { + var button, getEnclosingLink, widget; + + widget = this; + getEnclosingLink = function() { + var node; + + node = widget.options.editable.getSelection().commonAncestorContainer; + return $(node).parents('a').get(0); + }; + button = $(''); + button.hallobutton({ + uuid: this.options.uuid, + editable: this.options.editable, + label: 'Links', + icon: 'icon-link', + command: null, + queryState: function(event) { + return button.hallobutton('checked', !!getEnclosingLink()); + } + }); + toolbar.append(button); + return button.on("click", function(event) { + var enclosingLink, lastSelection, url; + + enclosingLink = getEnclosingLink(); + if (enclosingLink) { + $(enclosingLink).replaceWith(enclosingLink.innerHTML); + button.hallobutton('checked', false); + return widget.options.editable.element.trigger('change'); + } else { + lastSelection = widget.options.editable.getSelection(); + if (lastSelection.collapsed) { + url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true&prompt_for_link_text=true'; + } else { + url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true'; + } + return ModalWorkflow({ + url: url, + responses: { + pageChosen: function(pageData) { + var a; + + a = document.createElement('a'); + a.setAttribute('href', pageData.url); + if (pageData.id) { + a.setAttribute('data-id', pageData.id); + a.setAttribute('data-linktype', 'page'); + } + if ((!lastSelection.collapsed) && lastSelection.canSurroundContents()) { + lastSelection.surroundContents(a); + } else { + a.appendChild(document.createTextNode(pageData.title)); + lastSelection.insertNode(a); + } + return widget.options.editable.element.trigger('change'); + } + } + }); + } + }); + } + }); + })(jQuery); + +}).call(this); \ No newline at end of file diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html index 518a2c59b4..da26289664 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html @@ -13,16 +13,16 @@ - - - - - + + + + + {% comment %} - TODO: have a mechanism to specify image-chooser.js (and hallo-wagtailimage.coffee) + TODO: have a mechanism to specify image-chooser.js (and hallo-wagtailimage.js) within the wagtailimages app - ideally wagtailadmin shouldn't have to know anything at all about wagtailimages TODO: a method of injecting these sorts of things on demand when the modal is spawned. diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html index fdbfb19ff6..a0faa0da18 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html @@ -75,7 +75,7 @@ {{ parent_page.content_type.model_class.get_verbose_name }} - {% if not choosing and parent_page.live and not parent_page.is_root and 'view_live' not in hide_actions|default:'' %} + {% if not choosing and not moving and parent_page.live and not parent_page.is_root and 'view_live' not in hide_actions|default:'' %} {{ parent_page.status_string|capfirst }} {% else %} {{ parent_page.status_string|capfirst }} @@ -208,7 +208,7 @@ {% endif %} {{ page.content_type.model_class.get_verbose_name }} - {% if not choosing and page.live and 'view_live' not in hide_actions|default:'' %} + {% if not choosing and not moving and page.live and 'view_live' not in hide_actions|default:'' %} {{ page.status_string }} {% else %} {{ page.status_string }} diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 0927520375..90e6998080 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -6,11 +6,11 @@ from modelcluster.models import ClusterableModel from django.db import models, connection, transaction from django.db.models import get_model, Q from django.http import Http404 -from django.shortcuts import render from django.core.cache import cache from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import Group from django.conf import settings +from django.template.response import TemplateResponse from django.utils.translation import ugettext_lazy as _ from wagtail.wagtailcore.util import camelcase_to_underscore @@ -326,7 +326,7 @@ class Page(MP_Node, ClusterableModel, Indexed): return revision.as_page_object() def serve(self, request): - return render(request, self.template, { + return TemplateResponse(request, self.template, { 'self': self }) @@ -742,7 +742,7 @@ class PagePermissionTester(object): def can_move_to(self, destination): # reject the logically impossible cases first - if self.page == destination or destination.is_child_of(self.page): + if self.page == destination or destination.is_descendant_of(self.page): return False # and shortcut the trivial 'everything' / 'nothing' permissions diff --git a/wagtail/wagtailcore/tests.py b/wagtail/wagtailcore/tests.py index ccd91d0092..58ff51ada0 100644 --- a/wagtail/wagtailcore/tests.py +++ b/wagtail/wagtailcore/tests.py @@ -1,10 +1,37 @@ -from django.test import TestCase +from django.test import TestCase, Client +from django.http import HttpRequest, Http404 + +from django.contrib.auth.models import User + from wagtail.wagtailcore.models import Page, Site +from wagtail.tests.models import EventPage class TestRouting(TestCase): fixtures = ['test.json'] + def test_find_site_for_request(self): + default_site = Site.objects.get(is_default_site=True) + events_page = Page.objects.get(url_path='/home/events/') + events_site = Site.objects.create(hostname='events.example.com', root_page=events_page) + + # requests without a Host: header should be directed to the default site + request = HttpRequest() + request.path = '/' + self.assertEqual(Site.find_for_request(request), default_site) + + # requests with a known Host: header should be directed to the specific site + request = HttpRequest() + request.path = '/' + request.META['HTTP_HOST'] = 'events.example.com' + self.assertEqual(Site.find_for_request(request), events_site) + + # requests with an unrecognised Host: header should be directed to the default site + request = HttpRequest() + request.path = '/' + request.META['HTTP_HOST'] = 'unknown.example.com' + self.assertEqual(Site.find_for_request(request), default_site) + def test_urls(self): default_site = Site.objects.get(is_default_site=True) homepage = Page.objects.get(url_path='/home/') @@ -38,3 +65,234 @@ class TestRouting(TestCase): self.assertEqual(christmas_page.url, 'http://events.example.com/christmas/') self.assertEqual(christmas_page.relative_url(default_site), 'http://events.example.com/christmas/') self.assertEqual(christmas_page.relative_url(events_site), '/christmas/') + + def test_request_routing(self): + homepage = Page.objects.get(url_path='/home/') + christmas_page = EventPage.objects.get(url_path='/home/events/christmas/') + + request = HttpRequest() + request.path = '/events/christmas/' + response = homepage.route(request, ['events', 'christmas']) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context_data['self'], christmas_page) + used_template = response.resolve_template(response.template_name) + self.assertEqual(used_template.name, 'tests/event_page.html') + + def test_route_to_unknown_page_returns_404(self): + homepage = Page.objects.get(url_path='/home/') + + request = HttpRequest() + request.path = '/events/quinquagesima/' + with self.assertRaises(Http404): + homepage.route(request, ['events', 'quinquagesima']) + + def test_route_to_unpublished_page_returns_404(self): + homepage = Page.objects.get(url_path='/home/') + + request = HttpRequest() + request.path = '/events/tentative-unpublished-event/' + with self.assertRaises(Http404): + homepage.route(request, ['events', 'tentative-unpublished-event']) + + +class TestServeView(TestCase): + fixtures = ['test.json'] + + def test_serve(self): + response = self.client.get('/events/christmas/') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.templates[0].name, 'tests/event_page.html') + christmas_page = EventPage.objects.get(url_path='/home/events/christmas/') + self.assertEqual(response.context['self'], christmas_page) + + self.assertContains(response, '

Christmas

') + self.assertContains(response, '

Event

') + + def test_serve_unknown_page_returns_404(self): + response = self.client.get('/events/quinquagesima/') + self.assertEqual(response.status_code, 404) + + def test_serve_unpublished_page_returns_404(self): + response = self.client.get('/events/tentative-unpublished-event/') + self.assertEqual(response.status_code, 404) + + def test_serve_with_multiple_sites(self): + events_page = Page.objects.get(url_path='/home/events/') + Site.objects.create(hostname='events.example.com', root_page=events_page) + + response = self.client.get('/christmas/', HTTP_HOST='events.example.com') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.templates[0].name, 'tests/event_page.html') + christmas_page = EventPage.objects.get(url_path='/home/events/christmas/') + self.assertEqual(response.context['self'], christmas_page) + + self.assertContains(response, '

Christmas

') + self.assertContains(response, '

Event

') + + # same request to the default host should return a 404 + c = Client() + response = c.get('/christmas/', HTTP_HOST='localhost') + self.assertEqual(response.status_code, 404) + + +class TestPagePermission(TestCase): + fixtures = ['test.json'] + + def test_nonpublisher_page_permissions(self): + event_editor = User.objects.get(username='eventeditor') + homepage = Page.objects.get(url_path='/home/') + christmas_page = EventPage.objects.get(url_path='/home/events/christmas/') + unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/') + someone_elses_event_page = EventPage.objects.get(url_path='/home/events/someone-elses-event/') + + homepage_perms = homepage.permissions_for_user(event_editor) + christmas_page_perms = christmas_page.permissions_for_user(event_editor) + unpub_perms = unpublished_event_page.permissions_for_user(event_editor) + someone_elses_event_perms = someone_elses_event_page.permissions_for_user(event_editor) + + self.assertFalse(homepage_perms.can_add_subpage()) + self.assertTrue(christmas_page_perms.can_add_subpage()) + self.assertTrue(unpub_perms.can_add_subpage()) + self.assertTrue(someone_elses_event_perms.can_add_subpage()) + + self.assertFalse(homepage_perms.can_edit()) + self.assertTrue(christmas_page_perms.can_edit()) + self.assertTrue(unpub_perms.can_edit()) + self.assertFalse(someone_elses_event_perms.can_edit()) # basic 'add' permission doesn't allow editing pages owned by someone else + + self.assertFalse(homepage_perms.can_delete()) + self.assertFalse(christmas_page_perms.can_delete()) # cannot delete because it is published + self.assertTrue(unpub_perms.can_delete()) + self.assertFalse(someone_elses_event_perms.can_delete()) + + self.assertFalse(homepage_perms.can_publish()) + self.assertFalse(christmas_page_perms.can_publish()) + self.assertFalse(unpub_perms.can_publish()) + + self.assertFalse(homepage_perms.can_unpublish()) + self.assertFalse(christmas_page_perms.can_unpublish()) + self.assertFalse(unpub_perms.can_unpublish()) + + self.assertFalse(homepage_perms.can_publish_subpage()) + self.assertFalse(christmas_page_perms.can_publish_subpage()) + self.assertFalse(unpub_perms.can_publish_subpage()) + + self.assertFalse(homepage_perms.can_reorder_children()) + self.assertFalse(christmas_page_perms.can_reorder_children()) + self.assertFalse(unpub_perms.can_reorder_children()) + + self.assertFalse(homepage_perms.can_move()) + self.assertFalse(christmas_page_perms.can_move()) # cannot move because this would involve unpublishing from its current location + self.assertTrue(unpub_perms.can_move()) + self.assertFalse(someone_elses_event_perms.can_move()) + + self.assertFalse(christmas_page_perms.can_move_to(unpublished_event_page)) # cannot move because this would involve unpublishing from its current location + self.assertTrue(unpub_perms.can_move_to(christmas_page)) + self.assertFalse(unpub_perms.can_move_to(homepage)) # no permission to create pages at destination + self.assertFalse(unpub_perms.can_move_to(unpublished_event_page)) # cannot make page a child of itself + + + def test_publisher_page_permissions(self): + event_moderator = User.objects.get(username='eventmoderator') + homepage = Page.objects.get(url_path='/home/') + christmas_page = EventPage.objects.get(url_path='/home/events/christmas/') + unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/') + + homepage_perms = homepage.permissions_for_user(event_moderator) + christmas_page_perms = christmas_page.permissions_for_user(event_moderator) + unpub_perms = unpublished_event_page.permissions_for_user(event_moderator) + + self.assertFalse(homepage_perms.can_add_subpage()) + self.assertTrue(christmas_page_perms.can_add_subpage()) + self.assertTrue(unpub_perms.can_add_subpage()) + + self.assertFalse(homepage_perms.can_edit()) + self.assertTrue(christmas_page_perms.can_edit()) + self.assertTrue(unpub_perms.can_edit()) + + self.assertFalse(homepage_perms.can_delete()) + self.assertTrue(christmas_page_perms.can_delete()) # cannot delete because it is published + self.assertTrue(unpub_perms.can_delete()) + + self.assertFalse(homepage_perms.can_publish()) + self.assertTrue(christmas_page_perms.can_publish()) + self.assertTrue(unpub_perms.can_publish()) + + self.assertFalse(homepage_perms.can_unpublish()) + self.assertTrue(christmas_page_perms.can_unpublish()) + self.assertFalse(unpub_perms.can_unpublish()) # cannot unpublish a page that isn't published + + self.assertFalse(homepage_perms.can_publish_subpage()) + self.assertTrue(christmas_page_perms.can_publish_subpage()) + self.assertTrue(unpub_perms.can_publish_subpage()) + + self.assertFalse(homepage_perms.can_reorder_children()) + self.assertTrue(christmas_page_perms.can_reorder_children()) + self.assertTrue(unpub_perms.can_reorder_children()) + + self.assertFalse(homepage_perms.can_move()) + self.assertTrue(christmas_page_perms.can_move()) + self.assertTrue(unpub_perms.can_move()) + + self.assertTrue(christmas_page_perms.can_move_to(unpublished_event_page)) + self.assertTrue(unpub_perms.can_move_to(christmas_page)) + self.assertFalse(unpub_perms.can_move_to(homepage)) # no permission to create pages at destination + self.assertFalse(unpub_perms.can_move_to(unpublished_event_page)) # cannot make page a child of itself + + def test_inactive_user_has_no_permissions(self): + user = User.objects.get(username='inactiveuser') + christmas_page = EventPage.objects.get(url_path='/home/events/christmas/') + unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/') + + christmas_page_perms = christmas_page.permissions_for_user(user) + unpub_perms = unpublished_event_page.permissions_for_user(user) + + self.assertFalse(unpub_perms.can_add_subpage()) + self.assertFalse(unpub_perms.can_edit()) + self.assertFalse(unpub_perms.can_delete()) + self.assertFalse(unpub_perms.can_publish()) + self.assertFalse(christmas_page_perms.can_unpublish()) + self.assertFalse(unpub_perms.can_publish_subpage()) + self.assertFalse(unpub_perms.can_reorder_children()) + self.assertFalse(unpub_perms.can_move()) + self.assertFalse(unpub_perms.can_move_to(christmas_page)) + + def test_superuser_has_full_permissions(self): + user = User.objects.get(username='superuser') + homepage = Page.objects.get(url_path='/home/') + root = Page.objects.get(url_path='/') + unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/') + + homepage_perms = homepage.permissions_for_user(user) + root_perms = root.permissions_for_user(user) + unpub_perms = unpublished_event_page.permissions_for_user(user) + + self.assertTrue(homepage_perms.can_add_subpage()) + self.assertTrue(root_perms.can_add_subpage()) + + self.assertTrue(homepage_perms.can_edit()) + self.assertFalse(root_perms.can_edit()) # root is not a real editable page, even to superusers + + self.assertTrue(homepage_perms.can_delete()) + self.assertFalse(root_perms.can_delete()) + + self.assertTrue(homepage_perms.can_publish()) + self.assertFalse(root_perms.can_publish()) + + self.assertTrue(homepage_perms.can_unpublish()) + self.assertFalse(root_perms.can_unpublish()) + self.assertFalse(unpub_perms.can_unpublish()) + + self.assertTrue(homepage_perms.can_publish_subpage()) + self.assertTrue(root_perms.can_publish_subpage()) + + self.assertTrue(homepage_perms.can_reorder_children()) + self.assertTrue(root_perms.can_reorder_children()) + + self.assertTrue(homepage_perms.can_move()) + self.assertFalse(root_perms.can_move()) + + self.assertTrue(homepage_perms.can_move_to(root)) + self.assertFalse(homepage_perms.can_move_to(unpublished_event_page)) diff --git a/wagtail/wagtaildocs/static/wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.coffee b/wagtail/wagtaildocs/static/wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.coffee deleted file mode 100644 index 7e961444d3..0000000000 --- a/wagtail/wagtaildocs/static/wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.coffee +++ /dev/null @@ -1,45 +0,0 @@ -# plugin for hallo.js to allow inserting links using Wagtail's page chooser - -(($) -> - $.widget "IKS.hallowagtaildoclink", - options: - uuid: '' - editable: null - - populateToolbar: (toolbar) -> - widget = this - - # Create an element for holding the button - button = $('') - button.hallobutton - uuid: @options.uuid - editable: @options.editable - label: 'Documents' - icon: 'icon-file-text-alt' - command: null - - # Append the button to toolbar - toolbar.append button - - button.on "click", (event) -> - lastSelection = widget.options.editable.getSelection() - ModalWorkflow - url: window.chooserUrls.documentChooser - responses: - documentChosen: (docData) -> - a = document.createElement('a') - a.setAttribute('href', docData.url) - a.setAttribute('data-id', docData.id) - a.setAttribute('data-linktype', 'document') - - if (not lastSelection.collapsed) and lastSelection.canSurroundContents() - # use the selected content as the link text - lastSelection.surroundContents(a) - else - # no text is selected, so use the doc title as link text - a.appendChild(document.createTextNode docData.title) - lastSelection.insertNode(a) - - widget.options.editable.element.trigger('change') - -)(jQuery) diff --git a/wagtail/wagtaildocs/static/wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.js b/wagtail/wagtaildocs/static/wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.js new file mode 100644 index 0000000000..8f713a711b --- /dev/null +++ b/wagtail/wagtaildocs/static/wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.js @@ -0,0 +1,51 @@ +// Generated by CoffeeScript 1.6.2 +(function() { + (function($) { + return $.widget("IKS.hallowagtaildoclink", { + options: { + uuid: '', + editable: null + }, + populateToolbar: function(toolbar) { + var button, widget; + + widget = this; + button = $(''); + button.hallobutton({ + uuid: this.options.uuid, + editable: this.options.editable, + label: 'Documents', + icon: 'icon-file-text-alt', + command: null + }); + toolbar.append(button); + return button.on("click", function(event) { + var lastSelection; + + lastSelection = widget.options.editable.getSelection(); + return ModalWorkflow({ + url: window.chooserUrls.documentChooser, + responses: { + documentChosen: function(docData) { + var a; + + a = document.createElement('a'); + a.setAttribute('href', docData.url); + a.setAttribute('data-id', docData.id); + a.setAttribute('data-linktype', 'document'); + if ((!lastSelection.collapsed) && lastSelection.canSurroundContents()) { + lastSelection.surroundContents(a); + } else { + a.appendChild(document.createTextNode(docData.title)); + lastSelection.insertNode(a); + } + return widget.options.editable.element.trigger('change'); + } + } + }); + }); + } + }); + })(jQuery); + +}).call(this); \ No newline at end of file diff --git a/wagtail/wagtailembeds/static/wagtailembeds/js/hallo-plugins/hallo-wagtailembeds.coffee b/wagtail/wagtailembeds/static/wagtailembeds/js/hallo-plugins/hallo-wagtailembeds.coffee deleted file mode 100644 index 99e844f8d2..0000000000 --- a/wagtail/wagtailembeds/static/wagtailembeds/js/hallo-plugins/hallo-wagtailembeds.coffee +++ /dev/null @@ -1,36 +0,0 @@ -# plugin for hallo.js to allow inserting embeds - -(($) -> - $.widget "IKS.hallowagtailembeds", - options: - uuid: '' - editable: null - - populateToolbar: (toolbar) -> - widget = this - - # Create an element for holding the button - button = $('') - button.hallobutton - uuid: @options.uuid - editable: @options.editable - label: 'Embed' - icon: 'icon-media' - command: null - - # Append the button to toolbar - toolbar.append button - - button.on "click", (event) -> - lastSelection = widget.options.editable.getSelection() - insertionPoint = $(lastSelection.endContainer).parentsUntil('.richtext').last() - ModalWorkflow - url: window.chooserUrls.embedsChooser - responses: - embedChosen: (embedData) -> - elem = $(embedData).get(0) - lastSelection.insertNode(elem) - if elem.getAttribute('contenteditable') == 'false' - insertRichTextDeleteControl(elem) - widget.options.editable.element.trigger('change') -)(jQuery) diff --git a/wagtail/wagtailembeds/static/wagtailembeds/js/hallo-plugins/hallo-wagtailembeds.js b/wagtail/wagtailembeds/static/wagtailembeds/js/hallo-plugins/hallo-wagtailembeds.js new file mode 100644 index 0000000000..eb9f1e05a1 --- /dev/null +++ b/wagtail/wagtailembeds/static/wagtailembeds/js/hallo-plugins/hallo-wagtailembeds.js @@ -0,0 +1,47 @@ +// Generated by CoffeeScript 1.6.2 +(function() { + (function($) { + return $.widget("IKS.hallowagtailembeds", { + options: { + uuid: '', + editable: null + }, + populateToolbar: function(toolbar) { + var button, widget; + + widget = this; + button = $(''); + button.hallobutton({ + uuid: this.options.uuid, + editable: this.options.editable, + label: 'Embed', + icon: 'icon-media', + command: null + }); + toolbar.append(button); + return button.on("click", function(event) { + var insertionPoint, lastSelection; + + lastSelection = widget.options.editable.getSelection(); + insertionPoint = $(lastSelection.endContainer).parentsUntil('.richtext').last(); + return ModalWorkflow({ + url: window.chooserUrls.embedsChooser, + responses: { + embedChosen: function(embedData) { + var elem; + + elem = $(embedData).get(0); + lastSelection.insertNode(elem); + if (elem.getAttribute('contenteditable') === 'false') { + insertRichTextDeleteControl(elem); + } + return widget.options.editable.element.trigger('change'); + } + } + }); + }); + } + }); + })(jQuery); + +}).call(this); \ No newline at end of file diff --git a/wagtail/wagtailimages/static/wagtailimages/js/hallo-plugins/hallo-wagtailimage.coffee b/wagtail/wagtailimages/static/wagtailimages/js/hallo-plugins/hallo-wagtailimage.coffee deleted file mode 100644 index 4a4bcaf7ad..0000000000 --- a/wagtail/wagtailimages/static/wagtailimages/js/hallo-plugins/hallo-wagtailimage.coffee +++ /dev/null @@ -1,39 +0,0 @@ -# plugin for hallo.js to allow inserting images from the Wagtail image library - -(($) -> - $.widget "IKS.hallowagtailimage", - options: - uuid: '' - editable: null - - populateToolbar: (toolbar) -> - widget = this - - # Create an element for holding the button - button = $('') - button.hallobutton - uuid: @options.uuid - editable: @options.editable - label: 'Images' - icon: 'icon-picture' - command: null - - # Append the button to toolbar - toolbar.append button - - button.on "click", (event) -> - lastSelection = widget.options.editable.getSelection() - insertionPoint = $(lastSelection.endContainer).parentsUntil('.richtext').last() - ModalWorkflow - url: window.chooserUrls.imageChooser + '?select_format=true' - responses: - imageChosen: (imageData) -> - elem = $(imageData.html).get(0) - - lastSelection.insertNode(elem) - - if elem.getAttribute('contenteditable') == 'false' - insertRichTextDeleteControl(elem) - widget.options.editable.element.trigger('change') - -)(jQuery) diff --git a/wagtail/wagtailimages/static/wagtailimages/js/hallo-plugins/hallo-wagtailimage.js b/wagtail/wagtailimages/static/wagtailimages/js/hallo-plugins/hallo-wagtailimage.js new file mode 100644 index 0000000000..a0c9bb14b0 --- /dev/null +++ b/wagtail/wagtailimages/static/wagtailimages/js/hallo-plugins/hallo-wagtailimage.js @@ -0,0 +1,47 @@ +// Generated by CoffeeScript 1.6.2 +(function() { + (function($) { + return $.widget("IKS.hallowagtailimage", { + options: { + uuid: '', + editable: null + }, + populateToolbar: function(toolbar) { + var button, widget; + + widget = this; + button = $(''); + button.hallobutton({ + uuid: this.options.uuid, + editable: this.options.editable, + label: 'Images', + icon: 'icon-picture', + command: null + }); + toolbar.append(button); + return button.on("click", function(event) { + var insertionPoint, lastSelection; + + lastSelection = widget.options.editable.getSelection(); + insertionPoint = $(lastSelection.endContainer).parentsUntil('.richtext').last(); + return ModalWorkflow({ + url: window.chooserUrls.imageChooser + '?select_format=true', + responses: { + imageChosen: function(imageData) { + var elem; + + elem = $(imageData.html).get(0); + lastSelection.insertNode(elem); + if (elem.getAttribute('contenteditable') === 'false') { + insertRichTextDeleteControl(elem); + } + return widget.options.editable.element.trigger('change'); + } + } + }); + }); + } + }); + })(jQuery); + +}).call(this); \ No newline at end of file