From 17ae27df40f451947d818e5808d452dade92ac7c Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 17 Nov 2015 22:18:02 +0100 Subject: [PATCH] Updated ROC CMS documentation. Cosmetic, comments, typo. --- doc/concepts.md | 93 --------- doc/design.md | 0 doc/img_diagram.png | Bin 0 -> 47978 bytes doc/readme.md | 396 ++++++++++++++++++++++++++++++++++++ src/service/cms_execution.e | 1 + src/service/cms_module.e | 2 +- 6 files changed, 398 insertions(+), 94 deletions(-) delete mode 100644 doc/concepts.md delete mode 100644 doc/design.md create mode 100644 doc/img_diagram.png create mode 100644 doc/readme.md diff --git a/doc/concepts.md b/doc/concepts.md deleted file mode 100644 index 5d5850b..0000000 --- a/doc/concepts.md +++ /dev/null @@ -1,93 +0,0 @@ -CMS Concepts -============ ->Current implemented concepts - -##### Table of Contents - -1. [**Theme**](#theme) -2. [**Regions**](#regions) - - [**Default Page Layout**](#page_layout) - - [**Regions Holds blocks**](#regions_blocks) -3. [**Blocks**](#blocks) -4. [**Modules**](#modules) -5. [**Hooks**](#hooks) - - - -Theme ------ -In a CMS , a theme is a collection of templates files (HTML, CSS, Images, etc ) that determine how a CMS web site looks. The goal of a theme is to let you change the look and feel of the site. -Eiffel CMS is inspired by Drupal, and use the same default region names as default drupal theme. - -#### Important Classes - -* [CMS_THEME] (/library/src/theme/cms_theme.e): Abstraction defining the interface of a CMS theme. -* [SMARTY_CMS_THEME] (/library/src/theme/smarty_theme/smarty_cms_theme.e): Theme implemented using the [Eiffel Smarty library] (https://github.com/eiffelhub/template-smarty). -* [CMS_TEMPLATE] (/library/src/theme/cms_template.e): Template Abstraction that contains theme, variables needed by template when rendering page as html. At the moment there is only one implementation SMARTY_CMS_PAGE_TEMPLATE. At the moment there is only one implementation [SMARTY_CMS_PAGE_TEMPLATE] (/library/src/theme/smarty_theme/smarty_cms_page_template.e). - - -Regions -------- -The layout of a CMS web page has predefined area called **regions**. The Eiffel CMS uses the same default regions as Drupal, so let's see them in the following image. - - -![default page layout](http://themery.com/sites/default/files/figure-15-10.png) - -``` -regions[page_top] = Top -regions[header] = Header -regions[content] = Content -regions[highlighted] = Highlighted -regions[help] = Help -regions[footer] = Footer -regions[first_sidebar] = first sidebar -regions[second_sidebar] = second sidebar -regions[page_bottom] = Bottom -``` - -**A Region holds blocks** - -**What goes inside regions?** -Generally, regions hold smaller piece of content called blocks. Blocks hold chunks of content, like the user login form, navigation menu or the information for the footer. - -Regions are defined in a configuration file theme.info. - - - -CMS_BLOCK ---------- -**What is a cms block?** -Blocks are chunk of content that can be created to display whatever you want, and then can be placed in various resgions in your template (theme) layout. - -#### Important Classes - -* [CMS_BLOCK] (/library/src/kernel/content/cms_block.e): The deferred class CMS_BLOCK provides an abstraction to describe content to be placed inside Regions. -* [CMS_CONTENT_BLOCK] (/library/src/kernel/content/cms_content_block.e): The class CMS_CONTENT_BLOCK describe how to provide generic content. -* [CMS_MENU_BLOCK](/library/src/kernel/content/cms_menu_block.e): The class CMS_MENU_BLOCK describe how to provides a menu of navigational links. -* [CMS_SMARTY_TEMPLATE_BLOCK] (/library/src/kernel/content/cms_smarty_templateblock.e) The class CMS_SMARTY_TEMPLATE_BLOCK describe how to use a CMS block with smarty template file content. - - - -CMS_MODULES ------------ -**What is a cms module?** -Modules are piece of code that adds one or more features to your web site. -Modules can be plugged and combined to provide a web site customized to your needs. There are modules for many purposes, for example Administratiton, Basic Authentication, etc. - -#### Important Classes -* [CMS_MODULE] (/library/src/modules/cms_module.e): The deferred class CMS_MODULE provides an abstraction to describe a generic module that add features to your web site. -* [CMS_RESPONSE](/library/src/service/response/cms_response.e). The deferred class CMS_RESPONSE provide an abstraction to builds the content to get process to render the output. - - - -CMS_HOOK --------- -Hooks is a mechanism which provide a way for modules to interact with each other and extending blocks of the current CMS. - -* [CMS_HOOK] (/library/src/hooks/cms_hook.e): The deferred class CMS_HOOK is a marker interface for CMS Hook -* [CMS_HOOK_AUTO_REGISTER] (/library/src/hooks/cms_hook_auto_register.e): The deferred class provides an abstraction that when inheriting from this class, the declared hooks are automatically registered, otherwise, each descendant has to add it to the cms service itself. -* [CMS_HOOK_BLOCK](/library/src/hooks/cms_hook_block.e): The class CMS_HOOK_BLOCK describe a hook providing a way to alter a generic block. -* [CMS_HOOK_FORM_ALTER](/library/src/hooks/cms_hook_form_alter.e): The class CMS_HOOK_FORM_ATLER describe a hook providing a way to alter a form. -* [CMS_HOOK_MENU_ALTER](/library/src/hooks/cms_hook_menu_alter.e): The class CMS_HOOK_MENU_ATLER describe a hook providing a way to alter a menu. -* [CMS_HOOK_MENU_SYSTEM_ALTER](/library/src/hooks/cms_hook_menu_system_alter.e): The class CMS_HOOK_MENU_SYSTEM_ALTER describe a hook providing a way to alter the CMS menu system. -* [CMS_HOOK_VALUE_TABLE_ALTER](/library/src/hooks/cms_hook_value_table_alter.e):: The class CMS_HOOK_VALUE_TABLE_ALTER describe a hook providing a way to alter the value table for a response. diff --git a/doc/design.md b/doc/design.md deleted file mode 100644 index e69de29..0000000 diff --git a/doc/img_diagram.png b/doc/img_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..e7b8bf95fcc0a18747d75d7686a763c4b8876a66 GIT binary patch literal 47978 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

