pax_global_header00006660000000000000000000000064126436766210014527gustar00rootroot0000000000000052 comment=915098188119a930600a2e202137ad043f18a666 django-hijack-2.0.3/000077500000000000000000000000001264367662100142225ustar00rootroot00000000000000django-hijack-2.0.3/.coveragerc000066400000000000000000000000601264367662100163370ustar00rootroot00000000000000[run] source = hijack omit = hijack/tests/* django-hijack-2.0.3/.gitignore000066400000000000000000000014031264367662100162100ustar00rootroot00000000000000*.egg-info/ *.pyc *coverage/ db.sqlite dist/ docs/_build/ app_media/ app_static/ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ env?*/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .cache nosetests.xml coverage.xml # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ django-hijack-2.0.3/.travis.yml000066400000000000000000000014441264367662100163360ustar00rootroot00000000000000language: python python: - "2.7" - "3.4" - "3.5" env: - DJANGO=django==1.4.* - DJANGO=django==1.6.* - DJANGO=django==1.7.* - DJANGO=django==1.8.* - DJANGO=django==1.9.* sudo: false install: - pip install -r requirements.txt - pip install -r requirements-test.txt - pip install $DJANGO - pip install coveralls script: coverage run runtests.py after_success: coveralls matrix: exclude: - python: "3.4" env: DJANGO=django==1.4.* - python: "3.5" env: DJANGO=django==1.4.* - python: "3.5" env: DJANGO=django==1.6.* - python: "3.5" env: DJANGO=django==1.7.* allow_failures: - env: DJANGO=django==1.4.* - env: DJANGO=django==1.6.* fast_finish: true django-hijack-2.0.3/AUTHORS000066400000000000000000000014041264367662100152710ustar00rootroot00000000000000Current or previous core committers * Philippe O. Wagner (philippeowagner) * Walter Renner (walterrenner) * Yannik Ammann (yannik-ammann) Contributors * Alex Palcuie (palcu) * Derek Stegelman (dstegelman) * John Franey (johnfraney) * Maria Niță (maria) * (webdevelop) * Patrick Forringer (destos) * Raymond Penners (pennersr) * Simon Charette (charettes) * Vladysav Tarasenko (vladyslav2) * Harry Rybacki (HarryRybacki) * Remi (Remiz) * Frank Wickström (frwickst) * Alexander Allakhverdiyev (alexallah) * Ryan P Kilby (rpkilby) * David Friedman (dfrdmn) * Aleksey (bargool) * Gregory R. Warnes (warnes) * benjaminrigaud (benjaminrigaud) * Madis Väin (madisvain) * Ezequiel Bertti (ebertti) * cato- * Christofer Bertonha (bertonha) Your name could stand here :) django-hijack-2.0.3/CHANGELOG.txt000066400000000000000000000037121264367662100162550ustar00rootroot00000000000000=== (ongoing) === === (2.0.0) === - Unify settings; Django will show a warning if a deprecated setting name is used. See http://django-hijack.rtfd.org/en/latest/configuration/#settings-overview - Drop official support for Django 1.4, 1.6; add support for 1.9 using https://github.com/arteria/django-compat v1.0.8 - Add i18n support, see https://github.com/arteria/django-hijack/tree/v2.0.0/hijack/locale - Make `HIJACK_DECORATOR` configurable - Do not update last_login time of hijacked users - Add optional Bootstrap-optimized notification bar. Can be activated with `HIJACK_USE_BOOTSTRAP` - Add signals `hijack_started` and `hijack_ended` - Add AppConfig and various checks - Check "next" GET parameter for safety - Move documentation to http://django-hijack.readthedocs.org/ - Use https://github.com/arteria/django-sessioninfo as a dependency - Rewrite tests === (1.0.10) === - Bugfixes - Allow hijacking users with negative IDs === (1.0.9) === - Allow email usernames in login_with_username - Dropped support for django 1.5 === (1.0.8) === - Added RemoteUser support - Fixed hide button to redirect to correct full path - Custom hijack function === (1.0.7) === - Security update === (1.0.6) === - Do not allow staff users to hijack superusers - Add setting to choose which user attributes can be used for hijacking a user - Use a more liberal/naive approach to regex checking for an email - Code cleanup - Check staff status against logged in user instead the user being hijacked (bugfix) - Only include 'disable-hijack-warning' url if HIJACK_NOTIFY_ADMIN is enabled === (1.0.5) === - Fix an error related to django-compat on Django 1.7.x (get_user_model) - Updated dependencies to django-compat 1.0.1 === (1.0.4) === - Python3 support - Tests === (1.0.3) === - Support custom user models === (1.0.2) === - Store info in session (see #3 comments) - Signals while hijacking - Visual notifications for hijacking, see README for more detail - Django 1.6 compatibility (in urls.py) django-hijack-2.0.3/DESCRIPTION000066400000000000000000000001321264367662100157240ustar00rootroot00000000000000django-hijack allows superusers to hijack (=login as) and work on behalf of another user. django-hijack-2.0.3/LICENSE000066400000000000000000000020661264367662100152330ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013 arteria GmbH 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. django-hijack-2.0.3/MANIFEST.in000066400000000000000000000004051264367662100157570ustar00rootroot00000000000000include AUTHORS include LICENSE include DESCRIPTION include CHANGELOG.txt include README.md include requirements.txt graft hijack global-exclude *.orig *.pyc *.log *.swp prune hijack/tests/coverage prune hijack/.ropeproject recursive-include hijack/templates * django-hijack-2.0.3/Makefile000066400000000000000000000004131264367662100156600ustar00rootroot00000000000000develop: setup-git pip install "file://`pwd`#egg=hijack[dev]" pip install -e . setup-git: git config branch.autosetuprebase always cd .git/hooks && ln -sf ../../hooks/* ./ lint-python: @echo "Linting Python files" PYFLAKES_NODOCTEST=1 flake8 hijack @echo "" django-hijack-2.0.3/README.md000066400000000000000000000024141264367662100155020ustar00rootroot00000000000000# Django Hijack [![Build Status](https://travis-ci.org/arteria/django-hijack.svg?branch=master)](https://travis-ci.org/arteria/django-hijack) [![Stories in Ready](https://badge.waffle.io/arteria/django-hijack.png?label=ready&title=Ready)](https://waffle.io/arteria/django-hijack) [![Coverage Status](https://coveralls.io/repos/arteria/django-hijack/badge.svg?branch=master&service=github)](https://coveralls.io/github/arteria/django-hijack?branch=master) ![Screenshot of django-hijack in action on the admin site.](docs/admin-screenshot.png) ![Screenshot of the warning seen while hijacking another user.](docs/hijacker-screenshot.png) With Django Hijack, admins can log in and work on behalf of other users without having to know their credentials. ## Docs See http://django-hijack.readthedocs.org. ## Testimonials > I am using django-hijack on a project with hundreds of users, wonderful tool for support. Thank you! > > — [benzinonapoloni](https://www.reddit.com/user/benzinonapoloni) on [reddit](https://redd.it/39aggd) --- > This is a super useful tool. I have a site where I occasionally impersonating users to help troubleshoot their issues. This is a huge time saver. > > — [orangishyellow](https://www.reddit.com/user/orangishyellow) on [reddit](https://redd.it/2i35fu) django-hijack-2.0.3/docs/000077500000000000000000000000001264367662100151525ustar00rootroot00000000000000django-hijack-2.0.3/docs/about.md000066400000000000000000000024031264367662100166050ustar00rootroot00000000000000# TODOs, issues, and planned features * Handle hijack using URLs on non-unique email addresses. * unset_superuser example for signals * Store info in user's profile (see #3 comments, Use case: 'Notify users when they were hijacked') * "got it" Link in notification to remove notification and flag from session. This is useful if hijack is used to switch between users and ``HIJACK_DISPLAY_WARNING`` is True. * Support for named URLs for the hijack button. * Graceful support for custom user models that do not feature username / email # Django 1.7–1.9 compatibility with [django-compat](https://github.com/arteria/django-compat) All critical imports are carried out with the [compat library](https://github.com/arteria/django-compat) that ensures compatibility with Django 1.7 to 1.9. The app is also tested with Django 1.4 and 1.6. However, the tests are allowed to fail, and the package may not be fully compatible with those versions. # Similar projects Similar projects can be found and compared in the [user-switching](https://www.djangopackages.com/grids/g/user-switching/) or the [support](https://www.djangopackages.com/grids/g/support-apps/) grids at Django Packages. # Contributing If you want to contribute to this project, simply send us a pull request. Thanks. :)django-hijack-2.0.3/docs/admin-screenshot.png000066400000000000000000003472601264367662100211370ustar00rootroot00000000000000PNG  IHDR"h٩sRGB pHYs  YiTXtXML:com.adobe.xmp 1 L'Y@IDATxy|wBvHBv V\PmV+wZUl][Zm[ZZջU֭,*I !, $!Ir~9'9'9A̙z^1?? @@@@@@,}:@@@@@@\Q<      "@ jPX@@@@@@@      V*E@@@@@ 3      0(J@@@@@@D       AaR@@@@@@Q<      "@ jPX@@@@@@@      V*E@@@@@ 3      0(J@@@@@@D       AaR@@@@@@Q<      "pڗviPYoѓ|Y?>?W0e zC添iv!     :2K^SVkXcި/VtJ1M<[k:Zmu9NRaD?kVGEMyHW]ߵpf|ű\7Q법ӑǯj ߁ciJ57.ڣ13Lg.qҮ[/ޣTlrX(b      p Z *CjN)GUզ:DSW/פf H$=LVKN?h4MBk3R;EEMe F=N]v S-/ z[6Fn_+{~H_vffTO6X+ծkƶiWE~]6fYmkkJK/ n9 \5\TԬ| >ޘFkӯTvBj?RH5R."5]_Ԩ l)щV=Jx=ܬyؘ[5f &7)-ξhzeEkǺ,OsUhMWZjknF#Œ]~OLV-,nV5Φ[ +QtYӫ2ξsk&}6 Nt6dl@@@@@-pڔdŵhYi{tiQڳQμP鏬?F;m3uVIjuweߎSy}ZwJ]6ejy469ܵ)ՅkO^=WjӉOHЂW㻮M筵yڵ58%]`L}yl*I uĂۼzL'Ҫ_-Yk|,Ҵ^άz]imXB@vmf3fVdԺgGZ [+n];ђ3sw)_ga(E6.:]V9=?@?2Vư2u߬=Yٛ}nWB.    d%jzf>>ZU[t=޻+khOܲc:e[gj%Q4F|<_91>)ւQ z7tO qտ0!>ZJpYd;'JC^i')zKrp/Ec.-kw<0idwybo{[G3&f(=i?&Z7i Szw/뛖PK48\Cݵ'XE+>߯sDqӏiҭ [Χ\j gsg+ /_4ծl\kL[iĺ^qV-ƪz垾k==v$XY'e+ߡŒl: z*i^ &WҗF?}vx Su>v2K8KR)Qs,kkT4j!m+0F+w䪦]Wjz~%gl͗ҒGv]Zfɬ-IZ#Eʨ [l^El*}6֩|e r8Q(TNh~NN54on}cz Қޯ왞Qckb_]9Y74=u?^jւil<=]`}nVo7?Yh>qiҩ둒f%[@+g1m,M;un*;CHa=󵘯MxNUc$(k፯ҒraS_֩.y yޑ5R%iyuAk$}d+&ҖԵ=? :Gä5ox/^ނPq(UZt5 r+D3Ӝ=n`cJ"W.7Q2YSp+8_Kep `q}%" ps_/srs<=wFKnK5bt{)(?'ClKuf]h>9ajt`>mgNe.4W,KjfަY=Siz 56X&Neo33,S˞}̮`Û'[jJmͺt XVe}5Sb  ߲tVƋ-9z1Jk繁pZdKxLcUhoz)M\Rck<[N[=OH_))iwQ9aA5vf5jKӝ1WhEӭMMgm6Ų&fNU]lzŧuݭ zEU}-_g$\+,? E=og=+ zτgEs;Pf[3z(ʰ G}xqz;r883gYPu<XJC~oBD@@@@wce-'l_s*Y )ZRԩnͰ`J_iKX j[gIJb TOW&,{ppխЕ5lS̳)^>;$_7 }vNeH g}tu-0R_5Ym,7=mwNF4MJNgzo,ȳQ z7p؞饆y yz53G/u$*M (vve]2)͒ kxs}h%[`*kG=qW-*s<*U=uu.Ѓ~ C7ݼƮG[>RbT9=[UVyAnBGjG:Mz =Ǟ')t3Xʦj ByGbKVu\>-.QTsnY3)cMM;,<{ KXWLρVi٧r`z|ea&@-EJO>Y|>=c5.9| 2 aky;1N&Z8xtzڂ:׳]eFvri& Tqg[JkR𞾾ѽ+ݟYXy~Bw*P&zoD[\v>cC:n?gZ]z]U!    *PO{Ex^6Wb4ӂ=g[`s3uY,sT}C-zuXicgͫRVQzf0-߾>BK/*9Ab5 Etv}- c:oD^`T>Ky(: zm7V睛鶭rF7>VYԦXecKRZur\-Lw߰f[A /dy[U3'{* rXowXQ@MYhDm>=-\M϶`R&.SCVԅej"_]^VF Ekc[TCL>=lT_ gaHRuiO!]N<ВrzdJyvz.΂RS4 V,/jwLPkkHt[ݲa-G}bl     pb DWX=7K%i~VOlU<}jR0= -mhҞVv;`p=aӖp9&6Mycg`.7Ԟ]NTEz?Vq|Ϋ[u/H]Y?[L՟Tts P5M_ku^յ Wn5ZiӔkK =umst LUoUUT>zǷ!1Kz{]6UGw6<=ӯeO_(X-my`닋l%g<}n1xWza)i5=^tp`YN k>b} Z##&+\5r#6xX63}-rU%՜?l=zSi[t-)S5Ӵc(}osﬥ#Vu<;ZyU?VÇeNP9gW} :IkTA=چ~.vsг$@@@@@j.;fj[-N33ta0m_7telʞ߮mnɉn`r+ڒ$q0ΖiYޝףnˎZ7A鴴6{Gvv7tz}[cQ,nOZƎO7^ʦ*Ҧ{&5;I&Ͷ CQ>=^yMgS_ót}DEoҿ=_I*[\؎%{^“DmԶ]r^ڪ6Gh,E|I pu}juǟ} -p|pkX#};X51=~Ƈ޸ <ʹvҏjslӟV96{~=e:VeEZ9    'ZlOUFtVD;cjVoʛΩays{nU2^q8m^PCTT)yYF Gy4{]L/80O-ST@Lu*Цuzd.w @wzҵ=^$3Ͻ_(Ѭdk V|:/4IɂXvmq?i[k՗x@~q*ky<~@W~>[` qK+IҮ'j=4*^zVB'jP}3Vw-?rlJҲҕ66WY ڱzꯥns7[gc鬴xb"mi^omN.0MyMτ|:kDbl nI1-mݧǏrOUT2ml8U {w~Dz5f F&ƫͦ|w\MIzC1jXſz1XDs 5+n}Œ{kuc6a\=_-/ghxkc[`p:2XV͟U#K$OWaNl^=Js 8tI܋,e8A^q]ҎCJ[)ZD@&V\R r?Z /y1Uvٴfo<+=|eMNi,@ָz ]t,mK]?%-#Ҹ-kEUcF@@lh #    .U7W[7Yӿ[,+^ee 9D8XީӦ$[EJOY;s¼(խnt:V<]w Wg"%3xW`z r$kӫn,iVZw'۴ytOkܫ6*syW}u8M_/zVᨨ2}{:E+ͫ۾|m~zo﬒Օj8pw v?U+}טkY`4vpc>ܕ{βuj֔w/L_Y.R-oX[`m 0黤s@md 4]{5݇w6=r*^//w~S79wC#2xK_kj0T"ݿ]3"w誋l-<{m1|hZ`@ T{kgo<(ү+_tf(8]yztKUx3򚐿L@@@@@ 1}Mta=&<ջVןBM T8;Gwa'lSxtc8I[:A!A!HM?xrZ4czcysgS[Rnrɂ=7="}x荱^7t+.WoUײTkѥtMbx0oќQZ:ثc뎭*k4׏SV}yR}dzOl;4'yn5&?0^?%vEZQ&0Ӥ=HD5'ieHTᩯE\d''vmCEb    ʗֽkMDZ/ԭwPq1@RV>WKޔWbƛ?[3FN5B.mb#N׿Nؔ* QūXh5$(-F9Ipzc'g/[kҒ|մCi/NݎS#Ѻs}7fꂪҏ_)nC[K?uPdۃܨ촽=\y 0zoj-w3_ ?^S:8ZK~T^vX[Gf`R34֦sb <@T4]@@@@@~(mSK~=Eк^BD1p=s> j{#U^9N?޵Oߵ꺿.Z ꤥUs1z\ʂG-MkVX)8F~ : mxhreU).e&n֞> oͪa#z}{k|aego LJ啁v\{ck>Y-vXy-jVw{nGPMmQώ취|Nϰ8nR|Eۚu]_;>Ms2jl|Uh[[=x@4=xjŴXi >y5FW&36۳lف)u?F+_Qe>fX뾱g$}չ.΂`V_R#'CԭnՂl[-mt;WS9EWu-0mg9}]A@@@@@5Zr]{[/<4ovu K=]4L&9YOz A+^G,o8m0unh{*jQ^p0.S0|1Vk_5+:mxn]9e4UiN ˸wQOٞOl34^f1ٵolq @l@@@@@8qN@TJGflj¥KHutC9@@@@@8P6TX2ӵfФLhkAb8ĒswyA(;\YŊ;:|C@@@@@<SO7:#Xqڻ^9i>)uZSu-VmN uK =y;Z=@@@@@@_ D5(.sTW5]_G)X:Ev*'_)UXMV!|"     ' FTNe됯!Vo s2rM4@@@@@8N(@-smk8'P'ڸ@@@@@8qNωkE@@@@@@ @ jXE@@@@@[Q@@@@@@`EQ@@@@@@ %@@@@@@ @ jXE@@@@@[Q@@@@@@`EQ@@@@@@ %@@@@@@ @ jXE@@@@@[Q@@@@@@`EQ@@@@@@ %@@@@@@ @ jXE@@@@@@̼y_      S~BQ @@@@@@``Ei@@@@@@~ '@@@@@@&@ j`^F@@@@@~BQ @@@@@@``Ei@@@@@@~ '@@@@@@&@ j`^F@@@@@~BQ @@@@@@``Ei@@@@@@~ '@@@@@@&@ j`^F@@@@@~BbEY#w*G=x?TG#N"o{[q:k] f]7a@@@@@?M`pQT ZRibfN'3/Ӳr9uQoTϜ>h|?]7WkΛ9]~6֒#}`#׹ɳǻc}E%VT:̺19*mii|!G@@@@@fq=G^>!C3 2OE`㵨2a?ĉtӻ^muVkk׾|GiZ:7ԾZU hYy7&]gLp¹StӢI:8'AEmjG_B߳dzH?F 7EY,C$7Y24wd@@@@@`bN)pQg}KaR;!xaM=pbnĨ4{%PNu$%I8-Co~Q`Ʀ8dW#EkfHh]KBdzideHN2亦~ݺ_Z^O%Cו lݎ|;iCa@@@@@@T^'NeӔb)gk8x@?_*۟8s>Q}}L|- ZU3&g(ͽ]JYcY%{57ԃϖ=;6^df8@@ew}}մOz~[i )Qu*sLOeD%[.sJ7uZ66juJ}33sڸӭ} /Ѯ򊝺3NEcR2XqiRF%߭3.,جVCƲj_23Gjiy|0,xnsV/'KgRKmbFt;FSqn?}D*Ep|T8ֻ=Z,l38];KU3DϻGuCy5+:3ݯ5_ A N VAyӯ4}n{SG(|cmϮl:[w_Cw*`"^N(!ubHLOuM`ϖ=瞳>R["7nCOP-*_XvyOq:4%9Z TGϲRG4%{5qj265/pO$6PjiiծqZ`χrظ8MzT !    莢~M/= D_<>}y^Tw.ۢ;^ЀOy:7"Cx5vD8?ϯe7w߬_>QbSGZj{ޭ'}b˰p]ez`*a29?r-_YT{vн5va_FSTصX_|Co&E_H{ NN^|uݻlChQwY֬/m;Z'&ī\?ken;͢L8͝?변IjNq~S(Nծcqnԋ*8~6˚٬}Rν ׼=rqmlS^fmK:nk={>Vwez[_<~:x9cn6S~^~'$y֞|C^Ùc0ZS&q( 7v*)9Eg> +п/ICrG@eE шi|zZ7!&%t;٦W7Գ{YU8ա V~ei,RտQ @@@@@׻G,s'-!th7ɝ&jMIpizVٵu*m1`fsq Dmijh2Esټ X/k/Z[[mGSd3M/IJȏ*5#De+H*Necӛ9`ScV_׽{^u(I#h]nrš^? ZT2[wt ыl~w!{K> s֟z.Z:+O߸h,sݝit$GٱLgEV5YțѾyġZZJt٦9LRAXG_kl]g?Tve`Jzn<}e~\oت'ݢG뫽n,F ܯt~ lC;&VYiJԩMZ{V 29=˲:TԢ,decG}GD-람jWs=qtqjj@]eĪ8     0\zCd2O>sI<֟ϮL#N~|6XuSb1 "Gzkxaue)Z<7\ sz0>x|PX#?[iK5vi.s19hAaKq-robqMtreԤ RGO56= g ZRԋX_pKjZZG{<''^[G6ehj CF.yDȗѨC{6$kTblӦמ*vhAk,آBVssTl%۴}I5\On ^'     q@T&7쥱gދ47K*erGR"KWU!i).O˵l(u4 ˞1n{վ}e4`6Xqj bBNmÓܵJmW2+~Dw*9H*}UҗWYTM6Q?8w uXݵbS=YCj7H?ڛQg fΟNU`Mjk%ԢDv1» 8iA}gٮ /R{4) nnp(`1ҕf ) 2DD X<~hM^۲ ֙Y>B6m=]s& P^lYP ?5Slk`.`@@@@@X'Dzi;.>9)*=KK? ZEZ&yv^}޴%(sL>O*MWV_yz{X_^޶/2J5r:e۵1XŽb:)#UZ5X|)+.Q?svUIctŴZqOGl?k{_ȑUmK.1|U{X*+wR>TX9w-khnz=Іcg!+rx%t4Ax>;jA͘;K6Q^v,%Yf9k_ݻjGWS?&Ǣs{:/!6XUͫ68ql6^>75ʏ׳uGRE['-Fi]0.𦼫Ӎ -~}ƥ SKc;wQ>MScT0!_uMj'kLZlwe_EkVQvny&>˂WKfGeaEiLp%ýg4RgOW]Gw UA@@@@87;%_ysSvlܤ'w9 qRT'*rX" Οk\r@IDATu9gw LP3-#Ӓz5mVor_rut97 PN*5X$cX=oԹyj?|z5eS4#7٩4qw?6͞u?|OL:yҕeju_YRRS5ӂP퇛ݾǹ_QTdūhL[KYt! lc#ݒ>/kmz만и~5;%H1~eux0tPljJ,Xr^S[5|Y-Ɲf5)snn TȽ}eUɚ`ڳ6lʐ+E@@@@8FJL̟bl {Nկz2R||:hk 99ٴ^SY0e6p|V ~/7+͝s(b78Z:jگ޳z?cvα$G!c>XOgO;kJ%/wWwޢ496zVz|WjR;u{}4ҹ|6S&[}\Rzs<     81Qǿ_Ԉ      035w#      Z     NaS      1      I)@ V:      Z     NaS      1      I)@ V:      Z     NaS      1      I)B@@@@@R2#     'wl      0#     'wl      0#     'wl      0#     'wl      0#     'wl      0#     'wl      0#     'wl      0#     'wl      0#     'wl      0#     'wl      0#     'wl      0#     'wl      0@ԮggۇO}&}OێC@@@@@w@V>ojhn3BE3hUjl|BUV߬EJx\1>UV6"y@@@@@@`/#W_e5:Ԍg뼳hL!~J߹z7ȡ`v=S]h/xdܮ_~~!H1 O      ZXgo Q}RݠW^~ϟi};TQ)uĨ DLQ&d$@MA@@@@@@Oa Tdtyzhdzzwzl궭[u Tqq@+wB ^ItؑI}tHؑJ WsNI#{]_{ȶ|愶y>5:dsaeuI),VFRh=怕֯&;=dm+WzzrvhWLL2kޣ;TJMSӳ[|kkMPaaNoSnqiRfWeں˂r}3M9X'     Xfp|_ fE N{o莟Y.ВEGnjݳL?*l-F|IKuuoIطɟ>Z5/|ILQazuNZ} u }zư̭ѴxTԼV_=*zxOXߣER?U {ӟ$g~2ڶ1mF/M_GuB׋Z}{ya8蹻ՅC綇 37뿦qҗLO t>]ΰz'wqӌLX@@@@@Mێ?C~mn^(M&_t?y-u~&dojWu{ߥaR+;ݚkeA}>UuFi{ӍfǨt6mZs4d MP̞ժռB?t:uiӣէ7圦 fg3OQtizO_EѬĵVpBeez{uVST=*6Zob-3_0U^Q&Gi[pWOsOS`NڗNVkmPYYM͸̱@-ZC)% :1/ݠmeohqz,ǧM/=Rkƚ(~G]yZnUozY /@/ӰQSuUClƼ]u!,R;vi{0{U]Y`mXe k 증zikq6fn{7@@@@@,9K?jI_CuMEv8QK3]nWM_=k=7jǾ-cPтwtPgW[vVΜvd,sgjlXvQM㰓mqcMYxnÚYOߦ:zÙ{ _/лkF9S5]s4?W3δcܦկu Z&~H?㚕IwSm /oe#h^,WǟԦ̰)Ч]-WgUdeh+?'G% ͲZlXRf2~F g(æ1vx;_nhkϐ{өwƻB}@j{ }G_"3p뾡gWkЎa,w@@@@@tA D)f[xɲ]{H?Ɨt_֯] }^s E9]GݝQ/Pu|"mI;SYg8AmĩZ0SѕRSrD[nֆͿ?iN}pgSM 2vw6D=|rg:D]yݹϸ:ڷ+_ Rk~d^Qg7Ugɓ* YN5sʇؐmn@0t\v{-{Bo^S[D{Yj6hM͘lS#Xp{6t »?@@@@@@8Ko(Is.TuVY 㥕_ O?+c27ѱqBP$|k!gύ-Kk]ڻ|$I]=# е_֢CϿVZk?-~-y}#]_2>m,i DEa@"?ƃ}7yo] ? `3vu([Ti@@@@@gZq^Z'iT'Ь6(w]Rx?ZU)&kt@ݨoTQ?H"QDjϞU2VYJϿz)LZ9ZĄӷLw6o^JՙjZRWBLʹ?~JݿG 53]hѺxX[ӜlZ}۴pUW}IsקE0Nad}v{Yz%uV%+H:tm#Co%R~JyNK*?l~wW׷P5jW!*&~:V:s7Yަd6[ƤpnP^s\.X5jZM՚şۏ5a3WtpZ6.^aYξ%x.{Ud]s@@@@@\2_:ZP^۷G6GkhmK8Nr/za54m\ڢ=1Zv6o׉?6{q70 NU 12 i|Z9Zhܴ:evŚW̞d<..J۴TԶmڜK{wO`H mGm11:)ޭ.V@dp׀Ib<1z]Y G3qýLdQKL 4}ʹKX'H]ZВx޳͔u:Oۘj]Z|u|}*~W&\by.x6O9GM @I(Zjepz5ՈAZfVtf9p\!]'+qzo=DXA>*[f%x9qRv.qʻ.Ja      P'tվ9:v(8Mմg%f~NG7V{[y1ydƷhK""     P}y[NԖz衏v$ʹH3[řztPf@@@@@ G+Ey,aSZ+ϷPa󮮂A!     @%x4_%ᘭ~䤟TJ)w7ivJyYWXd@@@@@ Ucd     4,F kqYZ@@@@@@ US     40Q l      @M )iA@@@@@Y\@@@@@@DՔ4       L@T[,.      PSjJ|@@@@@@&@ p@@@@@)Q5%M>      @ V8     Ԕ&@@@@@@ j`+E@@@@@jJ@TMI      D5"     5%@ @@@@@h` gq@@@@@@ US     40Q l      @M )iA@@@@@Y\@@@@@@DՔ4       L@T[,.      PS~5QU䓝cǎ)55UU$i @ RժU+ԁQ@@@@@7]U$TiXA{I& Β@222SԽ{wQ5N6     @u 7$"<݇t2sj {&_O:@@@@@%UO J_E|qk_/Zl?tR﯎) e     @]@=r.\۷+>>^W׮]u5($$l G.; |~6o+B@@@@@fxٲez= HY.]{GGhE됽Db-\!)     P5Zn;եKM>][[EY-;W^yfC-P4UL#?Dt"{ ܭSUf9(    5zl#F׿_~8q|I%$$ȚGu@i& nCUӬYZ7W|OK+g    4|Xmڴixk%F Q|L]saQ&$#%D)V'﬙1_$̍@@@@@z'P#(KjdJq۹sg{\zz=j?I@ Pn k>a4$J>du2-K{G#jt4)I_s@nBagO/mT?OIbQGM*k -[E}Z!!!حCYSN%*6@ x $Nꪱ]4U![oWS_FOT#kA~MRWL!?Q{Rcz ^ŜTMPꬢc*m6:%YU]w@qog_ZNg|      ]jJ9k=ve˖JII믿;#uv&L*,I!@(\.}464S/ܮ/̈hBPg|ӟgZkEGkw4;aLks2.>@@@@@/O<󌢣?QV P%&&jѢEr@C:nf굍ջQYIJJ3'{vi`g*16ϼʊVϐ4WuNꐞwGL_Z΂YC@@@@@UqWo];/2>#@x3ҧ A!Pq-t][CuCND%%P51d~=ڛJZYRN5W=[=.-=S>7<<<})Oolxx x~c>֚)n:1|⬷8C*""B|򉢢쁷vƏQݮ]4b:6lP^<Kb=+?DjmY;RfXXd)7m<Ư.ܻ    4hji-ոqc͘1Dp+duV0߶[F 6=m``222 @qk_/Ztny q};l%Z|^v ʞ]XrO!     @@T鏫~-؁&+鄄(55@w> p X٩ DS7[bS0     @G?~-77W)))jڴ7oN@ʅIZBYA'NUV+#ߕley&Kީi T5-y!    @QutV0zϔ/;;: M% T-,@@@@@@ZQG      ,PWU4ERI      y&P-"     T%=@@@@@@[ZQ< @@@@@@ZQYYY"     4p>'^7@@@@@jiuԩ(+i"     GΣRT@@@@@@HZZDG@@@@@@:%ըQlhui޽[GUVVVuljݺzꥐ("#    W|UeddTu^ZZ֭[C*44TAAA^& P?233uEGGk̘1U?@@@@@hoPVԩSWŋA˨jv&y@@@@@Ыb8 >S%9 6Lv*>!     ʱҬwBr@19 X}<|9C     (ou$[kiZPߠ23k{oPZ,,     [Qn z@@@@@@RZZDTeI 'P>IE9S+Iao{Yt     Դ'?!P4->XszN/%MoDսUϑ|C@@@@@DU30#X|N83fy,Zi3⇝Ѻiy&e@@@@@L}GTnN-ZZr̂V|P=hF{UtN{|/Kӳʞ~4Wkg+*ˤ{λm1ZH;S RAڻ;~0)     %P-r71g~\IKڥj?C~vx%s]x'm6Vs=Wj*6q~Dr,>zTi:gKٹP/JRrb*{ -^6]gdv9Sxӭ۸xY5$'E[ԮS -,rja^f'Gq[خ&hJO@@@@@rTK :&I߭)fvjg8 ( K$wz쫀ꮻʝ"Pk &`/u7kȐ!a|PMh%a=2zr B5xheWO'(< gW4K<<4u[uWd,ByR/_f#l:;+߀|YV\L&G@@@@@Y=cZG1YƷQrju~VF]~|%ɍLjp@,<)ܠ^Z't$Ǟ>3S,8C)Mbm5++`>|wZ\x +B__X@kXeQPG'>&mǜȣ2_$*ÄxFb>.v~fb*hdT >GzE֟[سMiz+0+K:/^>}"0 ZׄK5 z"%g[Ûi5 Q_黭MDh%{T+\`ݎz+4)to$_g} jU]uՎ>ӊ|M0@@@@@Btmz\A/%Ԏ9b\i7iLSZh?CuŽg0̳M|)y< Yjօ=utwѼ&;9S4VO[j\dTș#hn =hK fAov ֨+oЀVYZZ|]?=UXۖRB9wtUX.3-|wOaJ2Af\&/״:`X2Oj7{Q0Ӗkez~ʘ,')e+yStUD6hk΅?     (r"۴ bXgRq1 ;q=~Z;)|h5n|n1]0}u-r\;6H:WgL2 nQ4^g<*$yH$s#cxnmÙj:J2/e1+T4A'=RWVQrcv s9z`v;ygYa%6t -VQ#w_uQdr< {r^Q{QVVթǧOR36CghVxЦ6f~,`H5V;,eU`Dy{1@Q~VfٽSO4-&rwD)T$v6&}-:lFw<[1I#4( AL`(Uf:@@@@@j%Ѵġ UĎEHm,,Xw_[ch%iF>k7tk.ǭ./Uפy׬ Frv\־5O{<=T@.]t멧~!CT*'35czLG}{\!ZI)|8C#{AG9'N5] nt\'}\L[$?4:i4vE/ 6rOo UL_뭵okM$C/ 76Û^af4w^"4ͤis.D+|G)4 휻Ps ^)}yMۘ# iUͲ|L *k7 P/nZgYuoA˚@@@@<0<($EDX-7]|Ή*==<Mܬ,@4EJ7-zqMv~f̘Q)TC=zN<5k֨Y3x%w_}ZϷ7 5y<%Q[DyG3䚗oR̂1e?&r㵳1{\}(<[n cGNv~wo3|TCucVTfjmWx~']&_ʍ~8Zz?+2RW5gt\;?[KZ>W֞tܢhbBG1f=:^0zBwb㬉tSOjȩUz)>Y/n=$_G~>\4g^\MyЃ@(Y4XfWgzw76 3=sp׾4X?}ڙ2S~N/9P3y]ӕ^]OV=gY2˺:)O}fޫ{'=5:p:Cq6qĕgPbYnֱJ~d=?*z巚Q?_-][Y8#,E~jc()ϴ?W\GCoXo#sS+>B`qR907 =Rk?k||sjd;8ޛk{s~xksL97_ռ$g-54kP 8e'n3q5}/ P/cNчv_$>,ܵXdhxrHQKYq[nϻZ?}&wGz5j>^=vcw/=BUk{)|^ZZ%nWp̵%*i{F|½-7s84״7k=o.pWs{܃k zkxuo*:U=*%-ygz 0@ݺuS-Jg.yi2̱n.gPBkuŘMݩmaݺI5~3E;1iw щs ej[ ߝí+6%3HA@5ɖwYv\iPVmuJGXeo4FSr 4;F<)"BtLZ>4в7h NRf1A(ۺliu\)<'”qXeߡIi-E*,㸖W7洝 $]tQkϺW&]dҝO μQ":)>U!n:n.AJ^|c1@}pc}e'\Ӓk7mD弩Ӫ"4}D]sdn+E:外E9qxmU-5ڪͿS׌hAHѤ^@:)<]'3c;anUf;/iֺ0u3'cY/̝ygKΓ̽W$њSs1s BYpڮ=g8RoYM3m ֨j|P6'.~jA^yL ў;uJa<TD9Fҵ}Q{?ho4?g8XǂoBukg3ǧҬx=Qs6e[v.WͤOzyZFwv&f_DL6uI箮2.*r3ZS֯kYq[=Gc>#ϵx|Vy}eȡ{fr]gK%o>+;Ҿ?5ƺ?sS9IQ/ ;B9 aufsoy7fq _'u4kxI@˳O>>΋qVʚ&:ij' W[ _ƫZy*)+w-T,t!}to<7I= ٣sb>X?Yr.Ԛ-4ȕtHWUJ0ua`WAt EXW#-(sэLˌ-(\i6\9B__%T-[iko2kWzx#WՌ?R-uj 3UW\]>ZRG6Re4 :n5m(b^63Yhi)sj[hAz!P?LQWt-\Ol-`%k>S7mOҞMuR}/f?.ڹ'V5ӟ".LJ~YV]O7̜Y$eVsܮfhfWxwhФYd2ύ"ZSX/K~{ +cT"&hŒJ]Я )~(G_jb(\=/s~ǘ'4LGMEڐ1R~P9OcLPI />*/ p%:ҥ{4~nan5G ]< b̍:z9&%hؠjlE'9X]`QzgcmGfXO&]x]Yfcx}as%V {~>)^r(v:x}eg):7jTYjFמÏ{x=1"=0efՠtǥm>r]=fHH_jw\~ľ&r>OPH ٰB&ܵ@~fu&jEl6]mѦ*03RXP2hίnW >XAKڋ2yd}iwxB[ՙVo-t4YW+bmQQ1H+Qg VQ&J.Oθy њ3kՃo/4A(:C~CM-o UyN78W׹As|=+‡MVHe~LbQV꼝[Txon4iN?`swvV_ -tEft[ Ԏlٗ<*$P湦yW˵v3o)̋^iszך;rSPϩ5:5,yon%:wEz.Gz{~@sZt$zrOI?u򞍷1':2_76 eF۟|ld=BGǴzop*vt#ђ @+\ZB-FE-:oe[z+ջ}n_w9oGAr~M͛BKԂ/>ղ-i'?pnsWkp}":mYٞۄ nۦ$G:am6dkGmXiCc7u kfܔ+Te5]}pwYuTU/_vnLll +& 4EG-[,_(WfG*if[4tq]ii囗hfk3޼ژ`=O=Q,{>>ꎿ];f^{'c1ڙgK_;V]s.kU':Sz)E7(zc*ނV5M= c._8O'̉w;EGt5̘Wh22{Z0011O~ ?摰viW_<-G#uk3,slZx=kse.&0"ܬ+}:074?%'詄@oE c~lbuNq@ɂ}NgSO|<kSѤ'g T9ƥT{G`nymJ1yeeV}D o_)jʣ*<~(@5,:9\/mTtFޗ\YcA+]ydRr6.=*2rX랟u1%\'y/_@᳥z^`"u辵 [15^ 6c`3tw؟6KzG,Vڸ|r3ռ՚{XM[ꥧOφqlk I_}jy<_{M :ʓ=2itC;/Mdh1֯?ub?ן^Yoުɷ:!CgIwOs=M0Oڱ6Z)ק& 5{Ag5uݠ$A> /Y-ؼXϥM7~cG16 w>I+j=l?>k[&kʪˬCG{qKOusO};٦o]il=^]Fv[MZ-|s7 GOz J{m~frk.EkOd2ш;?Mpzy~`UV~\ߜGZ3 Wvi<֮-dINN[E #蔶{j9~F)aYD/9I}}sR1֣[=x6 AR"?fi箮kEk] LY{Ofs=)7IQ1({ya6?rD4@M2?sV֏JY%ޜ6G#5.r7 iKX>\yt2LVu+ة3N=a_=;gb:/gj Io2ǫZ}ܤn6D}L(5?R Nxnf 3r[:v|(ܴ`J41/{?sw͛]UJ4!fƩM Zyr4}ΩS+x|g L\0%)и>\#v+bq]vYQ=1j%Y1_[q>"ܔ=y潻zAZ1ygl{&[5lySrM.*%Zˎ1ehn]fc.g\ܹ\k璴n"fkݷΚfH(AXϾf s`PǍJ2ō;e)6v>o~%?KݴWz!߼="E }Eehm;)zQe'ꉀ8Ylos|{[4Ks%7n65SQf?-.sLy˖)Mg^ǏW4-[h=4rvǞ?_f_C{b ZV8r|k=5^1st9 =ͱ-@o'kp }ꙺ'"&wl'EkM3b\松Pkj>VizoEݥfRf/A آ\ p%|m3\[w2_@tJɵ}NfEݠȃnZ_󨣿D5RԂ&EL672@̶zפ13hY!%D-Tq]c=i@92LsM_k oE,^u:ﵞoX]m;»-4__oR=2M!"Px{q?}#\|5̽k,f=.f{|/Z{ˣ^W'l7ڕkx-ZJZ>w]ם{-*sR%]h7>Zi^r(z4~ pgJOhwQ'՛r(= l?D5MT?]0Wp]vef.6Z8=s'ZG8Q~)ىOV#_*[=ܚ&_UeYKil4:dY{v,Z;u#=Uh*K%iRwٟnS yQݾ8Þkˋp]i8^/0W *mGiutm]2O!ٯE?͹Iק2Ҹu29Zܴn2Ǭ1rzl`~Zz\̮VYwUw_ ?3Uo߬/e1-->W`N,\;^~${x.V~cs4V57E^c~-[WLf?RM]ghכ%v8e=` 5h@&y"횔O ~ax!R펴 7T~o6Vӵ}o|ʼ#0rּGC.:hbN=7+lj jl[,|/.Pۄg#4lz\=ʗf?w\^m"~;S^;K7 jG|Ǎ^R]Y_v $m,~.1Q=;X]5i7uM|j~ܢ_ߛu#< Skʮ'Fei]Tڭ›hOʌ~N䭞(VOlcs>Mc܍ #M?TF5Lh4ѥuYL0}#ev(rru~ïHS~hiWݣMݨePxbo)it/i<5|Mkm~~z\xo~C;}/b#W7ן>خ/1L> zXٺVg{cYp*|?m{{_7Л!䛮4ü̻<ݪU4rH5iҤO:k8rtֻ^*2w.2kyIanKYdZ"P˵YzÕۭSP ne~e Uj[^Үwl^V hPV]`n to<"<*@|K,KU 7fEQEݯ^ҷ]MJVX& Whp5߶m[%''ԥXVg_ n(W=i#p> V/X TLb^ gjo5@m]X/K\| @-`^ -_ya0uU.k,W5XYrUzQPڽ:HjYVQ:tNgΘ&%t!!!Pִt      @ս{wCFI@@@@@@#Ш<1        UQ1G@@@@@(r11      @EDUT@@@@@@%@ \LL      PQQcz@@@@@@r *!     TT3gL4 P 쟵N    "\B+A N&j D5o޼VW޽{ս{Z-#PW?ꚡ\ꉺ.rINکMԎ;"P_C˚d9=ڳ?slV@K}ÕsI      P@@@@@@\\|"     T*$1@@@@@@O@@@@@m'NQfN^dRSު..E(-     y&`_N亳^[kV2 f%QO      C rwrXXw\Ȣ*DU&i!     PD`ﱴ"CZY镝jI@T--     TLkU̫.LM ZҡJL>l4:/N6nNMV:[ٙZHKJRRZV2d)~zmIPᡅ&(w1=kY..CJoUWWdT&D"    @u UgGڧ(mѨS4}ؙu+u2_2vm}NY'nY 5pN X Yp]|dԇtݳ Дv⑶3EտI—~Hrz4 DUGz(dtmmn̏j3?_/yAO~i*{Vy膿WUZO.3); MI QsDM~]_6.,&NjR~˖&exBrg]'ݫu 4|sQo8O/\d,-_+ns\-S@V"f\Lӯo?p]<5k떄m?T+te_gGj*$bZv1Ӧ8:JcM {wT]2UN,JWvuՅKRaCiL@*I D+C@w~Ygvhف2m{&uMBу@]>dз{NWmTi++.O*9ӾL%}tONEWim88tAj5  5J͚5+ĉ:piG(Ĺeu{JVm{|K *h(Q0 jI,QBL1_L+&K,1͎]coQcTl`O"FA@7{zgɻ2;۹ݝi6*+JG-PeѰv,r\FZ<dff»WYHZ$=$PY*L7ѥi*&+}HHJh6fF5.tFбUw4K?E+y[&f9Iˠ6^ͅ.D֟4Q):vxT)P">f&+4}AYZC~O0QHP$qdNCy\ >jg1Q}嫘u<@#ƥ2c)ҦbSMǪH<&{yuwqxd5#kRlQaTe`?}!]E1 qc5918-<8*~Am7; *勀_rp$5>|ϒ-vn"G# ߅hSk7xFVwfL<_XGIȐ}S_6Mc~YwZF Fc Ljk ?③ߊLިBɧ=oOJUvlDz^acv~dTٍ*q0s'C6`") \&ْ_$g6 OJw;1".sf7kzoAȵuXvs3B)%Wqjnk@~#.޵hRYz/ z:I8Gtr16p!yT{z^Y)Ruuų=#*%%󃛛"My?\Î hRb g?Q蓙 h2{w ='Āߣ=)EKdR4 ңP?.F)>szS@F n:u $ &o+mlr!!8.^6&I;ڎ¢i]RzԀNHm |L~svVsP/9!A:FuP.Csp86?Rg?GHPoO Iio㗅_Q;7'qY/qh3|z]=8lR5m {hH.V"w5e*fUڴ&V/ SbW1)=#e{mA:He' \Cu~+$/#`/;H Cqd>hQHUw YFFUS=S2_N5(xd)XI;:e$_T)ۤoo[?5Uz:C[K,Z7dBݸllyQw$}g+H lU|fM[V.4eI>?t|MwPB;Rnj_ԯ4|-}p0@.m~\׏ݏf]kFO 6Dm֌h2r`EːLfjoe˩zYdʿM٢T5+!p|jS{H>h 8C4rNY j5'u~YIˉ_W0}~es5ԧk+q0ْKA두p#hO2/h^%%%!m.q`9E«<Ȫm}EPPQJ>é4&ݒcWHDh7B*ᎦELL,,).:.-Ӌ, VMs3UDeggK' &T"o~~6zQ|ξejN(tfTrn(U i8J2@\WvCtCS5cطHشSwIU9x\F/PڍrH[UhQyk_bj8|/M)*%mČKbqtsbz51s&,[GvbIu %1 xAr㡡\~cN= G&ҧ5ƇDzAʗ>V/%vtŨޓMBm)_ɭ'S(Pވgf-67㖢mb:2/ÚǠz6rb͚w[ϑH{H%2;:eDۆeQa0DL9aisVէm\q0Ӗ8xNDHi#މǐz+l,- qU{zW \b8fL3cc8%=}44vQcmWHZ1u39\ gDp}KrWz6 ᨣq×J7&X/{u<<06}?#EXKH}&mJmvl{p@gaѷS4*ίVsqdJq3HMڦh{sӘhŠcNnqYQO)i;|4f +7z|XGI'eWY<ؒ-BRQx,H;…zQ鉴PiGJҹ]c%%1c_֧ys {WXNg[-媵s?bͲhO Uj~\4Wk,?Cqfxga %9x0;>k&k[~l+6Ml[/%*`{ܟX}Np}bOSE@RvKD~1$kE:6g;[7}?ĤWnL+{Fi@ 7n® Ju߃;i>%|*Wf #G-g&hI HK$ #4-x`E3=[ckڨvn]#?FpqT -7"Iիc7‰8@ /r3nWp[~cPnQ(Sw)r#R;:CqIىx 9ŇWJU\r!ٝ'&n_jKhDƛE&Y Mr^E;Eh.nm7$8+T{E/9^!FRV@u Â;P#,*M}UaT <<3W{.8a;,#a'tfY)yX],e@&{yRI|X?,U(-em%^ 㭹'M ,/63Y}pxڌe* -܆K K_%]`)J;z"7$Z Ƅ6UѦX.7bB@|T"Ň[Xœë_65^U%mW큞kP +h'iY? )UZO0k:5+"nBLT!D?Oj|WA ugX _?8"}I1?t@ʣáB咺 8pHV4EV\Ԅ?L)q#L&v~K.hwq/4 ]E"^Zh\qP B|gPڰ_3")EygȊo:{J@L!+T)qbOaD <Mb.mQkԞX^!MKJc_>Yco2k~VBl1j96 'Sa:?sg=$) 9F\t~ .o).](}ҙ26Gtcü&Q4B6X~ W%%#eg,ODb%q*ccK8GڢtB$s|w*z }&xŐ:#mʈ?R_۾Kt%[YrNi$($k?L|Ѫc?&r@Ҏ*^b’I1=78RȏnrGSE?GˈIRBqcסɷqHq?P"늙_g`2y>(&s|YrۤAۈPt/Ig?ܖ&PBAL)BVbrE\G.|nhyrT E06/w;͕Q[IR^IhKh#C |k3H9VP=u =K,ʋ 'suD5A]P3ޛ]?r78݇6{SyCCÔobfVSR ;JVAU5͐cqs?xW+o!\ҟXqCdRI'z YUWFk0&SC8@+!ӼF [!Fe!Nj;YbDnm\dKʕ Rk6eBR!;n_:>R,(g'1$ +B.SBn+iÜ12QiKj.`{+wbijn^@$ B ŖPy . Hxar,_}9q=-w.(UihVYMv$HAT EgR~Ec⑫@=O/*$E :xW.\%$E?/ )ܿ%!j׬*tMwP(_<܊;>aM歿S_7d}-|"Wm˳N3n\oz (w:Ia4JCި,wc҆}t=:G\POlZ}β58yP"98EvF C%l;WDԙy<+8bЂ- 7nƦ9wPƸp#qSTz=gEe$`h|BqDp:n'5¶D.4SiGI8*/?}jK vF.L<tƲC!ڽy2m A WgMFlAr g,>htπq#=;]D0TuGTTp=5zH.=O H]Mg](ڸcB $SkbSG\Ӈ@YX0\ÖO3i049 #Avا3<ʐ2~+t cc 'U2\Ǣm$WQC"+! #!A(KH14&o׌z?qI?$yQ BWX'ןKr]`bcz9$䶏wYӂ^hLUI&bӸ_8m8P@>n=nΙثie_Q$%1܏N 2,#Cޭ"r 4D)gNԲ,(v,/tiOAJ(/>nRu<Qװ'AV5ݥH`t-3KФxܯl16b;so)g2ei&테l[V& T-ɫ*(%}6r#P4@ɘNngF=1/C Ɗ1RRi\ȬC•H÷Q4ӎ.)вpoT'N愀Äo9ogGD iT6nkǠ!o2E~{hFWqs> 2"7@Uᄶ[8jEF^SM[ű|ZjC%i+Y(}% w WFUƸ~W0THw§t9^l >$ׁv_c5kǥݠh2M-lhbL@cqori=`,ڑuӤU^5z/EAYcķo(dEޓYK175ƒޖ0gop30j#>,܀[Y2䠤KvEVh4k_eHl,I?>"Zn cG&CG 'a[Lj_:$L=QDo.Frg4x3^^)qw~Pol&m(?,| z+&42[йSLF/[4_Hʼ\ڤ_AuBWl p"/Yg¬_{lr/@ ϋ!Y`ˠ%~X$2R!&n/_ؔ-|FZ].Ռi;"dq8+vk_k-HƎxBz6ÎoBWTM)ٸ)ιÄи 9Bk©B#:G 5S7MyYyY aF^.:'G0NXQD>@@(isBᅭՐdOW.rp*dj*\NXF#EYPYR.߯8^U 79)^`Wii,KEM )Jgԥ8CO<:|)':Ki*, l8<(TS݋`+&ZmrG%Fq&?dP?W.b6θK]$c+c4dJ~Hɔ?ʄ # \LS tXN|Q^WŎc"ӼMnm>K "E{޿F&]"{5J> )lѯmJOŀް:B\Ԕv */!ͱ8BPe1IP?@Hr9TC{E}Хi T,u'O7 *9#Z+KF|}x+[FRrx<3~]N 4p$7tYή2F߄6䎚!Azyy!%%B$a4G}fc8[PB Y aQszoYN՜feL+mwJ(QNώ:B~ ꙴ{Hq)]:@ӎ*9 #8gC}M>mWU(m2rgU<ҩ^P0(YI?|Tu$)R8ITOz#'q.] Gwz#b~&,UBR(]V%f @aWF=ƒ_q]]Q)K 3wia9tvvP>>>(TH6+  Avd^(.O@ڈ ǗOI z;2n F&Ϣ N|${H`};L?<1/h&\Q#2zt˒_Bϖrj)FSp4kÞϒEԃ@Ӛ,jǑ=X/)[|NsgDWJ dK<8j~&~e!̲Ehz'zFP,UXQeF0E@U#)OѽSY90#0## AT罕>Fx+`F`F`F`F`F9A@q;S$?'fh8h 2#0#0#0#0#0S&/0׬z>7`F`F`F`F`F#rIG_ RCY"/HKf"yF`F`F`F`F`:y°؃RO Of`F`F`F`F`FxA(#!Q/2#0#0#0#0#0#`EcaF`F`F`F`F`FECyiቃ 0Vaf,'tP = ٌ@>!2$d2 ˑhz i]y4-Q@!`(-o`<!47a?\#l#r~~{~ON=N<;ϊ9eFX-0#0#0#0#0#0#"*_db#0#0#0#0#0#0ZXEF`F`F`F`F`F`VD+L`F`F`F`F`F`F@@A2#0#0#0#0#0Aks\M{YM%L.|ݜѠb_zs #0#0#0#0#0/0B }2X 9<v{X?bn #0#0#0#0#0D ҵ'Y=m=Spt~"Di1#0#0#0#0#0&$1ۧ6{X?bn #0#0#0#0#0#"gu"hYeW]YaѺ*h]Bx:LGJJ*2L!/=D1ZҩSR򉖖?eF`F`F`Fx|U<5s.K%G7k2{gqt(5a!h}OQBꇡe'*i-u^oƠC˪3_ Q>v$Bb"2H3Jpzl?dEO䚋?d$`ÒMuwPMm>Jjq&mN=$I!Yqgؽr GEP]Kgx_x[3x=m=|)wpD"z Y qdwز/}BQQet˿Iqxn$n -jy2%ې@ͦMfaUJ,вRh`##th.S٫H;֩ jbp FN꣝d-X$ms ,H&zx<) Ҙfh(F?"s'l>} H:L2d!~h p ' HHÅ lĉ]q6Ma &vye&cZѡun;F`Fx1xQwA\\֭[~&lZNĮ8u6_nFkBZ -e_Kdz~O!}yz4_lI|x^#8BG7y8rJB /e -h;v=1j ~,^Eb1MIL$C{-݇kѣ/m?fEGS9g 㭷1lY;5I铃ڕԇC~Z]s-ՇKޓ8~j>vVϣX}\T 𱓱j*$~3o}%jՂiҢx"zi;¸~S>ZQ+K'Ft{OdlBYc漙d agB%tq7|Çc2ar5^n`YfR[Btn6"ðI3:4LAm#~Cu@œG }׮'@5+urۺGaߴ㤤$CC[|Eя3If7IOż3d,'9tv7|61^[ {ޡ8:s1!~|l!Xuq~I_܋xӆ@P7/&eLKe4D#vEިSCrşFЌ<:/\Q\]]-Vt-yVj1E: hF0+. ذ4Eߕ%l[būPʂVVdjeu>}*JWUdVPi*ěRAs-7HKF ?̄dwʣLdy' ˤr%< o|"ₓ(+V#MT;gp߂b4i 7|5\GfvDv0{f+,_ ˿1M77j)*f6$l3M&Z]&~p "\awBl- ߞDذץvrG.x'Ez !,ϦAqpp:_y9=Dȳ'mBIT$ې?m;8[l%o`(_C14>2*c8!|. & 6"ާvW۾dWsG'CVX5T$ϼаP"[)5t :/J(A^;G7^wq5YWlhTeTI0' §nA@*Hb%$mYM`Ѧ.:6Wu$IK> ? CR3m> n<5)#sN8IL-o ~IBXD|?:>b|n4#"w8 *i uXk"3gXk$Am :n UdJF/"jm;>e%˼LkFa8v}V^iF%;q꺳ŋ|JrJHA|J.DEB9HG}(':9G==PB O'ju5nnn4GpIj(7zL4o/W!Gw~\&Wɘ5w垜rnjrDJ "~JLXWƲIK{h%]U}5$P᫉}ZG%o?Ohʏ]qz|Cޢׯڡ4cv_6F"1@VGK6Em*'Lv@V 1vſiKÆae[MY+^4)׺omLLrÕ _+ 煂J*XW-:vJu,(y3?Uj^Ւ1h9/W֢/l яx⤜/v*ni;$~+f?!11cCekv[H:G/$}1,lY\x,?3¶=՟](&oa,V%V[G4rcC@x |U3I %!䋆- -h~tA0<ԗju3חRo%CtdnоwKg|N]8hU>oм>祲ˆ{"bm[]bf׿>=I~tPdžbfVM4}7g芜X)}˷YT62ٸT-qOٸ!`,LQ(Yv[w|PRB>Aa$ʑ)6 \ x> dMIq*Z RB,O9P?j@'ane,1L@c Px2\VƐpK7JRR UFj6CM3l6"f Ԩu üdUIeg)6cz[+!eFjO_4"r(1Ռ4y+U ,,) It2DE«<8UZ% dN%!6閔'6*N]~p%DXǶ#Rf(-Z/Z MM)a/;%sṈ#0M *&>?o^"Ў#DQߗO ZȲDPR'{ʕIwdMj5}ߠ8nؗZ8KqA)~wUn_E)73D'Ta_uux5?GB1t@$wĂx[1膭STW!beIaHm?cIVb. ۽{bhPaJ%M8nhJ;i\q$k8ƼbZO]=_h&5Y3)?x4UFKJKnqĤ]1b!: ITP9CI ʡƗ IڵfO;a[?*3T{v>c{d,9P&b1Y@EƤIfC"}.ZĂISQ ]#Gf3qå ƱYȕ_~1ѭM5 9+ k1/֌sum3~E+vDDFDQS[Gs>:69 zx4RBTkr#ʹfltD܋&,څQ xgQJLAm>u+z޳ѡU"7=/*ٚf@wøf{۠JsE䖾9$bSxTr) I[ռ= '˭d FNE2m_Q-IaÙGdC1XTuG+zVIVԞve){tLa`ۤ,Ln6LQ%SZ+L\n>Lc/5xFoiLH5)YŧX_\6ڌ6&[>*4DYFMPyti'6#'ҎJ+M\+n6Y%OeUz6odee+tnnP腠UEka jO[Y{NC2WU#kb{|w|JY% @^ЈlrUqs$D$EaZ (EVHf+ڿJ9VVz*l9;eaTU^A6E" T4A$@X'ʚs5k^X|ʔ/2i02rwCއis!g8,51o{MVYP IǶOIREp'l%-iW j;?lEv%vӥ-U/,`t Ey]D-дWMk0+ "c#l sbTs'O4=A1KEyCLJ>qnt-k@P(].eㆦ*.oī6 8_GwP;r C!  TKX).NpZA*VB%O8Qyu:ܗ&tМKl!knGAJs`D>YڝNTj7_>.;G`AEDZP8 yuԗT. _m)BQYIutqvkoFJdd @z*Ry~*y#a|坅Go%ݭ]8HPn+'.(ě(mYT,O&Fw[ h!z p׺Ae+( ߨCWnP|\"`4k_LB z[`H({UZ-4RnBp:8r~0w'A#r"Zt.xErAPXA}$L6xR->daU~"`=zֳ@>1fDdYmm@Pq5Wun誳*x oYI~,("3CjB;7ʐ#0hS9h?fwhg-S3FF;y!]NK:5mD_R wcST݇ϧkwԥ,cA  c屄Ed8x2 C4!O)XU$wɚ:iF.gE9훩v .)loƚO">~}`.[{"ݍY5 :x%b眆mϿ#p\ ^EtCqxL'96._0cUѝ =[~-5PU"I{*& Gu*R|8}NC"Fն0^U{&jwAxn9k…d.a:7cb|Ԡ/ OxMM_*Zקv4QJRވ"u*Wț:h'MD ܨrJ;ĸMCPxZ?}}$M il+4bh0LEqqdH [uZsoPiy2SY6-̦mYG.hs a+$9LH&ݞ;ow9QͤG H O}Pd -Z{ 2n޻8L/gϱvkqS}Eϱigu*Ci̥k|C{}.VzVڧ ]gB sy¬Yx7^|tܯ> Lx||0w5`k>7%w oωۺB>t~^DL ߦ1Wp!J?aӕC?hGey"J?4" 7yYbQ %,ı#eH1'ÉV=aw4w}q01avnC{[_>CfbUyV&Y^3re`wзP[MՒ6Yok~xX\x?\ rS:BF(KXM>y9:Q>4&b"KAUI5u]xzDYWw|N; <]u)Ҋ ^]иLMQ;P\J T|Eޔ[T8o~m2=!899=Kt\b.EpXţ͈LGk$%B%V)/Y+hJϺ/PR=rFpHc $ܪմh+cH\ #%TȠŘբEebu7iq&t-3 IZ|LVP}M2!-A[йV9$kzC\DdUhSN>\uB&wՅh+ΘP?Tjxur&UCZ2 j1eI*:pj䂻!MSKBӔ :[#74GhѲfksro(U3JAF)L֎ gm#5 "*S+t'R&_䲸_|0#aq̛[1s4atEav-#}x+4NIHXaX7ϑ- 2'q $,!l*2qdD\.߀]Ø Y E.% $ˑdR-P-zCs { Hj#742`Vtl8ROlB7%HrJm uB B ŖPR}d 'ת]ž¸ND-]P#)y:Βao'O":֔N EIBk_kT{:&jV_b-*7cxɔ{,Kp,Dk{T:<Aj{A B2XJOC -jCzr"FOU8|$) l( T& B`Ol >>)DZ`U8+aI;0f^d7 UΞ#}N1x!,& ?,LJ.3*f.) #+#\IZ\%')'EzϿaƐGD*6cm;0#w0=uaG\҈wRtWltfC\T\{O@Tj1#3zaNK{aخBҧ@g.KtN5鬥>|L9yfg.0*Y}#V34,E y1a*YO_wJ ==>0-EI²$hj:3ڑ_%5v [$\.]toJ2fޯ/K$HLIưn㠾AcGr}' 'pK;a3Mx$~T-Χ)gwیPkn`30wIr_O6Uf&cf߾ؙ^l}>"K/pO;9}FEӷ"Y#Zy֩rʋp'ʋ=%e5%6ߋ p39 _=Go!s_r)x\wUH %G"xB9J$L'oi"@J(hstqb^&׼ˠV EE4H磭Ӏ?WeU0]4]˹K:wS(=ƧvQ.EH%LGA}˛z"o JQt>Ld*Uư6R.qG9L]%erIwqSPO ܡ8V#NŮxyذ[EQBQ>0 Z#=|PUwI(%g]]*N']LYZeA|<ۅÑhL!|br@ĽYKX%Z)ޟ+}YݪMǬ~EnOgD%¹h*Hff}۵I)ANKŔ"Ey NkYk3Tdj4i/¨.֣D3Z$iH6q%mӶwp>%WwB>c@ 4ռtA(o:"M%ZHnT +79ao?zECNs⚂lL"G:TZlo"{4oi mb&"6߹/L}g3N2˘)롃z~ZEM}Zj&E &DW"gyVdw')nϕGL3"ĘXQf#%7V2)91K|jw$}ڳdxQO ߵo=jT_+h_ǩ~|'r,t'g@IDATZd& q-.|b<һWHi9/uķsߥq)>;oNvkcnu,a3T?/"OJƶmúI W{NUѽIrGNB?p8I^E@Pyay^h:m-śŏY0?Bf.uܡwMl P?HƮ#G*mӏK=}Qbq&-/پRE:ޒy,iT7cժMx[s 739ׂa ۲ ZW[Ig$QqIIWp7|ǁX7ιKElOKV@.rp*dj*\NV$G$_JtoĽ+8+E+kU`z=%^ƾXY]pE7̹酪+C^7rW=ɼa̹ŊT!r6W_T,LI'+! ],"줺9HUH.@Cģ JʙxНe p J't\de@e #y礙[N/ʋ`H3" Tتs l-rtNM b!Eb2ץZӶ' ~geub~KmT'>.dA|Nț ~HvƐeJJG|V^YDYO}QFN^>;aG+)3 }&laz!ؒS9 7 fu들+qdape:& C,3KӱfV'ZiR mLj' 9Hmt{P6iƴjY7J1X.U:{O!:| } #H=uԁ5Q%L% H1Ei+bR0mV?Eg&@xTEHBI HJ" HޤISA|WޔRDl(H@By^K HBH#n~g& g'ٽsgΜݲ3sfK/4D_oHiFy{/]Cny;=UϤ>oInRɴ|rmB |J0ӼKQQrMDۻdU=b~icwj"tjZe=^H{Djyܘ%bY.&dI ƳafSUTJ04 {?qNi ,. 2dzl#o\qhQ%ұJP hP!F(6]1ZlRZ2,y^%7ЈHIQk#Xga[)ay\3l` F\dTgKD:kaI.Ǖpg6/ϊL+6g=d*{%ϸDG}NL(&Gg#NZxF(q%lrRYĺd)O%Rlj r'o&PdrYvy1^AreV)jdiu0eVPB\^.ʰے8}[obo m3xxl2K\Y- sU4Քߎ;-Rc yT.*! ;> ZUw]MnHObR96 xzzS=/ԢYiLL銨GAXcccq>mi!Jp*SF^Vh!YTn,f %HO#ֳJξ {9È.B墘@~O(gN>ɉP;Dɹb՛wk6Ki~wH~P,>2OS|EI҄#%j筮#7Qyj꜎cc=rG{1u@Ztѵ\ W gv&O^$U"LE\ie.k!72GWui;d/s%f "@jAL'B5jHX,@%c%PQ7/ nL 0&@&qX)W D49L 0UbEث)JMPDžhˋeI *ʧz|LEjzL9`L 0&`L 0&`L 0D vvH>ęW}JP幪O96D=gL 0&`L 0&`L 0g"9K)޳̓kt{em`L 0&`L 0&`L 0&KR  1`L 0&`L 0&`%[\b[w enϒblz&ׅ 0&`L 0&`L 0&f*;X!M-/PU&`L 0&`L 0&(jWA VF;uxY NEL Рt #W 0&`L 0&`L 0&@(ǁ 4"]q/`L 0&`L 0&`L 0&  4`L 0&`L 0&`L 0F@\ݿ9+ fLHFOl)(C $a%GJŷQu ^)T(&o  !5`L!,<*mmoq"x\ւ 0&`L 0&`L 0&Qh$%`ϋ.r 1&`L 0&`L 0&x_Q=N6{ rs$ɲ`L 0&`L 0&`L d!p-N% @̽]%6D'cL 0&`L 0&`L 0&WNlz-4U@VÉ8D߸ػl, hԷR%ȢrJ  ZSx&Pt_-ʉ+r `L 0&`L 0g@gyF|4FP9_thVNBڣ+$Ay/"79_9w6Rcrٹi@!M :=Xr[ X:y;Oc5YQE_8*Z{CpX>_E0&Pp׭/ Q_@V!=|ޭ g&5(85=TQpnH,E?Ǔʹ`LЍ?Q7=+B[vjtq6, 1q.< Fko#[ƮC5y&_;PjOy]!x6%o|nմ;1@߿6$BB#CZ-^`XEsagUH?뚖=Y_*%$ۊ aY)èNEX]_uE">%g?e@],C&`LX0aB… v-jժN:(_>hFaEyX<5TƂuW) #TxR҇i1CF_G~i ߔ\u,Ѵ@9;x"2c/tsVD tET>6|[*Y dtĖ#sXms/ -y˩ @I`L"@+k#JwއB/WOVn! jo|0($ OCCݣ[`G0?.[b~T?绌F=7!@܁V}"ћ,:4lB%+ݝ[Q}r2DYBK8."d1U!l,h :֯8}=~˹{[;+9@.^qpqqX$ܼy"m`6" [f8&\E"'07mYhM&iVtiH :e,{+(Hb#b4 -<9:Ib3:TP5YUoBo9;G70{w}jl,Ϝd=شKL# 1ʤd8i(B~ 1ЧZ3+>d1bfҠIswdr_} r_mY^׈rdli9 غO l`oN" M>cme9MC6&Cm|y2@O'e\Ч.⛿5F>>iG8:RY/SlT8s+쎝pxХеIgc1p:\ؐ<6tZ" =!;Gb|'{msUM$?{&vn/obࢭ8^0cQqwX; f a}qd_2~~,ꖷEY E qF)pj!y`M@Wcz"u|>gUpęswT#{Xy4(E92D=Moe|&ǩ.D1|ZCǔKV5\.s@ Qr;\SGHRT[.fOiDڂ2P>/Xzmfo2< CgvmEq ~PV)P|ǹ?}Ns]ـ?lϟq\=C3;L؟^KXfwTRrQ  u|,YAA-}(C?BÑ{  ۀAAhѢsAb׾l}xgK}K{7崟u[ Po1QH W%{-Qwل-x 7 3%,?0&(#>%KztqJoC#IX?Z/O{`ƍRz'En+f\[ɕ?BOz{IE\6a*hna۔6ĨZ2`FD5j"=[ !ˠs/>@\`KF舟Õ=MEe.""@nh4 VߗМBa(xFaNýd~?~xϏ2_ųkRidt(=o7e~UڣQY*g^ .EoL_ 5`Li' z:wJLơc-U;/e}9ZjIk=8nCס>`PubiT^2B;[rYU(6d%o3#9 C۫:R;:xqF7&#^2+ Cmm}_tF/*{#8j쇚)lHM} L~py(UE?yAI# VBP+W""&PʕwyGx7+ť\˔DQ& <:IJ?JaVsY8É\ʕE5x{]HD.PB9jCYrXӛ"5DI'5VqNDڂPR1Yl(Ѳv%j!VBKWmøJAQc7p9[vf9OF `WaRc+>4 ~FHV[8+ VO}ԎwHm f.2Ɇ uB70cbrE?)`ﰉYl,Ϩع{Yau^V'FOs0oSAFX~6-/G57%cVO]ayʝa06ld`둘ڧ~uu@xٚV@dNƨ.3 yE0$jLX)5Z]/a8}K##f]Ǥ>boi4p'VL橽Ukbul: Ey~=#+栽{V y:(.K(@݌㋐]ظ/-tcaT>0if0D2~-?"]>FOb^z;BB`` ˆ{߻aMHIeZ&OhSA .Fn\ִ~AĔ¿j:j~=Xw=8uEѡq|,YpI[:eLOP߼uIwz2 Z| ႘`L ,h4lسma te`sr&2d[q /ec$űeH1 6m:dW<|j4Ogq˧+J*(y[=k^3c֞Ly'(TyAq/!'`_~^`EzUt5I%ILۨ *bE.vS [KfR~ЕGM?dn0V:mĊp?~/& wnJ+d2,x~X g(R vێ;R܃1a+卍;Stt}2/+ [-yF nѹ!u, _%B͞n '菴l%d744b8q++Z*~R;oZE~-\.Z E #`nH:B!}OIۇ\T 6!ww(N fPØ1~IM62*NZ]%(q<~} ~Or_;!M Foui>e2;M/ǐV_0H2VqD́7v{bSF)3jʧ{! *l)h$Rs` g|Xmr6'(y~Nr/LZcblhJ,EzvR4?x-=CҾ~1itr[琞ӱ~}E9>I%5u4ꠥ?(jݸ|&0x|6~-mį>YclFz75jr?ƧX eL,7đc:Sho/߃g_M{A}jpx[W߄=ɱ*>k!Ί c%y3a)yomJ O;{aH"}qDl ~ 筤>MzMtdRnQ&*~ oߖznFX%E<>H@070i_GW->畚4m3Ѫѷ3-*a^'FwGU4-`*]ܓE-\ܷzbfKw$*#eDt|1ieTc?T'!`$FjӪL] w10M)ZnUMiZf?COT<.Qeukb`^FYI[1#P([+rKڬymrNut?y (=>! !='rhSCsx,a'ssY)&?]9sЦd-/$#hé$.>"CP ܉ɦyQ6z~X3;V1:tRP_æY1$.(3JU]Ggdz .\VCDrsXVߢߩlesUC2jA<{$NblnCQWr5i:lrex֒q$x:̓B8"7 >GAM;h5[")y~muB;Z?%qDϢ1z/:럵7c\~nw=xD8fmm<iU͆4ِ`LK(Y F'2f!b̆<>Ubdi0Dű pUƾ#NKMgdر/%Ǖ %#T*|2P >xf~ @2B٧kȕ=Ge##5Ҿ5|܌F'gu "#=-Jѐ? u9vt?G4b^bʔh2}VwI5IbUI`bHuݭXU:XѷSrtjZ14SZ}%%ŊiXV8VPYza2T怭3ԖO~C0-7遑tW [q"% * Ux aL!7e*W6IݚN[vm&x(90A {P$-¼]Pj۔4gTǫMbU?'@4^63[sj:IV@WQΗ(1]2*}9̧~4?@+b:k+N`cYȊt2c2P:ܝb` $!#EN!MΟawo v+s?\V_%8Zzg\HDaZa"u/QoOU:$fq_#@1da֔ߐ~j9+)@. >,a]xqW)R^=tXL9<ܫ4D=m1/^?Nj(=?ӳgmOhSh!ջI"TfL 0@UZ;C&mV'S*AmFomq Bsjό&q86 !ВX?:G`ʯe`;F!*.QHCFCa(}}3X9\e&,"v,Wr?gڡ5L@{~1 mI(\8 3jKUgK}ճ00K!Yik^L"}/qzn,GE0DPm͑Wbrz9W@ʕ!|MSx  @bL4>-} v;o]X o4AMkj%xvX`%E4>OkO" }.)Mk(WS5l9swI5{*@{DQ cA})X|*S I} #Upt5`F5iX"4\C,>Pai"?4>nBpOčsU[o` @{Ǐvar3jJ 5+&jf6<)F$׆1ZE{R))C2@|m8N߼KbPTyo,ޞiq`K@4 .4-CTml"WZCqp%hsCW?A%cex:? QE5Q cň!>賓=,( wewvoc+{(t\*{`<MOpKbFV#3u7Sy|!t6.ɢ?#xa(iғfm|EX6 ݀ ƙ vLs-wKJ2V\:Wp[fLG Q6onoMƺ`tJN Uo@0”a$ocr}{A~{%M?Ğ$-DS:d?ɬ$1S!u}?A+cٮtLwifCM0z)gyz;oQIM#:to]Gۤ資ne 4rb3 EF73\јV@.z@/99`L <~W|{xgGj TmhB2[H9 Ҳ~fo&"!:Jd\xFi ŽS#N@i?}"aLt=%}YB,ނKܗHo4;fBbXvKJPb鞙vχh[|0e6 1u7o'̔i&k#rkOumZ7{ ~Cd)pCzK?Ū3|SKT"^80 LGii=D ,.r*UWrOHjKωfz? dK!AoG[8'Eeu5jWKMVW=L#{*#]/Wqq*C3;6 )偪nM%IUhS3yl\8 E2ԥ$1j^]yxˊa RG`` *T`Sdu6N^v ժICކL}םJfL)ܜf>h'-|> )R&!-' Jߧ5Qg,)Fֲjc䟟`AOh<}M D:sfO/_I-)N3z4 GvtsU\Hj[SN;[H[9K|}OdAg~X~4mFq^~mXIy  >y\8_{hsLi'Pn k:qp7@Py!LL׋luR]{[r& w+"ӖyyE ,rF(hET9)$҂Q/1QIA){*h'k7"osc1W T"u^Ei>;]ݗYޓ)"Ř50Ѕ&6Υs";K/4D_J|`dfƮŭʒp dwťxɷ42K'?f=e }EjT,ᆹdM7Uh/*J{WY0U_y&8BO R7T*I,s"H[,}F(rm1}iFbqY a%8Z5*YHF!-o: ެ}&0Qƺ4?Zgd%E&Ru,iҦ F-_JE:KFLݟPy (&ޯs{}/ǭcL 0&mOWu?5A2y-0y'hdYe+;}s&7Tu7 w|^K*o89 yVg&ۡdR?tQvёM(PxM֗Hnq4Ϋi&gwi`iH"16pu>)TL"M-: {HThIFҏdNLf.O%>ҡ Z##Bz:]Rere#: Pr&_銨GATҞ1HIII2BLyYń&@$LDx Kd}Ю `—1ct7مDP`V<{ +'"{&3=L^V(4)4Ż ;3AMʬ]!s-Qêj꜎cc=CG: -:L΅+v3;S`u|yp a&!)$W)=𺠲wYrS"ʔFlJ̥9CZjI+1&Pr8^7s |`L 0&`L 8VEI\<`LxUb% R*!)B}J+qBtED"mV٨5_$U$5B`L 0&`L 0&`L wH>d1LH 6j\A+!ӂ@a=B1D/_>`< 7aA e0'WX}RXrxzk#~ǫ:1GU1E}E"φA/vg L 0&`L 0&`L 0&`J Q1&`L 0&`L 0&`L !@?`L 0&`L 0&`L 0&' cL 0&`L 0&`L 0&0(mŸL 0&`L 0&`L 0&@v2\XCƤ)#v>(x[پi[*jQn;3^B_Ԫ8z.) i^|peħ$9~&c[uŷ;R}ѩG?7Yuc&P0;o6[o@\9l=oB2Ew <>m{. M{qx/{⭷>-ۭm7Ѐ޵/S6`L oε)kENL|/}5YTpt"󓻓q,႙U<}.$BWUwՔu"ҫdS}=8|(u ;Cp_m F]xhc8 T}dsN9q۳̭n1@IDATޘ&;;Fa/L0w{ǷcxcX$_!c;[[z\AK{+uMF"ZjEs#РJ܀lj 4ˉW#a3†l90 ( &jQ;:|,bS9]%/tCXD>"ͦsApw6~ÅHHif5id)͛7!6h@|g0JLD,j"sn$#chdF|x-JѹRYNyzVD7ǟLh48:Z|5hIVf7:OC&N R>qgwRgYndچ4@28@zjBiHAK:jzd|cl2D6#TJgRJgȟ|cCY hciF+ $H4^jy@28bNρgq$ u`Ň %FL=?xMu_ywT㜽/`i2]ĀVO*2Bul;ZWVCp4ax N 2 ž-70}Lch6Qц.Vv?3X@&s1Pq,mџUs&7; _4GӶc\{>kJ:SB~*1;DdCҳ6[ 7rw_?nCFlM ^E-wT  }qqmٷxo!Rc!w\NBP-hzlO& C_+ؓ~\90&M=r:?l,ͫf 3v% G!*hPFXZ6:98ǩʅ2RF=j"}2D\nQoFݺu7gOiDwp^LH.&}ݖ`3uNC'tw⟤ 9}evOSĹ!)}f[WG&DR$$]:>%&PR0kSn͛wPÏlތ ͛I;O/a^39#~&0d~*hMi/ω~F|=7o>D'uK.(,'$=J$wyh4k]JRV48, @_`˝;=evI"6yV>*;Dy:B4\O>xl=oC4к#+4I7GS;D 5ek)'CpmdHC~M!\}}0͟ʒʴHǁ 8r]̷qq)2RJh($?J!}_F*OOwtiAn>I:OiO"*"5DK'@ ! H!%g ]}a£bi-n=|X#ےіԨz:Jtp@@Rr,ьHJLe䇏$.݋/#ccO+Fy:x\ܔuL!gO'4w+C~ ^Ǿfvo$o æ3Q*Y}L.zD|orG^}g@7cTN0f'G{cEp0> *ٖ?Ƒ۫¨{cvߠ1Q$6 M膅Ͱ$x~\罇i`nhjC<?'U>){pH+Яa,t2B&SS~*wAO,h.? QT$7W@i\eZ/;*hL gUKلPf/nNF8~xkroVEJH7h08sۺ {tD'7ye4c-E)Ȁh_uێ>H{m&3C3~G}hUd؅u+Íҳ!iN||r%Ms4UߎE/^ykƻ.jp~[(^S|pPm.~ æ*R<T7~SJx^hvSB\+Of8>B^S5ȸ%ҬȏFo 9VHtE+D ?νwϽdκ*?H+O7cаg3S΋4^ױ+0mghS_@: ߏ ^tԯєQ+_t˵2M@{(hE@]8l%=;kMV5~3} 9' Vlh}DŤMW7d~W^+}V~KWEo,̞9'2;j=&x} 'j6Txc5dSl w#1׊{RNjpyKǥaw|=h*ɠ6f +KhмmGP4W-^unZ2h_=Hg45P.@,͢UTbwe I&v *6 : Hǟ5J򩎏.citH}?m]רƼ}Z74ʜkȬ5/EM@^TDZZ U͚ {ϓi>5Gr2E\?~NדrUTEJ&U\z9̺\4eLѾ9W :/# QzwZʹ NZ˱L@ql,LU ƴf"n;3ņyrFtg'n>G3 /lM\ (l?們:Jd9pxE8Ug u@"hR5z4Y:p s^ h@죏_7U^!\O\_:HtҧdU˴KtA|Sm+}nRv;{$Av4#}yDښd%뤚y==;VD̠:ƙVrL󻢄^BvnBW ,zomcs6ܡ56:˿B*Zy#Cf7' .}mā\FM5/MƖHZ-_fE&p*I>V5c*_kw_'Fʧ{|GCr3꿐,{ȹ5Y26S|\2 wc] ϛuQEn?ޛl|rd.sr76K0lOK{Lg$# =1huQYtb%2 )6r YyYU^@uE899OZ\ϰ8̔˻$#Yh6#'P^%D`aI-X8:םLiCWqB1d^n(:$2"8 / W/J= GifkkJ"r~/w8Ƚ˝@w(?@o_P,Oy]aYM/9'*<]~?W P[9E+~y 9]q:g)?{ʜxb[g2esؽ /f1ETC iM=i! pP?XtqdN15rwG8Hs)Li(a!כ ~Gž5 V.rwH܃LT~ڻХ"!+F5&WOX:a(?oߋ%۷M( 6!\ACI%vr]XŤHAJS`5#KvmNh^()X@]Q%\ƙLs$ч 叧xEBƯ~a)-Xīػ|MiPjѠ%Hkn]Rm7dmk8*AujB{,ǡ6>g16bm;%.|JO`Ɉ$C^B*IFZ>ڋo&{ H>:ƍpmٶ dH+ eg>ԞaS`bDW*xootmH䯘;w}b5{kozW4L$e7٫NJ8lWr5:n+HU*R):dZ>KC&Nm#f7(JHr熃^ÖqtoSDu_ ׸.]{55}sHkԺ.2ʜ:(^m<}d-r;Iys]%@ѿC|I.Ax?RtKsky+^Sz䦼W2hHԯLK jA/WPX~UGSơf9ݻ ,, aoX렵 #W2ť&Vbh+|g!xPA+h&DWb7vAM{klĚDS5ر(FT;{ٽ;8r3v.d pFp~6KazOoa>rd":6.׎L̛Dwӷ Mu5=!Lni>0T2ZQn11(/HANn:y[*Qu:m5?Ӳ!2 C#!ÀI ȇ&J.u#G^B cԟQ2LW 8!0l LGIϨKB?F(Rc5*_ .q!$Fm;wD+wG'i/`7-ٗHqު[ U-d4P:QV@S Rg[hdi?)Ii w0u@MZ^/Q먭>ŗ@KN)2+7E\VgH_ )-y Μ9 Z`{+W7N!}2_|5 9C,Ff4$WS8&e`>g"C.Npd('jđ("H崖*bi mrC}AI7S-R-G4[l-[ct >BKimDe. jOg!0B90H=8^"uE}Xg=!E2nbZoTɠ sZ7Y/l}hk<3/=QQkDmCR_{3} 8L #XO,9aj=YZR8=wRZsd*|,%%ܹFKݽ[i 'N"88eZ h՛QoKOB_8qڪuh<ݞKE}IudH) R^GoY>qz-tL`<7v*4طt9lD^9[LSBah0KE#ڽ+DƢLD0B0F(]m2VusJÐd5H X6B`aZ:6oC%NX'Y&M;[4mg:ׅ+ƿY KW0G`DgB+,Ha/&@Y[ZK2gXj^zv6zxKL ̟K-sG됬#ۮbPCk cU7ʄ d/Z:/ RfE'^4dKgA+Q-,5^Pq1|.GOKޝ=yWpt)5S&hd(3DBL&bPV,\= x*)TY k%VSGNHt+K?ḑ+h?Q^&\a-ӧ+\d3 ;g1 =@KyR]theia.]n57~ukF*-Oq|d˞ s@:${}g.=*=^)'f}rYBhFT]8y(4%#M/%""Ԓ@++iٿvho(ZPqe|(XRZ[򌨔; ' }n:F*U$+ĿL 0"tn㾥Y%18yXِ dL 0&`L ?>k2&kE<1XNHF%Zy;$IY# 5SeR=*m6-"rӒYG W QYW `L 0&`L 0&`Le%5p꿸R`CKyYPL 0&`L 0&`L 0&P<ݑ/KqC>+/ 0&`L 0&`L 0&g g%!~k`L 0&`L 0&`/!o/aV܋[ Dڲ!%\&`L 0&`L 0&?*?FVqg%!~k`L 0&`L 0&`/!7JAQD\^`zsf̹4`L 0&`L 0&`L 0&-1Jg߮8 0&`L 0&`L 0&`9D Q9aL 0&`L 0&`L 0&@~#-K=}49ڃ`i# 0|`'V 'b3,"uHd1L z$_|+EWZˎ{%;dZP@ȩgC(-bŊez!r[i@N#'(sL oz"o_Ҟ"w{"\+֔ #CNL oz$o]6+g~6z|z`L 0&`L 0&`L 0&8Y`L 0&`L 0&`L 0&$ 0&`L 0&`L 0&`L K !*Kq0&`L 0&`L 0&`L 0=eL 0&`L 0&`L 0&#o"?EB%ZD4{]#35c`L 0&`L 0&`L F'Bq3 2qe\e"9'el 3&`L 0&`L 0&MU/wstC9q6YI QYIe1&`L 0&`L 0&0#p3:̇O3C IirrpL 0&`L 0&`L 0&@xoc 6DyUԈRF>4iƑ4qq6`&P!22q/2?\&`B:n|`L 0&`L 0@0)˽K`{J\]ѶqtH;XmA5+-wu/|+6^UҊL ߀=OA 'kEzm`Nϸ8鄩v aaQwuT.N ̚0 [V™KxP5vS0\UKU1hҾOXiV-HXbP|M.\bz^ܶzCWѩKGkXHW|źu1DIH7hl^0´'qd3uM,^%RB#uka=0o ܊1 vCЦ08윍A]8%w}Γ7h^]q=!ؿ041pl$h. QF _6Q܋ W@̃Q Q֜&t#ϻŽ(Z`@'J%<¾OPU\8{% TN[Jkz) !r ֥۸2_Bjz*={~4ɒ{ܹR=<:#T0^1y %csriUC(Sz -G$&Q\=i-b_&`?j5R4û$dYJ?Ѹx$?' #yۢKm\/V;N%#EOFQ)2c8&yje((2A*0[ (=61sh9%S^Rbo!dgGһOؑJN ;5adA\L~E@zxR vCf =2Hn;IYx Ch J!1׻0IGM꿏oeWl) ˆ"S? gt&|#whV.5;xyj8ͿH.7;˻@;_Nw-:9^1[7"y\sc|2C\CՋӼ㚟 0 8yo+^"U*a{VuJwJL hx`Pݒ{ ?]yb^zUrŐ{AGW5,Ae2D) I2tWi4,K th ;Qh!,p?8r_9bMI2,G~)Q* UVEfqDܬtOTONZO*,6|,[~ ^w'Aik)ލG$SD7`r!{~f!bC@'ƶE C,?aR9TCy4f}^8#1cǙ>ZcuF(5#hoh.;7t!٭߀G!KkCd!BbtG~B ;hHp[9Vl#2G=3w>Y0z |'ɪM.q,]h ߀tE ׆RFg@lj+1o[RZq+, Ƽ+~a d&4~U8v)ah¤gT]1t>n&|;}᪸uksNKXxƥǮ d>rYD@@]n&ރ[t߃3vP;ٔC~ I3ItF dN,+ܭ9M\굣܌|u'Q(г%ZVtBs;b 2nԸ1ʽoi9A6zלh&I:0)o+i !&[PFNے>][i6|2LܫC֋fW3 |jWL(KF(c%Eǎ d;W ãJh(V0\źÅXD<℄F­$!ϓMKD>¿xxs- б3m2r$aH%f}U~ haǗ m7f]e'W.D߳q|I٨t/Ċ$DьxA\wltR fS0aNgeXJ0 ;6czO f~._bOؼc + :#>:PMuYy:.CnSQi91o"i-[]ܠQmGKQqrر¸NcNEbhIk T88:S'9lz7[? 8 X ߊu h=STZMbӒ[b_XGt2~)~"&},Jyۻy4FcУ D2ԙd'L 3hN67BvO1Nt0{zN;gנ=c*wꞿ19hHVxfT4@ I.hYќ='J5v oĴ;X? 最-󲜊ދ+_aff}s5bhۃ)_4F>}S*M7PZJ;K}i? fY$#rf']]$ >K_{ÒoSt~?0f5$nXA햇B.(ixv[tBJb'uH!VEKu9>#Gh9\ZSřRxig&azI(b<7[f-*f"LA3*(M0N$ j8wX2')Tj"O@l>#q_+.p~E7THPM0eu? Ru4x*٠L kCUƸh*+hF1;$YtPCŰc;vpw)jz8ǩS1ď@rq; SNC8F9{ԓ:|h#mPÞBuif3nŦ3gpfC~.p:6_HЩ3<J!Nh;} \Mb6K-8rkJMk<>=w :w~M0_ТWѲ|zu&SD;yhZgݰpܿVa(?вRfhQ^ﲹhXֿ5*ht2Jb!߶hO:I-1zL i)%ÛABQ/xT r><gC23]A2oPԘ058xڵhK$XymȘkk N OaZe\E,:߯HJ/zᢺ$:xuK#[K ڵX4:xXQ;V3lB4(h2% ^DDbLK쌽ԑ\c,Ӕ5i(0>—b@I.Hr~V$}U`"=P`_QwfN<ƌIaW":_R{˯%$q_eO@ p@!FW*Uh&r { pKt z #T*ѸGps}`JeruÀ1IyI0cMyFX6aMT7B+zdF70cm)l oxD hSd*M*}_sq]J݇Y#IAέ@C!Ҭ PXq8V1u`A>ȃfB+i-v2jDݦ% ia1kmi@gZgWTNt\IR+pŸhA&ym1aWx 1)铤#MvOp=3,S\ڷ몉f N@z ut4c8ZD ;P/,s4@rl8I9;w3EgzxJ2>WV.Z + wwku4Sy19{=^]R@kɫ'(_R#_#bT7s̵ue l@z~7<n ~PF)u-2(ѡҫPwzOZuǑQh6_xI=3S0v>L>z SZBKa0xŮO&*+79Щ~~boJ#Wp_ړ)3 |2K됔 'V/rbOs0@y{Vn1~L'p8_!G@!\<-KpR n*w=񹚖{ rUVd#lԬ}MwQ63Rchx,4ZhbF(d Wn.hnyy1R,(7cÓRыP9>giTjdl7oUCTn"+MsãP&@IDAT I[Gc ԻovOOt &!.Q~iM5rtLХ`ŽӳmF ~Eȝ8ZUeihOyoM\$-}A[eڗGUSI:͝+p~tik ֝n.@ ҄F_R712Li o_9)1<;8%"3Bl(S] wɨp4]OChit Af|RFblP"}{`}L81G.t3E2 'LKHlUFQ:T@qY"P-h*be)[Kk4nu~Z<Ø>"gEC ؛s)oCTZorps0/K:x[7PE}灆%Lc}8/>3vaHJ,#w? Ô4[[KJ>{}ߏP͠-b6&aNL S,`/&!9YKNU:$M4;> O[Qt' |k=^)("P<*wI~Յ D6>Np$>(dibbvz# 4NF{zEEa4e_ EٛʕBWXɒh\ %ܳƫio$|Y,[.yoXwk,Ox[ڵ _JXѫUq/󮮨hGQT S3LVy]'LK؁qeULYqm9Q4xij@6>uY4n׻5zIAxx0.ފEWb[u#ylǡK9RDk:rnu44sƎF@Q0itoZP0G¯9dC >i}1hԟ;7z ,: )5T\; j'xw!EѺ²JH:^Rf$_|@[]6G>IfO(o1lr3 a$B$0pBW7z>댙:1)QN;l CKh΁ֳ=z(u{.Q7rG2'3/M? Je?vM $|, @K8uAX"`*[1l>ZIt\w_g_b?J4C;GwNMYVuiІْy4yO+{Vn"WiP)G}̧zNuӺ~c6wkX}RV(1*~_oe(r|$~=~F8]۬ˁ$Z0'aE)8'\P3x_ŚhDc/Q+3ifCJEm<;ܴ (#&m9-e6:Z^/Z#q‚@rf= cT-͠+ c9A@`A4;HYlLJ/F@2.f=v .WHJS <;%˒G2⭺pq\10AC 8rC#) p|4FKG1}vRV~^8q*$<@@%[z>|~~~(RDOв`!!hҤIZQLo޼ *Y=I 4UOVdj?{WdY`"z8 d=jv/r2tXRC- @qL<,E:]Lk߃~ob/&x:N2(as&q"TƶεsN)s"yeBG\\X ,}m2VOLݳ'nFw?(=rce&4eq ȾUcD zˌۭ$ΨbGy]%L 2-Kd) Fl1BIldb%H;Lc9F@97XiꝉgN`L 0&`L 0&`L: %\GPu}զxɾ!ʾk`L 0&`L 0&`/_NqQ6w2\_^'fL 0&`L 0&`L 0&`@UwW|Pbf//gPw^'~E,(hzL 0&`L 0&`L 0&<a1FgD+eL 0&`L 0&`L 0&@`CTl`L 0&`L 0&`L 0&d|>uCC`@H {3&` X!84|zL pE Y JыYˎ{%;dZP@ȩgC(-… g}n9  |e΃ m\OS}SN>|OkŚ2{$u=^։ -\fu1}E9"φ^/^OO`L 0&`L 0&`L 0&' cL 0&`L 0&`L 0&`C2&`L 0&`L 0&`L d)6De)N`L 0&`L 0&`L 0&'PPL 0&`L 0&`L 0&@yQqϐ<{2yɥz9*e ~<#*\+֔ 0&`L 0&`L 0& a >salz't_ 9L Q9 cL 0&`L 0&`L 0E{\CTٜJlJ, 0&`L 0&`L 0&اf>|"^$9ala`L 0&`L 0&`L 0&yVFJ6DuΓ =5bT*ĩ^<*f`L 0&`L 0&@v(BĆPM_Z"/ufYG@s :`XY'7Tx 捪V É6[U9WlQTZ;cZp1Wϙ@:zt@8m y!C}v8Y|&g?Ջ{DX>nbfѱMC t 0&M W QO>ŵkGY$ oooTR g_EeJg"-Жɾg&q`TWl8ڭY=G!\[u $1") -<\țd9G@{ n F;mA<;&`'3?M4~\=\" M$~:' `0D=~M0Z}1bXRLr8{Yk~81i&bf"f <7zcsRlU x ] lccQaxc2DAH\">!J N'G6TӬɉ?m-Cr6_>NE'flr]!jN:Axz>ڰ 8KhLr}3zσ6 SqjjTŊ΢fp &}Eۯ0D ԿOr`}ASC}J {0l&t&O7TY +T0U=Q*xʞBWI崥ѮI d@_ ]6ɒ;w@ĭY>ܲ)  "dI))! IaJ 䬅Iur-e?&Z j'' Q*J#"$4 >>NrG=0sg,uKTMz8>oziύY uzǃ5^)j k\SaIf|2OއZTaJ}0)ɵRH>7=Yl%NpE8;ֽ޻VMcޱԀ2B ׀GXv4LΞԬHl&߮p3I>R@-{=s5ºע6](&?`ec6q2JDWzvc]8oI]Oɴ&Gu};)[ $CDGY a#IStrSbS[:,e\ 4 Dk):ʞguڸfFHotiw1.~伏T/ pU7Cu9ƠH[8ؼyh~n! {c qzIHl?mJioyy!]1u)z/%)q^ZZF~QGnS`AeYuJz  - dzƹ ce6Į߳K66ݿohg#pnδd;&b٩<{FϾfx.=JZ۱qBK_/Col/g@oHNǒD*w=|FmΜ÷N/fu,aX?VE5P7PD"&qZՕH.iv~cjMW;mPLd\BEj *(KeWbʄ'`ouZCmTVA$܊7|1.TCи}'Ou-_Z!,VysLa)>eSs>Bf~Xa[hЪSC"q'< ZNk9w'L(`2pЭ$~Źp"\cQ %_u4U+TbÅXD<℄F­$!ϓq/Jz"JFjzd|R(Ҷ0aqs=P避xo|.޻HRۯ- ;z,9Q'_ U+x 9w_" ?q,v9XL[X9V>Έ G@={cEetobnV76ǗdcƕM`2B;OaV -Q1f(I"l0DPXtkp,քFcJ<KH.KHDS D['Q)ȢS8l3߆ ڥ}qT?|Z?X0 }FS[076On舉o@e|JgGi_!T_[ހpǷ!'17ӷhóX20}}7[\ķ}So~k1pz, F`ql~ xZO8at*p1JZ>_q7Fw^\l&N2Ƹ+\MWbXoD ٲ|)U\\y2qmKS/)WS\5D98؞}F0.~:6ȦD I JT>kWfT!.!e]X{Xt~?G][Y`ɀ:p#ɕ&`GP{ز{|CZ- hX._ɨ9u_RZ'h2팥|8t3o7} 61&vlNL^¤#8v|h4q׺y +:V6P8y̎ dkF~ \06X}וDǩ~PҬ0LsE7F࿘Ҍ![wpq5Pim_ц05>N30wE0V SKmAf"Ԧڴ[zK*Z0$DT.6V^vK**CT*k.?!j1Cl[ˆ):DÒ3Ԧ4x@}8 cP83hKn_a}OqL~ŭF:/SNF,H:2Mg=#}i?R?~ŹN/c9D@o⑳39Ӻa@ژڤضfvDVatiX3;U ?7F70cm)l o-mjSZ_t2muݶ~ -[I (LKdӖHcXtiafh$AQfBH\>Ozņ{t8cѫ>tZdʚRkbU1W9ɣ'=PQGJhiO) E_92%urzOb`AНd\!=4as/gTkL>{QJS:Û>$X=IٝF p9/#1}GgicNPzs> 8bvɘbJ#SZ>^))+էE4I,NﺹJq]2MKnM\ /3%|Ǚ|@H[_}-fI 8 w.SF;js_(ϋd.1L -j僷7Cл_Ն(y&KvDQ4Ң)=l@,>)Nh$M h¦6Rn9ݻ+ƒqKݿڳ36y)LZLn1cbItNrn˷ڧפb?3g09ycL p\jQA! B3ƧaA>p縵(࿩$Ӧ@N vL ' a̅.81% Npng_!G@!\\YhL'e[Ukx0>{Ҳ{[qt8) \?J-ekGvO}bڳ9AjIvDmfH_s >0kqD}Q_53c0.C0?Z"ϸ6JCGj12WKw'WOQdm'V {N--'TdzxUFcpڂ.3m,n1g E" ~5jh h;&:D}sўA,Li}i[mcMQvqUi$Rid =d]#|dbO(qLpsQ&| >[]*h\Z~kkYދ‘P_yw0k1T hk}qgC( H/!ScמM,#B RvB7l8VX`Z]&O QJ-z+ܔ/d[P:ѓJNrν{Hz| \=gpLAV+Om| ދ+B>>(matx1B@4*U8.QB85cl(Ž.6v>LpJ+M8lyps҇C'U+]I*5}; juR}yT11͋88)1i~HkbiGEFH?xq- œM5l5:|iiRg|ML(n8|3t<8r@!l=8U /W^4WSm %-wp"Kvj#WGqu-4obOr^h)Nn9.4c/}og>xZըwl~"ql `>|<ˆYd!/lxZm2,,Kd߼Kf.EPnH_8}<0l&02#⧿CJå*2zI,aƑ X.b1d1Jv}?y%J:ky;DătqC0z g"P=x37ԠuؽruiWM?hG!DMСU4/CkuiMm߳0.F<ƨk1W"aXНfO%b޻8sSQݭ2}$W[P4l2 3&`Gbp_? Y@Q?s-{h_#uJh[\җ%)G4.-[ aҒyǥ0C}\SiԲlfїV|p:}PWCqGZrG-7ſvQW Gkذ)ŧ)*@ujvc;<>_w2 }>ѷ|,CnGTF\ {^B >֏6sWZ91Gre3TP\"._QRLaԑ5Oz&l~Yznޝ@U ;{B6 $ H-=Dՠnsl-l4[Tlj[~^?['9- )mw!ǹ[~?4}C?!,rKHn{, 3Qy -MߗoD^gkF曱3+1F+Jek~ů\3Kަ!y ]<)7~=MyߦNmytR} kȠ}{sg_j 78o?V1f;3,1{ ox רtwGV/⡸dY1rp\~G.Wk\<8>l)6F,x2Y_|d\~hפOdĺegs6ǯ~28*ޒE (mY5Mc7y9>lCGx=v„~EXvc߼}m6dM$. 3/v}זgRhpM;JmTWsNyW+VN\5Ҋkپ|ݶ~Η,1S;U!9td륫Ihn|t5VxKK]N4甎ѕ}c4;:n?֯r7tqw&e ceUo]zn߿Գ-dv:FvO[Njrz0^k|M,sZ__zRy[אFyX`SL7E3|o:k7^_4,5SQ,na z*'-+gAWz]M;>'i)xgO=/eB+E?]Aq`zF3?{2K^8*5.R'H_ް:Wb]c~ϴkqiʿeJ9}{n)Еqb`=|kɲ}K|<kc[cntX/cbV m\?꘲α{z&ŗsG n{Kݶ=/<]nkqn4פ}k.˖-,ДÊdi:ٺ=2e"AA@RRS*mW<Д /V*? l\O$Lkyk[\) iuV*۾|ĉnzF]iuT)_Rmu+e?ruaӮdǫyt:V!]n/zV_j8vk To]6ZiۂPV~۶]n{mU*zR-';]=^6G޿}V5CԦ,iUn ut dA7ˆsuPJF4t^ܶ*΂WŤP+ץ^Pi~1";/moo|._6n)1VO=t,ZGgߪHkqӯ/?$ B}ipֈ7+_w BR]O=0濲172ol.ח_[k*#jݺuE_zX-[oNCJ.+ZH:;?^yꁸka''>=`|/S&L }y1Ye䇇( t@g׉n(B5 8MpLt3ԙkHR];u#Ov܆.\Gb7oS#{GWb=AǦQx=xtA<"nشՖ M=ҳ(HcկɠFQZW+GTWFsEkN,{~z, 5_J?}cSybܒ~srՍ @ 0xs>T 5UG@ qnCR)ZaujiNRz6ZvRvÊTvUsF5v*F-!@ @ @=%)7=z@% @ @.3T@ꫲ @ @ @*0x@zmzU[ *wJ @ @ @@vRժؑC*r$ @ @ @8t={ecUb*yN @ @}_`ߝG'E3ʰr۾2grزz $@ @ @u&z# @ @ @zI@ C @ @7ow u8 (!(c1--t 8Nu5@7 tlԱH>HZO+=g[Dz*\=~n2"PkΏZۣC\'ߴst^-MlM\C~ o(mpm-CGzD9J @ @Tϯmoo ԹQ=zk( @h3ʝ IDAT @ @@Ti) @ @ @eDgS @ @ @Qm @ @ @! UM  @ @ @J DB @ @ P@Tx6%@ @ @(- UF  @ @ @@Qeٔ @ @ @@Ti) @ @ @eDgS @ @ @Qm @ @ @! UM  @ @ @J DB @ @ P2) @ @.ӪjKLk6V9 @ @ @xKˮV4j5*N 1mX*zO @ @ PD:OA|pXdI H  @ @ @*ZOMqc4].3οc`\o#Q-¸kG⚓Gn={?+Ʀ%88C}>n} \$6y~̝ol!㗞o/ @ @ @nh * hS|eѸ6ٗ|.ߪZĦƴޖiӚ5bC¸=11/}??:` B:>pc13w][]> y1sbޜ)N/ֳh.+ @ @T㿨?hD[6B.*FZ }?G\| hN1!={_>7$ް:ix|KbE۵icz7y.uJ,~;٧;q:fmLywsLN /?ӒEgEGT)=  @ @ @]߶oZC'94&x)Ebԩ[PiБ)JɒԷiO7lSι$^?3N輘MhK1~Z456. [GR1sޗN8Nz_8lIB @ @ P@e{D}dq\+{RctHa&39q "WwlqSl}QSⓇ~.nqg.Q1{c:MΚWK6GzL ۺw @ @ @e 44+wcV @ @ @#h< @ @ @@ ':Y%eu!@ @ @ԡ@:l& @ @ @Q @ @ @@= D^f @ @ @@/D" @ @ @( U{]  @ @ @  Ȋ @ @ @ԣ@T=um&@ @ @@T/ + @ @ PQ׵ @ @ Q @ @ @@= H jeZ6""B9B9B9B9B9B9B9B9B9B9B¹?>TXD @ @ @U-#=z U*O @ @G D?4_= m&@ @ @Ԗ@j @ @ @yFT_iz @ @ @TN@+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ PQ5{5 @ @ P9+ @ @ P((IENDB`django-hijack-2.0.3/docs/configuration.md000066400000000000000000000112621264367662100203450ustar00rootroot00000000000000 # Notification bar The template used for the notification bar is named "hijack/notifications.html", or "hijack/notifications_bootstrap.html" if Bootstrap is enabled. ## Bootstrap If your project uses Bootstrap, you may want to set `HIJACK_USE_BOOTSTRAP = True` in your project settings. Django Hijack will use a Bootstrap notification bar that does not overlap with the default navbar. ## Disabling the notification bar You can temporarily disable the notification bar by setting `HIJACK_DISPLAY_WARNING = False`. # Permissions By default, only superusers are allowed to hijack other users. Django Hijack gives you a variety of options to extend the group of authorized users. ## Staff members Set `HIJACK_AUTHORIZE_STAFF = True` in your project settings to authorize staff members to hijack non-staff users. If you want staff to be able to hijack other staff as well, enable `HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF`. Note that there is no option to authorize staff members to hijack superusers as this would undermine the distinction between staff users and superusers. ## Custom authorization function Advanced Django developers might want to define their own check whether a user may hijack another user. This can be achieved by setting `HIJACK_AUTHORIZATION_CHECK` to the dotted path of a function which accepts two User objects – `hijacker` and `hijacked` – and returns a Boolean value. Example: ```python # settings.py HIJACK_AUTHORIZATION_CHECK = 'mysite.utils.my_authorization_check' ``` ```python # mysite.utils.py def my_authorization_check(hijacker, hijacked): """ Checks if a user is authorized to hijack another user """ if my_condition: return True else: return False ``` **Warning: The setting `HIJACK_AUTHORIZATION_CHECK` overrides the other hijack authorization settings. Defining a custom authorization function can have dangerous effects on your application's authorization system. Potentially, it might allow any user to impersonate any other user and to take advantage of all their permissions.** ## Custom view decorator Django Hijack's views are decorated by Django's `staff_member_required` decorator. If you have written your own authorization function, or haven't installed `django.contrib.admin`, you may want to override this behaviour by setting `HIJACK_DECORATOR` to the dotted path of a custom decorator. Example: ```python HIJACK_DECORATOR = 'mysite.decorators.mydecorator' ``` # Remote users To work with `REMOTE_USER`, place `'hijack.middleware.HijackRemoteUserMiddleware'` between `'django.contrib.auth.middleware.AuthenticationMiddleware'` and `'django.contrib.auth.middleware.RemoteUserMiddleware'`: ```python # settings.py MIDDLEWARE_CLASSES = ( ..., 'django.contrib.auth.middleware.AuthenticationMiddleware', 'hijack.middleware.HijackRemoteUserMiddleware', 'django.contrib.auth.middleware.RemoteUserMiddleware', ..., ) ``` # Custom user models Django Hijack supports custom user models. Just modify your custom UserAdmin class as shown in this example: ```python # mysite/admin.py from hijack.admin import HijackUserAdminMixin class MyCustomUserAdmin(UserAdmin, HijackUserAdminMixin): list_display = ( ... 'hijack_field', # Hijack button ) ``` In addition, you should explicitly set `HIJACK_DISPLAY_ADMIN_BUTTON==False` in your project settings. # Settings overview ## `HIJACK_DISPLAY_ADMIN_BUTTON` Hide or display the "Hijack" buttons in the admin backend. Default: `True`. ## `HIJACK_DISPLAY_WARNING` Hide or display the yellow notificiation bar show to hijackers. Default: `True`. ## `HIJACK_USE_BOOTSTRAP` Whether a Bootstrap-optimized notification bar is used. Default: `False`. ## `HIJACK_URL_ALLOWED_ATTRIBUTES` User attributes by which a user can be hijacked over a URL. Default: `('user_id', 'email', 'username')`. May be changed to a subset of the default value. ## `HIJACK_AUTHORIZE_STAFF` Whether staff members are allowed to hijack. Default: `False`. ## `HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF` Whether staff members are allowed to hijack other staff members. Default: `False`. May not be True if `HIJACK_AUTHORIZE_STAFF` is disabled. ## `HIJACK_LOGIN_REDIRECT_URL` URL a hijacker is redirected to when starting a hijack. Default: `settings.LOGIN_REDIRECT_URL`. ## `HIJACK_LOGOUT_REDIRECT_URL` URL a hijacker is redirected to when ending a hijack. Default: `settings.LOGIN_REDIRECT_URL`. ## `HIJACK_AUTHORIZATION_CHECK` Dotted path of a function checking whether `hijacker` is allowed to hijack `hijacked`. Default: `'hijack.helpers.is_authorized_default'`. ## `HIJACK_DECORATOR` Dotted path of the decorator applied to the hijack views. Default: `'django.contrib.admin.views.decorators.staff_member_required'`. django-hijack-2.0.3/docs/hijacker-screenshot.png000066400000000000000000001550461264367662100216260ustar00rootroot00000000000000PNG  IHDR]4k iCCPICC ProfileHPY_D C98d * C!Q5"+IUk@D1(f]E@Yr m]WU߯U} &O%H <]QtЀ$J;O4{g0kHX@DzX)_@,/HFtt,@XZp,s,sܘ W'3? >d4<69&2SR1cŇo"O&#w 7?GJpaU$ 5JZ#b^s5r+tYiQ f,0)yg錐 ǥE`,pNBHgp8-)gqHD5 fp]p9 tg<>37I?D`E@+5v P1nuD Pj@S` p H @H ց  (g9.z@?x 0x&'0 A@THR4 = w " ! @P t~Z-z Bc{+ɰ4kF5 !J9p..+p#|xP$ 2@Y\Q(TQ blbbb}bo sċϋ HhJJ0%6HJH#-=!#%c.&%S*sIfid9%K-ٹnIߒ)٥Nq_rrIrˣug.?TzR>Qt*PRTTRT+V8DSrRJT:tYiLU>|E5]LO;* *^*B**ӪZ[TUլM+SUAаH8ѩ1]IsTKVUL]}_csDGֵM-սYqcmy 4C_-Moԍu00N6>iDdI{S]Si}3FfwzqGYP-,[[|XYY[XY=e}cb[Kts%؍.Z!{U{qC1GGcK'5')gDo]]. .S]PnnnR%/^2,F5c{w'ا祯_ßj :5}yVjZykUVf> `V0'ce,W!{,>0n$>0~cKpL(JrK*fÓS)1)-<)^cҚ5|=~ 6`Gp* J[֜.47]Bm6`CFḭYYl#99?Eem_no6nhߨ1w&MU6xKᖏ[÷*n湭6O,Op;v#.֮?T{,݋`㾪Bœ¡~xp[EE凈}{[IBIKi}Bβ##}G֕+== ͊'^ ;էO^ɫ ꨶQS kNG9vΠx=,8+<_9~| e ԆF1q)i9ŻծW_+/\,$sie3WrLƯrnz-}߼qZs畛7/޲rv;]] YmxnsMOk}}WݻqqNF?x~48'Onzy\y ^?`9pime˧C7m8UшH1+^ῙSϲo/WD;>}h}2pŧOS>W}5t7ܷ:[x623g s I8>P"ޡ|O<|?Gxo K*/ңERa2rmBlf&FZ,1gf>(k`ffHhKg#--%]$tPCiTXtXML:com.adobe.xmp 185 1920 1 0@IDATx UeՆיee4py@J2Ӳ,%,M??LyD˽Ls2 }7{}w/qu E-@ @ @ @Í@]&c'@ @ @ @É@c#>ÌQ@ @ @ @ t]-B K@ @ @ @Ï@2.2F@ @ @ @ .R b]ā@ @ @ @ >$?#@ @ @ @ lP m8@ @ @ P }q0@ @ @  " @ @ @ @ #xy/_/^lW\.>U鴵jʺuf:tD"?ͺsaɒ%16T!<n- F@ @ >r$pBΝkzB4X(p} <)Il+_Qt[sE~D2ayէ((oG:/T9KmZS3j>҆k):ISP9~Q7PJ&-cMWsHKR%G|`mcT($l͚:7o.k_~Jn~4ɔӪ*,ʆM6ZǾ5/ ȰBZ̓ȶ4.MJb9lJH>BṂlޒ j'1_9ڢƩZURzITBccsnϚe.^\j8fi?<^{5'wy@ @ m믿#gⲥVxI+<7 /Zr.s/KtUּa%XaӖ_5ׯMgN[b}-٢Vշ {`#R}Z;w׶غ6׸HEY&ZC9!8Np8t6!E 6t}D9{566ZϞ=շ\b.[zZAM&e=pe`K` HTȧd΋aٵRbm`B'OI憸O]u8Al ȸxE'Ϋ,;KR "7gRsBC$nAqKd'C +ˤWbz ѣ,@ @ @`[G u^,Νcٛlm~:֦mkӲZT[bt[: :nQՅ3-7֦֡jkױڶ-+5_D,QyV=~=Z[UUg[Ym\aOUO_nY-osΟ?F;9+fs#8"n(l@d*A"5r9v(p4yr8)RYM4I-qUHZ/D-ULJPF{K54ʊ|W'qz-]4=v?y_d^$~u~ˉV[:5QV=Z4zvFyht3fXΝ5oNfB,XvBI%2VR"L)>"eD.8X NY1:XΛY%,is Ƌ% ylSsNZ.W^J59vrIM$Gdִ>`)E #x .,%˽Ю-'י 2@ @ o|ʙMJv_-QƷر_VZPLpkbUK){meXo߮od[VZ"^Ĭ Kd6YBW.Φhm_&SNLYUVUkmE6 kt}l{7K.ZmgR3)y r/E0t\QT)ʺ9NZvG,JTrڦV(yRLC-F6SD̀IrFA*p6쎳7tFȻ>rQT8͵+`POI1$F "* RUVY˖-כ"suV\֋Xd&Nh~`Nl_$k"+ђKacljPēN%v&2UEJsnCRI^dGS_d +e(+BMH';G -^0?PC?_Yx:5nMx@ @ |@PVm kҋ$ ~񂃄)Tjʦ>6eڵsϫ:LN`uKkVMYs&Sس6ڷ&MhO>ؒ٥3=o5T=rFXYaQT:IKрBWe(4E!byWp؝R|$uJ]4bGh$YZf)PRp^*oKgow%b ?VAxi8E)3 qsǸvEλBpʓFL<ݺw}̕j  l=n8Ү2#P8)v)>k˗.%YԆ#v>OٰC]`jXFE*Z-IѣP&s}lϕoOCJGa0G4gi"$ a$sPbEc~tB8;*)m3hZi~ce>&Kj%M5%P`3n%wUDN{$zF/Y|fX5WD!͜=+8Ey[fW"1m7b-<_3noRvh2zP[?e iU>"2V@ݠV(_[s> !Dt)ƎVrj}ڔ=`}\eiӜ XS.o #gg"Q!%DlIcXꙜa3u`˗:tPj8~@ @ [#"\"b k߾9OD(_\k75f̡v]wPH+d uN9XB⬲/Ļ*:/ J^-& YS2(s1\@*,R*/ y$v]O+Of2NPA#Ή5r`W 7+Jdž |5'Qx8J-T(ؼOywQ$sh)'W^i 93gُ~#%B yc1}8YL(Uh8N0;Js`X 9R阤0JVy?hgթ)dK r +Wʚ:u1c\g*8\f n`9I'BօP >T]ˌ g l_s@My Di(؛ ۓFbl6Iٲ諅05mtEHU͔[+YcYVKTbx6'VDdQ_Z)HUhvp{2VBLowy_q;l0A=sIL[Jlv^} ۹ckl[Yd +Etb_ ՒKv*CU8Q:s*/"V=ayHwdGN*;K:D>&[!}yY}֥qq-\l-<Ѵ.i#؞Gʮ~վ딜ŋ9z;ȏ g}Q(|;u}[g*'Ju$PphJ#bY{:eOڡOݮm35;|oË^jgُnl/`}oAvmk[};إg ISO^Z/^E83g,/N'F£.dF<'2LFZ8+ߒH-H)w-Gl9!F~\r)4h,!ǥҖ z=S<n}0@eMD7zHT(A6Qcc35 AVC`%b4koj=Ir{vIJa*0s,@tk.^v;)IlmWۙWMEѨOg_쟳oԮ{]ˏWj{OZk' a]g|e'l`f#wFmލ\7T h?<.ꞶCG;쁋S&~Ʀ9ҍٟƯ;Odz[=/^Vk8lKvw= EoK*kAm]-6P#EiP( [Ч,e4Gn&|J5(珇l!<)fo BEH!9y[`L[cb3VX_Bq73)D ma}[s||1{jų6>RB)~(̓NwlgzY9|W\xwm!DF>p?Ga*Fm!E6sJ#vl[sLUEm}:q74݁6i6VZ$lh?YQQemv LXX`Z-*zm֋Kof~u7:Yv",GΉrH %nwKEe9(Qp0 dj ɁG߲KQB 7Zy=po+RRbqYm}˿bbG݇:U=QJݦjGkOj5\_}Ն[u#|0[ן[n؈s"g&ڽ}.?c韰|cmAnm7ٽ>l޸싎>Yq0 fI{m }&NvE(]6{|;mH'ʍsgVsp {W<og}Ϡpv>8`+7zf/[ {_[dBu݌g`*lϊ̗U6!1; W|F(Ug\#F}pA$_wHsr(NN:kQ"Z (GOѣF}<mGBPn#/`:j>׭ulnR~qr)fP-Yȕ{?3pSa x1'|վ:(Qv^}z+; -]e< 7Cijkx ;sP!]hw[ =Q\] vM0_Hf"[I9mBRpI7)8r6|aֿ[h|—RgC|\y/f}?z9ΐ79[r˴E/gl]R_Mtw{g62NwxWLqIѺL3Y[5L~^?sO'BITk2\6 ҟїYk׮K.*[vl…ֽ~-}D<`py)F)rLzʑ^ o}\\]"uWެN-@ @ zU2e>?R -Yĺ6 yՉR|Za?%GVm-1780/IH eYT4ILׁ]ls9ckm?^E% WB;$H$mf}g|69"ɑwǴlrz YZM;ce1RΌN($ PBAuO]wpƛ7Œ#2zKE)Z.儰clҫ}PaiDJPq(q6csSl/Bwe:b3ڋs_`8(q0^[D侈4r[p'zҘșp\/6yVvd9 ]f@V_q #'HV%/^ңfi'mo 8"f(=e}6C!fηVG~3ͫC>pkݖ"y/g~썳/=̆uIۂɼI,z~2} aVOgWϿ~2{yU-{/{}_wZf/4~ƯQp+ ل+jnt3kIO6懷K+QԼ`ן[spkjku>bDe(E>TZgkYc틌}8}}V-:~6~7q*s|so9^YZ?>/־p |`!Q,T"$m_?\_᫥;1@8K;z^LO1_MPybϞ5f͞avhfΘ %(\9ֶ?Fu7xU+ڥgzK+U]aeA0] @Dp-6*_`gmVZ_6d-M ֡`d2zSKxC! =|O[nywNq@-T.ŧ~| 9*;q\WVYN~̦=q^v7ҟ(ȖE_}|\/ɖ9oE8; {\vmK}k"|iꍮɢ@wVqkG" [m*Oz*  J.[&j/G9Fʓ(MV+xFs"tE HH%^6/mQQm+ u@ @ l 13-A@_~eqYݫ'pBq~#d2jb9] Ѿ3c[lZNzJfc$9NW;GU@PƯτ'ryi)A e]C.O4$=oh<,C%cLaCC($K!x9@6b˗/v;ؤ46mݑDg&-%3DH餕6'㾈Jg˕/yhyB{gso{.]n#lXVo?I'b8^F*ϸtVh1?;l|Ϫ?M[x74~RjB=$!I.]Ԗ6}IЩS'[ {IrI˥%&B؎"s"ݶnb>Ҷ$xHD<깝A->)vhc @ @ 6@_skfK '}O\y|(yw?ꋶ[{A>S-FqؓfBr 8)9TzAPh%'bG6e)Buœ@"2(+1%V4-Ӝwefo>yy|DPb8oRM1Uܶ#-3/Fb\ix[胏 +0cT9e[MN,GjAA6لU S__uD:w|v҉ǹUyvi: O)ƆcM*LEcD杔=UB':#69FXIWtN7ȑ, )T(V!V`R/p*(+Kw|5*> H|^5}uBDSYD:/h9:*UvzG͸8r\Hwp(_hd$H*3yJ4?%M֭+Ӭw>ֺukl7%dx?l@  ྊӧ yK_+ωE>røJEf4$%ݽ ud w<܌r(}RJLHΠp4;Iӌ+06$DEfh?#9GO__]L{F7CA]/4c.2 Z8R:wFi?,l.m[04cn61,Ka2*)ASH4dT7pa"c9'/r ]a\2>"Dͣ(?M̱uskH=L`z9"%Ո/ /Hۚ5  <~Cd@ @  ^x!C%B m]gUiWa$QQUPNQ|P$Xz_U*UCj_TT%%i ́FDLiL}3S}yF`Nht¸D<4uė9.Q]!&O] #1`Q@J(!ET]aW(Լ:OmL,'$|u}Uˆ1XDI sbʗ$RCDRlFmD:]!Xo[24yQ`EmF#s-Z̵k";R(1TgHkӆaFK\J$S)$þVÂ"Fr?I(Z"]V/ (Wqu Oz+)y$RyAd0')Gp' R_PxP&IeC NGءzWG*'Zo>An8D{" @& q5V5Z_b~atp2 _cA)wc$BJ+4dUr9FS}֌Q |"JQcIi#nĞ0.a $[G )%lꠈ<vAs}k Tr,Τ[NP=wjgG)iݷ(0 ;#=͟Q|Q|>Gl#9TQp53 "B!`N(o0qeE$ttB >P̲H9>E^ %rUOyR" -J-q* B<$[pHR$.BzivJr!*V3*jC(|A"\DR4Ȓ#Zkk-XOE/B$P8vţ$zM8ICyɬՔpUP_2RI96ЀZ~=׊IEq*p3W;RBID|Lj*$RMjKA0MY焑 Q*aȶ(((y!|O!sv"(|O+j'+9?.^GHĥ)ey))TD"K#t@) rFD!(pǖNR/HQKʥeE eQdd3 @8RHup\JQѐS]zLY'K{Wt b#6m!W)b/Q Ad9D;_{N %Γk+gRFqE97]R\"*@ @ @ @ ҥ ;D(L+OeفW=$W H1J哀1Q!l9#Ii)z-Igr(^T$@RIUi%.8E@!O/1;U2P!zlYI(L Gʟ+D%$I1alZ)Zu$RL|}6w\[zjgǭNJpϟ~ %׭/+1VoiF|h> \.g7|Δ3Znq"@ @ @ A %p1|BQC`P(ȓ$Kiy?(lsů.Ic9N:ĦӐ9yiNs - [$$/V,Sn G"r0Q7%K˽/yu޵ 4KSV\aU@`E4!R,) /RZMEkX]cW^qM}u|A;qXɄ rDuBU/NBJ$H܁AC# R^gh D$(S>#2G#b_yV+W3mA"w'Gaazط4!$d~'J:"QRpj4XzmiEWTTXUU'?QΞ}gYMҥ *a$l' 3#rO5eghoVu6p@O[\.3[uf;upm` gϲoV[6گڪb/@ @ @ H+Oalw<~񍃬j Λe]xƺ/|@ @ @ (I(F DBĜ(l bBDR_K9s }F#?w4$ɩ hOK='kfIAFɣR!2*׉!QB ]Zlku vze͝381ZeM32j`*d9$5ބ>47y6Pv_ : yjF2† Vz$ RO =5ז(^i_qk +1Ԭp7Zn6'tnRؾ4ʖћAV_ތT:;B=®J;]oϮ"رO4{v-8o8)4гʫ}(X/:@ @ @ m "ށ@ɢ8m 9 8D@IKz/3[H!-{ؑGe B2T7ąϨpR#Jd!x\-A;yD)r(h(ͱ^' n`m?Iu$DQRɯatzv ة"lΜyj"Iن88W$T:D`IF$zC=vޟ|3֪E%j9B(?N2"Kpq(bOB!ʂ0R|լkO]]u\CCFz|vɥ8ɮwzEvJ`_h.zM8$FIϾU7/}g[:NaMmNoGMkl=(o΍ۮx]z+7ľLF⿭۵?) -+®a=Ь>z| ?8ۑm}ծfBcݑ* 'G2屖 8GdRB"v҄es_exټysm% ⎷LDASnU-ZZNm\ԩSG[R?Iw'K  AcBvqrPr-R.$-dJ=!K8R$an˭ZΝ! t~H)J {ShF=yAI'Kj%С)uB*++L?[%J%QN&?9ofǝg~=yv!7ٜۯ'|iv[ÔvMZu=mod]u]ab?pbp.1v]r֣q{9vQ6z:*9XnO~Olch|vm9GVءl9 LY3}D #{ݮ8=}_h]DPie^ͻpGmgRF8E=LF|tZ/gQ[HJ&U[ @ @ @ " c3ATCDH Ȣ "J}c}qq7aC KWPig#[3Vd\nSe;W?~"R贛#=l񟵾loqU{J5ӏ]Sseg؊iN6^=WMܫ4ݮ}17DaV_۱wV=UǗh٨'g~nX:Y 8Ŏʎ=눂ױ2U!6mIh>;EoRk;G߳)UC\j/o;`GG\&-@ @ @ @`G *ZN=W$P *@xA !"G¦.I\*Yə[V;׋+I“HHad5UJIq# R龽:F #R'T<)C^@$}%#:.*ڢBTgx׸P*F+Yl %N 9˿em5UWj&uM*[]]ch._HFspAmJ1Luc?jdYKN:9|GeuY[>*_X/A}qzKr5W+ տTRMloL}\ͫ{.=sڵ5lҩ Evt}v»>~9l޷졩ګo< TDFϫBz!o*Gea@ @ @ <,a/$F&Fy|$O4!^y'x?DyZM%n8~SRMŒjrښy_u.~W5C^2ii6qv5wpo^K]&æ[,U^ʨƺu)}h+8kTϱtR;{P[Hslai $@(Ic @ @ @ 1HcK̑%BUȓ+]aĹlq6h JYZ~~u+\Ggh! B )Jƅ;ΑǗu*h),?)tWjd6EeYj^zy_[-nU]Ye7B6)4Fzi4tIJ)tD=vei c&g9Gzo_mնlr:}]J*-P u C5C;L&7Z O9;cD2}2+}Dn_I.VvЩ_?>ϟoفF.MٽmSϱGY1?K6u$/_o2Z.IS$,g&u?W ,Skw]%҅V%k'[n/4nq؊ TI%]#m$YI%@,!4$$7sokH7J9gfL|;gTCH3H"ub0H&\ZqUN4l$rG{ZfȉS0vՊf^5coԚ} ;t1Nt:v1ytIꫫQXe%څQJLh$4Y֊ǐKD簮X~26^ĺu$G:>DpihmԊf㝸G#I(1rI#8CNL&y ˉCeFH #LCOɚȪp1{.J@ (%PJ@ (%884xv̷cDq1 Sh0Mٔzi"[b 1Vx? ӇƎ EHvkFI On_+H!ZQqr ۭٗp2ks7@G&>nOBVDKL$RkF6fXT=#ZɜCR5164km[,dL*ZPJ@ (%PJ@ (3B !:bzFG$My!g%M^:$F !oj͡P%$.K &)_GS)*@>Yth2= ,/cX1rG^}P'hؑJW,Sa`qTCXQe[ɬIicY?/YHRqn("&QJ,\E'8ӫΚHL0uo F٦v)-OI PJ@ (%PJ@ (MOZhp8*2Ni#|?$8KN ˤaeBiD(i*D Xy[,4I-sF"hc%j2!DGF-yY+Fp-!xl$҉9ء9ň8+L-oWcԳ5#oO!q2wLf-NhTYhH}#!Ǘ_U4ǔH5άI5=[kz鵦ך^k{ޏ~#Ȋ9%oӿ_b 몏ӧA "S'> ȉi"=1\8Qzc ##ƍmu^|أ Iș Ie!)'PK\[Y(3FxgԙMkF!pj%J<3!bU(1AfM4݊ ׇ<2Q+ĈL`-VSwhMHΜHk26Y  ?9ȺTȪMOYP1a5åd8ʱV)nQHq_Tɵʰ,Dw^r<Yɱ_UfsHeWV"xm~;'eX'rhEsSo ={O#y|4Ho rzbX-QBC _XaXuUZNR>ȷ!cMQ$nF]v'}nAҀ},VKiw1O6%Lx9*#Ց^kz?{^_#j@Cl}5}=1?s\k6ӈ(L&{C,I,K cXv42IqF܈Q   ۲ F|F@iMZd5*)#MzoY2M$4$rږp_#L)p6"˰bq6$Zq.&T\ RO+#e:Y5Qz϶~G5/ HCwHߏyM!# "3~?jߏb0$y#CZ{#{hY^a/oc [dxDXÌh؎ ^tbzeb$+bȨ'q\$j"$1^*dR=&py,W9dzkY !&uExn:>2ɪAi'dj9 N8fLHd%v"a2@e:kMGzkZ~?=[EyMie{#}q>tQJ@ (%PJ@ (%`׉ ؊#o8fAж U;ѣޡig4sTWTWTWTWTWTWTWTWTWTWTWTWεbyOn)Tqm#D 8bqLT]9bqLT]9bqLT]9bqLT]9bqLT]9bqLT]9bqLT]9bqLT]9bqLT]9bqLT]9bqLT]9bqLT]9bqLlj-Bgrili;)MʜʜʜʜʜʜʜʜʜʜʜʜUsU5>ɒͥ'Yڎ0$ zpE C⚠rE C⚠rE C⚠rE C⚠rE C⚠rE C⚠rEќtũҜy:-vӛ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9榫3b 5FwOpImG0H[zD'8Ou#Җ*fNJƓa\iʜʜʜʜʜʜʜʜʜʜUKiC-&]0dћ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ92ZJ#u#!,竮,竮,竮,竮,竮,竮,$]1ԒôM G-U$:y`TW穮yDR]E iKuNp*G-U$:y`TW穮yDji:%c5ҭônd&-EuF&<]u-EuF&<]u-EuF&<]u-EuF&<]u-EuF&<]u-EuF&<]u-%ꤍHv8QqNӛ3TՕ4Օ3TՕ4Օ3TՕ4Օ3TՕ4Օ3TՕ4Օ3TՕ4Օ3Ԗ2Zj#C;LJ}[olBsTWDܷUWlBsTWDܷUWlBsTWDܷUWlBsTWDܷUWlBsTWDܷUWlBsTWDܷUWlBsZbCݒ&mG z'0WuH#zSc#Pq?jOsrKm툏CJÅm=%|z}ck#?" iKuNp*G-U$:y`TW穮yDj22Zz#}7|%TW>?JWb 3.~ XKj|N}S]ΨNpG0H[HtTW<"m" S]󈴥D'8Ou#C·FJ'j;"I98Oo<"m" kJ]I%)Lmv%ݬ9^洚p;tp&n)+72骫p&n)+72骫p&n)+72骫p&n)狮"CK#n2Oכ@8Օ֕ Hpg2Exs8^P\p:4g.N+'*i+g.N+'*i+g.N+'*i+g.N瓮\NؔizT]R]Ғ͋^׹9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+!_Nv93sV+sV狮[%EWs}לʜʜʜʜʜʜʜU1|;˼f'Wljg^Za|[%EWs}לʜʜʜʜʜʜʜչU1ռ;˼v%υkg^Ra|[%EWs}לʜʜʜʜʜʜʜչU1tjU(UҊ Uƛ{XHlv$FΦM;hOֱ: ϑzǼM<ǟAVp>B oͰWR˱riL|6೿D[]@^߱XwɢR#p8&_N1;sZTWTWTWTWTWTWTWΥ,c;+kbծ#952,e#ͻ"z[#s~YTS99EqЖvs$DVE{_#:3ݎbϦ7(7 OCyǿw ޴sE[l}݀6 ױ_W+_4v5`=^#O}0٭Į \mm%*+klɝz9lG*isTWTWTWTWTWTWTWεZ^gbSIlk>i.m9d4nkYug5X&І띐i#  N5E׵w4N;+H߄a\Ĉ#emW㫝cN}Uπe#vz?[xݷ9عx&_:Iz5Y|fՙkÜ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9梫7c#1CI^"|~t;w8^\k_ڂsa2 39d'= 96j!/g7y ~&MHxZL} <8KU~o8#~/eӚOuc1ԵP9Ҵ<>k{uw:_s`u0O5[<㋆ÒoW\x훕lRQؐeNHWIAܱg%9Id q<(r RC/}دF"yOش3Sc/^G쏁EWbim jE2 \5+l̞B-9bV5==S?y"=}:m7ptncqq-Sɨc}!CxcὍs_a]kVLAwd'#m8C mo,_0f\lkpQYsF:Ld%rcESz};Kt j; YYYYYYYY*|<9JrhCRۿ`Xyl5O\hZH<.ze(Nøo]pUA89.ү+-3ool) Qi `fN MJ} C[QA|_֗!^#Ď |@sZj߽_,#<+[D ڿC<Ĵ5ofaT;4.? W>wrod7.n}sx=~3GM:KɃ_#ŗbÐyHΎrJ \VaEx)Vd#G4j7 ǁ_YF39y쏓ΙND5xo[`rOi{*rQs5c!r_rUtHj\w6I;{~% Y}HFOmO@̸ 9.5p'`4F~1m߬-eZ @_O8p[Ε ~ n^7nTa3?Mև1'zP2̜ItϏ{IIeY~Jtiᕇeon9ɽ¬\7.kQ=p诅r(*F1p~5>9nA #'dpjG 4I|B>k3$ ]~wr>>p?79BdkoA&g{v-Ը{5nX+5|p:;-uu+OḌѱ>0-<á 7r<1=8ώF +B<Ĝ#4vaŨoqCؑMx4!8oǧ^.mm.-!r,Vmn!`#8e~܎2t;,2O9;3ѿj id-& d] X3Fȼձ=h񇁟ֹ>%r h(]cH&1᩷`ZgR-PKCPU1^{_4S&vsNoCG\+ c+';_'dȂKxt3]Ξ4EVVbm@SƭL%FI(8Wlb{[]3d݄wbڜ)ޒHm|}ʌon"~īQtw:9̿mrYpېMBZ\šeN`5'#r5d=o? sQ^W-(CP}H}*]wSz0mx qb;KZ߄IH3?amZCLj,_aUEQH>֑CNt y3[Ls6|{|z+2{OfDHQ{3{5g( ,MOp=ڪ?NM4;wxXͧb}xn{a/)QY0GWJ{Bz/sԔ7&v|k6'`ߐՀ˒Zcfn'hW|)^1^t]64&{cxmCd}; t-Fe>xrLoIap8W-zrxLLz s;Z 6-g 7h<Yz9lm88zwLfƗ6˶ʝ9 ߝVZD#;-psv}|ٟ,Nsb+sV+sV+sV+sV+sV+sV+sVYWvOQ}:^.}tCO>]or~mPX*!F.:< ѭ yEڇb 7.MUh ed]}&rޛ#/{qϝy|sw[~q™5[}9&b}m5F~b{k'?e饇h:ti4(Shvb*87ϲ#9Rt2?kyqIC=w Z,VWw` |=:wv| xx,|.S":l;NBE|3a|puC>W-ZFѼߧ76\TnGxy,j囫|Zwċ q t 2! &q,ν,b/GA# SiEZR;ayw[ X^ 5\8ˮǑGp 2/]dh'!v2Vz~ ?݁ڀ7UɭZIL}yW{~g@wĨZF[UN*st7V^퍪ܗ5%fI}\4'+ؐ˱".Jp }{swswafl%&>0犺 p ܄߿Cɬ.\GPO 6lӯb0e/;1W\ijAxXreYC?o^)nǤkV2G2G?d"ޟk`3qc0+k=&k+ ig0vnu@֔ak $G(7'ƞicO OLL>Ü9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9Un?a~'Y\=269y|gt>755eMu+Bc٥A pj[}Bv5޴kN|3iA,RPXs s`?ƺ5{qw0qb91Ih^Zcq?J@ # ՎS_6WSә-^,paؓMKL81"PJ kg-uai؆bz:K=3]0GlEeSh.ٙR˗YɬiF^[*QER@z9CvnZ5߹S֭[1v:4㔎nz}ơ\wv C⚠rE C⚠rE C⚠rE C⚠rE좄28T:|$dFL!?pVӃ[;Ngu7!8{8g]x(6ysR#U$9=܊X8Woʹ|Mu|?UWJ|$3P]HDT]Eg+IW#epY?YYYYYYYYYYYYYYEUP2C\I,s^:H%?لhqV] Q]qV] Q]qV] Q]qV] Q]qV] Q]qV] :C*PioDN,GueIJYYYYYYYYYYYYCYMYG /-q$]Ä]FÜʜʜʜʜʜʜʜʜʜʜUs1[#ͻ'#G- D iKuNp*G-U$:y`TW穮yDR]E iKuNp*G-U$:yQWMn 5FwٖÌқ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9+Օ9檫&5k#ͻ.0'7sV+sV+sV+sV+sV+sV+sV+sV+sV+sV+sV+sV+sVYWMf 5Fw07sV+sV+sV+sV+sV+sV+sV+sV+sV+sV+sV+sV+sV]W['1oKHj%k;&MMX* kMX* kMX* kMX* kMX* kMX* kMXFKUc*~2 -&vPMʜʜʜʜʜʜʜʜʜʜʜʜUKU\uGc{ƭl) mG4B| YD[S]E#W]YD[S]E#W]YD[S]E#W]YD[S]E#W]YD[S]E#W]YD[S]E#W]YD[kIY(jjju]PJ@ (%PJ@ (%h8|0ЦMĜZ˯5 .k~mPJ@ (%PJ@ (XS(11Qi(%PJ@ (%PJ@ |"Fm֦*%PJ@ (%PJ@ @kSHPJ@ (%PJ@ (%#dmPJ@ (%PJ@ (%PcHuPJ@ (%PJ@ ()5VJ@ (%PJ@ (%C%PJ@ (%PJ@ |M ~MۭVJ@ (!:TTTXjkk͕@||Y ZZV.QV%Pc'cnPJ@ (%PJ@ (G/RA^. гQ9J,);Ґ[2W0g)˧q-mMu_);vH{4w}ڥmηP} q,G%U1/4,z%Pb5EJjN&e,Eaq01+Ky wFK[>پ+wĿAMQvQk{#ߤZĶx6zaƿ؊^ݵi~Uϱ4kf:J@ (%Z$&ZY{3j PJc<Ӥk k u|yîX[-+!d4iUd Y]XJoo\OD[EsK6&r<\KWX +Oyu롺,Řg Cn7= އ.='8Gujq*G}Q6`i>Ln_~'<]dw**܂xIMGUX]inkc7M QmK: e{ک7Yۻ e(m46Ir+f: uvfasٵȻu:X-G+lYy+n_9omZ^? cWZH (%8yMj Z53~:n^2% +t /j'tБY.m2l,Kȱ}P|[^B$9!M7SD` U>Zʾ}0rxOTַQ:מOx3a/æ`lϐ[V2Cy.;eUijMԕD،149 q(vV/El #H"` J0b`0^S7#z8:O^[%_ź]CpsO݅ZuHsP3u].+uhpZم-S['v=FN5S i6@NǸ: 11cbڜ)4O~>n]/t)YԦ{PJ@ (%b 4evy$\(/E)Aqf .C'#5U(/AP6>?%ETp5M`M|<50zT)޼ R.:&Ƒ+`?sU.l.]b92:ǰ;Ez]O2Z8C^%V )bcw'́rWUy=:{t eoBeĦb}GqwVo";G=ُCV$+z׽=}gc#wc@g9hƃ5W|Y:Uy{g)ڮ[5RYG#MFՆ2NbQ!R1vX> a6Y6Ú"P~s\/.Ϭ@ǻQ .y_!v 0o"J^#C{{C mnw5l}yeuVep`p*nC.Ţ +\޾R~?}*h[2B)"#+Jx1voR-cHbz9f9U»W1q&S@tzi9/-A>MY7^[[Gl;[}`tG^] %tM $cA=L|kãgc)tc9<7x V@Ue-:EKՋ_*tQJ@ (% #qZMYG:r}Ifuq'ގa޿11)1/&'r!y.铧YمY)ku#F 태-C2˕Y{wΘ.05 OfCҰ(@=ay!T{P\!)tM r/9KQ_Z]wXQe0_'9OM--("h ̷i}oM1i3`NKp4xf:<̿ kWūx#s!31J{Al\FͤpB CHI(G A6ɖc-XUyHJY>Y30e}-dYːBn܍idn3~9g9zV2ڤ!e[qVӨQtJ}N 4fOT́dhhB,̞b߿_2Jh33t6XUT7/~bf*<9We(xrua^=|zv=q}@Ȓ 32ͥ+2*>C0D3 w$-gJ p8Gwh~mAދ;ޒ_V鱎/5ۇLO~talRyʱ&?b|B]:j7l{DT0^g]դz!Ltysq^~[ qW5_VaqPՋE]F3HWh[ kr+oe"8ˬ~`X^4w^>u%@oyĂ72=Ό1/&7\AWa ^[3 ]2fL&ҦygL5K3PVxzpBhYŦ ;O?U$]؄&YiatPQsl!ֽw_}݁ES{اIKG8\+ Wu8~+)dm퉛G3&TG\eBvq6.ZgG6s=X9|4ὖ:vF1ʲ}mֶ} ` 5Z)d5$3W1R})v|GD*q]J?}%ei%Fm%۷èn`VSl`wz8V0.i}7}]í%.rR (6;[*/d 8 oLʽs6{u1rV>! F8,d1?2} ,o9Ҋ5^]⃎4:y,_2P[ӭ)%Pg@ %M:mU!CB^I"ދ r`aA$ %%%_lGa_Ȫ8>]7Za`l[XiNZm̍M}+2auKYo{ĜkDƨ3s0φwY$#d-_2bb%&mCNrJ9 HARo 8;4ׄ7&k.TU!g :`66NWLhqŏ-@E:'K`^5(jQ[2Yfd`pGp;sM |ڹ3#:4x郴$ɕ}0Į8GzZ,r<(C֏'U[9Q\ pK&AEH:.hԔq~2)9z20~Z\<cqybaAHiA%yy̐םKo.38p¹[vF}}:EPs˯K?˧f|NEaE`$/qxPIimGdO,χP%u R2sΒگa#a˖"n"-5sy86ޣdqEK1nVDzhJ, b€a, kZErO8A= q}4 &4_v0 dB.:u5w9.搶2m (]Ne˳<{/cOCrow_5| dϱwfP ~*(j9A}AbF?XާC7Y"bR빚(Sx U"`/_O(G 8*%PgiG -s:һ$B=}֮cl*փ-\Jrڭ*·%vS+Z w9Y!jVld%p愉K{斑/Nb=9zj<> IDAT+oRj- pX-wğz{Y_†_dž]PgumZ&PɫWm\2Aº-9^Ws )«&:#cNJ|W~>2oȍWYFXG8W~|K?E:{"?ˋ5tUHVϟCӸ勐3 W-BlIee KG"N6/$l8n'Vc&hf`{i)>XeZQ-Ak)/§=(g)prTbUdM3X -Ɠ/ Mn۰֜:=b\Nm!..囗?]a寖&DoyS.23o$YxoBx'ǏF*'cg9~FwqDzslZ.9X$)&Cv2Zʷyxfht g@ƀpn G(sk?@|sRk^>C9&PKS(J|o Bz'~R6): m< J:'~ ൺ7g9'o9z+x{SaL 11jx{Z53%/  O6#d9֤{ *x߻y_xd!> evPo[1wLY̿Xez/-dWD;_t^^ƪCUJ@ (%N鳎8DNp34d pEJy8 XC6ywQw?Kcud$B6%uGahjeࠂ {?ܐ؃d [ 3+í 4s8XX3,Dpw{˃3z~߇ks߇S:P~+̃ؼ5f=nymmԯQ#:bv;0En@1gHGˈh&g'KD ;w솶v'سQbdmJ4''O_ڈ/_|V'vO"._ r˖$r兠Ib_}^yܼzZۜPkR흿_T3 Yrkais'>%~-9$Q'["y瓳t6=꾯.]hDzc%" CXH$GIrP+G~9'o}\a>?'dN`ˁ][Ѯ'FqF;tϾloGS .;do{Ȥ[ }KO$},=jgOzn]~:YCs*||IW -r4g[uu`H9gzi۶2ܹØ\ʧ1t6|z|\b2o`o2ɤs}R}cG_c3 O{ݕIɄ>k'Tr.h*Lo91;^AU_z3Nм JNCtn3/Y8Q~+rP,e.O k%mg)ywUxu/߇?>MOR!j螞L* iOQ53 WTrBfj$䓌{WLfex-_Vuΰl%Mnzi0CtsmwQhf7[w9{JO$;_qZ,@X^`h$փy H4_>-'|`:߇=O .RΔ(_ W4哛 ?ۡ'y NËQ%`]E ͓wk` oəWԥۊ\vCXV`  x8_D`豿E@DrjPԀP/2FsP~A]i@ږYfOJæ鰟l    PtG*zuJ4A' ]DRbdggWQS$1@@@ 04kh$bfC@@@VH A/FٻqS8   [`m$=@@@@ 0TR"   E 0TtRDx%N?ELdL5m5w3STUx;z@(PPp\ PZHDܹSZNݻaOM[Gj7 *@`P9C()p=11!nݢEIݹQXƴqFJkF]A ,Y`M"K*WjT% d2yZ5JaQrN]d19p@@E -JT'~#[w!   /P2"   e/@`o1D@@@ @@@({Ce     /Bfff@@@@lAw^V!    Bd$Js߈    @Y JL&%N*L@@@@Ws;9ЫIENDB`django-hijack-2.0.3/docs/index.md000066400000000000000000000071301264367662100166040ustar00rootroot00000000000000*With Django Hijack, admins can log in and work on behalf of other users without having to know their credentials. * # Installation Get the latest stable release from PyPi: pip install django-hijack In your ``settings.py``, add ``hijack`` and the dependency `compat` to your installed apps: ```python INSTALLED_APPS = ( ..., 'hijack', 'compat', ) ``` Finally, add the Django Hijack URLs to ``urls.py``: ```python urlpatterns = [ ... url(r'^hijack/', include('hijack.urls')), ] ``` ## After installing ### Setting up redirections You should specify a `HIJACK_LOGIN_REDIRECT_URL` and a `HIJACK_LOGOUT_REDIRECT_URL`. This is where admins are redirected to after hijacking or releasing a user. Both settings default to `LOGIN_REDIRECT_URL`. ```python # settings.py HIJACK_LOGIN_REDIRECT_URL = '/profile/' # Where admins are redirected to after hijacking a user HIJACK_LOGOUT_REDIRECT_URL = '/admin/auth/user/' # Where admins are redirected to after releasing a user ``` ### Setting up the notification bar We strongly recommend to display a notification bar to everyone who is hijacking another user. This reduces the risk of an admin hijacking someone inadvertently or forgetting to release the user afterwards. To set up the notification bar, add the following lines to your base.html / to the template in which want the notification bar to be displayed. ```html {% load staticfiles %} {% load hijack_tags %} ... ... {% hijack_notification %} ... ``` If your project uses Bootstrap, you may want to set `HIJACK_USE_BOOTSTRAP = True` in your project settings. Django Hijack will use a Bootstrap notification bar that does not overlap with the default navbar. # Usage Superusers can hijack a user by clicking the "Hijack" button in the Users admin or more directly by sending a GET request to a `/hijack/...` URL. If the hijacking is successful, you are redirected to the `HIJACK_LOGIN_REDIRECT_URL`, and a yellow notification bar is displayed at the top of the landing page. ## Hijack button By default, Django Hijack displays a button in the Django admin's user list, which usually is located at `/admin/auth/user/`. For instance, if you would like to hijack a user with the username "Max", click on the button named "Hijack Max". ## Hijacking by URL Alternatively, you can hijack a user directly from the address bar by specifying their ID, username, or e-mail address, respectively, in the following URLs: * `example.com/hijack/` * `example.com/hijack/username/` * `example.com/hijack/email/` ## Ending the hijack In order to end the hijack and switch back to your admin account, push the "Release" button in the yellow notification bar: ![Screenshot of the release button in the notification bar](release-button.png) As an alternative, navigate directly to `/hijack/release-hijack/`. After releasing, you are redirected to the `HIJACK_LOGOUT_REDIRECT_URL`. ## Signals You can catch a signal when someone is hijacked or released. Here is an example: ```python from hijack.signals import hijack_started, hijack_ended def print_hijack_started(sender, hijacker_id, hijacked_id, **kwargs): print('%d has hijacked %d' % (hijacker_id, hijacked_id)) hijack_started.connect(print_hijack_started) def print_hijack_ended(sender, hijacker_id, hijacked_id, **kwargs): print('%d has released %d' % (hijacker_id, hijacked_id)) hijack_ended.connect(print_hijack_ended) ```django-hijack-2.0.3/docs/mkdocs.yml000066400000000000000000000003251264367662100171550ustar00rootroot00000000000000site_name: Django Hijack repo_url: https://github.com/arteria/django-hijack/ pages: - Overview: index.md - Configuration: configuration.md - Troubleshooting: troubleshooting.md - About: about.md theme: readthedocsdjango-hijack-2.0.3/docs/release-button.png000066400000000000000000000515221264367662100206160ustar00rootroot00000000000000PNG  IHDRpdsRGB pHYs  YiTXtXML:com.adobe.xmp 1 L'Y@IDATx] | W~"$؃c_;E[ڷ_[mUݿEuA_[բKԒ $D""߹N6xϏܙyΙwΌݝK#Q} .Wq`9C+zWb^NJy+zW*mYڄ-r C=rNJy+zWb^NJy+zJ#JL1.ª*}NC#^ l!Og^鱰uļ>yBt [G+[K+JD1.Cˡ'X1cżRJ=V+X1cżRUiU+ƥYXC@=V+X1cżRJ=V+X1cUy^9K%sjbKzf"V#\c^.GF]U0IX SJ=V+b3|Py򲨩sx2YcC=VbQyjBDڔxg^Y4yeselLSWX>g^Y4,qYpXC4b`T!֏W1Le^ayeT!֏W1L-*b\օˡE//1`^i˼6J_m9WZ$l2lcQxZ1.Šc9ˋm9WZ$l2lc"aye#m ۿ+is^zrhkis0H-.^y )e.T1NQBC=l5?a 4c<'^T˓8xGy+1VB)βGfR^>l,ci:իp3VKRuW=V+X7^Yu(oªI3zWX Kqz#S21^dZ(|x~s^wcżRUy畑b\ޅU?s$6J_m8GDqy+zW걲+bl-Aa*!֏W1L-/2<W,z.+zJ$Ÿp[8yķdDa'a;ZR'Gm9SZqJV;o~i~5M-;mr@@ϫ+HnC=Z<b^ǪB' oߝX}Br}탯 ZPZU?<9K{CȧtpvEy"(;Ҕ71e?XbghȊ[Cb9pYfNJr^FmaG1udhkZc~Ai\kBAxlqI1~nZ-h r_f3Gح8c]vZ)zeҔl¼1F*!HXxNJyī_aurVĘuǓt>%@[U.|p=+QRn^C+^%,| B>'PNf1,< UPsej!PQ8de9ЂJk1N~iVSb~Ny #R[CP<ؽFFz7/B)`_Te5͹ I;⯙N mR<;kj[.z0),={C~=~ (jGyF%;f*XxNJy++zYJU?K%0W٥vI\_ ]46ڒ^atknOq.U$eAJ8¹}aB.q܆~MX G|:6uO✰֑A~ZǙ1tnDW##JkrYL_B&HΉocdKGSO2iX5 [R4T_S5Kɍ1l)?W ,ɘ1|/~!~sӍxT R=&$W9 >: J_~d*&2&푤Y!: B$^ʙuy*2`xwM2~*Z(qGg-|Km 3X,1t|EmCŦ5t~se?pJx/ %Y k؏q;$ YvC߇[~NR?)a"}{xp/6U\lEU,ӖߔҸ'l_= eg`+M%R8xGO,ʒLkPCxXSԔZ+TȉċSλ}Kڇ?~E`aR PGم8E[&u&X9I+Wʚ!L{v†g)& t<_&)a dzny6v}Ⱦ9p>d䥦RMĸŔ7)ZbnX؇U|f6&`p5/GU#Kji:w!Q2>فfģݬn{eGZwIkʍhl m!†ѧF.H¨UF " YoY@۟T|5o Gv_ZcWU @5RO&?c%X=^sc݄.j_> zE>H]w~-GrHWŭ8t~n0&nϒ,vaЬh;3}ܯI8ixCC7iڤ@-SKE#sn 5 _QMKɀлx:`8v[vy$-KFC LX6<5YauG R+ zP~0c;vK Pte_fcgP FbwtVPO)4^,i+尅>yBpOۤTy.0̢د:n i" E!D%_bϼzSg)XKޠA6@JXn @@qB{xZ=>Gb`:ER^6!mm *|L]/S򘔯9icK X۟ZNr}!3sl[4rƊv5Es=ݪV/PbC2QH=5#&>M!GCnyNlkWvu5U5 &|}?~7Qb7a}k镮r'AI,Lh7QwQ#\-}GS`X\) "q2>EIaS(з*⽭1ҨX4 㚋1m10x hu|9^fm4,SWGw|E^5Ή]Ai_.7tc_qg;4xb ;c?_T,;+C} /oae'qΊ>*6拾6yϭC?x#Ge7dKE?ALjt8wb 1BܓAx1)_ fEP=*xȒ;7p*R˵a}ݥIUԨt U^ j`o7-"[=ζέc PUXF;/p,2okA^72`*$GBw)X(gYĭlډʮ*yK. $x%:+wKH)8!YU$>|+kQ0XymU@xTfV^#[R<10RLiU($G w{Z PV"( p4s4ռMTqKƋ+92ߒ%ӹi 26qNd\4|kb2) -/.YBeڼ)Z(OiC?pHOew1(cDG'EOoLa#=8q;y+2jJuˡ3bWtYw7s+gqNq<`1?"X `;["p*&`[ʓ'ѝp{Qa G.̨!RU%sѱڵw`DZ kh'ONA"h>v'>Gtq::*kn{⹞"oh_s;9g63{pQr}(>^Y#aSD,3,ccRT^)k{8%_LtMc87C4|I֥\ G6V߬Upr )ţ [?d=kI~Q3gZm^'wo rpV6@}5b-eWP(.oQ1hM0sn&eMI3GG"pzhMOMO,YC*-]g|n\?Vhy!ݘ~㬱^j3Y(!Yi;+ X ,1UÌBnڝ۞K7ͭ͞z1 puыHs GnĈ fpqLj١ޏcDEB^YU$ٚ{$i7RK[30(ߎZx~u`e˅#%_4fg!\_&pu?-*/M[4ǔP@32^M NHI4onF(m$Ą{4x.~e=h:M.]J^Ǜsq'cƊyt#+XtCGLP s4+.ӑ(vzT%C JrX1cU싵AkпU=vGz}7 =18ר*O*#x+aC%HqGf 7ӛ ½IF:)3(|^GƓ%$mwo Ou"EHe_z)Y(aPC.J:5oDn*9tՔ9k$[VVJķɽC-23-޲_suz[͋ Hm'/Hr?IvB3;7*zm(ulj&_}~>JV+㵶tBiZ)QSsƶ^KvQ8~g'*~jfs &Iѱ5;{ 0SpvNO=XKT+wvXU%YWT2$Ӵs-}L{٭Y9>D郇\p| jr5֠C"CVSf_]un8WF;mWI5*4+>onmɡdWI3[F7xS9aR3q*hnrqk-ZY,1HɃˤ8sᆦqqWA%ЗYO7YC hũH՚j.QINKn6L2hNUYqlަr+Q=~8|]02rқ 1\JHjZ}PEqaV8,jiX4$%'i`(X?K!WFѦciW5r_:VPlF}P;aᱍf4Ʌ^Uo12[x~s^wcżRUyUŸ ~ 'z̘Wb^c옋L͗Y̓9x;;-*P۽SPsX1cżRJ=V+X1ceW:Z&MYɃfTC43cʼ2D1:>+C43cʼ2D1:>x%)ƶ2VXc4xNJy+zWb^NJy+zWRë j2oR9'2.J<J(1qQe^)ǼRE)yrJX*q+e\bJJTIqGQ<'9&bx<,!cϼ2R 2+sL,0,!cϼ2R+RX0L>=SqN1,FFbS6#Xu\N"W&"&X9a,&zd^Ǫ,j%GsCPUQxU14xE}#^?| p?xUZYHG@=ř|b^ǪjCS#!ˬFRE<ϭc'+%2pŘɧBt [G+[әWz,l1l!Og^鱰uļ>yQqȊ1ҧנk4?0R %dWXa^YBt2#0#`Xũ#0#0@9Gs`F`F`v*N`F`F@ P׵1#0#0@yA2,#0#0@`ŸHqaF`F`+e$YF`F`"!P\`F(#"%%E]Fz|prr_F40weDld[aw)e6Gf0(BCJˁ(-5b~0TܭR ǩoOdffB/o6éƒBÊ1+e]FH\~wE͚5;;"DžD ??yyy<<WBԭ[W&$$sqō(0#P.^^^bdp`J7Rw4iRں@#,/^4©:īCq!0#ZVuʕY).#7lavPb'|cŸ0qF`2O!{:}CΊ#0#0@)@R0F`F`+~ #0#0~+AL7ґGy]=Qܜ0#0#P(V8)do#<1赙Rl~;$a^wadg/x>cF`F(b/Bg!h_4,{ݻqw|f`PYGV^:Nn]ħв'[q575܊n/%OpЬKq7]jꋋ ÍhFL 4PJE38C+<¯CG(42(;M3@3vD%sCw1yuѶY?8Ⱦ|>(` ҷ%<\!ŸI!,(ŵ: ~EtX<]?:_iEoi4}&=JC#ޣ:/.& !?شF|v.%s{ _F\GmH$r 8yO#dVt6+bZpK  7T3Nbx5x=6h])L."dMfyaM8\qA-8 e@1E $ YλC6C4Ul3y|JҺX0f&"_[e,&Z-Yob8*_ɋX[pçfO8'nc|*&5"28A/FكQ]d;M \c';<z>FۂEVQ%>ͭwAy+H='qþz1- {hwN_QFذk;o>w[`u|35ܫ#?ؒjjY>p`!p. Xꘋļ,٩u)vTR]?q b!Oeqd24}u4XR[PR\%yi Y3gl ƖS1]e Wb3@p`B FO+1b`H4ҬTFЗD/=JD˕ {}0×2sg(U:yߣu=݌% )E"IYG1:}19Ի荺yMp`e An'(!{$ByJGJ\_ǘN>>[!sp&)I,2g<ud5~x%_96), _`[t$߁~/ݞ\T(+$<]+RH]D &xI9NDRz v3BS="$QxgW_Gcѥ8wyumNWB'Fԯ++.86CQ<,FcACaHn&&!YMP? NDSq7tp>ipj=C]`m>`c_9:{lF Xtvii&DߖtƎC+y˻ WBuCX4_=K8w~zgLqzX+?Lpb2Xdmg3z"z<@s=#ڑ=CV>y W_\"6dqMT]}b馔Ɖ<Zҍ0}^vD܅s<6Bcd馽Vx~L74+7!Up^MM$"kaDlZw1-rȺD:ryY).0 rYbn Řqd .õv[ $fƽƝPy:zhw5Nbp,7ahӲݫ1$J2<88{EݛZ`E69Cy{7i44JN~hu~bI7؋ٜ-Agp;G?vxPk/37>G&&vެj 1YYKѦ^<>~=Đ.q͋Vׇ;pB,rH ? ]Fs͵cw%vu{EV6R߱1`x|pfiJJ>zc|04X,zݞ?fcO%4J1Y&Meg`Sѱ=¦!R>?by} kUM;x:!c,qq.Ð #ŵX{/ſp='5H*ߵVm_e ej_܁n16AI)Ҧ;z5A^l;s )\JʓOHb\ʵC4RLґ&45MˈCH"%U á*бiV^Tb1R\8\L+IIIGgXۼ(Z+ޖv!#[ t,?-ͯ Ckbe.&$runNd%ҍrRsСW74sGp6Ӹy?vBrv,$J eJR0U-scFs16Fv!Pi׬rӶĝĝpQ+moJN IgDծ,m⩸+h-͵ztu*%rs&].ZN":.yN5j &`uTGx5l9H:7Yh\ǎ R|5Dˆ5tyni7$rn^Rs`U+" RRӛnV>6N kFa74Yb@F.<:a0oqbOf!i1)39]قy>Ä)XdEsND_0?1ҧpE`q죧`we'& B瑞j-:{ys똦 ]C~KVO sw聼s衱GDJT-^(1ieSS$뙈:x6:a-x.LV(_tlesZ^Y?!>& I9ʦ]ñqFV?۷ȑ#MKyqk.ʗ0l>OA='r:Ehɦ ǝ 薙_ݞvֶ=)\A_RnAtƤA-%\z49%["!%ͤ&~ŶɸJZG3'MaƷ?⣹`{;u;LO 6ut@dwe_xOÜOu[0L k]qa2,y(-t Np oΩ F([J|5)BgbA桻C+C&7=Ive +7\CFl]L9̈́XfBӴVI͓bQ6G>hMɇ`!}HXL\ Փ&γH0t{ o5n^,pF3aiOSSچ3+ۚ=ڲ-I1$׊R*ǐ>\nzca B^דK]wk/YVG'4ο4) ߖjƸRs<+\Plf&m*+"3ۏ݋ ݐߟ8/6[.c)%y-gѭcmϝ}I\JO'o E7B_R,:ڶ7b@AJw.ֺ;m}ywCdEd8ݿJRo gy\\OLŴ Al&e&+5ڛzqOl iUDnqpYo !A9PE8fLу#'\(DĤ*^}Wjc`xncbp\Cel-;]>'_䗕GmF\oE6nk/ <G0?e0+@eXRL],i]Kj)CR~S帬)Œtd뺑шxEi:Q:Evz] k`i0zFxM,)r$Y9JFF_Tv{~hQX PW>]8ɒjF󉸺);/N~tH-QqіYO@ Yr_Zu(+8mt^GjREs1HGdҙdOb嚕Xx^-chۅ|"lI1⁽roB9X^Bۮo>D/@ݍ m*#_$JxIDAT\sUt]ct_ny%w D!&Xxi!iPuʖ\ .~+*Tf1ΰWuh8&>O+*z՛>;io^f|&Plв %0{y 8vw'.hv\kgڨ /6hF"r7C#cQG> *N?d<ޞ=MrY56^>h>kymK0z~#:7Ѥ>HXirETd۲ Ԇ2]!W%<6Exj tKd2)ÃPJ:u=y55[yKq$i #h]l]!ҿB-푦6z`nu(fKݭ-H$TDN;9)cuϤ8ޠu\1/Uzb/hătutu$v7XJsrHr`GOh3_Úߑu&;AY_xWF=x7T+8$HT(wCڮ/iG<8hl.3A)DJVDEitavt7zFd-3>*o;riY=%c,UtW<>r i7o hzۅftSpgXkF}TjC<;*ѷ}m"[ű:3u&.dv)'W:I?vӽBsAGB']%b ƌxOPYqFz0Q(8M[a=@QIouFUƆlUZeCģz&\Wv6Tk?b?.|+~ޔHθ*J e$KI`Š)U馉NɠhF#1PEHڥ;Qۛv(ŹIMЭil<C^L՗ <."(~ej-Ǘ.]*S: e&m[9}F^F*[IiUGu׀k[q < B {> 2Wde ʦ gƣ#^y *cSerR~EzTN0v$W (촫sU̧4F7qz"ki+ּԤ''RD,IJZѹ3*E,}-{vF(零BsqFHdJdSm4REj7/ng׭%i;KΚEeֵr*ܿmw0=,"V2f"dbͻWoZFbwkVO>WAfmh} kWΝ5uohVE JХK.6üq!&|/`X|IpP @>t:>zYG8N7Gu .|oD~k3PYV$ס<;nPp@!;ro-,qO;^靱CGC^!2lMhIg"2.K6QtSڿ1"(X/I8Rl>˴U/sO*87!;v,% SشT~}A E7GёGp,AnxozZ\q_ep$?0қ0bdW[:7}m+v 0|![a`z޻z|i|MKt| lymۏC/?ћg~c(/ӛKJ~-AadqY)6ñ%>+ u*GvbG%'tkx@o.D\:&-9y8Ce (.p6AlzͧJfۆC_'ѶuM¸Lk9EÍup] ~eGcu>FKoNNhAķP& }gx^N[(^XScl{4(UUlWR%[zzNXHYq9hXJ=wF;~eR~1}͉ˈIBdqY t*HDytۿ&2~7gyHIa@݀1[ V$ v|9k7Y`A[1wbMjݚ<5"מEF̚zy '1)`D*Eʳ|7|r=K{Z2;xHGV VFK3Э|eY-AG[Y$ldulu&ę ^]|~☔&)E r67^h^};G-UEoamӞm,;p1ȱd;iH.dIJ>1}r܉#[WkҫɚO5"36B>dѯi%l@M'7⤦POCB]P/ +3!k&O$)n1a%"bh"wߖ֋5iԸhu s{410vY.';ԁc]ha6)C,_` GW&"5ƈ՚?:ʰG|Xj[Б-4&e%#: }\c=bQ֔N ۆ[y@/=V!mbJ_eQJ6хOE䗣YlE]:E(}FX\aI=m^Th--Q KMͤv hѶRwHF wpLVw(gTZBF{ԡZSo.nY \I;0VyJ$jPu"[x.գ\:W̲iǡl(E +pGMl < Y6K`iug P^6Z-1PƢe\ 幓IV`{GT"Ka>w ٜ*I9wp twTcW,t[8! rIzf@ 1 h# KMw(wd(6 (I)Q;>/-4\+Y-ɈǏT\mZ1.r XPVU`І+e7=ɼu]<+=PaD;)]MLs[Ӥ7!CPVB꟰_5|=p)p_b;W0GHq@(ŸP])JJ1rZ˜Tt!].b.URUâ =\hiL"Y|ծ)_F`F`N1•&4 DKep\=#0#0zX1c@\V79s`F`Fx`bί O i{3#0#= #0#0#P^`Ÿ$0#0#P$X1.|\`F, J/ ]>>KXkCc$' E8aŸqQF`}5lt{"pmTRH~.jBaF(">”ַR;JoDŽTp355`]03w8{ܗKP, YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-11-10 10:10-0600\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: hijack/admin.py:36 msgid "Hijack user" msgstr "Anmelden als" #: hijack/templates/hijack/admin_button.html:4 #, python-format msgid "Hijack %(username)s" msgstr "Als %(username)s anmelden" #: hijack/templates/hijack/notifications.html:5 #, python-format msgid "You are currently working on behalf of %(user)s." msgstr "Sie arbeiten mit dem Account von %(user)s." #: hijack/templates/hijack/notifications.html:6 #, python-format msgid "release %(user)s" msgstr "Nicht mehr als %(user)s arbeiten" #: hijack/templates/hijack/notifications.html:7 msgid "hide" msgstr "Ausblenden" django-hijack-2.0.3/hijack/locale/en/000077500000000000000000000000001264367662100173145ustar00rootroot00000000000000django-hijack-2.0.3/hijack/locale/en/LC_MESSAGES/000077500000000000000000000000001264367662100211015ustar00rootroot00000000000000django-hijack-2.0.3/hijack/locale/en/LC_MESSAGES/django.po000066400000000000000000000021671264367662100227110ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-11-10 10:11-0600\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: hijack/admin.py:36 msgid "Hijack user" msgstr "Hijack user" #: hijack/templates/hijack/admin_button.html:4 #, python-format msgid "Hijack %(username)s" msgstr "Log in as %(username)s" #: hijack/templates/hijack/notifications.html:5 #, python-format msgid "You are currently working on behalf of %(user)s." msgstr "You are currently working on behalf of %(user)s." #: hijack/templates/hijack/notifications.html:6 #, python-format msgid "release %(user)s" msgstr "release %(user)s" #: hijack/templates/hijack/notifications.html:7 msgid "hide" msgstr "hide" django-hijack-2.0.3/hijack/middleware.py000066400000000000000000000016571264367662100201530ustar00rootroot00000000000000class HijackRemoteUserMiddleware(object): """ Middleware for hijack RemoteUser. One must place this middleware between 'django.contrib.auth.middleware.AuthenticationMiddleware' and 'django.contrib.auth.middleware.RemoteUserMiddleware' in MIDDLEWARE_CLASSES Just makes remote user same as hijacked """ header = "REMOTE_USER" def process_request(self, request): is_hijacked = request.session.get('is_hijacked_user', False) remote_username = request.META.get(self.header, None) if not is_hijacked or not remote_username: return # Ok, we hijacked and remote. Just assign hijacked user to remote if request.user.is_authenticated(): username = request.user.get_username() if username != remote_username: request.META[self.header] = username def authenticate(self, *args, **kwargs): return None django-hijack-2.0.3/hijack/models.py000066400000000000000000000001151264367662100173050ustar00rootroot00000000000000"""Just an empty models file to let the testrunner recognize this as app.""" django-hijack-2.0.3/hijack/settings.py000066400000000000000000000035471264367662100176760ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.conf import settings as django_settings SETTINGS = ( { 'name': 'HIJACK_DISPLAY_ADMIN_BUTTON', 'default': True, 'legacy_name': 'SHOW_HIJACKUSER_IN_ADMIN', }, { 'name': 'HIJACK_DISPLAY_WARNING', 'default': True, 'legacy_name': 'HIJACK_NOTIFY_ADMIN', }, { 'name': 'HIJACK_URL_ALLOWED_ATTRIBUTES', 'default': ('user_id', 'email', 'username'), 'legacy_name': 'ALLOWED_HIJACKING_USER_ATTRIBUTES', }, { 'name': 'HIJACK_AUTHORIZE_STAFF', 'default': False, 'legacy_name': 'ALLOW_STAFF_TO_HIJACKUSER', }, { 'name': 'HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF', 'default': False, 'legacy_name': 'ALLOW_STAFF_TO_HIJACK_STAFF_USER', }, { 'name': 'HIJACK_LOGIN_REDIRECT_URL', 'default': getattr(django_settings, 'LOGIN_REDIRECT_URL', '/'), 'legacy_name': None, }, { 'name': 'HIJACK_LOGOUT_REDIRECT_URL', 'default': getattr(django_settings, 'LOGIN_REDIRECT_URL', '/'), 'legacy_name': 'REVERSE_HIJACK_LOGIN_REDIRECT_URL', }, { 'name': 'HIJACK_AUTHORIZATION_CHECK', 'default': 'hijack.helpers.is_authorized_default', 'legacy_name': 'CUSTOM_HIJACK_HANDLER', }, { 'name': 'HIJACK_DECORATOR', 'default': 'django.contrib.admin.views.decorators.staff_member_required', 'legacy_name': None, }, { 'name': 'HIJACK_USE_BOOTSTRAP', 'default': False, 'legacy_name': None, }, ) for setting in SETTINGS: if setting['legacy_name']: default = getattr(django_settings, setting['legacy_name'], setting['default']) else: default = setting['default'] value = getattr(django_settings, setting['name'], default) globals()[setting['name']] = value django-hijack-2.0.3/hijack/signals.py000066400000000000000000000004461264367662100174710ustar00rootroot00000000000000from django.dispatch import Signal post_superuser_login = Signal(providing_args=['user_id']) post_superuser_logout = Signal(providing_args=['user_id']) hijack_started = Signal(providing_args=['hijacker_id', 'hijacked_id']) hijack_ended= Signal(providing_args=['hijacker_id', 'hijacked_id']) django-hijack-2.0.3/hijack/static/000077500000000000000000000000001264367662100167425ustar00rootroot00000000000000django-hijack-2.0.3/hijack/static/hijack/000077500000000000000000000000001264367662100201735ustar00rootroot00000000000000django-hijack-2.0.3/hijack/static/hijack/hijack-styles.css000066400000000000000000000052611264367662100234630ustar00rootroot00000000000000.hijacked-warning { background-size: 50px 50px; background-color: #ffe761; color: #000; background-image: -webkit-linear-gradient(-45deg, rgba(0, 0, 0, .04) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, .04) 50%, rgba(0, 0, 0, .04) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(-45deg, rgba(0, 0, 0, .04) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, .04) 50%, rgba(0, 0, 0, .04) 75%, transparent 75%, transparent); background-image: linear-gradient(135deg, rgba(0, 0, 0, .04) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, .04) 50%, rgba(0, 0, 0, .04) 75%, transparent 75%, transparent); } .hijacked-warning-default { z-index: 1000000; font-weight: 600; text-align: center; position:fixed; top: 0; left: 0; right: 0; width: 100%; padding: 5px 7px; border-bottom: 2px solid #ae9e49; font-size: 12px; font-family: sans-serif; } .hijacked-warning-boostrap { margin-bottom:0; } .django-hijack-button-default { -moz-box-shadow:inset 0px 1px 0px 0px #ffffff; -webkit-box-shadow:inset 0px 1px 0px 0px #ffffff; box-shadow:inset 0px 1px 0px 0px #ffffff; background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #ffffff), color-stop(1, #f6f6f6)); background:-moz-linear-gradient(top, #ffffff 5%, #f6f6f6 100%); background:-webkit-linear-gradient(top, #ffffff 5%, #f6f6f6 100%); background:-o-linear-gradient(top, #ffffff 5%, #f6f6f6 100%); background:-ms-linear-gradient(top, #ffffff 5%, #f6f6f6 100%); background:linear-gradient(to bottom, #ffffff 5%, #f6f6f6 100%); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f6f6f6',GradientType=0); background-color:#ffffff; -moz-border-radius:6px; -webkit-border-radius:6px; border-radius:6px; border:1px solid #dcdcdc; display:inline-block; cursor:pointer; color:#666666; font-family:Arial; font-size:11px; font-weight:bold; padding:3px 12px; text-decoration:none; text-shadow:0px 1px 0px #ffffff; } .django-hijack-button-default:hover { background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #f6f6f6), color-stop(1, #ffffff)); background:-moz-linear-gradient(top, #f6f6f6 5%, #ffffff 100%); background:-webkit-linear-gradient(top, #f6f6f6 5%, #ffffff 100%); background:-o-linear-gradient(top, #f6f6f6 5%, #ffffff 100%); background:-ms-linear-gradient(top, #f6f6f6 5%, #ffffff 100%); background:linear-gradient(to bottom, #f6f6f6 5%, #ffffff 100%); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f6f6f6', endColorstr='#ffffff',GradientType=0); background-color:#f6f6f6; } .django-hijack-button-default:active { position:relative; top:1px; } .django-hijack-button-bootstrap { } django-hijack-2.0.3/hijack/templates/000077500000000000000000000000001264367662100174515ustar00rootroot00000000000000django-hijack-2.0.3/hijack/templates/hijack/000077500000000000000000000000001264367662100207025ustar00rootroot00000000000000django-hijack-2.0.3/hijack/templates/hijack/admin_button.html000066400000000000000000000001751264367662100242560ustar00rootroot00000000000000{% load i18n %} {% blocktrans %}Hijack {{ username }}{% endblocktrans %} django-hijack-2.0.3/hijack/templates/hijack/notifications.html000066400000000000000000000012071264367662100244410ustar00rootroot00000000000000{% load url from compat %} {% load i18n %}
{% blocktrans with user=request.user%}You are currently working on behalf of {{ user }}.{% endblocktrans %} {% blocktrans with user=request.user %}release {{ user }}{% endblocktrans %} {% trans "hide" %}
django-hijack-2.0.3/hijack/templates/hijack/notifications_bootstrap.html000066400000000000000000000011131264367662100265320ustar00rootroot00000000000000{% load url from compat %} {% load i18n %} django-hijack-2.0.3/hijack/templatetags/000077500000000000000000000000001264367662100201455ustar00rootroot00000000000000django-hijack-2.0.3/hijack/templatetags/__init__.py000066400000000000000000000000001264367662100222440ustar00rootroot00000000000000django-hijack-2.0.3/hijack/templatetags/hijack_tags.py000066400000000000000000000025171264367662100227730ustar00rootroot00000000000000from django import template from django.utils.safestring import mark_safe from django.template.loader import render_to_string from django.template import RequestContext from compat import import_string from hijack import settings as hijack_settings register = template.Library() # Deprecated. Use the template tag below @register.filter def hijackNotification(request): return _render_hijack_notification(request) @register.simple_tag(takes_context=True) def hijack_notification(context): request = context['request'] return _render_hijack_notification(request) def _render_hijack_notification(request): if hijack_settings.HIJACK_USE_BOOTSTRAP: template_name = 'hijack/notifications_bootstrap.html' else: template_name = 'hijack/notifications.html' ans = '' if (hijack_settings.HIJACK_DISPLAY_WARNING and request and request.session.get('is_hijacked_user', False) and request.session.get('display_hijack_warning', False) ): ans = render_to_string(template_name, {}, context_instance=RequestContext(request)) return mark_safe(ans) @register.filter def can_hijack(hijacker, hijacked): check_authorization = import_string(hijack_settings.HIJACK_AUTHORIZATION_CHECK) return check_authorization(hijacker, hijacked) django-hijack-2.0.3/hijack/tests/000077500000000000000000000000001264367662100166155ustar00rootroot00000000000000django-hijack-2.0.3/hijack/tests/__init__.py000066400000000000000000000000001264367662100207140ustar00rootroot00000000000000django-hijack-2.0.3/hijack/tests/checks_tests.py000066400000000000000000000142611264367662100216550ustar00rootroot00000000000000# -*- coding: utf-8 -*- import django if django.VERSION < (1, 7): pass else: from django.conf import settings from django.core.checks import Error, Warning from django.test import TestCase from hijack import checks from hijack import settings as hijack_settings from hijack.apps import HijackConfig from hijack.tests.utils import SettingsOverride class ChecksTests(TestCase): def test_check_display_admin_button_with_custom_user_model(self): warnings = checks.check_display_admin_button_with_custom_user_model(HijackConfig) self.assertFalse(warnings) with SettingsOverride(hijack_settings, HIJACK_DISPLAY_ADMIN_BUTTON=False): warnings = checks.check_display_admin_button_with_custom_user_model(HijackConfig) self.assertFalse(warnings) with SettingsOverride(hijack_settings, HIJACK_DISPLAY_ADMIN_BUTTON=True): warnings = checks.check_display_admin_button_with_custom_user_model(HijackConfig) self.assertFalse(warnings) with self.settings(AUTH_USER_MODEL='my_auth_user_model'): warnings = checks.check_display_admin_button_with_custom_user_model(HijackConfig) expected_warnings = [ Warning( 'Setting HIJACK_DISPLAY_ADMIN_BUTTON, which is True by default, ' 'does not work with a custom user model. ' 'Mix HijackUserAdminMixin into your custom UserAdmin or set HIJACK_DISPLAY_ADMIN_BUTTON to False.', hint=None, obj=settings.AUTH_USER_MODEL, id='hijack.W001', ) ] self.assertEqual(warnings, expected_warnings) def test_check_legacy_settings(self): with SettingsOverride(settings, SHOW_HIJACKUSER_IN_ADMIN=False): warnings = checks.check_legacy_settings(HijackConfig) expected_warnings = [ Warning( 'Deprecation warning: Setting "SHOW_HIJACKUSER_IN_ADMIN" has been renamed to "HIJACK_DISPLAY_ADMIN_BUTTON"', hint=None, obj=None, id='hijack.W002' ) ] self.assertEqual(warnings, expected_warnings) def test_check_url_allowed_attributes(self): errors = checks.check_url_allowed_attributes(HijackConfig) self.assertFalse(errors) with SettingsOverride(hijack_settings, HIJACK_URL_ALLOWED_ATTRIBUTES=('username',)): errors = checks.check_url_allowed_attributes(HijackConfig) self.assertFalse(errors) with SettingsOverride(hijack_settings, HIJACK_URL_ALLOWED_ATTRIBUTES=('username', 'email')): errors = checks.check_url_allowed_attributes(HijackConfig) self.assertFalse(errors) with SettingsOverride(hijack_settings, HIJACK_URL_ALLOWED_ATTRIBUTES=('other',)): errors = checks.check_url_allowed_attributes(HijackConfig) expected_errors = [ Error( 'Setting HIJACK_URL_ALLOWED_ATTRIBUTES needs to be ' 'subset of (user_id, email, username)', hint=None, obj=hijack_settings.HIJACK_URL_ALLOWED_ATTRIBUTES, id='hijack.E001', ) ] self.assertEqual(errors, expected_errors) def test_check_custom_authorization_check_importable(self): errors = checks.check_custom_authorization_check_importable(HijackConfig) self.assertFalse(errors) with SettingsOverride(hijack_settings, HIJACK_AUTHORIZATION_CHECK='not.importable'): expected_errors = [ Error( 'Setting HIJACK_AUTHORIZATION_CHECK cannot be imported', hint=None, obj='not.importable', id='hijack.E002', ) ] errors = checks.check_custom_authorization_check_importable(HijackConfig) self.assertEqual(errors, expected_errors) def test_hijack_decorator_importable(self): errors = checks.check_hijack_decorator_importable(HijackConfig) self.assertFalse(errors) with SettingsOverride(hijack_settings, HIJACK_DECORATOR='not.importable'): expected_errors = [ Error( 'Setting HIJACK_DECORATOR cannot be imported', hint=None, obj='not.importable', id='hijack.E003', ) ] errors = checks.check_hijack_decorator_importable(HijackConfig) self.assertEqual(errors, expected_errors) def test_check_staff_authorization_settings(self): errors = checks.check_staff_authorization_settings(HijackConfig) self.assertFalse(errors) with SettingsOverride(hijack_settings, HIJACK_AUTHORIZE_STAFF=True): errors = checks.check_staff_authorization_settings(HijackConfig) self.assertFalse(errors) with SettingsOverride(hijack_settings, HIJACK_AUTHORIZE_STAFF=True, HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF=True): errors = checks.check_staff_authorization_settings(HijackConfig) self.assertFalse(errors) with SettingsOverride(hijack_settings, HIJACK_AUTHORIZE_STAFF=False, HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF=True): errors = checks.check_staff_authorization_settings(HijackConfig) expected_errors = [ Error( 'Setting HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF may not be True if HIJACK_AUTHORIZE_STAFF is False.', hint=None, obj=None, id='hijack.E004', ) ] self.assertEqual(errors, expected_errors)django-hijack-2.0.3/hijack/tests/hijack_tests.py000066400000000000000000000400671264367662100216510ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.contrib.auth.models import User from django.core.urlresolvers import reverse from django.test import TestCase, Client from compat import import_string, unquote_plus from hijack import settings as hijack_settings from hijack.helpers import is_authorized from hijack.signals import hijack_started, hijack_ended from hijack.templatetags.hijack_tags import can_hijack from hijack.tests.utils import SettingsOverride class HijackTests(TestCase): def setUp(self): self.superuser_username = 'superuser' self.superuser_email = 'superuser@example.com' self.superuser_password = 'superuser_pw' self.superuser = User.objects.create_superuser(self.superuser_username, self.superuser_email, self.superuser_password) self.staff_user_username = 'staff_user' self.staff_user_email = 'staff_user@example.com' self.staff_user_password = 'staff_user_pw' self.staff_user = User.objects.create_user(self.staff_user_username, self.staff_user_email, self.staff_user_password) self.staff_user.is_staff = True self.staff_user.save() self.user_username = 'user' self.user_email = 'user@example.com' self.user_password = 'user_pw' self.user = User.objects.create_user(self.user_username, self.user_email, self.user_password) self.client = Client() self.client.login(username=self.superuser_username, password=self.superuser_password) def tearDown(self): self.client.logout() def test_basic_hijack(self): client = Client() client.login(username=self.superuser_username, password=self.superuser_password) hijacked_response = client.get('/hijack/%d/' % self.user.id, follow=True) self.assertEqual(hijacked_response.status_code, 200) hijack_released_response = client.get('/hijack/release-hijack/', follow=True) self.assertEqual(hijack_released_response.status_code, 200) client.logout() def assertHijackSuccess(self, response): self.assertEqual(response.status_code, 200) self.assertTrue(self.client.session['is_hijacked_user']) self.assertTrue('hijacked-warning' in str(response.content)) self.assertFalse('Log in' in str(response.content)) def assertHijackPermissionDenied(self, response): self.assertEqual(response.status_code, 403) self.assertFalse(getattr(self.client.session, 'is_hijacked_user', False)) self.assertFalse('hijacked-warning' in str(response.content)) def _hijack(self, user): return self.client.get('/hijack/%d/' % user.id, follow=True) def _release_hijack(self): response = self.client.get('/hijack/release-hijack/', follow=True) self.assertEqual(response.status_code, 200) self.assertFalse('hijacked-warning' in str(response.content)) return response def test_hijack_urls(self): self.assertEqual('/hijack/disable-hijack-warning/', reverse('disable_hijack_warning')) self.assertEqual('/hijack/release-hijack/', reverse('release_hijack')) self.assertEqual('/hijack/1/', reverse('login_with_id', args=[1])) self.assertEqual('/hijack/2/', reverse('login_with_id', kwargs={'user_id': 2})) self.assertEqual('/hijack/username/bob/', reverse('login_with_username', args=['bob'])) self.assertEqual('/hijack/username/bob_too/', reverse('login_with_username', kwargs={'username': 'bob_too'})) self.assertEqual('/hijack/email/bob@bobsburgers.com/', unquote_plus(reverse('login_with_email', args=['bob@bobsburgers.com']))) self.assertEqual('/hijack/email/bob_too@bobsburgers.com/', unquote_plus(reverse('login_with_email', kwargs={'email': 'bob_too@bobsburgers.com'}))) def test_hijack_url_user_id(self): response = self.client.get('/hijack/%d/' % self.user.id, follow=True) self.assertHijackSuccess(response) self._release_hijack() response = self.client.get('/hijack/%s/' % self.user.username, follow=True) self.assertEqual(response.status_code, 400) response = self.client.get('/hijack/-1/', follow=True) self.assertEqual(response.status_code, 404) def test_hijack_url_username(self): response = self.client.get('/hijack/username/%s/' % self.user_username, follow=True) self.assertHijackSuccess(response) self._release_hijack() response = self.client.get('/hijack/username/dfjakhdl/', follow=True) self.assertEqual(response.status_code, 404) def test_hijack_url_email(self): response = self.client.get('/hijack/email/%s/' % self.user_email, follow=True) self.assertHijackSuccess(response) self._release_hijack() response = self.client.get('/hijack/email/dfjak@hdl.com/', follow=True) self.assertEqual(response.status_code, 404) def test_hijack_permission_denied(self): self.client.logout() self.client.login(username=self.staff_user_username, password=self.staff_user_password) response = self._hijack(self.superuser) self.assertHijackPermissionDenied(response) response = self._hijack(self.staff_user) self.assertHijackPermissionDenied(response) response = self._hijack(self.user) self.assertHijackPermissionDenied(response) def test_release_before_hijack(self): response = self.client.get('/hijack/release-hijack/', follow=True) self.assertHijackPermissionDenied(response) def test_last_login_time_not_changed(self): self.client.logout() self.client.login(username=self.user_username, password=self.user_password) self.client.logout() last_non_hijack_login = User.objects.get(id=self.user.id).last_login self.assertIsNotNone(last_non_hijack_login) self.client.login(username=self.superuser_username, password=self.superuser_password) response = self._hijack(self.user) self.assertHijackSuccess(response) self._release_hijack() self.assertEqual(User.objects.get(id=self.user.id).last_login, last_non_hijack_login) def test_hijack_button(self): response = self.client.get('/admin/auth/user/') self.assertTrue('' % self.user.id in str(response.content)) def test_disable_hijack_warning(self): response = self._hijack(self.user) self.assertTrue('hijacked-warning' in str(response.content)) self.assertTrue(self.client.session['is_hijacked_user']) self.assertTrue(self.client.session['display_hijack_warning']) response = self.client.get('/hijack/disable-hijack-warning/?next=/hello/', follow=True) self.assertEqual(response.status_code, 200) self.assertFalse('hijacked-warning' in str(response.content)) self.assertTrue(self.client.session['is_hijacked_user']) self.assertFalse(self.client.session['display_hijack_warning']) self._release_hijack() def test_permissions(self): self.assertTrue(self.superuser.is_superuser) self.assertTrue(self.superuser.is_staff) self.assertFalse(self.staff_user.is_superuser) self.assertTrue(self.staff_user.is_staff) self.assertFalse(self.user.is_superuser) self.assertFalse(self.user.is_staff) def test_settings(self): self.assertTrue(hasattr(hijack_settings, 'HIJACK_DISPLAY_ADMIN_BUTTON')) self.assertTrue(hijack_settings.HIJACK_DISPLAY_ADMIN_BUTTON) self.assertTrue(hasattr(hijack_settings, 'HIJACK_DISPLAY_WARNING')) self.assertTrue(hijack_settings.HIJACK_DISPLAY_WARNING) self.assertTrue(hasattr(hijack_settings, 'HIJACK_URL_ALLOWED_ATTRIBUTES')) self.assertEqual(hijack_settings.HIJACK_URL_ALLOWED_ATTRIBUTES, ('user_id', 'email', 'username')) self.assertTrue(hasattr(hijack_settings, 'HIJACK_AUTHORIZE_STAFF')) self.assertFalse(hijack_settings.HIJACK_AUTHORIZE_STAFF) self.assertTrue(hasattr(hijack_settings, 'HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF')) self.assertFalse(hijack_settings.HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF) self.assertTrue(hasattr(hijack_settings, 'HIJACK_LOGIN_REDIRECT_URL')) self.assertEqual(hijack_settings.HIJACK_LOGIN_REDIRECT_URL, '/hello/') self.assertTrue(hasattr(hijack_settings, 'HIJACK_LOGOUT_REDIRECT_URL')) self.assertEqual(hijack_settings.HIJACK_LOGOUT_REDIRECT_URL, '/hello/') self.assertTrue(hasattr(hijack_settings, 'HIJACK_AUTHORIZATION_CHECK')) self.assertEqual(hijack_settings.HIJACK_AUTHORIZATION_CHECK, 'hijack.helpers.is_authorized_default') self.assertTrue(hasattr(hijack_settings, 'HIJACK_DECORATOR')) self.assertEqual(hijack_settings.HIJACK_DECORATOR, 'django.contrib.admin.views.decorators.staff_member_required') self.assertTrue(hasattr(hijack_settings, 'HIJACK_USE_BOOTSTRAP')) self.assertFalse(hijack_settings.HIJACK_USE_BOOTSTRAP) def test_settings_override(self): self.assertTrue(hijack_settings.HIJACK_DISPLAY_ADMIN_BUTTON) with SettingsOverride(hijack_settings, HIJACK_DISPLAY_ADMIN_BUTTON=False): self.assertFalse(hijack_settings.HIJACK_DISPLAY_ADMIN_BUTTON) self.assertTrue(hijack_settings.HIJACK_DISPLAY_ADMIN_BUTTON) def test_is_authorized(self): self.assertFalse(hijack_settings.HIJACK_AUTHORIZE_STAFF) self.assertFalse(hijack_settings.HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF) self.assertTrue(is_authorized(self.superuser, self.superuser)) self.assertTrue(is_authorized(self.superuser, self.staff_user)) self.assertTrue(is_authorized(self.superuser, self.user)) self.assertFalse(is_authorized(self.staff_user, self.superuser)) self.assertFalse(is_authorized(self.staff_user, self.staff_user)) self.assertFalse(is_authorized(self.staff_user, self.user)) self.assertFalse(is_authorized(self.user, self.superuser)) self.assertFalse(is_authorized(self.user, self.staff_user)) self.assertFalse(is_authorized(self.user, self.user)) def test_is_authorized_staff_authorized(self): with SettingsOverride(hijack_settings, HIJACK_AUTHORIZE_STAFF=True): self.assertTrue(hijack_settings.HIJACK_AUTHORIZE_STAFF) self.assertFalse(hijack_settings.HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF) self.assertTrue(is_authorized(self.superuser, self.superuser)) self.assertTrue(is_authorized(self.superuser, self.staff_user)) self.assertTrue(is_authorized(self.superuser, self.user)) self.assertFalse(is_authorized(self.staff_user, self.superuser)) self.assertFalse(is_authorized(self.staff_user, self.staff_user)) self.assertTrue(is_authorized(self.staff_user, self.user)) self.assertFalse(is_authorized(self.user, self.superuser)) self.assertFalse(is_authorized(self.user, self.staff_user)) self.assertFalse(is_authorized(self.user, self.user)) def test_is_authorized_staff_authorized_to_hijack_staff(self): with SettingsOverride(hijack_settings, HIJACK_AUTHORIZE_STAFF=True, HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF=True): self.assertTrue(hijack_settings.HIJACK_AUTHORIZE_STAFF) self.assertTrue(hijack_settings.HIJACK_AUTHORIZE_STAFF_TO_HIJACK_STAFF) self.assertTrue(is_authorized(self.superuser, self.superuser)) self.assertTrue(is_authorized(self.superuser, self.staff_user)) self.assertTrue(is_authorized(self.superuser, self.user)) self.assertFalse(is_authorized(self.staff_user, self.superuser)) self.assertTrue(is_authorized(self.staff_user, self.staff_user)) self.assertTrue(is_authorized(self.staff_user, self.user)) self.assertFalse(is_authorized(self.user, self.superuser)) self.assertFalse(is_authorized(self.user, self.staff_user)) self.assertFalse(is_authorized(self.user, self.user)) def test_notification_tag(self): response = self._hijack(self.user) self.assertHijackSuccess(response) response = self.client.get('/hello/') self.assertEqual(response.status_code, 200) self.assertTrue('Notification tag' in str(response.content)) self.assertTrue('hijacked-warning' in str(response.content)) def test_notification_filter(self): response = self._hijack(self.user) self.assertHijackSuccess(response) response = self.client.get('/hello/filter/') self.assertEqual(response.status_code, 200) self.assertTrue('Notification filter' in str(response.content)) self.assertTrue('hijacked-warning' in str(response.content)) def test_bootstrap_option(self): with SettingsOverride(hijack_settings, HIJACK_USE_BOOTSTRAP=True): response = self._hijack(self.user) self.assertHijackSuccess(response) response = self.client.get('/hello/') self.assertTrue('hijacked-warning-boostrap' in str(response.content)) response = self.client.get('/hello/filter/') self.assertTrue('hijacked-warning-boostrap' in str(response.content)) def test_can_hijack_filter(self): self.assertTrue(can_hijack(self.superuser, self.staff_user)) self.assertFalse(can_hijack(self.user, self.superuser)) def test_custom_authorization_check(self): for custom_check_path in [ 'hijack.tests.test_app.authorization_checks.can_hijack_default', 'hijack.tests.test_app.authorization_checks.everybody_can_hijack', 'hijack.tests.test_app.authorization_checks.nobody_can_hijack', ]: with SettingsOverride(hijack_settings, HIJACK_AUTHORIZATION_CHECK=custom_check_path): custom_check = import_string(custom_check_path) for hijacker, hijacked in [ (self.superuser, self.superuser), (self.superuser, self.staff_user), (self.superuser, self.user), (self.staff_user, self.superuser), (self.staff_user, self.staff_user), (self.staff_user, self.user), (self.user, self.superuser), (self.user, self.staff_user), (self.user, self.user), ]: self.assertEqual(custom_check(hijacker, hijacked), is_authorized(hijacker, hijacked)) def test_default_decorator(self): self.client.logout() self.client.login(username=self.user_username, password=self.user_password) response = self._hijack(self.staff_user) self.assertEqual(response.status_code, 200) self.assertIn('Log in', str(response.content)) # def test_custom_decorator(self): # custom_decorator_path = 'hijack.tests.test_app.decorators.no_decorator' # with SettingsOverride(hijack_settings, HIJACK_DECORATOR=custom_decorator_path): # self.assertEqual(hijack_settings.HIJACK_DECORATOR, custom_decorator_path) # self.client.logout() # self.client.login(username=self.user_username, password=self.user_password) # response = self._hijack(self.staff_user) # self.assertEqual(response.status_code, 403) def test_signals(self): self.recieved_signals = [] def hijack_started_reciever(sender, hijacker_id, hijacked_id, **kwargs): self.recieved_signals.append('hijack_started_%d_%d' % (hijacker_id, hijacked_id)) hijack_started.connect(hijack_started_reciever) def hijack_ended_reciever(sender, hijacker_id, hijacked_id, **kwargs): self.recieved_signals.append('hijack_ended_%d_%d' % (hijacker_id, hijacked_id)) hijack_ended.connect(hijack_ended_reciever) self.assertEqual(len(self.recieved_signals), 0) self._hijack(self.user) self.assertEqual(len(self.recieved_signals), 1) self.assertIn('hijack_started_%d_%d' % (self.superuser.id, self.user.id), self.recieved_signals) self._release_hijack() self.assertEqual(len(self.recieved_signals), 2) self.assertIn('hijack_ended_%d_%d' % (self.superuser.id, self.user.id), self.recieved_signals)django-hijack-2.0.3/hijack/tests/manage_settings.py000066400000000000000000000002621264367662100223370ustar00rootroot00000000000000from .test_settings import * DATABASES = { 'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'test', } } LOCALE_PATHS = ( 'hijack/locale/', ) django-hijack-2.0.3/hijack/tests/south_settings.py000066400000000000000000000010541264367662100222510ustar00rootroot00000000000000""" These settings are used by the ``manage.py`` command. With normal tests we want to use the fastest possible way which is an in-memory sqlite database but if you want to create South migrations you need a persistant database. Unfortunately there seems to be an issue with either South or syncdb so that defining two routers ("default" and "south") does not work. """ from .test_settings import * # NOQA DATABASES = { 'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'db.sqlite', } } INSTALLED_APPS.append('south', ) django-hijack-2.0.3/hijack/tests/test_app/000077500000000000000000000000001264367662100204345ustar00rootroot00000000000000django-hijack-2.0.3/hijack/tests/test_app/__init__.py000066400000000000000000000000001264367662100225330ustar00rootroot00000000000000django-hijack-2.0.3/hijack/tests/test_app/authorization_checks.py000066400000000000000000000004231264367662100252250ustar00rootroot00000000000000 from hijack.helpers import is_authorized_default def can_hijack_default(hijacker, hijacked): return is_authorized_default(hijacker, hijacked) def everybody_can_hijack(hijacker, hijacked): return True def nobody_can_hijack(hijacker, hijacked): return False django-hijack-2.0.3/hijack/tests/test_app/decorators.py000066400000000000000000000000441264367662100231510ustar00rootroot00000000000000 def no_decorator(fn): return fndjango-hijack-2.0.3/hijack/tests/test_app/models.py000066400000000000000000000000001264367662100222570ustar00rootroot00000000000000django-hijack-2.0.3/hijack/tests/test_app/templates/000077500000000000000000000000001264367662100224325ustar00rootroot00000000000000django-hijack-2.0.3/hijack/tests/test_app/templates/404.html000066400000000000000000000000001264367662100236150ustar00rootroot00000000000000django-hijack-2.0.3/hijack/tests/test_app/templates/hello.html000066400000000000000000000001431264367662100244210ustar00rootroot00000000000000{% load hijack_tags %} Notification tag: {% hijack_notification %}

