From d7faec10cb85ad286673eec266ccc397142bc20c Mon Sep 17 00:00:00 2001
From: Marcin Koziej <marcin.koziej@nowoczesnapolska.org.pl>
Date: Wed, 9 May 2012 11:00:24 +0200
Subject: [PATCH] menu shows, time to code js to it

---
 apps/catalogue/templates/catalogue/base.html  |   4 +-
 .../templates/catalogue/book_list/book.html   |   2 +
 .../catalogue/book_list/book_list.html        |   8 +
 .../templates/catalogue/book_list/chunk.html  |   1 +
 .../templates/catalogue/document_list.html    |   9 +
 apps/catalogue/urls.py                        |   2 +
 apps/catalogue/views.py                       |  21 ++
 redakcja/settings/compress.py                 |  18 +-
 redakcja/static/contextmenu2/images/cut.png   | Bin 0 -> 648 bytes
 redakcja/static/contextmenu2/images/door.png  | Bin 0 -> 412 bytes
 .../contextmenu2/images/page_white_copy.png   | Bin 0 -> 309 bytes
 .../contextmenu2/images/page_white_delete.png | Bin 0 -> 536 bytes
 .../contextmenu2/images/page_white_edit.png   | Bin 0 -> 618 bytes
 .../contextmenu2/images/page_white_paste.png  | Bin 0 -> 620 bytes
 redakcja/static/contextmenu2/index.html       | 200 +++++++++++++++++
 .../contextmenu2/jquery.contextMenu.css       |  62 +++++
 .../static/contextmenu2/jquery.contextMenu.js | 211 ++++++++++++++++++
 redakcja/static/js/catalogue/book_list.js     |  56 +++++
 18 files changed, 591 insertions(+), 3 deletions(-)
 create mode 100644 redakcja/static/contextmenu2/images/cut.png
 create mode 100644 redakcja/static/contextmenu2/images/door.png
 create mode 100644 redakcja/static/contextmenu2/images/page_white_copy.png
 create mode 100644 redakcja/static/contextmenu2/images/page_white_delete.png
 create mode 100644 redakcja/static/contextmenu2/images/page_white_edit.png
 create mode 100644 redakcja/static/contextmenu2/images/page_white_paste.png
 create mode 100644 redakcja/static/contextmenu2/index.html
 create mode 100644 redakcja/static/contextmenu2/jquery.contextMenu.css
 create mode 100644 redakcja/static/contextmenu2/jquery.contextMenu.js
 create mode 100644 redakcja/static/js/catalogue/book_list.js

diff --git a/apps/catalogue/templates/catalogue/base.html b/apps/catalogue/templates/catalogue/base.html
index f3ebcd98..949b5f29 100644
--- a/apps/catalogue/templates/catalogue/base.html
+++ b/apps/catalogue/templates/catalogue/base.html
@@ -6,6 +6,7 @@
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
     {% compressed_css 'catalogue' %}
     <title>{% block title %}{% trans "Platforma Redakcyjna" %}{% endblock title %}</title>
+    {% block add_css %}{% endblock %}
 </head>
 <body>
 
@@ -42,8 +43,9 @@
 </div>
 
 
-<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
+<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
 {% compressed_js 'catalogue' %}
+{% block add_js %}{% endblock %}
 {% block extrabody %}
 {% endblock %}
 </body>
diff --git a/apps/catalogue/templates/catalogue/book_list/book.html b/apps/catalogue/templates/catalogue/book_list/book.html
index 46d5ae12..44534541 100755
--- a/apps/catalogue/templates/catalogue/book_list/book.html
+++ b/apps/catalogue/templates/catalogue/book_list/book.html
@@ -3,6 +3,7 @@
 {% if book.single %}
     {% with book.0 as chunk %}
     <tr>
+        <td><input type="checkbox" name="select_book" value="{{book.id}}"/></td>
         <td><a href="{% url catalogue_book book.slug %}" title='{% trans "Book settings" %}'>[B]</a></td>
         <td><a href="{% url catalogue_chunk_edit book.slug chunk.slug %}" title='{% trans "Chunk settings" %}'>[c]</a></td>
         <td><a target="_blank"
@@ -22,6 +23,7 @@
     {% endwith %}
 {% else %}
     <tr>
+        <td><input type="checkbox" name="select_book" value="{{book.id}}"/></td>
         <td class='book-settings-link'><a href="{% url catalogue_book book.slug %}" title='{% trans "Book settings" %}'>[B]</a></td>
         <td></td>
         <td>{{ book.title }}</td>
diff --git a/apps/catalogue/templates/catalogue/book_list/book_list.html b/apps/catalogue/templates/catalogue/book_list/book_list.html
index 73811cab..ad8cbf94 100755
--- a/apps/catalogue/templates/catalogue/book_list/book_list.html
+++ b/apps/catalogue/templates/catalogue/book_list/book_list.html
@@ -12,8 +12,10 @@
 <input type='hidden' name="status" value="{{ request.GET.status }}" />
 </form>
 