dKM_HPi-beC9+=6pJ?k zNuY)<#!8ZX1v$B~iqfuBA4KjCzH~`wy$T_IKnND*1=2Gy;JPUdit6QknI~)+v|>`< z%Hmlp8(4Dpq`kPJLUf5*BDu=4rFdnc>8Nr8kT9QtZqH2uS0L%ke7Bt*=R%dqhQ4lm zPW;g|@FmTuwNV`_H%*CTT?{jS1-uTu`h-oZ$qhX){e|U8)Ekm(O1ob#5JAr4=f}wW zDed`DokR1?`gv z4b`x14@o}Aduse+v%Vlm;W8YaOKfmGL~7l8-uJ@A+0S{iE8FU?=(vz0x=c`2{2AfyAF||vc@eUh!HthJ^$PmJ(N8oIFj$_@S|Y)iXeRmww!n@e%`V! zYoq=!;oqddziIUakxZWi-h~7NgH1^8Xk^DytGWlAl?Ghc8RcV9hw`+O1 zv0;zIw18-tkqOM|`-@Hm7Mkwhk&Tbf{w9?8@eJ4rVj-|g#8VMn%8`go~2l%S;PuBdoQP+lnocj zP7nLaAYNCS>0ZeXZFyat*&X9#;~DPhEk-102q6~0KgI~rrBm|`+8twTRuam#nrGR` z)5R6?{w^gfCI3QZ$W_)a@J9??R>jtxJPL7(FU?F`m97mzb!qiIVvz~9WL(@Atj-u#lQ~kCK}d~9 zrb${;D|PlK(+}TMcQwtptW&!8k{eY=G>Xu-i6-p~H%FR9m6xHo=bj1#RYnAwuB*ZJ zXvQt?#U`Lu9%l|`dVUIKdo(;sui48J4fSLgM`R8i3635RiudF91mOo5@39r7DVd9? z>%X6-*Z*~z;uOJYs?aGn3TjqiE67@FD$f|-h*8suEZo=mEOgVRG~Y7E`xH%!1YMG! zb@>*?dpNLxHGcEV)%scPV0~WbRdw{YS2`bM1|&W==Sl>2X7TQh1Kgj`S$4%yIA`Nq zlunKx1$hswi_=w2MWIGls9hGDuc0PXo;jR1)w(TCu*ca;2?GV`I<0fsZd(XQKO&4y#r3 zms?$~8PuAC(r>aWQ$-5a)=~+qT^go+^Alx786|QLtM*+B?E{E8zV2`^S6Cna^E@E?-Q*W?sHPHEu;)p&`v2^RfNS`ULio z7%&D^^?9yx2UAp^kAh#qn63=J6C8|~lacuJLPK)wSL3C*{rJndvDdFP=jFqmv#6wK zPN~kmOX7f>*Z;|*;4~Q8Xw*u_v+USF1NAZR-HmME#T|>C;y7+t0UUMm&7>gQh}KvN z>FKzR9Xm)?YW6~(yf$BI$YB&FftX17(-Ah4roJP;L$)R*c91abeS|NWG&rdcKR^18 z+SOkX5|(-wD&jFjZ6tSoOcC)~rx@oFC-?&jxlI}Pk*JZAaK(~1-a?-vB`w9Mc4Cu) zK;Qw`43aiw%`I&?U_E2hJDeLo*^4EiesM#*g7k`; zc~MdpV}eM|v(nk~Gq?GGA?E6#vZjdxR^uMIQMkBR6Ayv2Td(mfTE5GEO9k~1@^D46 z0T{x*$LB|19o_Mw?xTQ^laO3vQ08djdBzj)eQ5J_&F=}6TT&`b(icQ)27-p2J}(X2 zTB*$VZi=aafVXM)&yyG4j>qP;mnw~!pmf%y5l!VDNrgCIXq#VLthpkFZ{sd-ZuyRA z7Bt=i7v^m*VI$cdSK_Uxtn3;4W8z7e$b;$ znXnbq{Xa{aqFH0zJxvdaEsVQ#J4CMF5>cLQUs=V zZ5A=&{A_cjbZExSFWe`^Nr4J`Fg_s`nm1aW++4js5K-^m?BZR~*D+q$6*d#6Xmbg| zH9q?c>FL?+d2s^C9x3v9vxO%iumzeV@e>__=n<5EN&+2p?s6U?5+Ci+yQq8lFw|L_ z#!%>8>QVL9kA&)b7L5zOq2HQ?d>wTSMFp~%r05bDCWi_lgItcCmdjC|i<9NPm;oDo zLC+%eA|?CkT()LZB6eV%XauuOGdNsmiweFcEbe;{&kgduSwSPyzm851!$iIMM3MQm ziq*AJT@A>VnTA|pGR4((6`AnOynVmEyg$=VR=2QI;aHzuy6%+0=t9NeQ#1uctUjMZ z4|9M^+^EBSqGu=Kh~1lu9+3P4b}^6Dw~hy&>3km>(CAGiKmTz{ARgg;&2!t*@_j)uL1qey3Jb>m^c8hVEuQ zI4rTnmHYaViB+Xp7>c!q_+`(zVwKpX6GxWIME34=uNyU&M;_EH zC#nZrd#_$IqmQMOCJu@Qf~E9Z&0}Ow|13?MX7bOR6lR4WM2IGmhDjq8Qxt0YZV;E` zk`-moeShkyXR(`fGH%kp_sdCcaI?Ynya5)oh%l?$Pn>@l-hiY`;Xt*d7qs<>RAkJj zY_9c9t@+q1RV=8q9y9A7OAzFL4D(D;-VO_Z3j^U_dVL7&ZMdb?WAOd;?~pwYuZB_+)VwF zvEAL|TlO=0D1v1}db#*-88!tCl~db!F@l{teW>@M{V5D7c|_i18>`5Jd7=DG^)kbq z$}F#k>4ykS#Eue5TM&i*ZCZws^RAF_lC8&vooxT8@mw@da6)y?csVcZYEu|a4S!N9 ztSlK34a0i%Huvu=^JFie2D?T!u>1822JBejz|TszoeG{nwRRuZJ}++V{Zh%$F>K>C zTKtIRhgIG=>*fAGt8*5l<@b74EjVNtj#ocLbU9(tMjHekt{x5WJN)LO8yW+f7k1C< zTA8Np-t{*}Ug62dmYROYv-}am$p+zMW$D>2TNMm%mbaeVe>imuAI&|phNFao(CUL z1q7SNJ3_`}(F_3(Cm2u@UD+-#+R7Xcg!LAYy*}An6hC$<05Zid=)ZQ{rp(d5=o9ZTajeak<-I=$$ohsf4Y-2Cd_bNxS{CO zvi+io=hiX*VNVs!pj@99EJEK<;b`6oF23L7Vm1@Q4nNY?K8n>~XFwM4vkR7VDV6OY zYpPuap<6}lHbR~{luEX!AG$rPv0`H<;+y$xE$!nT-kJ=xI8`&4@vKuZe5*i^%okvN zeps!qseG!+Ns5(~HMCo+$gZ>CGn7{ZTiFApF0Av_wa}02W@^xi@jweFxx*ERfh{?i z@0thJ6zaJcgi%-$i*do^v(~>mtXgC}Twg_bnl19;TE~%=_0G$DJf@~ICblqAW(R`HOv`qtJk(QP z-?0Pv9s1oP!Jd@CLXJ(ABiBv!ut;tv1$zWag^InYp%Qrc(}9e7jqZXhAN<579QIHW6>iss$@jTN;($@&9iGy6owL=nOcpJx0)e8en6!K{Ai`Rpey`M z7;A@%Lb!_}r9YsD?{RkfLmQtjHEhmmagC6{QH%V)uHR`mE!0px-iNc-RkJ1?OZL?p zoPMuc9i8#bo=-U~yXG!hyBE*iPNiJaiOOiJ-u+!|AXqUa4WUi!bF1FZ#jGgzHCEEY z{GfhYVo{Kqp89L^a-Qcqevw_P0Be@L7|^xs*Ws;X^t=`>R*1UUJnshQeS6M6bqXWS z=Vf0{^Gb=cy4c66ejXu27 zJm&Ax)c@-TSa%FiTTXp!tNRl*hdcACXoeAw-OAn-6C9@a2ky-)Iuc4lZS6F}x1Zvx zAQMRD{utu&1cB4W^UwOn-@(U2Z_bkaUU@P?&CM7;utfZ@*2PPbFVN(B^CnJo%H+f& zshSBQTeSGYYG>m28GXO@)YjcHzqc&gD7Fn^M>^B9~M@Gu{qCJj>JT;I*n&ZiO zxGw+R!qpF0YCfMNG4fX+Mw_T5wE1L2b6YKYmGV4t{uz?`HvKIT8ipeu-11z5++`gx zYeg|HnZw^oN>O^r`D9}!ErK#LH3n|-77f(}r4j2@y@iwOy#2ennqMA6=exH0nl1*I zE}gSZC~cl$9*qfbO(zOSoAA7Net2F2N$ulAhHyRA>tK!Ggi&UzIUP80SdHHkBF-UmV*liDCL$ z{4_;Ry9TgzqJ^u)3n-cUPN8WNN+UtmDViq4Rs5`@zTTSO3~*Y)+2IGtok#@yAScYt z`p8&hs2JX`#*xB+Fx#wg$Mbh=rD)P9<)k@qe7;KzmfH>N)J6<_gvCusG@%> zbXt~-J2t@`G)2^lP^rsZ(bc$D5$bA8pfqN!7eg(qE(^Dp37OdX9#&( znXP-63-6U11gl(wIT-ETbD<6-O}#+iO1S}PQfg$o=4|4@I2ihm7NB}x-zLT1)3II0 zdk(m1?aHV|2mf+xGiuA-4#ozf8iG9NID!4r2@)3!25fQkI^XhM6f z5_y@qWiNm5_Brd0qr)Hem7A(=8dd#LeTq`9ebV9}Tq+SVi8wGuP3@QW#Y30+Rz_NIgumx9&j| z{ZO=zZuS0ZE~)mx;o52&hu<5YYs!VFAHW!|*S-5Nygstuv7r<(rJ2SNAj8zS@ioO? z_wI_MJ7#-D=;Y&tbK1}lDLuhbnD@Q!9}$^fE#apbFMitBie^5ap8@4+1U@=TK zd6DNcy-rj)eeP1}it$|hc3VLVLUe@(Q z)$O_o&$V%=C$5Div|#B|sRBWTVG=6B`QQ9di|3EvgK#r_+*&Y{-5Et_+Hkm14i_Li zIrAd5&BRwQoh3>=Egmza7S$Z3U^b-RCZben89}UGI&sb+x4b4Z;ueD4<9tK!d`wxqkN|x1Wn116<`A~c zdhnh^^WwzlBP$#3^cgL1Df?V8FZ;+Z_nwgYPy&*4FV?ww z1!r1&sd1JkBu)94@$$nGxP z4vO5N`$1qxA79{mrj>T1An^(ssX~JN)VH*L?X{s^Z?B%Dc9WJMA@+@N9WkUQCxc>f zwg$MzhtG-c60S4%clDUv9<$f5qNO`gZVZL-X32J4o>jycFS6~_5Rh$nj9q`bS9Z5Z za+LE0tq9egJ;t*6Dns*gBz5}DUdB0bF9X~SLbN?ok)#$Nx5OKPVamB?WTJzRJ5O#M zBvGRhaUMy0BM{t=D-MDu|0egVbl>97@du!dR(UXA8mWz60KFIiK+%xEHmX>c%jNe8 zHylvqF=Wqpe}n*2Y}7xWHc=xFshw=t5+j9 zaek@}waCnU{#PuXrsp&VZ{4|FOIT+RU~9QYxM{#JBFh=7Edd^8@&Y_8px|4o?lyz{ zMG^8%Ui-c0oL515piv7y#6MP)09rvk4vUcB&@r=K2>!6CP`p^#Wb-*Y_%gj+Iq z?Jr)G0zEN-S%BQRR!La8K!j(1<(UGY;O50KJn0*lqfd_Jm8S)q@X!bFJp$i@?*VDF z2Arlp!B+$jEBfQV`8h@?{#Qn5z%zCM6$Jh}38>{%DKLC+;}iaa-gogW{1w5=jYMD> z!B1EOf|rHvoPDqX-}>s`m+^dlCHMy5H%r9ut^f9S_!2zkZy!xyg$B8pWd4D1fLcFQ+FN=-DtP{^*IQq5WX`olblp~nqaEPKxd!8 zY=$ssgT#R3I|xnVPcT~#KBKOHaRh_ANENHELCmjcgX_`(a+l#xvjfg2&iRIaBh4~z zFYl!gBG5xI=;6JvJw2ZgE>_aGp|y(r?FpE@+gD1EvAoS|MtFi!Ry2ZD_z!ySpid+2 z`M4j827*Jc!a;z^2v{jv**$Sy zTc{B`Cv-U}&P2<5R=PJFpPzKzo7JpA`9+L)--ge) zjxdhUn_rXoNkDQ{8~R>7J5|WKX&1J|oj81OFgP2eOa5-H$e~`d7ZnaAEB#2q=l}LA zLh^RcD`mQ$j9K%8%*_fxe&rwT7m=bcavC(HAWzdM-mb%B{bMkfKKf3vhB+T*8)}_H6_w6d997T9H}f!o zt0yc>3$8slkL;OjR%nhOprI&)@g<$C@?34g@aQ#|mOep)t+EfcuJLnF>qGuViGLaA zeGL~H-slko9k1CWrp$WtRS?QMvD;6}#%3qJ^<<+!P&3qS4d>@0l~QNHfKs0}Ivy^Dg>uxm z#OfOE{27P-E26UO)r6i=Y(h@H+JJ5{?1*vNeba_#7KY^p0cp6a-uBzEYKFJKQ@+h_ zd%fXh-SJiGahyNOw-Hq@vW&R+eu@m_J0MNZ?L&)baGd>splmJg^+%G^FO@rQ4S9M<`4+>jG~jfUVrodZeg1r! zKK0htoQVvK;X*ZQ1qklKKuTO>zS?eZ!iB7xl+fBJ5OL*uP_ZK`C%Z|H0rr2QvKzSn zKxHjo45|dzNh?x>j=Ni|(IKZ#Pt!zo_XMx(fVmQ?$_Sv#r3IvuU2Q&|2DWAr9@Pn| zYp2rYcaC^`qIIfw>?RXg`_O*sTxke|CRPQ+LTnq8Rq4^u6pK3zw1z?B@op>GP(kcu zMA;T1`LUQj!vjwPeXb&np#8H`Wr9u=Mb>uiDZBrgJ<^i0nAFkVAIrwMmY_Z-ysZCX z;J4M~$C-Wl16;aq#8iv>JvPS&@X$_$Ii@QLl$Y_{dI2XE30guxKVw|-Nv&alJ1+O|Xt zu#F+cU%8C}%er)K=rOIYw>=D9e!_7EZ*1!vI*J|t+(6f2;I>Im^C_@*rG>I6`~1>p z5^EUicD2@iV@ee2i1 z?o_x8yBzm%_~o#M9(?sh{V`aVj_`GvicW*(ozf+F%<7vJicA@AhQ+w})umS(3sh*vbnrYXN+(wWFUJ zM!ia_%`13OgA435SKQK3vFzi5;Wm1rYwvy%p#Ydfz;4H;ONOo%XC67^Z7OkP!>EChZq4kY0B5Eps(?TziyV;BU6V;`{cbP@K-f_l46$;Q zpJ2DI^d6)VkY|;b&z>ZuHH&$?^QNL5zvBf6EHQ6=D8QhrwcD#ea8OH;2<8FEW&|n( z4Q56uRq8D*66VkJI24~(TOC$k7n~ewY22rheKE-5dSwshIJwB@3tymFl+x)$_48p5 z+#Y2|R7}v*;&aO(8FK?6$g2`GzT=u>UyWhd6yxMOOGf8Z-&Y~UlSEDj&E4jn3(7m6 zM8UmXtOg7IJe0R_*}7a$41WmK@+(ZlzAF~(wIgS&k2zjj{S1r1a~}z>d@3EjpEym% zlO?e0NJO9rwqB3@GS@?LPS>)4r4DP8iAZBp2kCX>pM``xQ^!vAE8{es`OZs8aqlXf8QmYXcQJRa}(bO3dwfJ8sDx_3<`+LRXJO?UaQFcYcJ5YgyX&1npeq z0)`t()GRjZc`**8j956Ux)zP4uG9x`6z+pKvj^Cwp+U=p;1IsRPL$gZlpE13p9_=j zb6A7QU1qkQFu(EU(1J?lcuz8s!QEO6I>6(^)$@BWrfLG@d zO}y@-Crq;E1>;;H=yaRVolUh~Wh$10EP1S-0zrP-dy_Kpd)EhApCb-<@nRwMF}s2} zkl2UWYE=JHXOkYgG9`6&-xjqGf_ug`J!mR7p^ZCZQ8KV71R>u>c$fRWlk&d&(%T5H zr!KoUOpkV9ZaWg#6bfX6Cu+xBM+X)j+2~d{wsu?_)wm)nqhsxP1Il81`xSG3ENU_& zi9Ixoe(?Lw=lA5s9|g!?!^lbZE!-Ar1HXV`Z`7AVjWK!oA_^;*>pP}+2r;|9AXqwnJq}%jx_R%Ka`O^z-pjlB zA~^%$H$SnIYQqaF*<@sMm8Z%ADk~j_0|uXXrILnJ4t)$l}C9mN}X$>Ncdc$F2z6bw=W@VU>1_{aFy#7RkC5nPMZA7$ zD*rsEC0Ln|pqf(jhcGw${SJZaYT$O05wQ_(3;71|O0}qPYg!VsO{sz$=Y-+K4lu_6 zq2EqXjI&9o;g(tU@;)Y=%`^%3b?&m7FvrDjy3#q~jAOnwT_g;F@sdCF<(e$l-h5B? z{aaO$^U@R95G&&hX$jETV=vCaY8juLy+)?eCl@Ie3x>N@KTHP=jRae^l-x(?#sbJs z{HO#eF0gyuURc2^XfoWFGAjO4*i*u;OvW>aO)Xd0WQ_ZKxlmjK!J0m##&8Sp46c*%bR=9MAY>a1_C~E2tcXsmz*9Z zkqY%s2^2cOWsIJJPA@T?VQTpx2Kj1&Mwsj^zsZO+S0WI|0c0%yT%pDJ#X)Dr#B3Vy zBoQOZ@qFDS7jON>P7;Cehj84ifa*E`rx2x$I|A_oS#2iYTMabCFbc(f@y_S`wdW~b z+|UArkC0iZi!`@y6q4}b(X>D5WSkPV4|tugA2EhHE%7_QgSSI{;9P?qi$id9(mL(0 z$a0q41*YK2)+Zyut1&jo@#AQgYz?*X;==m;@7|B|BS`MA@087aj#)1<`G~L(q3hhX zemC!fFwlIQr8^x?`fx(WFk5K6R+9+hyszhFJMjlVO_N4F{L_u|AEk_{rdj<<%f)e1 zoh;XiCF{nks`^5mQPsOPn~`c<;9A_QKI4Jb>3}?NCFx`66B^a}yDcM^WvPS}-l8`& zWF$*;KjC$bAth6`5?p*RNGW{?`}k4b1@!&xyhnIxPZUXXkAm6vu(|JYItoB7Nb_o9 zIOej>=ZgGn3GMAS5*{jJvE2UrWtsV*TykRx`t=!q@(Ok7TvfM&wNkdsALPMPpnWzp zqF|!mgGB|X{uR)Fp;ERIyL&>uzY=MGOjTks^~PLZ(o(J_RQhq|huBk{5SM(tmS~=P zCfRdzqe5%e9PmU-7K;Y<I)w6j?>lv%>dbHRO6;~OmJX?bc?3YQmLVle&tAl^-caS(WW!_QUN0PRoXN9Qh`i3vb_ z^oLhEnrq8e2B}Vv+^3fKbOZj|Q6)%+tK#J6t@WZFrXn`jf$k%irTgx)39InELn~Eh zOZD73sl41QPj5Q3mXg%4PxPxRLi;C-Ss$Y2KZg)Z3!l13x5ktid@13d5?mSZhBpJw zg(PkU#@mD^lv3BUb-bjDzP_RF)GNovWy$x=_SfYD$ymwY%s_WFiHQ`2o6&>}ID6J| z`Fs7YrIWj6Qx3D$99iJc{?>>pmblK;LS!qN*1eHTfQL6`E{UkaJZ+ z$2vo1UY0u0H8`xXJYlU~HcTFRf3y8ZYb2Mf_LQ|%#SX20a}M2H$4j#t+E91*Z=OQY zi=KVzV_%;{br+?J&ba)HcIqu)*j^U84DL+29pKuBa0Pb^0zvtl4r|6uxfl3g>@ruG z;S5F3o6_1s>Brw;fAwS3-|5uNOZ5zGs?7{HLt1=V?rO+A2sGz~tfnTk;1D*57RH~~#MBugGY z`GzP_&3kRmfT=qC%e4KsI*b$i;>8swJa3jIKEyJyaXQ`&3@S|*d)_(;oQRnlX1zJ(BD;Sf#lDpDm)bk5n^n-SpAm@Y$PMJPCa)<1wxKNsQ2`Y#e?*-YFuk` z&IOso1$C-aVhv&e%rzf`w zt;GS)1|GyI5crX)lI372=of$M(l0gnZ1u-Wx>$_B`3Ks^4s!NrMCch=M>y#tjO|mv zwUh<(E(7&5&5@p2#!hFp{)F(DV9rAu^;aFjN7=3ez>-amm-Wy`7(8Y{uP-C@#JrOI zPJLeE88*Va@7_PYsPN(MTP$Iuq6tedml=bp@hQZaYX^RPGr4~J8V>fpc&z$a1`KiZ z`E#eiqr7P*7+znkVOa7%Ojn%hq?7{Qcxf~^{k!RE+WMF2(jR|-S91#;R(-vK>>Cbv z6)Ngbal-ZBEudv7@Uqa-O(l?D8et77@sS7t!sJ($4Qu(PXVWzFJwuX`GdZ#fpJ=nH z8E5%-JM^3TKkU%mmq16!RW@|nbWG1eSASv&#(_WP%PU~B?FqVVa+n+w7hTiqP2>Ff zqK!s!xBq(?R(h(w=*%y1vdwkInK3(XzNq$aE>C+gQNIA>)<^lg3uUz06bG|eW&=Lm22ezvrJJ0N{KnR~*w9x$K4uY3sxQ6Ooc@rY}MS_yVIShnJ^wK>BwJ+Khp4MTk` z{$(E;{@RC{@xeUrmXT(wgzyut*6=oG#@IP1OU7&27oemvPg#R8I;W4n*!FJgT>a~> z9}x|CJ(UZzOuc7E<_EkUsrdNXS4L*_NA3N-KAL|6#^R495gAdJhZUe7UDe`x9DOo3 zVtneHu)M9QdS+g0Nxk;yH#=g!oW|OvSsUu}I>JeXeRlo1hl2Ew{ z<3;55JOO~yev8ivfg1%_EgP(G-2clNBNqODpD`1uQMiy#?wOlkvbG=#Vsa-gdg5@F zp6IVR^U^PO%ktdc^G|_xR&Y*6W`ivHk@h zTu2o&zAlXVPu)FCuhyjIEQDPTm`o}THjVbi^b2R4{7+4Ilv zXTmA07*sWYm53F>=c0WIj`Y6L4uwK!n551YXebX*A>|G1+*0mWmyAq)#-1gt8uNh5 zp@lu4J~m7^n1Y*_jJ=FbYQo$xySobpeeZ10kXYWwj)7?=cxw*v*M8w2n#Pzp{L|$h zEx=hgZWIIZiI4u(-sSx0ZDPEMfyfue2d~ENjVKgL3mo5Rl$`n7qLF~mdKZ8Y&;n96 zCpfxtsqZX1HTo-9R%$!1?5HYY^G^t<)A@e}*$WBbaEc+eiSZ=W;#gbeQcPl^3-EQI zR!AthG^vnB%&jJOgKs(f>)pmc4ufZ9Uec9y`25}Ny1(SXzY2f$o~vM#5ocNq)KMP3 z+-FZz=05(Hl;Q;s;&g1**>bGnxq3=&l|ETyedS${8dW?`tOp&RV(E}RWKq$rwn9uO zGkXoO00Ic_2x*Q?t$BPLjI3oe3(9XxKL5v0jkpT>g^&>NRsYTJ8G(|a|IbPJ-@K7D zC;!Npwrsu}<~oJ0a$d0rJaJV^nJqbLpq%Z$s*y^j!{N)}D#oOH#4s34zWS1c!*VZO z-&s;F+_LK1zRPlNk_q^TU zMcf4LN_H$M*m0{8gx#-Z^9n=U+YX@s+XHHDV(POZcobvfo-tCWsvMBIxY85c@8#Dx zpFiUd1_Un(#SI@xa~yhvS8hTYVWnL4uY5Kf?-rVxj8>$TjFA5!D6JgEMZCJAd0#tj zwmpocDwf_xCSUU?WSqw4ul8X0m$_PAU;6Y)Y0=-+EGMbiN$A4a<}8l=TUh{8?!%N* zv%WTGPqNkXCh?(M?)1ZTKf5Y7I1CP}mZeXoy*9)ka!TZL$V9ghI40B{Tcpu`+)h8k9 zx6iLiuy-fvw}Y$W)MU~$(EW1A3F8GJ&*7hrm=VPGR|v!*c!f z-*SqG%j#{M&dmSVgR9hr;G#^tdSN{2OuewSljp_aUAatl`nL@0=cC;rMpCO~EU1Ri zRn4xLYG{kIS$ioK$1GGE`@YEc693~3F9qdI=DHPrZgvL-@Uu@ZTyK-Aer?*wRDF6i z>37lUZgK_c*wZJ=+4YHh-Nm6H&rSRAFP0S?uqM}vz5-Q9uMaWgx0H>W++iu4gH%4n3dAesomF;GUlToFfK6r3;KvwaRN2SxL$FCh&z6s%RP*=EcEM4!P?z%; zM#KqyrEulmCRx(>>IieulJ9Idw|(-0N%fDs37qs?@_A6$bxHnWLTa@Lm;4E$>%l;A z68$8rqy@F+wG^HW83?31<`dDVbBvcnUSQ>L_)N2BUYn##teMOKThRuS96#M*M`$6;7AQ?j{82}c=sy~Vnu zo6UgqgNw3;oQFtRx?iyGV;9rn!^4 zbZ%j4+{JMqqm8Ula=-MmF5OqJW<<1hX&n38GYD1Nfw*p90Z0;4leloS~5!y8x_z^6U$qqi_Dp7qnY@`2GZ4*|X`>n7py|0&{)WBJ* z4JeYkh?bofa&|^RqHen=%+I%Ibr!*oIcca*@0%_S$FLMen%Q`JC`!@l(i?awP)-Mf zDhTEtghyyXgR6VgS?`#L-BpVF%P6r1M_b)9%@(ao?a~Li>NgFoj+yV@-k#^qO2 zz`gtpbMg=yz3Za1_A+H;6JH~+riPmd-&`Ba=QXQ${5Dj8MsHY*VASNaC9IEb3vS9? z$36hJFMaoZt@I&JJLO2XgVixDClhcqpwv-O+Dz5FmVUo=@TEFGcd+cMj-1VjUCqd1 zP#4ViF~-I0%6KC_t{OJ~dcD;GoA%rb1wgO>C>k#SWkmjYkFm9>=Q zD%4DC83e7*PAY6_X6A+h82&`&L)bEnCS0fAV2FH+-GxtdsrGKK6KZd)vp*(}t49Gf z3ep6v{EvOsC4RO|Sk@QP*jeKR!HUC)5h#!}9&ZbK+@a($m)U%{ceF%;ZWaS?%gHG$ z`gov=*pppdkJq<1Fi95#9S>n)eg=`kmP6AJv;bb0?wj85G-jO?T?n5+YFpUb|5InO z_M1){+0r$WyVCobEM4IS*A~P!(7Edk5et-!*&Z8~-sU1Xak%{!dW6^Vo7KHBZ1|_X zV`!k!z|96Tw1;mqvs!p!m;AQv5e;NT{a=)syl{IS+A81lk>yKuKoNxu#<r)NW3W z^J}FG%Y;^DNXz>N)bN#rujilYmas6CDySy zMWDM-+C5qBsy0jjor{_nr8Ko_t_&`?FJ1 zia}tQl6>O^9gqq<=Dm8O(4s}jTgakru70DI4aiKi<}sAMM+St>h*WW`z{_M2Qi)fObIGt4BnOgs$gAd|n-hDf2`$ zw8Qt_DCx+v==^u|R>JVsC3Wbae1cMQ@R6m3o4S`yzdB%h`F3+2QAw7Z9D;0FrjPo4 zRwjdCU*`KL9#HVgw&xPY=KaeOW^pr|A>vwbPzV2B$O4$kuSBCay8ctNuyy=DMGI?J zsD_2RPlPQCl4|inSc|S+_M()k0N?Qeoo@%>pxkaX`h>d^FBh>B>+c2E=j;t<{qB4B zbN}9FF{Bvfss5|m8`sbwtJ4m(1>UBRyLb=ojdaPw6Jyi2He)aXS zWJf5qE9Z$eR1F=tjz0wPUs-W6K&=HtrB?=9HGEDG88KLGkWAE36U;-V@+L>A$gY^* zH|c4b{*q?vRAhr;{Jr(&n@|F)8obt>bH=+p1zi;Sw6Wf4IZQ)CBdYJzk&EsP)V4H* zq<0e3rL~TDmK;0#d|dX(-TC<@zxk}V#Yk@y|z;flxW?#m}Ph zZ)bFJ);wKr#k?0IbNQWU5;mPHMK8(N^3=vvXV;o2FH~7QTa8V@<{uz1eWv+GmvrAc z@ZB2l9aCeUmPp#-rMRj3*iYJ=UG!I<>u8qO$Nhh4O!g5aQhzljX8)-%>9m2dtY6mo zt1W(6kao0W$LU2*Xz4)=HI0tL{6XE1Y%cXJJ)!STCdZ~O0GYD3bI)QIcRVC|Z%m;>( zFvhsZu~+7GqRY%I^KY!PchF@VAYHbj5VCAqJ#!8VjW2ieuxXvILe73e30VW*C? zQc`S`QZV)*W?#9Fo-p;cg@?2~DbN#z>DmdTq|W_>2DdbM zbv1KYms|5py=9~C-c?myzH54Pu8yN{+grd>QM1o)UZ=Qb;z_fW6mCn5A;n99KQaG? zSS5UYVz33X=foeLD4K6KQ=jG~VK?MH))0A`aL{+DB13rR6c@B%ahi78cL-;PxpMj0 z(ddPr(hn$#Z_JZc9q_l>QppuAXdI^Oe|! zi>L&T^aPrN(2wr}fvY|I0z{nuOc7yf#FY~ZE4CB_zkUMZ|GjAi8uei9@z z-o|s`cE{%iGLC8AK>;D2P8C~Au>vyv`UbQi*zPNaait~0F}a?fz5EpH+J5t$hhwwL z{C|jbW>i<3I}g0?6`r#_1_nVlcL%f{>v!?x{bBAB41ah%^bfx4fOx+)qgvT#GFb89 z`)`DmjXvJUdB; zL@`JM0(uzxM$wA;dIJMldA|WsjkadI`mavO?M}4{f~jTX+$o)nP|gkIQ!bNt<$kx#@Cf{a?#qPl-mppyQ~G z_Q`yL{Xo=5@mcq(;3x+x_>U;1HD}DbawFqAKea&YHtvQPWfe1~B1Xj}#X=LSozXZD zg63u$aWs1eC7iVCO*d-3Dbm3L+TeT9Vel?Au>FNT6MW2YMz4JZ(VE>=DzfMupN!Bw z&Ie=rHXZvKzrt`~n>rkoti96cs!9%n1yQ;96CM^w;J)!?HD~RdF6UZdo{yMNXm*qW z!Q^glZq&PRJcrl(q%s-aKQ(q?o2GwjW8ZZ}0l022jlystoz5OTAx-IUo$Zav)gi>C~!QY`sTYLB@$=_ z?$6DpDX8i8WKEPo6#kluiZ81;VYz*%f;ow@d*>ZYzx{sl%AeZlhu46$67}L6`LYUX zAfW6`4TnTtWNwEl<)Fj)Cj}8Hmq`bWJj>e`>QFvij@SKx;}1mm6B@Y@U43R>5{Hq2 zmgM%Y$+qA3T=M|eWFGiSkoWbzFPoEDH9H)4rP}6pJ(I#!g!_W*a}rz+GX??uV51$< zr@|ADE!4&wj2F)85bji$Mc{-`AF7O~AscPb_4=ecNV2j6;r%RHSzp2bJmP!aST)sza(KnbPNvPg>OKD0KdC}7tobwXS<=ikoV*(_ z%Ygg^I%Cyr^%sTs8AKb+=(}}5stv_oFo}S)7x;2hr`W(GKjQ3&?LF2LkOX5Us)Een ztm5GnuY{Rko_NAPF;Ff==5RT=t%YG(fq60hpOCdMn0IJ|Qz6KX7F{~+EQ0HBIN$LA zGz6@EnY}*AX}aCCYN_POqyPDw1R!PX{w2hc7N)q?>THpn_^GqtZWtNq?3ag^aE938 zPOEbzc4k=MTL2j({t_o{TsB55VV`&d5z9~jiV!nZfkk~eBZ7#Fbi;jP8L;O@c>um1`@ ze~V&RnA4K1?BP2+b)C#z>I_K4M;U(!*}$UV*qCQN2~X>ol%d!b3R*H!_-WHiIC+sO zsefNUb&7oXGJQ!?XLSK=)c9ZRK+gsRCN!13?_fQ5{N{QPZ`PFpGBZSqMjGXg+lPq- zJb3XBc<@~ZE6u+)ILip+pV7ReZ(aBG{>buVteG3)Y+Vr8Ywd`DSG8nzKRjBG8I|C- zrIp|p;pzS0#$NlU3-xD7%be!dP>3T{IalHE&l)b3YuIK=$_F9o=g%rN{6hb}U*p5Y zh)9KhV!CH?0+|;P|AP5u3{#mv=}*RZR#F?ro1chKd>$pwVYTTe?<)i6sdZCIjcY8?J*N51{ug8O+;8Qds;e8av@lL%mA=(2m8@0NPERtlUi zIYI75NwldKDTL|S^m}uA7C!%b2+se$N3ncv7TeUB;Nk*DeAM79$#>-V_A<;qH3>t; z=kCi#LraU@y%egat82L>03Ns&?2n(^M>dJLK7`{~vn0+J5EixZw_K4N9-`7ip)k-Q z-QsmtTQv3Fr!*aQZoShxlK#D^a6;;6y1Q;%Uw;XPVSnTqk@V=;OhmMC!ObsZ)=8w}bhZ=Ne+3+i!fQphYx2;q%; z)V%H}%o$u_@nb`D7}Lj*rZ@2}eS@IYNLYJ`*mIv9kS4g2EFSy@b4eB;t-Jbk+@4Zp zU|EB~-fuJ)FBfEQ;1f_jLwx%|+b|7D_>)U=&o{^-nWl*F^_w@1?pLTsFDYwZ0jz4E z$|BAxa<)TqtCm*Amf-D&tYHelKy!o-v3jzM*AKIf^_-LE9tTWZ%)Sq_* zbg>*n#LFCOda&`CbmlbNaM;{0Iy$q_oaKH^W!n*kxFO|TK=RJApRuWoYhpbvWr`+U z!TZd_@el&4&$~FR<`Hwmz-a~>?_rX2R++ONQ$z|_nZy9Sr)Q`D`Cupe$9H%yf==m* znN~4)$xRO9H0ez3!Y4DRaD8l=Z{B2_FIz3;9Ez!l>O=ta2` zUTQ4Z11m|Qw|Ulmz?th#VSB&mT-eGcuU$pO$gGIRkI@`+csj?P9j;2%dSfL0RF%63 zL2Ov?V2f=uB)x0P#Y<7)oSy1fhSb^WUNj~NQ8JXkTcE$P3kJTwJctbb9q2P$4L8#d zj&R>);}Cy-Eo)^%K?xu1qUFAcpZ#UKhSVQeZbC*ZlxpB8U9Hw;;Rh~%D^>c4p zqor~YY|r?ItpORjQ_JvbqqZNfB`p{T4D7~f{KEGy=|8K|ynoW#8R1-vw^?3WQ+8cY zZAnKQjrIOkd+K(7Ymbq$ri{g1`gzxi&EpRhmct?5TW8YG5mTLsrH@!OHT~l5Q34Gj zpe z$-vMRGM0+J%E`zt?;}|>Vtlp_Y46@H$ZC7a+FB{OJvhaJ|DfrO*7*jlNtR3?svgc; zI((TrMO0r`w|i}^0IUGacNnW|EH$FWD{|=@EjF#}^|N<+_r}>i+&&13WOWHmGrUsm zX;k&CYd(!b7^feR2JH_&k`7y{=8ok&m|5|O7-{X#*qi=ZBX-nNpK#7t1!@P&r(}&n z;W`r()g5{PrMKqgjQPzlI=H87_pt0oi9g46*Um;5C)%jigQ=1b$v(v$COwZVJlXvv z-tgy@vMZIpFYBRRLIXQyMxG7^yRxP9P)3V}!M1#AQ<;}2oS@#}*0LSF+!0*qWi3BZ z`>ed}Z>a*kk*fKG65a&bD>sFnu3$1w^XH9gpXGnLwOlFUN*bq(phK#W0Ukz>0gYvzAuoM+ z{8W&CM2=dM{aNIkHZwUEK>@a-^>4mroW{%VHh5pFc&X*dZ;{gHYi{)EPmZsR(iNz~ zbA}_D$St>?D*Y}an!+%g(m1*-(5DuofH1lO<|u17=l%=THIWb+=oY82)dF7}7i{fj z<0!IJ&K65_Pq#(Tj^QcV!|1{YIYvy9Ws zwF={-tRXG+w}e&!85^%oM;R2V38ZTks0&|0*a%#bhK{QP&WKpc``ZZYUA6UwQ4;7$N{%FukBNB8#Y|&YX8( z;ny1~r_(Ga>(K0gB*g_omr`aWsyiWYVaZA^awWeA76TL(wd>Iuoafc9fxq{iI$r2x zY(}&Ymh!^r_(vc{kvlr{59#DZr5783kE66qPfM&2kGyvN{fL>ENu40i$IxY4XK_LF zLrX1UXl_@pPLHJazHQOiEr0oL=*8Bu_40<#CK?SM$srDgflcv`1rsVfx_nk)wSu}wE+cy0JNpmny^S+QUeE%= zD4E=^_=-pHtf17Fr>}$zrm71}nUx~A^BJD2^rXZ~R``g#X|3sO8-q0S6fwYQq47#Q zCxw$IU4r9tSA%aNYwXZ2D!sjkwY>%YCBFJ*C_And#bc#h zJXb&7wy43Be{O$mSB3pLHxfHuj;~Hz+<;^Gu{Jb8n8+`BNmwNYM&Y3zUS`5=GIiLVWjchaMRFLYW?%rXz z%%9J|ac^@}$jp|u)c#X}>1?UaROmLL{rx%l7KSK69(4F;Hp@&e*O$QZu5k zz54{;sMk6wHNW(RGA~TvTv2 zD)q^cK@ZqI0IL}6om{=VY`;xmac^K~=Q9*Cn00;=6~C<%>?$ex)nlc)O^jgA`_kB*hdqGUC#4wT9qch}5RAu|3DvV0 zFYSFtb_g06$vC|m8H$#M)AG}@j%m7$PKBbIgRA|%{Gb%w*-&TqR$F@gE>TJDq8?ie z9nq$`(}_MSv4rRj_1aI3=N<2cuYT77GK?)UR`>lfX4O^`n6Kb-8Ipr-e&LUY1Z+dS zL0(prhW?&ZBO5Q;oQMZwBSjNB3AZQ|zIy?)0u7ciK%xDziNK~v^&1?`Yk_KvC=Go= zMP#c5dpE*T?t&2#nL3#mECf1{zY3Vq+RlI94_Z9~E^>qE;+t`y@KKJ40X^WY2a}_3 z-y)!0v1>X61Tav)-&kh_0uLmH2i?umB)&{6IA|W>rw~?&Z{EDonK&M;ug{3YHDAe@ zq-|_$WMDbcceO7N(cW!;OyLZkKLUE)%$FGas{vHK@()uZk^L(=b>4e()m=wB+pF`#TSq1e!hqI5Zq6f+#Qbc9wqNT`LH<>A%*2M zewzE)*k0~0D4wMN@34Q=z=f{|7ZMNwxYe%)=LNU+q>nF&^A1~zT|(pYKUQlr#3)4oC#_ zPI)ZBA)49tG`zzd+Sd0)O@KfbUU0sZ$2dOu0A&pqV>s65`N6r)Rc+f$<0o12nNDYL z6hJ^X4~QJk3Xm|M&+A!eKRPj(A|WpR7;Jb9V*%5$UT0KTqNj+igJl1JrtmJQV_9K( zA1+bpam`^O2L;sElY}yj+!DjT;#&Ur-YRDdCPXT4iB_$CZvIs5sbT0j_hN7JfOHUTnQ7(+lupmOqy*<} z@iD&`^mB|QJ>VOXMgTPWxwxy2B-VvmDy*rOYLx3H#%SPn z`fTY+e%GGhJgG^}Fv<6p)uTt6!o3ku0$t^jU7Z6gLpwVnaXUxf`g4~-zuOOIpOUU( zqROe-SJ&8PXBlAe!Ft|dJ~N~P_bz<;+zZ`Lk}&|q(DZT^d}!peCf3I_ zGufeK7P&(VOTEdFO74dh84aoV%kQSP1r@#Q5G(%8N%)~JoX(SZ^xM2NVfL6Ad$4h% z)e&Xf-(-Z|sCbG@jQeB~PSPScluc?o=tgaspofkUqN&`6)jnAed{A5f7`bOL6bq*9 zZ(hBx`96!HKFk*Ug~AAzz31%jn7q z`IB;RKGkn;>7xq-mORTFp(b>VC zVZI#+`dXOyrNP`TS06X~yR$nYfjdX2As4{F=hk%9HbvQ7cda#HIX_iN!fP&HuGUoDzGN9jWX3ft=f)VN_Uyr)^OtzHqL|I4*&3z7#X9uli zsGFiEZHdK0!?I=lWHR0mWRL5?I$T=0<4frIG4yd{`;K=A+v-Sg0xF0;AF^d!Gj1&2 z*Ju(EJ!zMH;=70#vQTso5M+Rr51KjYg&z+GmBC+~FYCDlV!Y)c-951t;C-Omk#Y5& zQ?SqorMTcUI)e-$*?hJ`oG;iH7W@R%%K`%KkWro2(6%n4Zt!q!G%Wp2nf^;Jc`Ghk z!li_{;lw-Jwj%FU{`lSUuALdyn6Ub_#JDFhypWqKdCA~T5&rOq=NivZw4txZP15)8 z-$_zc52s{xv``p^J(gQr-T-rT^Ni*MCaS!?Tj?&>Y;YgTm8Qj+E^R6&&z~d85r<3o zXKcJdRD7Y~l~S&ghvUqkC)<)ESw6p&lhO#EQ&{C;>5#gq3h6Cc-cQe7#!+TOQ@y^} zgPQ%Sf}W*l8!H}Z$u6H-FPC4QIgXpeOvmKaI*>Z?olT@syo8m8BrN%3?R;LKr@O|=I&;U;f&x6 z)6#Pu7};4?-_dTX_R)CNq$cC2-KwLxH-#8dTs0UPHy5Mp?oTq+o_I$JZ)enT3{vla zzr2ja_u#}>crJYESVPRctaGizv~Qf~Xla2np0yEY?bcd+wUATJnGtg_Dyof2Ip@_y zcgnv~A$coI{ycEeJH0gL@*tp%XKM~+5&RrA8|ucD1hVkzK32$ znSLlDCODhmT$%XKe&qN?b9sjMJD^KbG1TD|&a{l>OQ$EZW1P47lAB;>tF->UZ&av@ zmX=p?BGY>z>qUuweHOC1rBa4_vP;A3wO4X|yEjzth>C7B?fuUF4sP-bJI9R|M=&96 zt4#i)uy`xvnUnHFcYzEB>KG5p2K=baAv=fGz7WPTCUvru`%<9tNe56kd+TQbNyi}D zBcGhqyjv|#8vDFC-Ps(tR-U@GLr16(I9ZCdjbC5S`aX#drCgpI(s-kC^Wyv?%kC#9 z;fZP~s&57cjq6mR!dJAK@Mq-V*rI%CqleyOTCqXu5qv2>oQi&r=FiGK<-J#js}M`{ z{>H`3)Wgd5dP!rnCX^tb-L3WLZ`t*?_mDM6y!>0xq?YI@*&mr2Vp5FSLLYmz3`VO= zso#PR$W<4}czz~0nk7H4M(?!pnNbukn9W|Z&b!E{(eRZOxmzT;_Dma4B|mlI)LK(U zKVB@7e{neCn2?K*XKL@q^;AAvF?s5WdXa}S7egX5 zYq+(zWKQ->_}7EZIdct7glwBc?a$b37Dj|kiQadtcIhOBKFCve+))K8>0162nfGet zgMeje0v!>q>1opbYT%P-0s@hIf4N)`(T}@*H@J&0``v>O{D`|YCXes@8OxHC4yW9= zlnjZ&B?!8oYaI3!*1M&)!m1jn*K583nsk*4-xnwfhkNM@XLdL zE#c%Mqo*KtBbYQFT|tk|Khb(z|KKxhjER-ieoNr~|MQF$RkRp7( zpvUDu-mLtC|K-ifm5lif6{-Yq-d@S+6*9E(cQd6Oh>_fh=Ugae_UptUZHC=6tY9}? zpwb*}#p_JKMx)}@@NcgfDi^95+IrVqT(T(qZRg{547ciy0N>QJ3TgbZ5P?#q>j!Hk zp)0x`dW8qG><@JYDd0yr?^tYVSEQ5AeEVUw-VEw9eHm1ztEqtzp%Qwk0=qj7c}z-JCo3hM)wMaT3V@tom>Pn2m?eL5((Au^lDX8C zIe@2{PI&I=z|(PEw=9juLjd;qTWL-t*;y^La{26<&1F@Sc*~;*M89K|>dLZ6)Guzm zmx5fo#N57`ss(JLs}tBUbjK%xAUMH=%{uN09Wp1GKU}$(59z|$;a|H}mf|zFw>tW| z?yh5T>KoYbPcC@*cfboj31m{WT7+l*|Q?tZX8Sl7!(n-Ze$q-PVHi;q}+4#5UT zmAQ zKzQzwE6Ld=y6jQV^z+5U!|xXqC;tEVZNR#?M%$fi7w(@ndsMb)RU<;*YvNwwyb7-|t#rR1+1mh5yF8 z=&s|&T`lQg0NJ!*-B%$;a7qLX#SE{o8(XhBzQ;ZOW-+_7opv#!H~ia>vK?E0MnnA2 zX5ZwJn4@4-)7JiyUDy?=BHe=0d+Rbgn)ihDSg01Cx9s^TtS&_R-pTE=-$O$%KYt`_ zXfvy`5lVZ6-DM-Il7+i@cw|&=W2QVK1$;WvKe?I)E# zRqAH#?bpnF)3jo_@H_p~Qs3nmU$w^i+hl`dcc)w8GcYB#uq%$u=W3hVtG*aGYKB3g^-wkn7_dXuB(iF zzz>tCgNP|9{rkq8a(64!Uc1|&jg78Sb#a>6sV+_w60UmPI8ElJ z$Hcj$I}x@Vak5LzVbGIA0nP9!j-cHp^8#{HsgqK8Ff<^-Q_?D_2@8dmsNVteJ{Be^$Ewm7g)(9;C^Gb#hyxqWUfh) zi{xnVTgFq3mGW$>I05=s@SFsNZGU)yAsa;Rcg#+nRpk_*f88a9y6-~3@ne9q?@H^F z;WBINZ$MU~PnQE##1Uxt1zsAw4wCwJ^eIce?{~q;ZX{R*!}S8OF?8wByxb{Wk8<#U z|MR546pw2ORN+(vSZ|Jh1b=^M6@f{!K)mVTmvw^tcnyWQO4mb!8J2SHP^#hnn%KJr R(^TkKNlxWq{sZH{{{wv5SIGbX literal 0 HcmV?d00001 diff --git a/doc/readme.md b/doc/readme.md new file mode 100644 index 0000000..71a988e --- /dev/null +++ b/doc/readme.md @@ -0,0 +1,396 @@ +ROC CMS Documentation +===================== + +[TOC] + +## Overview + +**ROC CMS** stands for "REST On CMS", however until now, no particular focus was done on REST API approach, and so far a more pragmatic approach dominated. + +Part of the design is inspired by Drupal (blocks, hooks, Role-based access control, ...), and other parts related to Eiffel. Priorities, modules and related have been driven by concrete need, in order to fulfill the https://eiffel.org/ websites. Also a contribution (as student projects, or others) helped building various modules or functionality. + +Currently, **ROC CMS** is a library or **framework** that provides components, tools and resources to build a CMS (Content Management System). It is not currently a CMS product, one can install and customize without any code. + +Thus, it will be interesting for people willing to build a website using **Eiffel**. This will enable to reuse other Eiffel component, better integration with other Eiffel projects, and of course benefit from all the goodies of the Eiffel technologies (Eiffel language, DbC, re-usability, portability, IDE, debugger, ...). + +It depends on the **Eiffel Web Framework** (known as "Eiffel Web" or "EWF"), and thus can be executed as standalone, or CGI, libFCGI mode on Apache2 for instance, and on any Windows or Linux platform). + +The main notions are: +- CMS Execution +- CMS APIs +- CMS Response +- CMS Modules +- CMS Hooks +- CMS Theme, blocks, links, ... + +Those notions will be described later in appropriated sections. + +## Setup + + +The ROC CMS source is available either with latest EiffelStudio release under +- $ISE_LIBRARY\unstable\library\web\cms +- or from github project https://github.com/EiffelWebFramework/ROC branch v0 for now. +``` +git clone https://github.com/EiffelWebFramework/ROC -b v0 +``` + +Note that if you use the source code from the github repository, you will need to use the latest release of EiffelStudio as it relieѕ on recent version of various libraries such as EWF, sqlite3, .... +And using the "master" branch, even the trunk version of EiffelStudio libraries. So for now, we encourage you to use the ROC CMS shipped with your EiffelStudio. + +Once you have the source code, you should compile project cms/example/demo/demo-safe.ecf target "demo_standalone". +``` +# from Command line +cd example +cd demo +ec -config demo-safe.ecf -c_compile -finalize +cp ./EIFGENs/demo_standalone/F_Code/demo.exe demo.exe +demo.exe +# or launch EiffelStudio, and open that project, compile and execute it inside the debugger for instance. +```` + +This demo includes all the official ROC CMS modules, files, and use libsqlite3 as default storage engine. So you should be able to execute it easily. The **standalone** target is configured to listen on port 9090 by default. (mostly to avoid conflict on other app that my listen on port 80 or 8080). + +In the directory site you will find all the expected files that should be in the root directory. +* config/ : it contains the various configuration files, especially the **cms.ini**. +* modules/ : files associated with each installed ROC CMS modules. +* scripts/ : common scripts used mainly to initialize SQL databases. +* themes/ : folder containing the available ROC CMS themes. +* files/ : folder containing files available from the ROC CMS app. +* And also demo.ini that contains the settings for the web launcher, (in our case, the standalone Eiffel server), such as port_number. + +Now that you know how to compile, execute, and see the related configuration files, let's describes the main notions of the ROC CMS, first from +* a admin point of view (dev using ROC CMS to build its site), +* and then from a developper point of view (in case you want to contribute to ROC CMS). + +## Usage + +### Main entries +As a CMS administrator, you will need to setup your CMS application (here the demo example). For this purpose, the main entry points are the CMS_EXECUTION interface, and then the site/ files (configuration, themes, templates, ...). + +### CMS initialization/Execution +The `CMS_EXECUTION` interface is deferred, and your CMS application needs to inherit from it and define `setup_storage`, `initial_cms_setup` and `setup_modules`. See for instance `DEMO_CMS_EXECUTION`. + +So, the descendant of `CMS_EXECUTION` (`DEMO_CMS_EXECUTION` in the example), is creating the `CMS_SETUP`, declares the available **storage** builders (for persistency), and declares the available **modules**. + +#### Persistence/Storage +Depending on the **configuration**, the CMS engine will instantiate and use a specific **CMS_STORAGE** (the default is based on `Eiffel sqlite3`, otherwise `EiffelStore+MySQL` and `EiffelStore+ODBC` are available). The storage solution is used to implement the persistence layer, and thus store and load CMS data to disk, or database. + +The CMS provides, for now, storage based on +* EiffelStore + MySQL +* EiffelStore + ODBC (could be used for MySQL, sqlite, SQLserver, ...) +* Eiffel sqlite3 : that one is the default storage, since it is convenient for testing, but it is recommended to use EiffelStore+MySQL in production CMS site. +A typical implementation of setup_storage is: +```eiffel + setup_storage (a_setup: CMS_SETUP) + do + a_setup.storage_drivers.force (create {CMS_STORAGE_SQLITE3_BUILDER}.make, "sqlite3") +-- a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_ODBC_BUILDER}.make, "odbc") + end +``` +And the CMS decides which storage should be used. It depends on the application configuration. See the **configuration** section. + +Those data could be user information (login, email, password, ...), custom values, logs, emails, path aliases, ... and any data modules may need to store (for instance node content, for the `node` module.) + +#### Modules + +The `setup_module` is used to declare available **modules** (instances of `CMS_MODULE` effective types). +The module design provides a simple way to extend or alter the CMS functionalities/behaviors. +Most of the CMS functionalities are implemented by modules, and each module relies on the core of the CMS core. +This **core** contains the `CMS_API`, `CMS_USER_API`, and various internal mechanisms such as mailer, logger, ... + +Use `setup_module (a_setup: CMS_SETUP)` to customize the `CMS_SETUP` object created by `initial_cms_setup`. +For your convenience, ROC CMS provides a `CMS_DEFAULT_SETUP` that imports configuration from `site/config/cms.ini` + +So far, what you need to remember is `CMS_EXECUTION` class and descendants are used to setup the ROC CMS application, for storage, modules, and also how to load configuration. + +Note that a module can have 3 states: +- not installed, +- installed and enabled, +- installed and disabled. + +At first, to install the modules, open your browser at location `https://hostname:port/admin/install` and click the associated button. +(Note: for new module addition, you also need to install them, using the same link, in the future, there will be a proper module management interface, in the admin front-end.) + +To enable or disable a module, you will need to use the `cms.ini` configuration file, please see the **configuration** section. + +Existing modules: +- **admin**: basic administration pages, to manage modules, roles, permissions, users, caches, ... (note: it is still very basic, and need effort to improve it.) +- authentication modules based on **auth**: + - **basic_auth**: account signing using basic HTTP Authorization solution + - **oauth20**: sign using a thirdparty OAuth2.0 account (such as Google, Facebook, github, ...) + - **openid**: sign using an OpenID account. +- **node**: the base of node management, include **Page** content type. +- **blog**: extends the **node** module with a **blog** content type. +- **recent_changes**: compute recent changes of CMS (integration with **node** management, and any modules that implement the `CMS_RECENT_CHANGES_HOOK`). +- **feed_aggregator**: aggregate one or many feeds (rss, atom, ...), and provide associated pages or blocks. +- **google_search**: provides search facilities using the Google Custom Search API. + +### Configuration +When `CMS_DEFAULT_SETUP` is used, the CMS configuration is loaded from `site/config/cms.ini`. +That files contains a few sections: +- **site**: to set the `name`, `email` and name of the `theme'. (See "Themes" section pour information.) +- **layout**: the application layout (or environment) can precise the `root-dir`, `themes-dir`, `modules-dir`. If unset, the values are computed from Current working directory. +- **mailer**: the CMS can send email notification for various reason, such as new users, or reset password functionalities, ... In this section, you can use + - `smtp` settings to precise a SMTP server (+ port), + - or `sendmail` to use an external script using the sendmail usage, + - or just an `output` file such as @stderr, or a path to a file on disk. +- **modules**: used to enable or disable modules. + - `*=on` -> modules are enabled by default + - `*=off` -> modules are disabled by default + - Note the default value is `on` + - For each module, this can be overwritten with `module_name=on|off` +- **blocks**: settings for blocks (See Themes, Blocks sections for more information on block). A few parameters are available to customize blocks. The general form is `block-name.param=value` (note that "foo.bar" is a value block name.) + - `block-name.region`: assign the block `block-name` to a specific region. A block can be assigned to **only one region**. + - `block-name.title`: used to overwrite the block title (with , the title is hidden). + - `block-name.weight`: used to order blocks in the same region (blocks with lower weight goes first). + - `block-name.expiration`: used to provide a basic cache system based on expiration. The value is a number of seconds before the cache expires (-1: never expires, 0: never cache, n: cache expires after `n` seconds). + - `block-name.condition`, or `block-name.conditions[]`: include `block-name` only under specific condition(s). The condition can be + - `is_front`: which is True only for the front page, usually at url "/" + - `path:foo/bar`, `path:foo/*/bar`: True only for CMS location matching the patterns after "path:" + - ``: related block is disabled. + - *note: There can be multiple conditions processed as any of the conditions (i.e: "or").* + - `block-name.options[varname]: pass a table of options `varname => value` to the related block. This can be used to pass parameters for block builder (for instance, recent_changes modules accept parameters "size" to know how many changes should be included.) + - To be able to include a block content into multiple region, it is possible to use aliases functionality. For instance `&aliases[new_block]=block-name`, in this case, a `new_block` is declared, and it has same content as `block-name`, on this alias, the parameters `region, condition(s), title, weight` are supported, but not `options[]`. +- **admin**: various admin related settings such as + - `installation_access` which accepts 3 values: "all", "none" or "permission", to precise who has access to the modules installation page; either "all" for anyone, "none" to disable installation of new modules, or "permission" to use the CMS permissions solution to determine if current user can install new module. + +Then, the configuration `cms.ini` can also define other parameters, and sections, that may be used by specific modules. +Note it is also possible to include another ini file with instruction `@include=path-to-file.ini`. + +Check the `example/demo/site/cms.ini` for example. + +### User management +The CMS core includes the notion of user, via interface `CMS_USER`, which has an id, a name, a password, ... and profile. Without any module, the CMS does not include any mean to authenticate, but still the CMS has the support for user management, and permissions system for current user. To be able to sign into the CMS, the site should include the module `auth`, and one or many of: +- `basic_auth`: authentication using the HTTP Authorization header. +- `oauth20`: being able to sign with an OAuth2.0 account (such as Google, Facebook, ...) +- `OpenID`: being able to sign with an OpenID account. + +Whatever authentication solution is used, when a user is signed-in, there is an instance of `CMS_USER` representing the associated CMS user account. + +There is a predefined user `admin` who is the administrator of the CMS, and by definition, this **admin** has all the permissions. It is initialized by default with username `admin` and password `istrator#`. + +The access control is role-based permissions system. This means, a user can have one or many *roles*, and each *role* includes a list of *permissions*. +There are two built-in roles: +- **anonymous**: when no user is signed in (typically anonymous visitors). +- **authenticated**: when a user is signed in the CMS. +With those 2 built-ins roles, and any custom role the admin will create, it is possible to give specific permissions, to a group of users. +The CMS core defines a few permissions, and each modules can also defines their own permissions, for instance: "view any page", "create page", "edit page", "delete page", "clear cache", "install modules", ... (when the administrator is signed-in, go to url `/admin/role/1/edit` to see all the available permissions). + +### Modules +A module is the way to extend the CMS engine. +First via the inherited `CMS_MODULE` interface that enables a module to: +- have a custom `install` and `uninstall` procedure by redefining the related routines. +- add its **routes** via `setup_router`. (i.e associated url or template of url with a specific request handler). +- register itself to hooks via `register_hooks`. +- declare new permissions by redefining `permissions`. +- provide a specific module api by redefining `module_api`. +- add its **filters** by redefining `filters`. + +Using the `hooks` system, a module can be deeply integrated with the CMS engine, and even alter behaviors (for instance, add link, add css, javascript, ...). See related developpers documentation on hooks. + +It is simple to create your own modules (check the developper documentation). +The ROC CMS library provides a few modules for now, for instance: basic_auth, oauth20, openid, node, blog, feed_aggregator, recent_changes, google_search, ... and others (the list keeps growing...). + +**Reminder**: to include a module to your CMS site, you need to +- include the associated .ecf file in your CMS site .ecf file. +- and also declare them in your descendant of CMS_EXECUTION. +- copy the eventual resources, configuration, ... files in the corresponding `site`. +Note: a tool **roc** is under development to ease such operations, for now it only copy needed files from module to site location. In the future, it should also update .ecf files, associated CMS_EXECUTION effective class. + +### Themes +When talking about CMS, a major topic is how a request is rendered in a web browser. Here comes the notion of **theme** which is a collection of templates, accepting various values as input (including the content of blocks), and renders as an html5 page. It also includes various assets such as css, javascript, icons, images, ... +The ROC CMS theming is inspired by Drupal, with the notion of **region** and **block**. + +Note: for now, there is no simple "theme" module or similar, and the common way to start your CMS site is to copy an existing project such as the one available with the demo example (i.e: copying the source code, but also the `site` folder). + +Currently the default theme of the demo example `SMARTY_CMS_THEME` is based on Eiffel **smarty** template library (Check [smarty doc](https://svn.eiffel.com/eiffelstudio/trunk/Src/contrib/library/text/template/smarty/README.md) for syntax and functionalities). + +The layout of a CMS web page has predefined area called **regions**. The Eiffel CMS uses the same default regions as Drupal, so let's see them in the following image. + +``` + +----------------------------------------------------------+ + | Page_top | + +----------------------------------------------------------+ + | Header | + +---------------+-------------------------+----------------+ + | | Highlighted | | + | Sidebar_first +-------------------------+ Sidebar_second | + | | Help | | + | +-------------------------+ | + | | | | + | | Content | | + | | | | + +---------------+-------------------------+----------------+ + | Footer | + +----------------------------------------------------------+ + | Page_bottom | + +----------------------------------------------------------+ +``` + +The regions available for a theme, are defined in a configuration file `theme.info` located in the theme directory. For example: +``` +name=default_theme +engine=smarty +version=0.1 +regions[page_top] = Top +regions[header] = Header +regions[content] = Content +regions[highlighted] = Highlighted +regions[help] = Help +regions[footer] = Footer +regions[sidebar_first] = first sidebar +regions[sidebar_second] = second sidebar +regions[page_bottom] = Bottom +``` +Note: the value for each region is human readable region name. + +Note the regions may be disposed with other layout (two sidebar on the left, or right, ... and so on), responsive design or not, and so on. But on the CMS side, a *block* can be inserted into a *region*, and depending if the region is included in the theme, the related block content will be displayed or not. +To sort *block* inside a region, the CMS is using the `weight` property (that can be set via code, and/or overridden via configuration, i.e: `cms.ini`). +This is how a site can support many themes, using the region as content holders, and theme for the layout and style. + +Internally the block content are stored in values associated to each region. +The theme also has access to specific `values` such as +- `site_url`: the absolute url for the CMS website. +- `host`: the host name. +- `is_https`: True if connection is using https:// +- `user`: contains the username of the signed user, if any. +- `site_title`: site title. +- `page_title`: per page title. +- and also `page: CMS_HTML_PAGE` which represents the CMS page to render with theme. + - `page` provides values via expression, such as `$page.type`, `$page.is_front`, `$page.is_https`, `$page.title`, ... + - and also smart expression for region via `$page.region_xyz` for region `xyz` if any, ... (note the region are also availabe with expression like `$region_xyz` or `$page.region_xyz` ...) + +==Note for developpers: internally, the deferred class `CMS_RESPONSE` provides an abstraction to render request response using the **theme**, in fact, the theme is controlled by the CMS_RESPONSE implementation (to set value, build expected theme, and finally render as html).== + +### Blocks +As previously said, a region holds smaller piece of content called blocks. +Blocks hold chunks of content, like the user login form, navigation menu, information for the footer, or any thing provided by each module. +For instance the `feed_aggregator` module provide a block to display the latest elements of a aggregated feed. + +Currently there are different kind of `CMS_BLOCK`: +- `CMS_CONTENT_BLOCK`: it holds a simple text to render as it is in the page. +- `CMS_MENU_BLOCK`: it holds a `CMS_MENU` as a collection of `CMS_LINK` generally used to hold a menu, or set of links such as navigation or management menus. +- `CMS_SMARTY_TEMPLATE_BLOCK`: it holds a simple text to render as it is in the page. + +Internally, there are two other kinds of block: +- `CMS_ALIAS_BLOCK`: being the alias of another block, but with specific properties. +- `CMS_CACHE_BLOCK`: there is a simple cache solution for blocks, based on expiration. See the configuration section to know how to define the expiration for a block. + +For now, creating a block is only possible via block, an evolution of ROC CMS should allow the administrator to add new block without coding. + +### Persistence +The persistence or storage layer is used by the CMS to store custom values, path aliases, logs, emails, users information, but it is also used by module (unless a module wants to use its own persistence solution, disk, cloud, ...). + +Currently, there is only SQL based implementations of that `CMS_STORAGE`, but nothing prevents to implement it with other solutions (plain text file, NoSQL db, ...). +The current implementation are using either: +- EiffelStore + MySQL: recommended for production, however Eiffel MySQL requires to configure your environment by setting for instance MYSQL variable on Windows, and MYSQLINC on Linux. +- EiffelStore + ODBC: via ODBC, there is a large range of available database (MySQL, SQLite, SQLserver, ...), but it requires to setup your environment (for instance install sqliteODBC driver to use SQLite db). +- Eiffel sqlite3 wrapper: it is very convenient for development, but maybe not recommended for production website. It does not require any environment setup, so this is a simple solution to build tests for instance. + +In practice, how to use a storage or another? +The project needs to include the expected storage, the following instructions explains how to include sqlite3, EiffelStore+ODBC and EiffelStore+MYSQL storage. +1. First the associated .ecf file need to be included in your project file (.ecf) +For instance +```xml + + + +``` +2. Then in the descendant of `CMS_EXECUTION`, in the demo `DEMO_CMS_EXECUTION`, see the code of `setup_storage`: +```eiffel +setup_storage (a_setup: CMS_SETUP) + do + a_setup.storage_drivers.force (create {CMS_STORAGE_SQLITE3_BUILDER}.make, "sqlite3") + a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_MYSQL_BUILDER}.make, "mysql") + a_setup.storage_drivers.force (create {CMS_STORAGE_STORE_ODBC_BUILDER}.make, "odbc") + end +``` + +3. And finally, in the configuration file `site/config/demo.json` (in fact, the executable name + ".json"), precise the driver and environment of the datasource. For instance the following code define **sqlite3** as default CMS storage, and environment *sqlite3* that defines the path of SQLite database as "site/database.sqlite3". Note the way to declare sqlite with ODBC, mysql with ODBC, or mysql directly with EiffelStore. +```json +{ + "database": { + "datasource": { + "driver": "sqlite3", + "environment": "sqlite3", + }, + "environments": { + "sqlite3": { + "connection_string":"Database=./site/database.sqlite3;" + }, + "odbc-sqlite": { + "connection_string":"Driver=SQLite3 ODBC Driver;Database=./site/database.sqlite;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;" + }, + "odbc-mysql": { + "connection_string":"Driver=mysql ODBC Driver;Server=localhost;Port=3306;Database=roc;Uid=roc;Pwd=roc;" + }, + "mysql": { + "connection_string":"Driver=mysql;Server=localhost;Port=3306;Database=roc;Uid=roc;Pwd=roc;" + } + } + } +} +``` +To use EiffelStore+MySQL, just change the "driver" to be "mysql" and "environment" to "mysql". The connection string for server database defines the credentials with "Uid" and "Pwd". + +### How to run the CMS site? +As any Eiffel Web application (EWF), it can be executed as +- **standalone**: using Eiffel standalone httpd server included in the "standalone" connector, and then no setup is needed. +- **CGI** or **libFCGI** server: using, for instance, Apache2. Please refer to the Eiffel Web Framework documentation. + +### Conclusion +At this point, you know enough to build and administrate a ROC CMS site. +However, for real site, it is likely that you will need to build your own modules, you will learn how to do that in the Developper Documentation. + +*** +## Developper Documentation + +This diagram shows the main interfaces, they will be described in this documentation, but for now, it introduces those class names. + + +![Diagram](img_diagram.png) + + +### CMS APIs +An instance of CMS_API is available either via argument, or via attribute/function of various CMS components. +It provides routine specific to the ROC CMS engine (access to setup, modules, logs, custom values, ...). + +### CMS Hooks +Hooks is a mechanism which provide a way for modules to interact with each other and extending blocks of the current CMS. + +- [CMS_HOOK](../library/src/hooks/cms_hook.e): deferred class CMS_HOOK is a marker interface for CMS Hook +- [CMS_HOOK_AUTO_REGISTER](../library/src/hooks/cms_hook_auto_register.e): when inheriting from this deferred class, the declared hooks are automatically registered (note only the CMS core hooks are supported, as opposed to hook a module may propose). Otherwise, each descendant has to register itself to the associated hook manager. +- [CMS_HOOK_BLOCK](../library/src/hooks/cms_hook_block.e): it provides a way to declare and build blocks. +- [CMS_HOOK_FORM_ALTER](../library/src/hooks/cms_hook_form_alter.e): it provides a way to alter a web form `CMS_FORM`. +- [CMS_HOOK_MENU_ALTER](../library/src/hooks/cms_hook_menu_alter.e): it provides a way to alter a menu, and thus add or remove a link. This is how a module can add a link into a specific `CMS_MENU`. +- [CMS_HOOK_MENU_SYSTEM_ALTER](../library/src/hooks/cms_hook_menu_system_alter.e): similar to CMS_HOOK_MENU_ALTER, but on built-in menu such as management, navigation menus, and other. +- [CMS_HOOK_VALUE_TABLE_ALTER](../library/src/hooks/cms_hook_value_table_alter.e): it provides a way to alter the values table for a response (i.e: inserting custom values, or even override existing values). +- [CMS_HOOK_EXPORT](../library/src/hooks/cms_hook_export.e): it provides a simple export solution for each modules. Typically used to archive data associated with a module, for instance for backup purpose. In the future, a `CMS_HOOK_IMPORT` should also be available, and it would allow importing data exported by `CMS_HOOK_EXPORT`. +- and for more hooks ... please check descendants of `CMS_HOOK`. + +### Custom Module +How to build a new module? +A module is usually develop as an Eiffel library, and provide one or many implementation of `CMS_MODULE`. +It has to set or implement: +- **name**: a unique name identifying the module +- **description**: a human text to describe the purpose of the module, it will mainly be used by the administration front-end. +- **package**: put current module into a package, mainly for admin front-end. +- **version**: version information +- **dependencies**: defines dependencies on other modules. +- **permissions**: defines permissions used by the modules (mainly for admin front-end) +- **setup_router**: associate routes with request handler (declare various url or url template and associated request handler). +- **filters**: similar to routers setup, but for WSF Filters (See EWF documentation for more details). +- **register_hooks**: register current module to various hooks if needed. + +A module can also redefine `install` and `uninstall`. This could be used during installation to create new database tables, or anything needed by the module, or clean similar resources when being uninstalled. + +In addition, a module can also implement `module_api: detachabe CMS_MODULE_API` in order to be integrated easily with other modules (see for instance the CMS_NODE_API defined in **node** module). + +Please have a look at the [tutorial](tutorial.md) page. + +## References +For the interface references, please have a look at [ROC CMS source code](https://github.com/EiffelWebFramework/ROC). + +*** +*(last modified: Nov/17/2015 by Jocelyn.)* diff --git a/src/service/cms_execution.e b/src/service/cms_execution.e index d15d749..bf54521 100644 --- a/src/service/cms_execution.e +++ b/src/service/cms_execution.e @@ -74,6 +74,7 @@ feature {NONE} -- Initialization feature -- Factory initial_cms_setup: CMS_SETUP + -- Default setup object that Current interface can customize. deferred end diff --git a/src/service/cms_module.e b/src/service/cms_module.e index 5dfb22f..8c87807 100644 --- a/src/service/cms_module.e +++ b/src/service/cms_module.e @@ -27,7 +27,7 @@ feature -- Access -- Mostly to group modules by package/category. version: STRING - -- Version od the module? + -- Version of the module? dependencies: detachable LIST [TYPE [CMS_MODULE]] -- Optional dependencies.