Hello

{{ user }}

django-hijack-2.0.3/hijack/tests/test_app/templates/hello_filter.html000066400000000000000000000001551264367662100257710ustar00rootroot00000000000000{% load hijack_tags %} Notification filter: {{ request|hijackNotification }}

Hello

{{ user }}

django-hijack-2.0.3/hijack/tests/test_app/urls.py000066400000000000000000000004151264367662100217730ustar00rootroot00000000000000from compat import url from django.views.generic import TemplateView urlpatterns = [ url(r'^filter/$', TemplateView.as_view(template_name='hello_filter.html'), name='hello_filter'), url(r'^$', TemplateView.as_view(template_name='hello.html'), name='hello'), ] django-hijack-2.0.3/hijack/tests/test_app/views.py000066400000000000000000000000001264367662100221310ustar00rootroot00000000000000django-hijack-2.0.3/hijack/tests/test_settings.py000066400000000000000000000045111264367662100220670ustar00rootroot00000000000000"""Settings that need to be set in order to run the tests.""" import os DEBUG = True SITE_ID = 1 APP_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) DATABASES = { 'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', } } LOGIN_REDIRECT_URL = '/hello' HIJACK_LOGIN_REDIRECT_URL = '/hello/' HIJACK_LOGOUT_REDIRECT_URL = '/hello/' ROOT_URLCONF = 'hijack.tests.urls' STATIC_URL = '/static/' STATIC_ROOT = os.path.join(APP_ROOT, '../app_static') MEDIA_ROOT = os.path.join(APP_ROOT, '../app_media') STATICFILES_DIRS = (os.path.join(APP_ROOT, 'static'), ) NOSE_ARGS = [] TEMPLATE_DIRS = (os.path.join(APP_ROOT, 'tests/test_app/templates'), ) PASSWORD_HASHERS = ('django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.BCryptPasswordHasher', 'django.contrib.auth.hashers.SHA1PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher', 'django.contrib.auth.hashers.CryptPasswordHasher', ) MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', # 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', ) TEMPLATE_CONTEXT_PROCESSORS = ( "django.contrib.auth.context_processors.auth", "django.core.context_processors.debug", "django.core.context_processors.i18n", "django.core.context_processors.media", "django.core.context_processors.static", "django.core.context_processors.tz", "django.contrib.messages.context_processors.messages", 'django.core.context_processors.request', ) EXTERNAL_APPS = [ 'django.contrib.admin', 'django.contrib.admindocs', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.messages', 'django.contrib.sessions', 'django.contrib.staticfiles', 'django.contrib.sitemaps', 'django.contrib.sites', 'django_nose', 'compat', ] INTERNAL_APPS = ['hijack', 'hijack.tests.test_app', ] INSTALLED_APPS = EXTERNAL_APPS + INTERNAL_APPS SECRET_KEY = 'foobar' LANGUAGE_CODE = 'en-us' USE_I18N = True USE_L10N = Truedjango-hijack-2.0.3/hijack/tests/urls.py000066400000000000000000000005251264367662100201560ustar00rootroot00000000000000"""URLs to run the tests.""" from compat import patterns, include, url from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', url(r'^hijack/', include('hijack.urls')), url(r'^admin/', include(admin.site.urls)), url(r'^hello/', include('hijack.tests.test_app.urls'))) django-hijack-2.0.3/hijack/tests/utils.py000066400000000000000000000016701264367662100203330ustar00rootroot00000000000000 # Borrowed from https://github.com/stefanfoulis/django-filer/blob/develop/filer/tests/helpers.py class SettingsOverride(object): """ Overrides Django settings within a context and resets them to their inital values on exit. Example: with SettingsOverride(DEBUG=True): # do something """ def __init__(self, settings_module, **overrides): self.settings_module = settings_module self.overrides = overrides def __enter__(self): self.old = {} for key, value in list(self.overrides.items()): self.old[key] = getattr(self.settings_module, key, None) setattr(self.settings_module, key, value) def __exit__(self, _type, value, traceback): for key, value in list(self.old.items()): if value is not None: setattr(self.settings_module, key, value) else: delattr(self.settings_module, key)django-hijack-2.0.3/hijack/urls.py000066400000000000000000000016711264367662100170170ustar00rootroot00000000000000from compat import url from hijack import settings as hijack_settings from hijack.views import release_hijack, disable_hijack_warning, login_with_id, login_with_email, login_with_username urlpatterns = [ url(r'^release-hijack/$', release_hijack, name='release_hijack') ] if hijack_settings.HIJACK_DISPLAY_WARNING: urlpatterns.append(url(r'^disable-hijack-warning/$', disable_hijack_warning, name='disable_hijack_warning')) hijacking_user_attributes = hijack_settings.HIJACK_URL_ALLOWED_ATTRIBUTES if 'email' in hijacking_user_attributes: urlpatterns.append(url(r'^email/(?P[^@]+@[^@]+\.[^@]+)/$', login_with_email, name='login_with_email')) if 'username' in hijacking_user_attributes: urlpatterns.append(url(r'^username/(?P.*)/$', login_with_username, name='login_with_username')) if 'user_id' in hijacking_user_attributes: urlpatterns.append(url(r'^(?P[\w-]+)/$', login_with_id, name='login_with_id')) django-hijack-2.0.3/hijack/views.py000077500000000000000000000023721264367662100171710ustar00rootroot00000000000000# -*- encoding: utf-8 -*- from django.contrib.auth.decorators import login_required from django.shortcuts import get_object_or_404 from django.http import HttpResponseBadRequest from hijack.decorators import hijack_decorator from hijack.helpers import login_user, redirect_to_next from hijack.helpers import release_hijack as release_hijack_fx from compat import get_user_model @hijack_decorator def login_with_id(request, user_id): # input(user_id) is unicode try: user_id = int(user_id) except ValueError: return HttpResponseBadRequest('user_id must be an integer value.') user = get_object_or_404(get_user_model(), pk=user_id) return login_user(request, user) @hijack_decorator def login_with_email(request, email): user = get_object_or_404(get_user_model(), email=email) return login_user(request, user) @hijack_decorator def login_with_username(request, username): user = get_object_or_404(get_user_model(), username=username) return login_user(request, user) @login_required def release_hijack(request): return release_hijack_fx(request) @login_required def disable_hijack_warning(request): request.session['display_hijack_warning'] = False return redirect_to_next(request, default_url='/') django-hijack-2.0.3/hooks/000077500000000000000000000000001264367662100153455ustar00rootroot00000000000000django-hijack-2.0.3/hooks/pre-commit000077500000000000000000000020601264367662100173450ustar00rootroot00000000000000#!/usr/bin/env python import glob import os import sys os.environ['PYFLAKES_NODOCTEST'] = '1' # pep8.py uses sys.argv to find setup.cfg sys.argv = [os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)] # git usurbs your bin path for hooks and will always run system python if 'VIRTUAL_ENV' in os.environ: site_packages = glob.glob( '%s/lib/*/site-packages' % os.environ['VIRTUAL_ENV'])[0] sys.path.insert(0, site_packages) def main(): from flake8.main import DEFAULT_CONFIG from flake8.engine import get_style_guide from flake8.hooks import run gitcmd = "git diff-index --cached --name-only HEAD" _, files_modified, _ = run(gitcmd) # remove non-py files and files which no longer exist files_modified = filter( lambda x: x.endswith('.py') and os.path.exists(x), files_modified) flake8_style = get_style_guide(parse_argv=True, config_file=DEFAULT_CONFIG) report = flake8_style.check_files(files_modified) return report.total_errors if __name__ == '__main__': sys.exit(main()) django-hijack-2.0.3/manage.py000066400000000000000000000004401264367662100160220ustar00rootroot00000000000000#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "hijack.tests.manage_settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-hijack-2.0.3/requirements-test.txt000066400000000000000000000000451264367662100204620ustar00rootroot00000000000000django-nose coverage django-coverage django-hijack-2.0.3/requirements.txt000066400000000000000000000000471264367662100175070ustar00rootroot00000000000000django>=1.4,<1.10 django-compat>=1.0.8 django-hijack-2.0.3/runtests.py000077500000000000000000000015161264367662100164710ustar00rootroot00000000000000#!/usr/bin/env python """ This script is a trick to setup a fake Django environment, since this reusable app will be developed and tested outside any specifiv Django project. Via ``settings.configure`` you will be able to set all necessary settings for your app and run the tests as if you were calling ``./manage.py test``. """ import sys from django.conf import settings from hijack.tests import test_settings if not settings.configured: settings.configure(**test_settings.__dict__) from django_nose import NoseTestSuiteRunner def runtests(*test_args): import django if django.VERSION >= (1, 7): django.setup() failures = NoseTestSuiteRunner(verbosity=2, interactive=True).run_tests(test_args) sys.exit(failures) if __name__ == '__main__': runtests(*sys.argv[1:]) django-hijack-2.0.3/setup.cfg000066400000000000000000000001251264367662100160410ustar00rootroot00000000000000[flake8] ignore = F999,E501,E128,E124 exclude = .git,*/migrations/*,*/static/CACHE/* django-hijack-2.0.3/setup.py000066400000000000000000000043771264367662100157470ustar00rootroot00000000000000# -*- encoding: utf-8 -*- """ Python setup file for the hijack app. In order to register your app at pypi.python.org, create an account at pypi.python.org and login, then register your new app like so: python setup.py register If your name is still free, you can now make your first release but first you should check if you are uploading the correct files: python setup.py sdist Inspect the output thoroughly. There shouldn't be any temp files and if your app includes staticfiles or templates, make sure that they appear in the list. If something is wrong, you need to edit MANIFEST.in and run the command again. If all looks good, you can make your first release: python setup.py sdist upload For new releases, you need to bump the version number in hijack/__init__.py and re-run the above command. For more information on creating source distributions, see http://docs.python.org/2/distutils/sourcedist.html """ import os from setuptools import setup, find_packages import hijack as app dev_requires = ['flake8', ] def read(fname): try: return open(os.path.join(os.path.dirname(__file__), fname)).read() except IOError: return '' setup(name="django-hijack", version=app.__version__, description=read('DESCRIPTION'), long_description=read('README.md'), license='The MIT License', platforms=['OS Independent'], keywords='django, hijack, support, customer support, debugging', author='arteria GmbH', author_email='admin@arteria.ch', url="https://github.com/arteria/django-hijack", packages=find_packages(), include_package_data=True, install_requires=open('requirements.txt').read().split('\n'), extras_require={'dev': dev_requires, }, classifiers=['Development Status :: 5 - Production/Stable', 'Framework :: Django', 'License :: OSI Approved :: MIT License', 'Framework :: Django', 'Framework :: Django :: 1.7', 'Framework :: Django :: 1.8', # 'Framework :: Django :: compat', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', ], ) django-hijack-2.0.3/upload.sh000077500000000000000000000000471264367662100160460ustar00rootroot00000000000000#!/bin/sh python setup.py sdist upload