+
 <table id="file-list"{% if viewed_user %} class="book-list-user"{% endif %}>
     <thead><tr>
+	<th></th>
         <th></th>
         <th>
             <input class='check-filter' type='checkbox' name='all' title='{% trans "Show hidden books" %}'
@@ -79,3 +81,9 @@
 {% if not books %}
     <p>{% trans "No books found." %}</p>
 {% endif %}
+
+<form name='chunk_mass_edit' action='{% url catalogue_chunk_mass_edit %}' class="hidden">
+{% csrf_token %}
+<input type="hidden" name="ids" />
+<input type="hidden" stage="stage" />
+</form>
diff --git a/apps/catalogue/templates/catalogue/book_list/chunk.html b/apps/catalogue/templates/catalogue/book_list/chunk.html
index 14599428..fc893fdf 100755
--- a/apps/catalogue/templates/catalogue/book_list/chunk.html
+++ b/apps/catalogue/templates/catalogue/book_list/chunk.html
@@ -1,6 +1,7 @@
 {% load i18n %}
 
 <tr>
+    <td><input type="checkbox" name="select_chunk" value="{{chunk.id}}" data-book-id="{{chunk.book.id}}" /></td>
     <td class='book-settings-column'></td>
     <td><a href="{% url catalogue_chunk_edit chunk.book.slug chunk.slug %}" title='{% trans "Chunk settings" %}'>[c]</a></td>
     <td><a target="_blank" href="{{ chunk.get_absolute_url }}">
diff --git a/apps/catalogue/templates/catalogue/document_list.html b/apps/catalogue/templates/catalogue/document_list.html
index 920f25a6..294c6299 100644
--- a/apps/catalogue/templates/catalogue/document_list.html
+++ b/apps/catalogue/templates/catalogue/document_list.html
@@ -2,8 +2,17 @@
 
 {% load i18n %}
 {% load catalogue book_list %}
+{% load compressed %}
 
 
+{% block add_js %}
+{% compressed_js 'book_list' %}
+{% endblock %}
+
+{% block add_css %}
+{% compressed_css 'book_list' %}
+{% endblock %}
+
 {% block content %}
     {% book_list %}
 {% endblock content %}
diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py
index 9bd56bbe..14e4a69a 100644
--- a/apps/catalogue/urls.py
+++ b/apps/catalogue/urls.py
@@ -39,6 +39,8 @@ urlpatterns = patterns('catalogue.views',
         'chunk_edit', name="catalogue_chunk_edit"),
     url(r'^book_append/(?P<slug>[^/]+)/$',
         'book_append', name="catalogue_book_append"),
+    url(r'^chunk_mass_edit',
+        'chunk_mass_edit', name='catalogue_chunk_mass_edit'),
 
     url(r'^track/(?P<slug>[^/]*)/$', PublishTrackFeed()),
 )
diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py
index d0213daa..f8ff5996 100644
--- a/apps/catalogue/views.py
+++ b/apps/catalogue/views.py
@@ -10,6 +10,7 @@ from django.contrib.auth.models import User
 from django.contrib.auth.decorators import login_required, permission_required
 from django.core.urlresolvers import reverse
 from django.db.models import Count, Q
+from django.db import transaction
 from django import http
 from django.http import Http404, HttpResponse, HttpResponseForbidden
 from django.shortcuts import get_object_or_404, render, render_to_response
@@ -387,6 +388,26 @@ def chunk_edit(request, slug, chunk):
     })
 
 
+@transaction.commit_on_success
+def chunk_mass_edit(request):
+    if request.method == 'POST':
+        ids = map(int, request.POST.get('ids').split(','))
+        chunks = map(lambda i: Chunk.objects.get(id=i), ids)
+        try:
+            stage = Chunk.tag_model.objects.get(slug=request.POST.get('stage'))
+            for c in chunks: c.stage = stage
+        except KeyError: pass
+
+        try:
+            user = User.objects.get(username=request.POST.get('user'))
+            for c in chunks: c.user = user
+        except KeyError: pass
+
+        for c in chunks: c.save()
+    else:
+        raise Http404
+
+
 @permission_required('catalogue.change_book')
 def book_append(request, slug):
     book = get_object_or_404(Book, slug=slug)
diff --git a/redakcja/settings/compress.py b/redakcja/settings/compress.py
index bdcf44bb..d0f0df9f 100644
--- a/redakcja/settings/compress.py
+++ b/redakcja/settings/compress.py
@@ -24,7 +24,13 @@ COMPRESS_CSS = {
             'css/book.css',
         ),
         'output_filename': 'compressed/book_?.css',         
-     }
+    },
+    'book_list': {
+        'source_filenames': (
+            'contextmenu/jquery.contextMenu.css',
+        ),
+        'output_filename': 'compressed/book_list_?.css',
+    },
 }
 
 COMPRESS_JS = {
@@ -80,7 +86,15 @@ COMPRESS_JS = {
             'js/book_text/book.js',
         ),
         'output_filename': 'compressed/book_?.js',
-         }
+         },
+    'book_list': {
+        'source_filenames': (
+            'contextmenu/jquery.ui.position.js',
+            'contextmenu/jquery.contextMenu.js',
+            'js/catalogue/book_list.js',
+        ),
+        'output_filename': 'compressed/book_list_?.js',
+    }
 }
 
 COMPRESS = True
diff --git a/redakcja/static/contextmenu2/images/cut.png b/redakcja/static/contextmenu2/images/cut.png
new file mode 100644
index 0000000000000000000000000000000000000000..f215d6f6b7c81ab344a3e53e0e5e756c58c82d90
GIT binary patch
literal 648
zcmV;30(bq1P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!8c9S!R5;6H
z`2YVu11_wZHB-y3digxb#7V5U)G**Pz^-~(X8XF+PefuTa3YI~Crn}@$^hrOm3edb
zT>Sxb0Y6MkDSd{nPwTp^L>b`TxVmKBiF^NLQ>My+_!0?|)hBPe_#}P$?rlUF;M20U
z`oNWE|K(DrsR+gN%g)?+`OfqmmmiA8O_U(YfPnV(E$8mN{jZQVJ-L7LxzmTQJ^!PS
zHqDyoKn&^H)Oq>Q$Nzns&wsn~^6P)|vPC&W#R9Kw95|FC`?q!91!~-K_R;^uDLWSs
zj7A}sG%2IZxvQP(HeS-nn71T-`ku2F9(?#8KXKbp!Qe&~yaot4r%3=c-cDF`=YPn&
z3!hUrzxqFO*Ny)VU;p?Y-nniMuVuI+UIUD>=B$ZZdhM^2Z-JeJcb<R3+9&^K@4fSX
z>Dl-H)3?9>AG_?ve_7A86r2Wlb?y7)ShoTkzeah}rl)Oq{=a0=sekDkpZ?d4n{v>%
zcgJPxg7sgx4Lmup8sO5j^?B5iYcCv|)*VgT_U6A?-trsfIcr`APCfD&B(EM;R_0K#
z{lAb!kTg~U<bun?0;eB;6SeHdKeL==r@2f6B}_9`JPcoO@fW9-qnue*-yHkWEq{28
zeFbpFf{1IngqTN`0*tR8H~(1jrsx0tC+&ZlwEFIUqohSQNXm^ohF<K3i3_gVmu>rQ
ilDgypkCC?sx*7n<IG)#?gSv?T0000<MNUMnLSTYqRz!RN

literal 0
HcmV?d00001

diff --git a/redakcja/static/contextmenu2/images/door.png b/redakcja/static/contextmenu2/images/door.png
new file mode 100644
index 0000000000000000000000000000000000000000..369fc46ed259191014664e8a16bea76e7513f8b6
GIT binary patch
literal 412
zcmV;N0b~A&P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzE=fc|R5;6}
zlRZiVK@@~*{6W{iWkmxO4`O8CF+7B!xmVcSBN%UD<Q)VtG7uGH!Dw{et15#YXIIc!
zVIMTFJE{Jv`gLU+%hU>%thM>W-PO4z!6ryTDvQjnlZlgy>*`kliP$?n1x#3mBv=H*
zZvSv|{Wk%bFo}=%C%C=cB-|2gZto+r=@*a)F<})kF(DFGf{cEF%qX{I6ar>BlQ09$
zKtwM=E70{BMdbz}7ZEfCIhx`>6H;PlIK|<?8y=sB$c%mqYAtm5Dt$T~p#EI{4?%$0
z{IKx)vH^1yTf6Efh-hXZGTQK3A(cHD3kb4gV3qX0DXj9e3o<Ac2BRa`f|;;XmI<+p
z^L$mdF_}zU*EQnyvYYq$`Jep>!Y1xVX8?flcwAM>e&;Vo+<mi;5kDUQ0000<MNUMn
GLSTZ8Af{~q

literal 0
HcmV?d00001

diff --git a/redakcja/static/contextmenu2/images/page_white_copy.png b/redakcja/static/contextmenu2/images/page_white_copy.png
new file mode 100644
index 0000000000000000000000000000000000000000..a9f31a278e17993d8d4e13beac2f9d5f7b42d08f
GIT binary patch
literal 309
zcmV-50m}Y~P)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy$4Nv%R4C7N
zkxg#GFcb#A<Q_%21Hw5EM?&g?t8|A@3u~?cLP(?}@sBiJIM^nrnvECZ=aHYZ&`KuJ
zs758y%Gp3<<a0AuBoi3uxN#=Xv)!n96GnROTnGd_YE`RJRS2Gu2X{_<aYSpixkobb
z-bl|5$Gm3}qO#|`CkX+|H?KVbnL=><kbAD2OTpuD9LPN#xA_FmF`X<uzmw3UZ*H~D
z&sl~jAXO8!+C0tSo<jQ6D)&VG5i+*Z@^!UMv3Mh+r9S=u_uEaN=S{!n00000NkvXX
Hu0mjfd8B?p

literal 0
HcmV?d00001

diff --git a/redakcja/static/contextmenu2/images/page_white_delete.png b/redakcja/static/contextmenu2/images/page_white_delete.png
new file mode 100644
index 0000000000000000000000000000000000000000..af1ecaf2981fa37628c8b8b15ee389f9575e5f6b
GIT binary patch
literal 536
zcmV+z0_XjSP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzs!2paR5;6(
zlRHa7VHn5NqaUEDhHgSb1WkrbLTI-60g9F=A_y89lv>(?iiXTHIMmcoLoO94I8;j@
zv^2DJ5#orqydFJX|Gm$_Bi_vyew+j6{r}$Qc@D1%fQqeAhJj)1!z4pP83k2MV2~s!
zSt^w(<#HLFVBg_#xz1W8ioi(WY&Hu~6zil?DI^jJgu`K35(<UD^L&$pmYM{#A)n8q
zP$<YxB9qA=nM@)QiO7OnE(bcq?RLwOHUWhdNI-#fI*n*F+SXESCh8^uYooq_-l=b;
zl~^n$S0V^PcR?$S5~_se5s%0JFKCodJ2Vp#4J06n#iBeHk`Mz6sEY4fCtlWO;J4`T
zv@!vHUc>hkP)H%@Imesbg#5!Ps_$Ni*SiR8&sKb9?M`0-mH)gtg&YgRX#*TXz@Z+|
z;|2H@xzE0TfuORhuO2k6#K8#sW^J`mQ0+E@$K`QkFV+DTlI$w{GJ;zid{*v9xeIe_
z$|Bp`@iKkgoFK3{4Z)#DWKV~W4K@5WZN+Ql_7%YxNqSx7%cWud&cX>)_PvD*UzxZg
a%Kia9Rjz_59@~-t0000<MNUMnLSTY4li~gV

literal 0
HcmV?d00001

diff --git a/redakcja/static/contextmenu2/images/page_white_edit.png b/redakcja/static/contextmenu2/images/page_white_edit.png
new file mode 100644
index 0000000000000000000000000000000000000000..b93e77600def75c9a144d3d0a5088a62c02cbb0b
GIT binary patch
literal 618
zcmV-w0+s!VP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz{7FPXR5;6x
zliy2IVHn3(e?>$>5Y&axjp2O=VLu>*f>1L;s0)kkvKC!*u?s6CVL=HJ6oP~pNfZc;
zsKr=bq;7MITw8NXw{SZ<XXl*#?9=-mwz0{k9{6w$ywCS@-simMu>m%59TId2x_9BQ
zV86`NuvG<J`Fvg}l}eRzxlAj>I!>o^V!Na!=$7GJE{Cq`b+XwknM{UcGHFTTfmuS+
zm-zYC!P3+zmY;SG$?!fYkOih`QYaLxyF}A86h$GGN}<Kyu;Cg-S$PLqg3QV+p2xkL
zO#&INAq9qEpsFgHKW0d;KA^BZ&&H=m7`{2mQ8&I9*UTn?Xrtb<e3pGH>kFj)_o*0e
zjPMP%zTG7FYMAfO2Nn1D`D0Cj?Wl>5q<wd(m>%@CE10nX)KxpNmwk+!IWkzywiYD(
zqUXiYYIq3qcRyMGJ;IY`(Gz~E$J$zu2+R{)xGlE*88b3WK6V*J>}2iPY1HH|tER0W
z_+^^FdppY?o)Gt5M2`%xwRDH@R3G}^i1l4|6uchm0X0f!@&YdVLB5K&dd7Rv{)DXX
zt^&vP;}kqj3f>94j+4xd93>s|Q!Ezi>?r8(Il$P}PFxSqu{d*!Y%*#cX(R0f|Juz#
z3o0_xI14Al->1uky@W-rCI_%l&><yz80Q*t^gCqsKOt_NEc}hgZ~y=R07*qoM6N<$
Ef@@nDvH$=8

literal 0
HcmV?d00001

diff --git a/redakcja/static/contextmenu2/images/page_white_paste.png b/redakcja/static/contextmenu2/images/page_white_paste.png
new file mode 100644
index 0000000000000000000000000000000000000000..5b2cbb3fd02a5e708d9f9de4ea118965972a02b0
GIT binary patch
literal 620
zcmV-y0+aoTP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz{z*hZR5;6Z
zk~?TrQ4of|lY6;w$wtB@#23+sogj#yT`Fr|!OC)7$QB{Bq)AG<1uKQ^1R;e%5DS}t
zSSbiLwk}CPNOpy+y6EQ2nX$O{?juVE4s(k0e>4A_ab^a<i|Xa8z&FODv)pLTa6-7}
zj~8wL8^E1%dWJ`9>vY?n0hpS-#mn_4{O$e%cm-@NH=3`90Wq+3`~HKArSdfX`&Z12
z(CY$VW-MNtXX4xy%<SR&H*fC(V}MJg@@bBKA0R3;H|t;q7;XE4n&J2NI5m4AF-V+q
z&I;c-05o7COw61`gJ7ooXm|P9`G*0ze~8-?c3M4vo6fldVAsqv1~64r1ob`C_rQ!+
z>yUeE?}*~0-|iByA@ZrwXgph4S*bhcc5{HB!DFVm_v}P*g7+Q~K}7K0lcp(^N@X>U
zV`{ZpeIf${R6Hgg4FL^`X$Eu75k(PE6ycl$AW0Ic)#@rR7Z(7;V?i-dR1K935Jgcx
zPfkwK>2wGokf!Nih^ARp6-6arYFG#(9Ta!x93nFEjoA==z(g?#sDg?Owk?Mg7K+>l
zWYsf(<`#+$h9Sp6gFOg_dd+80SkUpk&xM7h0`Sov9W73spU;G<m_o_7`j3VdZ=`ww
zc=};`dm6aj7XUl09!n>P073|VfZ&Gd$J$*0<~TV5aPS|qWH57|VJz+d0000<MNUMn
GLSTY;sS5i5

literal 0
HcmV?d00001

diff --git a/redakcja/static/contextmenu2/index.html b/redakcja/static/contextmenu2/index.html
new file mode 100644
index 00000000..4efa62b8
--- /dev/null
+++ b/redakcja/static/contextmenu2/index.html
@@ -0,0 +1,200 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+	<head>
+		<title>jQuery Context Menu Plugin Demo</title>
+		<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+		
+		<style type="text/css">
+			BODY,
+			HTML {
+				padding: 0px;
+				margin: 0px;
+			}
+			BODY {
+				font-family: Verdana, Arial, Helvetica, sans-serif;
+				font-size: 11px;
+				background: #FFF;
+				padding: 15px;
+			}
+			
+			H1 {
+				font-family: Georgia, serif;
+				font-size: 20px;
+				font-weight: normal;
+			}
+			
+			H2 {
+				font-family: Georgia, serif;
+				font-size: 16px;
+				font-weight: normal;
+				margin: 0px 0px 10px 0px;
+			}
+			
+			#myDiv {
+				width: 150px;
+				border: solid 1px #2AA7DE;
+				background: #6CC8EF;
+				text-align: center;
+				padding: 4em .5em;
+				margin: 1em;
+				float: left;
+			}
+			
+			#myList {
+				margin: 1em;
+				float: left;
+			}
+			
+			#myList UL {
+				padding: 0px;
+				margin: 0em 1em;
+			}
+			
+			#myList LI {
+				width: 100px;
+				border: solid 1px #2AA7DE;
+				background: #6CC8EF;
+				padding: 5px 5px;
+				margin: 2px 0px;
+				list-style: none;
+			}
+			
+			#options {
+				clear: left;
+			}
+			
+			#options INPUT {
+				font-family: Verdana, Arial, Helvetica, sans-serif;
+				font-size: 11px;
+				width: 150px;
+			}
+			
+		</style>		
+		
+		<script src="../js/lib/jquery-1.4.2.min.js" type="text/javascript"></script>
+		<script src="jquery.contextMenu.js" type="text/javascript"></script>
+		<link href="jquery.contextMenu.css" rel="stylesheet" type="text/css" />
+		
+		<script type="text/javascript">
+			
+			$(document).ready( function() {
+				
+				// Show menu when #myDiv is clicked
+				$("#myDiv").contextMenu({
+					menu: 'myMenu'
+				},
+					function(action, el, pos) {
+					alert(
+						'Action: ' + action + '\n\n' +
+						'Element ID: ' + $(el).attr('id') + '\n\n' + 
+						'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + 
+						'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'
+						);
+				});
+				
+				// Show menu when a list item is clicked
+				$("#myList UL LI").contextMenu({
+					menu: 'myMenu'
+				}, function(action, el, pos) {
+					alert(
+						'Action: ' + action + '\n\n' +
+						'Element text: ' + $(el).text() + '\n\n' + 
+						'X: ' + pos.x + '  Y: ' + pos.y + ' (relative to element)\n\n' + 
+						'X: ' + pos.docX + '  Y: ' + pos.docY+ ' (relative to document)'
+						);
+				});
+				
+				// Disable menus
+				$("#disableMenus").click( function() {
+					$('#myDiv, #myList UL LI').disableContextMenu();
+					$(this).attr('disabled', true);
+					$("#enableMenus").attr('disabled', false);
+				});
+				
+				// Enable menus
+				$("#enableMenus").click( function() {
+					$('#myDiv, #myList UL LI').enableContextMenu();
+					$(this).attr('disabled', true);
+					$("#disableMenus").attr('disabled', false);
+				});
+				
+				// Disable cut/copy
+				$("#disableItems").click( function() {
+					$('#myMenu').disableContextMenuItems('#cut,#copy');
+					$(this).attr('disabled', true);
+					$("#enableItems").attr('disabled', false);
+				});
+				
+				// Enable cut/copy
+				$("#enableItems").click( function() {
+					$('#myMenu').enableContextMenuItems('#cut,#copy');
+					$(this).attr('disabled', true);
+					$("#disableItems").attr('disabled', false);
+				});				
+				
+			});
+			
+		</script>
+	</head>
+	
+	<body>
+		
+		<h1>jQuery Context Menu Plugin Demo</h1>
+		<p>
+			This plugin lets you add context menu functionality to your web applications.
+		</p>
+		
+		<p>
+			<strong>Tip:</strong> Try using your keyboard to make a selection.
+		</p>
+		
+		<p>
+			<a href="http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/">Back to the project page</a>
+		</p>
+		
+		<h2>Demo</h2>
+		
+		<div id="myDiv">
+			Right click to view the context menu
+		</div>
+		
+		<div id="myList">
+			<ul>
+				<li>Item 1</li>
+				<li>Item 2</li>
+				<li>Item 3</li>
+				<li>Item 4</li>
+				<li>Item 5</li>
+				<li>Item 6</li>
+			</ul>
+		</div>
+		
+		<div id="options">
+			<p>
+				<input type="button" id="disableItems" value="Disable Cut/Copy" />
+				<input type="button" id="enableItems" value="Enable Cut/Copy" disabled="disabled" />
+			</p>
+			
+			<p>
+				<input type="button" id="disableMenus" value="Disable Context Menus" />
+				<input type="button" id="enableMenus" value="Enable Context Menus" disabled="disabled" />
+			</p>
+		</div>
+		
+		<ul id="myMenu" class="contextMenu">
+			<li class="edit"><a href="#edit">Edit</a></li>
+			<li class="cut separator"><a href="#cut">Cut</a>
+			  <ul>
+			    <li class="pe"><a href="#pe">Pe</a></li>
+			  </ul>
+			</li>
+			<li class="copy"><a href="#copy">Copy</a></li>
+			<li class="paste"><a href="#paste">Paste</a></li>
+			<li class="delete"><a href="#delete">Delete</a></li>
+			<li class="quit separator"><a href="#quit">Quit</a></li>
+		</ul>
+		
+	</body>
+</html>
diff --git a/redakcja/static/contextmenu2/jquery.contextMenu.css b/redakcja/static/contextmenu2/jquery.contextMenu.css
new file mode 100644
index 00000000..5b2dd906
--- /dev/null
+++ b/redakcja/static/contextmenu2/jquery.contextMenu.css
@@ -0,0 +1,62 @@
+/* Generic context menu styles */
+.contextMenu {
+	position: absolute;
+	width: 120px;
+	z-index: 99999;
+	border: solid 1px #CCC;
+	background: #EEE;
+	padding: 0px;
+	margin: 0px;
+	display: none;
+}
+
+.contextMenu LI {
+	list-style: none;
+	padding: 0px;
+	margin: 0px;
+}
+
+.contextMenu A {
+	color: #333;
+	text-decoration: none;
+	display: block;
+	line-height: 20px;
+	height: 20px;
+	background-position: 6px center;
+	background-repeat: no-repeat;
+	outline: none;
+	padding: 1px 5px;
+	padding-left: 28px;
+}
+
+.contextMenu LI.hover A {
+	color: #FFF;
+	background-color: #3399FF;
+}
+
+.contextMenu LI.disabled A {
+	color: #AAA;
+	cursor: default;
+}
+
+.contextMenu LI.hover.disabled A {
+	background-color: transparent;
+}
+
+.contextMenu LI.separator {
+	border-top: solid 1px #CCC;
+}
+
+/*
+	Adding Icons
+	
+	You can add icons to the context menu by adding
+	classes to the respective LI element(s)
+*/
+
+.contextMenu LI.edit A { background-image: url(images/page_white_edit.png); }
+.contextMenu LI.cut A { background-image: url(images/cut.png); }
+.contextMenu LI.copy A { background-image: url(images/page_white_copy.png); }
+.contextMenu LI.paste A { background-image: url(images/page_white_paste.png); }
+.contextMenu LI.delete A { background-image: url(images/page_white_delete.png); }
+.contextMenu LI.quit A { background-image: url(images/door.png); }
diff --git a/redakcja/static/contextmenu2/jquery.contextMenu.js b/redakcja/static/contextmenu2/jquery.contextMenu.js
new file mode 100644
index 00000000..59c1737d
--- /dev/null
+++ b/redakcja/static/contextmenu2/jquery.contextMenu.js
@@ -0,0 +1,211 @@
+// jQuery Context Menu Plugin
+//
+// Version 1.01
+//
+// Cory S.N. LaViska
+// A Beautiful Site (http://abeautifulsite.net/)
+//
+// More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/
+//
+// Terms of Use
+//
+// This plugin is dual-licensed under the GNU General Public License
+//   and the MIT License and is copyright A Beautiful Site, LLC.
+//
+if(jQuery)( function() {
+	$.extend($.fn, {
+		
+		contextMenu: function(o, callback) {
+			// Defaults
+			if( o.menu == undefined ) return false;
+			if( o.inSpeed == undefined ) o.inSpeed = 150;
+			if( o.outSpeed == undefined ) o.outSpeed = 75;
+			// 0 needs to be -1 for expected results (no fade)
+			if( o.inSpeed == 0 ) o.inSpeed = -1;
+			if( o.outSpeed == 0 ) o.outSpeed = -1;
+			// Loop each context menu
+			$(this).each( function() {
+				var el = $(this);
+				var offset = $(el).offset();
+				// Add contextMenu class
+				$('#' + o.menu).addClass('contextMenu');
+				// Simulate a true right click
+				$(this).mousedown( function(e) {
+					var evt = e;
+					evt.stopPropagation();
+					$(this).mouseup( function(e) {
+						e.stopPropagation();
+						var srcElement = $(this);
+						$(this).unbind('mouseup');
+						if( evt.button == 2 ) {
+							// Hide context menus that may be showing
+							$(".contextMenu").hide();
+							// Get this context menu
+							var menu = $('#' + o.menu);
+							
+							if( $(el).hasClass('disabled') ) return false;
+							
+							// Detect mouse position
+							var d = {}, x, y;
+							if( self.innerHeight ) {
+								d.pageYOffset = self.pageYOffset;
+								d.pageXOffset = self.pageXOffset;
+								d.innerHeight = self.innerHeight;
+								d.innerWidth = self.innerWidth;
+							} else if( document.documentElement &&
+								document.documentElement.clientHeight ) {
+								d.pageYOffset = document.documentElement.scrollTop;
+								d.pageXOffset = document.documentElement.scrollLeft;
+								d.innerHeight = document.documentElement.clientHeight;
+								d.innerWidth = document.documentElement.clientWidth;
+							} else if( document.body ) {
+								d.pageYOffset = document.body.scrollTop;
+								d.pageXOffset = document.body.scrollLeft;
+								d.innerHeight = document.body.clientHeight;
+								d.innerWidth = document.body.clientWidth;
+							}
+							(e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;
+							(e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop;
+							
+							// Show the menu
+							$(document).unbind('click');
+							$(menu).css({ top: y, left: x }).fadeIn(o.inSpeed);
+							// Hover events
+							$(menu).find('A').mouseover( function() {
+								$(menu).find('LI.hover').removeClass('hover');
+								$(this).parent().addClass('hover');
+							}).mouseout( function() {
+								$(menu).find('LI.hover').removeClass('hover');
+							});
+							
+							// Keyboard
+							$(document).keypress( function(e) {
+								switch( e.keyCode ) {
+									case 38: // up
+										if( $(menu).find('LI.hover').size() == 0 ) {
+											$(menu).find('LI:last').addClass('hover');
+										} else {
+											$(menu).find('LI.hover').removeClass('hover').prevAll('LI:not(.disabled)').eq(0).addClass('hover');
+											if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:last').addClass('hover');
+										}
+									break;
+									case 40: // down
+										if( $(menu).find('LI.hover').size() == 0 ) {
+											$(menu).find('LI:first').addClass('hover');
+										} else {
+											$(menu).find('LI.hover').removeClass('hover').nextAll('LI:not(.disabled)').eq(0).addClass('hover');
+											if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:first').addClass('hover');
+										}
+									break;
+									case 13: // enter
+										$(menu).find('LI.hover A').trigger('click');
+									break;
+									case 27: // esc
+										$(document).trigger('click');
+									break
+								}
+							});
+							
+							// When items are selected
+							$('#' + o.menu).find('A').unbind('click');
+							$('#' + o.menu).find('LI:not(.disabled) A').click( function() {
+								$(document).unbind('click').unbind('keypress');
+								$(".contextMenu").hide();
+								// Callback
+								if( callback ) callback( $(this).attr('href').substr(1), $(srcElement), {x: x - offset.left, y: y - offset.top, docX: x, docY: y} );
+								return false;
+							});
+							
+							// Hide bindings
+							setTimeout( function() { // Delay for Mozilla
+								$(document).click( function() {
+									$(document).unbind('click').unbind('keypress');
+									$(menu).fadeOut(o.outSpeed);
+									return false;
+								});
+							}, 0);
+						}
+					});
+				});
+				
+				// Disable text selection
+				if( $.browser.mozilla ) {
+					$('#' + o.menu).each( function() { $(this).css({ 'MozUserSelect' : 'none' }); });
+				} else if( $.browser.msie ) {
+					$('#' + o.menu).each( function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); });
+				} else {
+					$('#' + o.menu).each(function() { $(this).bind('mousedown.disableTextSelect', function() { return false; }); });
+				}
+				// Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome)
+				$(el).add($('UL.contextMenu')).bind('contextmenu', function() { return false; });
+				
+			});
+			return $(this);
+		},
+		
+		// Disable context menu items on the fly
+		disableContextMenuItems: function(o) {
+			if( o == undefined ) {
+				// Disable all
+				$(this).find('LI').addClass('disabled');
+				return( $(this) );
+			}
+			$(this).each( function() {
+				if( o != undefined ) {
+					var d = o.split(',');
+					for( var i = 0; i < d.length; i++ ) {
+						$(this).find('A[href="' + d[i] + '"]').parent().addClass('disabled');
+						
+					}
+				}
+			});
+			return( $(this) );
+		},
+		
+		// Enable context menu items on the fly
+		enableContextMenuItems: function(o) {
+			if( o == undefined ) {
+				// Enable all
+				$(this).find('LI.disabled').removeClass('disabled');
+				return( $(this) );
+			}
+			$(this).each( function() {
+				if( o != undefined ) {
+					var d = o.split(',');
+					for( var i = 0; i < d.length; i++ ) {
+						$(this).find('A[href="' + d[i] + '"]').parent().removeClass('disabled');
+						
+					}
+				}
+			});
+			return( $(this) );
+		},
+		
+		// Disable context menu(s)
+		disableContextMenu: function() {
+			$(this).each( function() {
+				$(this).addClass('disabled');
+			});
+			return( $(this) );
+		},
+		
+		// Enable context menu(s)
+		enableContextMenu: function() {
+			$(this).each( function() {
+				$(this).removeClass('disabled');
+			});
+			return( $(this) );
+		},
+		
+		// Destroy context menu(s)
+		destroyContextMenu: function() {
+			// Destroy specified context menus
+			$(this).each( function() {
+				// Disable action
+				$(this).unbind('mousedown').unbind('mouseup');
+			});
+			return( $(this) );
+		}
+		
+	});
+})(jQuery);
\ No newline at end of file
diff --git a/redakcja/static/js/catalogue/book_list.js b/redakcja/static/js/catalogue/book_list.js
new file mode 100644
index 00000000..1ff5f064
--- /dev/null
+++ b/redakcja/static/js/catalogue/book_list.js
@@ -0,0 +1,56 @@
+(function($) {
+    $(function() {
+	// clicking on book checks chunks, too
+	$("input[name=select_book]").change(function(ev) {
+	    $book = $(this);
+	    $book.closest("table").find("input[name=select_chunk][data-book-id=" + $book.val() + "]").attr("checked", $book.is(':checked'));
+	    });
+
+	// initialize context menu
+
+	var get_ids = function() {
+	    return $.map($("input[name=select_chunk]:checked"), function(ele, idx) {
+		return ele.value;
+		}).join();
+	};
+
+	var set_stage = function(key, opt) {
+	    var stage = $("select[name=stage] option[value!=]").eq(key).val();
+	    $.post($('input[name=chunk_mass_edit_url]').val(),
+		   {
+		       ids: get_ids(),
+		       stage: stage,
+		   },
+		   function(data, status) {
+		       location.reload(true);
+		   }
+		  );
+	};
+
+	$.contextMenu({
+	    selector: '#file-list',
+	    items: {
+		"stage": { 
+		    name: "Set stage",
+		    items: $.map($("select[name=stage] option[value!=]"),
+				 function(ele, idx) {
+				     return { 
+					 name: $(ele).text(),
+					 callback: set_stage,
+				     };
+				 }),
+		},
+/*		"user": { 
+		    name: "Set user",
+
+			  },
+		"status": { 
+		    name: "Set status",
+		    items: 
+			  },*/
+
+	    },
+	});
+	
+    });
+})(jQuery);
-- 
2.20.1