From b119fb0743f257aa4d98c30979d2eb4d6d4bff6a Mon Sep 17 00:00:00 2001 From: MathiasReker Date: Thu, 27 Dec 2018 20:30:07 +0100 Subject: [PATCH 01/45] No alias functions --- ps_googleanalytics.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index f6639c0..e8c5ffd 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -314,7 +314,7 @@ public function hookdisplayHeader($params, $back_office = false) $ga_anonymize_enabled = Configuration::get('GA_ANONYMIZE_ENABLED'); - if ((int)Configuration::get('GA_CROSSDOMAIN_ENABLED') && $is_multistore_active && sizeof($shops) > 1) { + if ((int)Configuration::get('GA_CROSSDOMAIN_ENABLED') && $is_multistore_active && count($shops) > 1) { $ga_crossdomain_enabled = true; } From 14f26e2828028569750d29d14da2357bad574e18 Mon Sep 17 00:00:00 2001 From: MathiasReker Date: Thu, 27 Dec 2018 20:35:13 +0100 Subject: [PATCH 02/45] Lossless compression --- logo.png | Bin 2618 -> 2164 bytes views/img/add_to_cart_icon.png | Bin 2265 -> 777 bytes views/img/campaign_clicks_icon.png | Bin 1891 -> 571 bytes views/img/checkout_icon.png | Bin 1824 -> 607 bytes views/img/create_account_btn.png | Bin 3910 -> 1200 bytes views/img/ga_logo.png | Bin 8966 -> 5454 bytes views/img/merchandising_tools_icon.png | Bin 3115 -> 1397 bytes views/img/product_detail_icon.png | Bin 2130 -> 790 bytes views/img/purchase_icon.png | Bin 1868 -> 578 bytes views/img/stats.png | Bin 59457 -> 49139 bytes 10 files changed, 0 insertions(+), 0 deletions(-) diff --git a/logo.png b/logo.png index a089b082dab995baa018440fec1e8b6770c3f4fe..e1a0212f4ee2c69707aa8906f5c8aec37189bfe4 100755 GIT binary patch delta 2152 zcmV-u2$%P|6!Z{~BYy}xNkluJSEzj2vk8P~oT@{p^bjJ*%?fC~wl%UBVl&$!I7 z9m=83`w&p$a8%+Tlu1vDA$fWU+RDy6$`l`vM+FlHfA@E2 z%i2Mt)ETcJ`G4(S1dtivykSKiDNG;giq!2GKv#v3MrbpQbt$h?5oG2-JEE9Q?h*Qb zT5y{zD3hNAV2B};haeBT-t2xxNP*EzgX<^|20Qm}{WmCc-vbOmq!ffQu8BtWuNfgl zyXS!u0UtR=n`|gko(2qE44K#udC+CNF2#}JKyO?LIDe&S!v9O#*>(kxx*sdtNfmYl2irT# z$LdfBee+0LYjh}cMj0-KyQO{zq|kHqkB{%JdfqrhISQxqktXM@}bH7J}F0B6w{cLN^v6baN@^ zhC)gaV(;Dob-_drq|jQlx+n;=K;4g|M-6>9S8&^ z3AEwrAoZ-LH1Kx{V{On*#Zaa_L#i08GJ^XNzNe9ax0O>okPLO^)(S>?by4aTfVR3P z3?%}qjKgc8OnQ`2?k>847OqzVr*|W#7k^{v|GZtdN=QR4hQ5CZ>=k?e4)a0@S7}31 z1d$XEWSd;bL$2lr)n-cDFqlv^%^2N~AySjhd4r|-u(p##o zE9%q-bm7|oRe`&-9g`tXxDUGb=tEmM8(P$P3#5Sk&ZP@?0`>hn z5&x(O(U-1-H1KOEQ~E<5-z)VFKYaf&bbulRuP;EDp6lch?V<14f`6l_WG6b_t)&PP zl7a3nmV1n<2(oonK<;-QUEl&Ike=USZ{LdGwYdlp#C$jnUio(fugXI3I`fgI6nx?e z@QF(hedTIMW4or_RErnCY((yb3mHVm}Zc zoKJKspO{n0xrGi&-G7L^a|@K&BLGeC({DvW?Bo0SNy7t3u>-}C(4>F^xM3-xFJ8e9 zptMmMBp?jk=NPjPAMe6U7$Is@E3I$(^ zVxK)k=PDKe3U|>yu1Y}Pz7~;(PcuIsvHp=UGRce zkDf`uJpQ8qQV4oWNow?s@Ih~2GLHC{kJ1t;@B_%m;8lMQ*hVxn=q*mDxyE82-VJU0 zy0k!j-*zOszJG<_^|>B5nT&vpq%Q%%jm4pMEZu1%Po9h9fG?rU84Y#WbSMkPr#=Xd zzIGh}^X#vI6d|C`NQMevt(2#3D8U;F5Wcq&kw;ELxal|(PbmV!cw29r6X-vZp<-Bj zZn7@YzMUYBfj%I8z>4FIDi8sb2rhG^|5%EzIAQ;h9RG3tBgNxCP8?ZCK?}|` zCh`}Q4J{O?Q*un?4=LBSP~=OLADKuqKcn11Iox78$iUy4J_~7e^iPzFC{2`B|EIZz el0*5CW%~z*o?m0@dZerX0000HcuWN< zde3nP2=Hs5Nq+@4IG^LQH39SNz{w0@OX9!GP)=a7Jv3fa2GHNsUJUK|e?@^_*gtbCAqf%+U_?#~%d1wPEf5AGjK@RwMSDtFh0%9_(RZm+T>&H}1mQ z_UEC7tN|!ee+>!(Gp71OJ41T$YSfgADj#Q>NiDn&HGg3?&TF^D&PNS8poo7d@gGZq zA%vj6gbM@U^^%%*V|BntvrfMX`}8Zp%~gG_`VS|SaIs)gLUN_&L6V8kkK=9h;NVzDd>9NTowH z8itMY?|&unYod_GrmB+czvHPYy|`xy*G|NDKn6c7WDk*;QahMqd#lD-)h{W=0Y>ez z($K10F}5uNDuwP?mpD90V>5Q^O-R#p@bdkEl={!Af(Ia?!UZT?65NmWEBUI0>E4UC z^{IqYRH#W8kiP6$tkb>&D%BTAsiCHEfCady$A2yJs)g?qfXiV8P$|5Qb+xDG_F}Tf z!s%tkkhd z$3De*V=dm!4`{gjhgg$N1g1)QPi#eRT~iNIVm|f(P$V9*99WFsPy2sU^Y1`1 z&3^&3#(DD}IBz_MjBLgn`vh&|s7=MF2048ao?%)-|E?(3;$z?n58= z5Xqbn$#Wy&!m>eY+>Pt-c5Nc{&c!rdegiV9xi6B8B+;U(sBR3PL=%HZUlScO0&Zup zXI+Dgo?d*l^XfA=ZO=oxu}lf16jTK46z1?jdS7}HZ}WQWISZ3OnqsOXjbkuw9)H|! zkW{dbi+V>Y0VRAcQ4b?v&{2Z~&%v7hb*!mh4c~tFAlXOm^3yw!+WYE4GNCNc8n0s; z-rk*YkP>mp&Dis9LQTIENuA)oqiYN9#?^ScUI492Rj~q4`EVr;$e)ZDg%RW1r16k3 zGq7fU7gEEE@9+8TjhK8kSWr!=Dt{zso?Anb#zVEtBYnx^r5ja1nr4t%xC+(22Iuu# zLHAZ)D3K6Q1c^wzAVo1?M1h^gK4lqd+*!pDdS7}1chh>Xl<#r-*{UZ4+C@fACcX5L z3c!->&GgGiwchO4T}pNr=z%FvntS)nKQh#NqC;f!< za@eguhV;n#`?#6QkufvCcop^`!2~3#zRD^(t|Dl|XzV#Z3QJC&Oa94~n4Tl?AgMHdnsOB?5Z~Ds9xc_PM?R^kRN~)SI z%7`zNB)D~>%gD*4`#2M`+<(US6EB1!?Vu(uz?!){RMXv?>3#W0Os+dVOELqtNx`^1 zeH%%8{&!3yBosi5!5AInr8?;2*H4W-47>HVVoA~Dy6JuSkLb>Al@)>VLYE8#QrYOP zcIY{fI7_gmE+M`6AtW;noSx!@41v_5pxS^onBHTg=Kd5JF)4Hf?|;9J^Xi(gj%s2= zXliInQgv0sTl%(C_Asn3euMO~wb-++1FNy9mZl)p0Mr-{<7QD)m!c*w4wd21Uh->J zf$Lxdj4`E9188=I(-|?5^rGJ&<7U@)Ir%K2EJ&q;jHn5_^(R=TeiP@dmFSN)fR}~z zDAa^`SW}ijW<2!lM}M`<55tp_$9eHFyqzB)jl)Y-vs#|oM{2tSaMUzX=iiM?JU6_3 z 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 diff --git a/views/img/campaign_clicks_icon.png b/views/img/campaign_clicks_icon.png index ebd7745452b41674f5ccb631e797551d7ffca386..e1c877bacf0f4a1467d216d40d3719095b72a6f1 100755 GIT binary patch delta 557 zcmV+|0@D5C4!Z=98Gi%-008N@NR|Kq0s=`yK~#9!?brj%98nZN;kUJI+dj3UGEQwb zPHkIJZ8xeF*0yci_N#B&Ve#hY=0uyj*>A^)gQ62x(E)UqzQjQ}rRaEjXLF$~Z9yTC z9qrnMfLNJ3cO}}fsTg9ZP2j9g(VGpkiKvNlg(CC-lIELY!)y+o;M~;k0raECEMX)= zB9@&-3d^@D(vm^UXB%09Vke>eGS5CDi3v6fM{Je#U>Euk|9;%o^k7HIBt%hCMHD+* zCaL*^<`D;3%6~XIQ;U4W4~jjBk`ACPedH>enZp2@QIQPpY%v!gZ+C{topdIY vS$3i@PA0YC4RA9jZE8i2T3u@vtI$%BhmX>_K5(r&L2&zrr*qEC_kH)f zzk7f8b!Mw3f9b4{*bokfGfS1DEMV7k_La^IX1@y0#yEC~VY0PMA*E-^F&oLr(or}G zs4SR)EFdvm#o9CEA`T~Ti>XM1zYfo3p;PZxkiSeWqf{lc{#6K$fIlqK8&I$9}@5d0+7`J?G;uAbAeWS z!n6e?X(wzZnlVvUz;6-5DF-9xvP_?cV4>CO7ly6&sYJ0UJ<7if$WL)ncK4JZ_o zRR@yQ7-6#dg|GVy^*nARX)`%V-4YvR0VXpoGrfcbfsl}tg!LeSkpfVJA~=XkP!TBA z=@F?wFOo=b0e1?o`&9;?cPcAkBcZ1W$p{jM^?DQnQ4}UY7?wytsTk6MIF3v85QK`w zdXmd$Df0cz^Q&f>7O@@b-@X_P_V8l3NGm&0Hg?GyR%f%^-SYxV~HM{{7=hch!YJ=JYFb1E-Zb6sI2 z-ihP8Q>v3A5AQc$NjVs@@-6R#_SwGI$Y^2uu5-?&lAn9(ud4qh}={96X!JIlHw>7CxQ3#+S2=lkLwG|%0?)qBs+0ZAdVqCM7Jo$ z^QTC~)Xwh?0(Iv<8mcZ%tG{(s_ULwaRxIe4bHW%q zbm5K0KW8|eZr};cV}NsO{U7=R?>lp23_FbfB(6E%}cL6oOtx)*yXPJo>ga_9Wt-^y34eB zc}kgvms32lx)S$oI(TSVvFBe6dn$hVQ@d&)IOyuxM``Fp;F-+I?_2 ztpO`jb0dN-ch%imaD1*}f{xyIrRT`cH(%ctxAp8uvoX0pfcoR8@2;da>6I2JwmtA| z)#&!I@}##8_g1*$h9b5MZos?#x}vP9w7HqShh*mLzp%1Fe&f!q6_*o_r7itCqv#xw z*1p$%d)@rz#G2WIqu>HeLeYP3b;3AnjoIJqHzNkizRrizIwD!4!P$nV$5 zOTxdsy5L!dbP+L1NAg%ci_@% diff --git a/views/img/checkout_icon.png b/views/img/checkout_icon.png index a5f2468fe1a6c51f53801674db4ae9c3e93e9793..6ff8eac7af432c750bc9217e794f3dc8aa28f49b 100755 GIT binary patch delta 593 zcmV-X0`91HGA44S`a*HD3aVWwq9#To{p$zIv-%#9B zwm5H4Us}d~upXu9Ql7CTHLF-}KN*D5eNS0W*s{Dw=?=6X#eZajNqfw0Af`z0w0qW0 zyJh{;?paN*HR&+fH5S$$qO!!K!(f*XRdm9j9kQBUXV7&eh)~yH*ZE~hOHNC3F&brb z7j?)(PVCJ?9d@CNjwYA&Hm!&-4D|~-OnCcC;J1SMHT@6&3-HEY0&f&zQ-wx!;IPl5 z_e`WQUASnh%YSJ`bJiK#X*$xFiM&Tyd!6pIq7hYyE#E^_Hef&R`0XMAul2U<+ZKUM zdV5N|Z)<0iwf)2v@?$^Gqc4u>t(o4s>TSE;@}R6OX@_I-+IpT-UmVg~BfWLf+a|r` z#QrUdBlfV#DZEd7g-%Z4KEC0sPT_+79{(54;5YvNy?+d86*HV6(*fin7zu{5GRXN`3sO=-qTV>?b;8Znl4 zD5Gg4xJqyho`DVl}SG%00000NkvXXu0mjfB(WYC literal 1824 zcmaJ?Yfuwc6o#f!pv6}sDv~iqRG@5jH*69%8W>0rf(8{*N_?`rxgjg$!Df+Q9n?sx z7ONI@R1_7gMRW!uG9rpqq>8Au3Qnz}^;LWz7GEgJsNEn~e>m>U?!EWi^PTgZ*X;7C ziE)0u0lpp{9)6m5brQEu;9l6(NbdUrH zLvB_h;1Im;XrKsc35s{EKC`I2$ zwTRZH0wy|sJ_C~HC+evA85C|1MMVlHI24=#Ghhj!!<=ccD;!Euk6i_~cZ*?>ut$Z> zP>SA?O4Uvks;mqUO2tx$LL{hAj*C&LR4$i<2_*=M!3YYY;Sh=|Fie3U!rn*3d1DMl zMUr}AuP^ST6q#7orhws`oE&jZxY)|1!zhm9Fd~5^5{T1)?70?}a6lIO=spW-V5b<` z#?n@c&~1?*t=X(n#4&vvg4w3kzB6pG_a=%<8SEfzFe*l1v)LV2kG7pn0{_$aptfC? zYXfi+uv@bkimQilbRU?@-Nzld6*)GFDGbdOg~(J}scbW_uo|^e#NCJuv_TP$QYcqr z0;0%p9Kxt@85AKUNeICul+=L82$UrH?0g81Nu|+p86GW_$WauH#qbDR79Am%sMJ_2 zhTv!)R%5ZVgoOfqzG=>PFBbnOR-s}5!CD!e)tcGY1yfB{)@nCdZ9TEbPg&g8l#tN(zt&fEpo+01^npr6fekaSVzu7^M*sBPN%V z5>YSS@LmSrb}A>~BC+)mQc!?IjYb@Sa2y2?ipu3sgbXo2BuPdX5d@dXj6ei)6k&Js zyw^;9BCbQ-+jpbE9o`KWuy7N_aHBKGZ~Sd;A`y)`TIaajUZgbfqxlUv@{uWC$3B)sAlc+-EHF|A5<-lbopY==yP+22dp-?M#`A2-BbS@J1Fu-O$AS0 zH;M-aFxT2*0zdg~93Oyt{g*5#hpYLO{9A)>Gz&YD-xQ5%5?mR&j@S8Wh1wBNH7rap zw#BK*m@dk!eCh=C`P#J=@hNeCLB-{j?CN%)@&O7;x^I2&8 z(#HL&c`4OsTvsc&m$`_|$*;WXS*b4%i-i_ico|s7-nvPyi*W~s2Hsd1$$Jvh7~ty1 zIt8AcI|pb_RwWcY$U8PN{n&}yCGwkPBr~&QToV{G?qJ|WS7OWsManT!@6bDZd{+gX z%*i+2DsHk;0c)Xz1R?xC>ariM>uwGS{HY^4 zYQrZ{2Z}vsWX;tN_}8g)o`4q>RNU`OD{NsJkdE3b`VoyCk1uq4$ri7wkj#BkyKTz) z)%xP5+@&G%2V5GRcU}q{B>_oWG7>&-;CG%-R`RzGc@|=83UwW@?0nohrwHu&_6~Nx z-(hn5VB+$O$YqcIdHQMni+!yAhbQgDGfpZtYlovZ&UKOv3Nlo9`{&eOZ-lid)Mbgu{H$L=Avf{jQ zP5+ko&AbsulU;>V#c|g3Nvmr33f#^2%C&tX@z^ zkPAFA(4Qww^P`)q3vvbea#th$%e-qr$Hwkys5w3`&u{kd;BK$_Tn5dDwh-Eamcmm> s#PmE%YFl%|8^ML8p0*UGd8DV8N8p@RQ{C=5vHPE;iA_{j#H2a@1Enp&a{vGU diff --git a/views/img/create_account_btn.png b/views/img/create_account_btn.png index a2076578c657da34d5ae6ea3b72a8c42981dde09..5b6f3e7095dd4a2b7022e6618263695e3ac25930 100755 GIT binary patch delta 1191 zcmV;Y1X%mV9kM%-@ z^n`rii2wiq33O6UQ}golzJyLqOaRJ*Jiq_|1Pe(-K~#9!?0?WX0RS)n0x|j{0sl&C zg7G3DvhPyRqV;Tf2VL8GLEF^|+LlJpHWc)+$||d@f>l;o1*@#G3RYQV6`$QQU9b~` zq44j+9+c(%pR|`mxG9~@?%Qc+%!oM&pNPJzfOMB?O#n=){lC5+)dM1W$JwO*OLgK{ zF`?Z~)ADXRp?{3O3)KSn@?iTOvy5L%H7Yz7?QWV@#cNdUccL1e6%J7G2))+;7+$+A z8NeUC6NU~w#!YbK#(^y0vfQUOJ4q;6F4AV=0$(3BJ%HFH5MRR_2#!0-RxS(}YMeZB z;JT_QN?@rO=rkF3)DH__beFvxdqZde=4qU`t06J~rGL9r4f2z!H={~5z~vgjD%}WG zJvOJ_D^TZ^v{MZLQ_^PBrS{B6+phqAr)sVX3Z8ffGI>>#)DG*J$2c5@r=MDH4s=1%V?&Jtf*(G_J(P_qVxQ7xuJ-;2?BG}r z35G9iRDa>w^_-_!QbE^bd%nzrbeB5KjB(0yG0fNus<7p0OyjTWq#4h*{2tWV5LcQ9}6RJ$g@!g+Nj#;uFd9DPnf2I1i!Ds&B1fbiBoNO zH6SZv&yE>k_T}%d)%v7r<Hls~R(%RNE_5-F>RuiQA}lvzD)iFZ&Kqw+EU$PW?1aW>!(%%-Vc` z9e*$H0GW}!M^)EkWeIeb8&%%A&nxldZMnTb)gvz?rr+KHIFBdWQd?95f6CM}++yzs z^NDbJOnv&ne*WgDkn1<9+RV$_s>#csk$ziAn6Xo}h}#3EYO=xvsJhf?@sZRl&YofF zgDU?Hld3W%*x{6FZw6t$ts>}Ms{2PC%73C-?R+wbj|?J@h}76(CB^8uGlZ3;scN%x zmXvd5o$>0rjcSw(mgOW}Q?)>P20G1%F~b7_yTI5u`5r*>B?Dkjf4>SDl&1PUNKqjhE6;!1vRY6s% zQWczHlOPD<1ER%2c0ja@lkDOpMIcZVX&fC8C5p46I01%+>nwm7_hSG6002ovPDHLk FV1hHXQC_{2%nW9XE%Zc5L@IfN^hCC#>|{%_ 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 diff --git a/views/img/ga_logo.png b/views/img/ga_logo.png index 8f1e29a2bdba19252236350d256aee4513007802..8055dfb4617dcc2825b439b41baa64b81c790ac8 100755 GIT binary patch literal 5454 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 EKmM;wP5=M^ diff --git a/views/img/merchandising_tools_icon.png b/views/img/merchandising_tools_icon.png index 9433d3478c6df7278b365db5ec4f6995f5e9b652..acedbb3c401ae53a2e7a8cdd91ce0280e44c3ae0 100755 GIT binary patch delta 1389 zcmV-z1(N!!81)K}8Gi%-007ElE*Jm+1w2VaK~#9!#hC+eEK3-Ld-H7DHos@vwrxw- zwr$(CZQHh`V9%@S>gu{vGvVfL&KI;cbGG?+))*@;r#g#?6K6RMxz7}o_<{o{KxfW2 z$rqoX2-b32vW;BG9)#G)H(bVryuol}+0HG=wNe%#HgQQ{z<*K^Vm0R`)5=VQ_&He0 zv(8NaWLil>h$}SNE&7|ZkYX7)*BWnjEL}BB_QC!L+d`d~0(u5y4JwMGfM~HQt5mEOG zLX6=93e$w4EPo{x1=z`79G`#vvKJwGA3jLLc%J4wk8;2&J|ZQ5%zT3^HGRpsZu@@; z<&bGSX%ZJP+968=jxvUmn1w9wpj^_(O0A7f=czGHAq)C|6|Aj0+Pra}pzw zc71KXPAlYumhd8%a}Y(a@$g~bQxw5i9!Sj2 zK!n)Fzg*8-tU!@`=c2$rIL5BRRVb3KT$9K!DO(m|=K}4BbE!XAqe-Ps9>aE0Sd6ma zTiX&ubALDKx4OQ(opKxqwckZyPB6I_U**tB2}FP1=X`!cmbctt(lT>*$8Evc>fwEv3ZxhST%B7@+#@L3P)b)MxG(voA({Iw${&uGq_t@M^ zfq&P3$7mE{l5LxnLc7C0e|zfgHp7{Sjy8P;>$%7Lxy%@6@LM(_N6v0r^gsco<7Yjmu!cB~ zx=TrO9<-_c_&Ax9SdTna0)P2>gol^1w|{a|C`4Gz$M!6~#*TW$V0*40?$?=}HJgUOA!jUgz+OrGL679f7Gt=`3kpno=f zUEqDc?`GHfh-l^tlUwmDvrvfjh^?+4Mj~dBV$j7Uc(}=yeCW>g)qjDD|M4bqaW!30h$Xz~=f!!fLhf8TF7oE^vB@uS zKVwmd@owL}lQ{dhAk(ODIw>YU#TzU_A-Zur!x3d{o731H8ItC=IGP{XOmt{;vYWDR zvkV0oVDevnzu1omvY(=y=x-7A1Ch%uNA{zOW@I3Otm8|!>Gcm?Q;wv#g=T&z#2lXI zslYCDkx7Pll4&SJ8uFAg$r34i#7aaoTR7DuQykA<>_id#Y?3d|q#CKA{rDJDFyab2 vAUwQf3R2w7NPj5AOhJp6nafV@&aD1_tHwX1dg0vH00000NkvXXu0mjf)IqI! literal 3115 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 J>NZCt{|~OTEs_8L diff --git a/views/img/product_detail_icon.png b/views/img/product_detail_icon.png index 779c458b2c3c3ed22483c5a8e72bb6d27a223ce7..c197e265cf76913a5c5ac221542a46ecb2e3ccd1 100755 GIT binary patch delta 778 zcmV+l1NHpU5S9jz8Gi%-004^9^XC8n0^LbOK~#9!wbuiz-Eb6u@iW`D?R~Rt+YD#h zwrz&9ZQIdo+d5mCm*$_QPwl(;U87%($OvJVlY#8y0l!d`H=Jb-HHn6JBZN&P8gLeM z^_-!^4O~1or_c~RrG8-I9r%WZ!BiqebbJsRW~+!4k?|gAxPOd~xOjQ~pbj6{z<7Ew zjt#tX$ybkh3=tkN2oy4UrI6 z(b$6;ZWGCGygh2TXwPmpNgPYRxZn6*)KCHm(E!vi%wIe*pHSN=Borz5i`q8(i|0WN z{gF^zL2Z})#eb`!hDu1NHleorh$T7&4OtPQ1ij9OkWX;e1T_>yLbbt+yUxEO2nkWk zdxZCbyQZk2BoeB_sO=JBDef&^7B#dFH65MLsBIHsY2YoM0X3`-HC_!hj6f{Iyv2pb zsO=l6LWv)A-N~%*8efMRmLr69Z}^bNh-H=6cuf>jM}Gu&CHaaP1|fdqk+^{x{?OO; zfYHX0eo1`)@vPiO9Zph-$e7#29@L=|;y0d^$Ed4UY+)XY*u--br;Ui`xS96gBJsV& z{im;z46#J<7WbasY)^G0CV7po^$>nVXT&yv`(ER9P*+d*h`M;lU=kpPD_-JBgJ#+l z)Zr&RiGNER8Zd+X+~7GcxWzU`QW|s9i$_jx@q9cdD}qpjAC7cABn--PhYnui`FMr0 z1+moQdpOfsS&yO~;z8-4KU)fNCd}#hY~~N@A#S9tm~pLDnYBhbmi1WMdW=h6QWBjJ z%s|M9qTb@UvC>Aq3xZQ|W(N&U5IUfYvIca;Uw`~GiqioKIpVAS3qbrY>f$STj2RxV z_&e8hC3A)cF1|dJ^aoZ2E}n+h!KTac0S#naD9#(#bR52-fsPAlSk60?%alZHQK?|b ziICHJl%U6yR3M}EeG!D1RIsH)C}Lkw=cIz6pfSuc2xa~Oy1II7l{SJm00000NkvXX It^-0~f*j>_ZvX%Q literal 2130 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 diff --git a/views/img/stats.png b/views/img/stats.png index f8ea5df6dbf337b35ffc65384d156ace8dbddc27..114cec7ccf77f1613eefa4e4581d715b4dcda72f 100755 GIT binary patch literal 49139 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

@g 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 LN)lCK2EYFgv%{$Q literal 59457 zcmaI7RahKNv^E+d1cFO|;0zwzoeAzTxclHRxJwe80VcRZaQEQu!QCymyZhm@efGtE z{<`Vz>SeWRm2|(WLKNjCP(I*)c=hTPij<_N(yLdmfv;Y{=_A0u)MS*GoV;8MqpAY8$&a&64=nh-M$aZ|LWD7KnoQOCk;7SUSo(2qv2mPjBYk| zFWj$Q@dMrL42`Y8PNYU)GYeY*z;RDl_6Eh1F3mXFq4=+1AFEcagzXkB3&B4T!S4mX- zUtKRx0swO-Cp%syCRbNiMprgQh=Umu3l9$u6EiClD=Whb1%sozt&^b}gRLX^e-cE& zj>Zlac1{)$ThhNI8X7^Iodf_cmj2TO8#_6<{}F8K_^+d0T*l;PXvf6D$joG8^H;n6 zrgn5v0{{PJ{GZg0D(-e*CMB>V#M#04r9Vu`|6_jf-TzzBUxY8#@G3Z1ymX48wJ5~c z*#>OuBqb^UczMHUVqwC|#$wF!(#M7j#zt&B4D7~ioDAF?Mn(+GJgmkXCd{0MEJlX^ z$@#zdnK`(H#aLNbI9Rz@SXji^dANBvg}Av`MMT-f*qM1){^OOhb#yYcH3t8u+u}v{ zzr4)|9(% ztbl*{P5ze$|B2L>nwOT?{bvxbG1!R3)Rc#rfrp0$%)r9J#l^tQ$!x-4WMsr`%FN8e z$!Q7(Fuj<_^jGlwuVDJG>Lo(|y8WM_@$&FLg9~i?GNK$_h7M!xs`jf_8z3oBAr-gz zgVgsaDpPaMtrKQ7*BvgiIS1o>k{EA=up29&lvh!BjVCb3>#cAqe3>qdNn9G6hw!9*G{0Y2yYUkzB_)+>(3^eyqV(`LH*Uznq1uWZRWyB=K6+>!=QT_Pd->A%ZJ{opD7A0o}{C#LJ&G-B#&@*jytT%0Atl+up^Dk(xUgx=Tp2Li> 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 From 469e988b3fa2d2e5a4d8eff68b918b3d9c58b08e Mon Sep 17 00:00:00 2001 From: Daniel Hlavacek Date: Wed, 4 Mar 2020 13:10:59 +0100 Subject: [PATCH 03/45] Update Readme.md --- Readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Readme.md b/Readme.md index b98ca9e..efd8fc7 100644 --- a/Readme.md +++ b/Readme.md @@ -4,6 +4,10 @@ Gain clear insights into important metrics about your customers, using Google Analytics. +## Notes + +Google Tag Assistant will report "No HTTP Response detected" error in Prestashop back-office. **This is not a bug.** This happens, because the module does not send page views in BO, not to influence statistics. Only events, like refunds, are sent from BO. + ## Contributing PrestaShop modules are open-source extensions to the PrestaShop e-commerce solution. Everyone is welcome and even encouraged to contribute with their own improvements. From 022d581e46eab461483640c3976de6f27c2aae91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Gaud=C3=A9?= Date: Mon, 9 Mar 2020 12:53:24 +0100 Subject: [PATCH 04/45] Escape the $data var in _manageData() using pSQL() --- ps_googleanalytics.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 28470bf..8be5285 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -767,7 +767,7 @@ protected function _manageData($data, $action) return json_decode($dataretour,true); } if ($action == 'W') { - return Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'ganalytics_data` (id_cart, id_shop, data) VALUES(\''.(int)$this->context->cart->id.'\',\''.(int)$this->context->shop->id.'\',\''.json_encode($data).'\') ON DUPLICATE KEY UPDATE data =\''.json_encode($data).'\' ;'); + return Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'ganalytics_data` (id_cart, id_shop, data) VALUES(\''.(int)$this->context->cart->id.'\',\''.(int)$this->context->shop->id.'\',\''.pSQL(json_encode($data)).'\') ON DUPLICATE KEY UPDATE data =\''.pSQL(json_encode($data)).'\' ;'); } if ($action == 'A') { $dataretour = Db::getInstance()->getValue('SELECT data FROM `'._DB_PREFIX_.'ganalytics_data` WHERE id_cart = \''.(int)$this->context->cart->id.'\' AND id_shop = \''.(int)$this->context->shop->id.'\''); From ffd9ad66de67dff86e303621c251e7fb4a699dfd Mon Sep 17 00:00:00 2001 From: Daniel Hlavacek Date: Tue, 7 Apr 2020 10:47:00 +0200 Subject: [PATCH 05/45] Update Readme.md --- Readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index efd8fc7..634108c 100644 --- a/Readme.md +++ b/Readme.md @@ -6,6 +6,8 @@ Gain clear insights into important metrics about your customers, using Google An ## Notes +Enhanced Ecommerce must be enabled in Google Analytics settings for full functionality. Otherwise, some data (refunds etc.) will not be visible. Follow [instructions][4]. + Google Tag Assistant will report "No HTTP Response detected" error in Prestashop back-office. **This is not a bug.** This happens, because the module does not send page views in BO, not to influence statistics. Only events, like refunds, are sent from BO. ## Contributing @@ -40,5 +42,5 @@ That's it: you have contributed to this open-source project! Congratulations! [1]: http://doc.prestashop.com/display/PS16/Coding+Standards [2]: http://doc.prestashop.com/display/PS16/How+to+write+a+commit+message [3]: https://help.github.com/articles/using-pull-requests - +[4]: https://support.google.com/analytics/answer/6032539 From 5a57fa157ae32fc71cd16d66128486bb48ac4eaa Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 5 May 2020 15:42:29 +0200 Subject: [PATCH 06/45] New date header 2007 to 2020 --- controllers/admin/AdminGanalyticsAjax.php | 4 ++-- controllers/admin/index.php | 4 ++-- controllers/front/ajax.php | 4 ++-- controllers/front/index.php | 4 ++-- controllers/index.php | 4 ++-- index.php | 4 ++-- translations/index.php | 4 ++-- upgrade/index.php | 4 ++-- views/css/ganalytics-nobootstrap.css | 4 ++-- views/css/index.php | 4 ++-- views/img/index.php | 4 ++-- views/index.php | 4 ++-- views/js/GoogleAnalyticActionLib.js | 6 +++--- views/js/index.php | 4 ++-- views/templates/admin/index.php | 4 ++-- views/templates/hook/index.php | 4 ++-- views/templates/index.php | 4 ++-- 17 files changed, 35 insertions(+), 35 deletions(-) diff --git a/controllers/admin/AdminGanalyticsAjax.php b/controllers/admin/AdminGanalyticsAjax.php index f90b950..02cd066 100644 --- a/controllers/admin/AdminGanalyticsAjax.php +++ b/controllers/admin/AdminGanalyticsAjax.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/controllers/admin/index.php b/controllers/admin/index.php index 041b9eb..94088c7 100644 --- a/controllers/admin/index.php +++ b/controllers/admin/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/controllers/front/ajax.php b/controllers/front/ajax.php index 20490e2..b127d66 100755 --- a/controllers/front/ajax.php +++ b/controllers/front/ajax.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/controllers/front/index.php b/controllers/front/index.php index 041b9eb..94088c7 100755 --- a/controllers/front/index.php +++ b/controllers/front/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/controllers/index.php b/controllers/index.php index 041b9eb..94088c7 100755 --- a/controllers/index.php +++ b/controllers/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/index.php b/index.php index 041b9eb..94088c7 100755 --- a/index.php +++ b/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/translations/index.php b/translations/index.php index 041b9eb..94088c7 100755 --- a/translations/index.php +++ b/translations/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/upgrade/index.php b/upgrade/index.php index 041b9eb..94088c7 100644 --- a/upgrade/index.php +++ b/upgrade/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/views/css/ganalytics-nobootstrap.css b/views/css/ganalytics-nobootstrap.css index ab30063..1a73f00 100644 --- a/views/css/ganalytics-nobootstrap.css +++ b/views/css/ganalytics-nobootstrap.css @@ -1,5 +1,5 @@ /** -* 2007-2015 PrestaShop +* 2007-2020 PrestaShop * * NOTICE OF LICENSE * @@ -18,7 +18,7 @@ * needs please refer to http://www.prestashop.com for more information. * * @author PrestaShop SA -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/views/css/index.php b/views/css/index.php index 041b9eb..94088c7 100644 --- a/views/css/index.php +++ b/views/css/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/views/img/index.php b/views/img/index.php index 041b9eb..94088c7 100755 --- a/views/img/index.php +++ b/views/img/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/views/index.php b/views/index.php index 041b9eb..94088c7 100755 --- a/views/index.php +++ b/views/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/views/js/GoogleAnalyticActionLib.js b/views/js/GoogleAnalyticActionLib.js index 2467d50..d7026b1 100755 --- a/views/js/GoogleAnalyticActionLib.js +++ b/views/js/GoogleAnalyticActionLib.js @@ -1,5 +1,5 @@ /** - * 2007-2015 PrestaShop + * 2007-2020 PrestaShop * * NOTICE OF LICENSE * @@ -18,7 +18,7 @@ * needs please refer to http://www.prestashop.com for more information. * * @author PrestaShop SA - * @copyright 2007-2015 PrestaShop SA + * @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA **/ @@ -175,7 +175,7 @@ var GoogleAnalyticEnhancedECommerce = { }); ga('send', 'pageview'); }, - + addCheckoutOption: function(Step,Option) { ga('ec:setAction', 'checkout_option', { 'step': Step, diff --git a/views/js/index.php b/views/js/index.php index 041b9eb..94088c7 100755 --- a/views/js/index.php +++ b/views/js/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/views/templates/admin/index.php b/views/templates/admin/index.php index 041b9eb..94088c7 100755 --- a/views/templates/admin/index.php +++ b/views/templates/admin/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/views/templates/hook/index.php b/views/templates/hook/index.php index 041b9eb..94088c7 100755 --- a/views/templates/hook/index.php +++ b/views/templates/hook/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ diff --git a/views/templates/index.php b/views/templates/index.php index 041b9eb..94088c7 100755 --- a/views/templates/index.php +++ b/views/templates/index.php @@ -1,6 +1,6 @@ -* @copyright 2007-2015 PrestaShop SA +* @copyright 2007-2020 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ From e21849b60380445e0081344748b6204a007d4e63 Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 5 May 2020 16:32:40 +0200 Subject: [PATCH 07/45] Move translations files into translations folder --- fr.php | 49 ----------------------------------- de.php => translations/de.php | 0 es.php => translations/es.php | 0 translations/fr.php | 39 ++++++++++++---------------- it.php => translations/it.php | 0 5 files changed, 17 insertions(+), 71 deletions(-) delete mode 100644 fr.php rename de.php => translations/de.php (100%) rename es.php => translations/es.php (100%) rename it.php => translations/it.php (100%) diff --git a/fr.php b/fr.php deleted file mode 100644 index 18e3779..0000000 --- a/fr.php +++ /dev/null @@ -1,49 +0,0 @@ -ps_googleanalytics_7510f8b22dd3e10476096425f78d4239'] = 'Vous n\'avez pas encore renseigné votre ID Google Analytics'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_aba1a7971f85c725ba4aed21343eeb4b'] = 'Intègre le script de Google Analytics à votre boutique'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_fa214007826415a21a8456e3e09f999d'] = 'Etes-vous sûr de vouloir supprimer vos paramètres ?'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c888438d14855d7d96a2724ee9c306bd'] = 'Paramètres mis à jour'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_f4f70727dc34561dfde1a3c529b6205c'] = 'Paramètres'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d7c9ce3337a28d63ce6626d90fdaedaa'] = 'Votre identifiant'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_81eeab9506186e2dca8faefa78d54067'] = 'Exemple :'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_a3e7f361e6fc12caf872119338642143'] = 'Enregistrer l\'identifiant'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_6a26f548831e6a8c26bfbbd9f6ec61e0'] = 'Aide'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d78b0fcff6b55bafe615f3fc1572c282'] = 'La première étape pour analyser les transactions e-commerce consiste à activer l\'archivage sur le profil de votre site.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d02388589ecfd3b58e0a90e42127ca61'] = 'Pour activer l\'option e-commerce, suivez ces étapes :'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_11f4b4ba23dc72ee5e86ff5a90bdde60'] = 'Connectez-vous à votre compte'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_b452493b16ead8b4b25ab3fb7174b8f5'] = 'Cliquez sur Edit, à côté du profil que vous désirez activer'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_95df6d8c97e880d8f6895d824d72c353'] = 'Sur la page \"Paramètres des profils\", cliquez sur \"Modifier\" en regard de l\'option \"Informations sur le profil du site principal\"'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_5189c0be0481408c9301c377818e69dc'] = 'Modifiez le bouton radio \"E-commerce\" sur \"Oui\"'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_98d12a01ab2aeeb10b7100eecf1974a8'] = 'Afin de définir vos objectifs, saisissez les informations de l\'objectif :'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_a44698a775d2fec04549ba6a2ac00491'] = 'Retournez sur la page d\'accueil de votre compte'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_dbec126ee3be2cd285a32a8d413fd1fa'] = 'Trouvez le profil correspondant à l\'objectif et éditez-le'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c4af40d39b97cf98040540639841e27a'] = 'Sélectionnez un des 4 emplacements disponibles pour ce profil et cliquez sur Editer'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c3b99aa9b4638183dc1bf55f955ed9ae'] = 'Entrez l\'URL de la page d\'objectif. Une conversion est enregistrée chaque fois qu\'un visiteur accède à cette page'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_54849fd1a3b8bdde81bfd979bc43aaa9'] = 'Entrez le nom de l\'objectif tel qu\'il doit apparaître dans votre compte Google Analytics.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_62034d209119c06315f59a5de39c6717'] = 'Activez l\'objectif'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_792795e7f3cedd66897ca35349101ba8'] = 'Ensuite, définissez un entonnoir de conversion en suivant ces étapes :'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_467ed594f2aa9b3e802037a10bad61b1'] = 'Entrez l\'URL de la première page de votre entonnoir de conversion. Cette page doit être commune à tous les internautes que vous souhaitez amener jusqu\'à votre objectif'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_2edf3f56ba30ad663429ba0e812a2396'] = 'Attribuez un nom à cette étape.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c909bea215f88ff0de9888dc95eed1ca'] = 'Si cette étape est obligatoire dans l\'objectif, cochez la case à droite de cette étape.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_9d25cb4b66791343ca319b3cdb82dba6'] = 'Entrez toutes les étapes précédant l\'objectif jusqu\'à ce que vous ayez terminé de définir l\'entonnoir de conversion. Vous pouvez entrer de 1 à 10 étapes'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_1c681b64f4cd8f52aecbb55c98f5c768'] = 'Pour finir, configurez les Paramètres complémentaires en procédant comme suit :'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_bb1ca9cfa0f3f7dbd06a2364c9a0dc9c'] = 'Si les URL entrées ci-dessus sont sensibles à la casse, cochez la case.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_047cf7562621a3639b3e04a63dc6b41f'] = 'Sélectionnez l\'objectif approprié. ('; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d59048f21fd887ad520398ce677be586'] = 'En savoir plus'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_f0c5b61aabf16d5ce925722b65c9aad8'] = 'sur les types de correspondances et sur la façon de choisir le type de correspondance approprié à votre objectif)'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_3dcb5dddc6b7c11d4ea76a60dc1a0466'] = 'Entrez un objectif. Cette valeur est utilisée par les calculs ROI de Google Analytics.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_a39f026ca75c6279ac9487265c00e3f5'] = 'Cliquez sur Enregistrer les modifications pour créer cet objectif et cet entonnoir de conversion ou sur Annuler pour quitter sans enregistrer.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_cd4952b65560a1242c68ffc980a6e515'] = 'Démonstration : Le processus de commande'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_efbf206b1b60cc0400c6147723509bd6'] = 'Après avoir activé vos reports e-commerce et sélectionné les profils respectifs, entrez \'order-confirmation.php\' comme page cible.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_68db264effa9364901183d9d12fac500'] = 'Nommez cet objectif (par exemple \'Processus de commande\')'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_9b8bc3519c65da8fac122f91ac5b7673'] = 'Activez l\'objectif'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_86a358dd6df1ba0cb3565b88380ba7a6'] = 'Ajoutez \'product.php\' comme la première page du cheminement de vos visiteurs'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_b7f896191caad09b5fc34e79f1cefa76'] = 'Donnez lui un nom (par exemple \'Page produit\')'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_ef7060dd05e779dcfbf82f236df43823'] = 'N\'activez pas la case \'requis\' parce que les clients pourraient visiter directement par un bouton \'Ajouter au panier\', tel que dans le module HomeFeatured sur la page d\'accueil.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_3bab28a0105361c49ca261ca08a19bf7'] = 'Faites de même en entrant les urls suivantes comme étapes de l\'objectif'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_9bfb6e6af6e6793bfa9387e728187c87'] = 'requis'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_6a47891ae07f45802bc948d2618e36f5'] = 'Vérifiez l\'option \'sensibilité de la casse\''; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d2d0c38d112e1d775057388122ae7545'] = 'Sauvegardez cet objectif'; diff --git a/de.php b/translations/de.php similarity index 100% rename from de.php rename to translations/de.php diff --git a/es.php b/translations/es.php similarity index 100% rename from es.php rename to translations/es.php diff --git a/translations/fr.php b/translations/fr.php index 03b6fd6..18e3779 100644 --- a/translations/fr.php +++ b/translations/fr.php @@ -2,53 +2,48 @@ global $_MODULE; $_MODULE = array(); - $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_7510f8b22dd3e10476096425f78d4239'] = 'Vous n\'avez pas encore renseigné votre ID Google Analytics'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_aba1a7971f85c725ba4aed21343eeb4b'] = 'Intègre le script de Google Analytics à votre boutique'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_fa214007826415a21a8456e3e09f999d'] = 'Êtes-vous sûr de vouloir tout supprimer ?'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c888438d14855d7d96a2724ee9c306bd'] = 'Mise à jour réussie'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_fa214007826415a21a8456e3e09f999d'] = 'Etes-vous sûr de vouloir supprimer vos paramètres ?'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c888438d14855d7d96a2724ee9c306bd'] = 'Paramètres mis à jour'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_f4f70727dc34561dfde1a3c529b6205c'] = 'Paramètres'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d7c9ce3337a28d63ce6626d90fdaedaa'] = 'Votre identifiant'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_81eeab9506186e2dca8faefa78d54067'] = 'Exemple :'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_b2e71617778d8acc54945b2e7cd5b16e'] = 'Universal Analytics activé'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_a3e7f361e6fc12caf872119338642143'] = 'Enregistrer l\'identifiant'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_6a26f548831e6a8c26bfbbd9f6ec61e0'] = 'Aide'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d78b0fcff6b55bafe615f3fc1572c282'] = 'La première étape pour analyser les transactions e-commerce consiste à activer l\'archivage sur le profil de votre site.'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d02388589ecfd3b58e0a90e42127ca61'] = 'Pour activer l\'option e-commerce, suivez ces étapes :'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_11f4b4ba23dc72ee5e86ff5a90bdde60'] = 'Connectez-vous à votre compte'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_b452493b16ead8b4b25ab3fb7174b8f5'] = 'Cliquez sur Edit, à côté du profil que vous désirez activer'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_95df6d8c97e880d8f6895d824d72c353'] = 'Sur la page \\"Paramètres des profils\\", cliquez sur \\"Modifier\\" en regard de l\'option \\"Informations sur le profil du site principal\\"'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_5189c0be0481408c9301c377818e69dc'] = 'Modifiez le bouton radio \\"E-commerce\\" sur \\"Oui\\"'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_95df6d8c97e880d8f6895d824d72c353'] = 'Sur la page \"Paramètres des profils\", cliquez sur \"Modifier\" en regard de l\'option \"Informations sur le profil du site principal\"'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_5189c0be0481408c9301c377818e69dc'] = 'Modifiez le bouton radio \"E-commerce\" sur \"Oui\"'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_98d12a01ab2aeeb10b7100eecf1974a8'] = 'Afin de définir vos objectifs, saisissez les informations de l\'objectif :'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_a44698a775d2fec04549ba6a2ac00491'] = 'Retournez sur la page d\'accueil de votre compte'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_dbec126ee3be2cd285a32a8d413fd1fa'] = 'Trouvez le profil correspondant à l\'objectif et éditez-le'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c4af40d39b97cf98040540639841e27a'] = 'Sélectionnez un des 4 emplacements disponibles pour ce profil et cliquez sur Modifier'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c3b99aa9b4638183dc1bf55f955ed9ae'] = 'Saisissez l\'URL de la page d\'objectif. Une transformation est enregistrée chaque fois qu\'un visiteur accède à cette page.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_54849fd1a3b8bdde81bfd979bc43aaa9'] = 'Saisissez le nom de l\'objectif tel qu\'il doit apparaître dans votre compte Google Analytics.'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c4af40d39b97cf98040540639841e27a'] = 'Sélectionnez un des 4 emplacements disponibles pour ce profil et cliquez sur Editer'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c3b99aa9b4638183dc1bf55f955ed9ae'] = 'Entrez l\'URL de la page d\'objectif. Une conversion est enregistrée chaque fois qu\'un visiteur accède à cette page'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_54849fd1a3b8bdde81bfd979bc43aaa9'] = 'Entrez le nom de l\'objectif tel qu\'il doit apparaître dans votre compte Google Analytics.'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_62034d209119c06315f59a5de39c6717'] = 'Activez l\'objectif'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_792795e7f3cedd66897ca35349101ba8'] = 'Ensuite, définissez un tunnel de conversion en suivant ces étapes :'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_467ed594f2aa9b3e802037a10bad61b1'] = 'Saisissez l\'URL de la première page de votre tunnel de conversion. Cette page doit être commune à tous les internautes que vous souhaitez amener jusqu\'à votre objectif.'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_792795e7f3cedd66897ca35349101ba8'] = 'Ensuite, définissez un entonnoir de conversion en suivant ces étapes :'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_467ed594f2aa9b3e802037a10bad61b1'] = 'Entrez l\'URL de la première page de votre entonnoir de conversion. Cette page doit être commune à tous les internautes que vous souhaitez amener jusqu\'à votre objectif'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_2edf3f56ba30ad663429ba0e812a2396'] = 'Attribuez un nom à cette étape.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c909bea215f88ff0de9888dc95eed1ca'] = 'Si cette étape est obligatoire dans le processus de transformation, cochez la case à droite de cette étape.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_9d25cb4b66791343ca319b3cdb82dba6'] = 'Saisissez toutes les étapes précédant l\'objectif jusqu\'à ce que vous ayez terminé de définir le tunnel de conversion. Vous pouvez saisir de 1 à 10 étapes.'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_c909bea215f88ff0de9888dc95eed1ca'] = 'Si cette étape est obligatoire dans l\'objectif, cochez la case à droite de cette étape.'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_9d25cb4b66791343ca319b3cdb82dba6'] = 'Entrez toutes les étapes précédant l\'objectif jusqu\'à ce que vous ayez terminé de définir l\'entonnoir de conversion. Vous pouvez entrer de 1 à 10 étapes'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_1c681b64f4cd8f52aecbb55c98f5c768'] = 'Pour finir, configurez les Paramètres complémentaires en procédant comme suit :'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_bb1ca9cfa0f3f7dbd06a2364c9a0dc9c'] = 'Si les URL saisies ci-dessus sont sensibles à la casse, cochez la case.'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_bb1ca9cfa0f3f7dbd06a2364c9a0dc9c'] = 'Si les URL entrées ci-dessus sont sensibles à la casse, cochez la case.'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_047cf7562621a3639b3e04a63dc6b41f'] = 'Sélectionnez l\'objectif approprié. ('; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d59048f21fd887ad520398ce677be586'] = 'En savoir plus'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_f0c5b61aabf16d5ce925722b65c9aad8'] = 'sur les types de correspondances et sur la façon de choisir le type de correspondance approprié à votre objectif)'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_3dcb5dddc6b7c11d4ea76a60dc1a0466'] = 'Saisissez une valeur d\'objectif. Cette valeur est utilisée par les calculs ROI de Google Analytics.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_a39f026ca75c6279ac9487265c00e3f5'] = 'Cliquez sur "Enregistrer les modifications" pour créer cet objectif et ce tunnel de conversion, ou sur "Annuler" pour quitter sans enregistrer.'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_3dcb5dddc6b7c11d4ea76a60dc1a0466'] = 'Entrez un objectif. Cette valeur est utilisée par les calculs ROI de Google Analytics.'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_a39f026ca75c6279ac9487265c00e3f5'] = 'Cliquez sur Enregistrer les modifications pour créer cet objectif et cet entonnoir de conversion ou sur Annuler pour quitter sans enregistrer.'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_cd4952b65560a1242c68ffc980a6e515'] = 'Démonstration : Le processus de commande'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_efbf206b1b60cc0400c6147723509bd6'] = 'Après avoir activé vos rapports e-commerce et sélectionné le profil respectif, mettez \'order-confirmation.php\' comme page cible.'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_efbf206b1b60cc0400c6147723509bd6'] = 'Après avoir activé vos reports e-commerce et sélectionné les profils respectifs, entrez \'order-confirmation.php\' comme page cible.'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_68db264effa9364901183d9d12fac500'] = 'Nommez cet objectif (par exemple \'Processus de commande\')'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_9b8bc3519c65da8fac122f91ac5b7673'] = 'Activez l\'objectif'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_86a358dd6df1ba0cb3565b88380ba7a6'] = 'Ajoutez \'product.php\' comme la première page de votre tunnel de conversion'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_86a358dd6df1ba0cb3565b88380ba7a6'] = 'Ajoutez \'product.php\' comme la première page du cheminement de vos visiteurs'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_b7f896191caad09b5fc34e79f1cefa76'] = 'Donnez lui un nom (par exemple \'Page produit\')'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_ef7060dd05e779dcfbf82f236df43823'] = 'N\'activez pas la case \'requis\' parce que les clients pourraient visiter directement par un bouton \'Ajouter au panier\', tel que dans le module HomeFeatured sur la page d\'accueil.'; -$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_3bab28a0105361c49ca261ca08a19bf7'] = 'Continuez en saisissant les URL suivantes comme étapes de l\'objectif :'; +$_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_3bab28a0105361c49ca261ca08a19bf7'] = 'Faites de même en entrant les urls suivantes comme étapes de l\'objectif'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_9bfb6e6af6e6793bfa9387e728187c87'] = 'requis'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_6a47891ae07f45802bc948d2618e36f5'] = 'Vérifiez l\'option \'sensibilité de la casse\''; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d2d0c38d112e1d775057388122ae7545'] = 'Sauvegardez cet objectif'; - - -return $_MODULE; diff --git a/it.php b/translations/it.php similarity index 100% rename from it.php rename to translations/it.php From ab5729814eda43da75fa3073823e97b510ad5d8b Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 5 May 2020 16:33:10 +0200 Subject: [PATCH 08/45] Change ->trans to ->l to be 1.6 compliant --- ps_googleanalytics.php | 48 +++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 0ceb55f..4620683 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -46,17 +46,17 @@ public function __construct() $this->name = 'ps_googleanalytics'; $this->tab = 'analytics_stats'; $this->version = '3.2.0'; - $this->ps_versions_compliancy = array('min' => '1.7.0.0', 'max' => _PS_VERSION_); + $this->ps_versions_compliancy = array('min' => '1.6', 'max' => _PS_VERSION_); $this->author = 'PrestaShop'; $this->module_key = 'fd2aaefea84ac1bb512e6f1878d990b8'; $this->bootstrap = true; parent::__construct(); - $this->displayName = $this->trans('Google Analytics', array(), 'Modules.GAnalytics.Admin'); - $this->description = $this->trans('Gain clear insights into important metrics about your customers, using Google Analytics', array(), 'Modules.GAnalytics.Admin'); + $this->displayName = $this->l('Google Analytics', array(), 'Modules.GAnalytics.Admin'); + $this->description = $this->l('Gain clear insights into important metrics about your customers, using Google Analytics', array(), 'Modules.GAnalytics.Admin'); - $this->confirmUninstall = $this->trans('Are you sure you want to uninstall Google Analytics? You will lose all the data related to this module.', array(), 'Modules.GAnalytics.Admin'); + $this->confirmUninstall = $this->l('Are you sure you want to uninstall Google Analytics? You will lose all the data related to this module.', array(), 'Modules.GAnalytics.Admin'); } public function install() { @@ -193,13 +193,13 @@ public function displayForm() $helper->toolbar_btn = array( 'save' => array( - 'desc' => $this->trans('Save', array(), 'Admin.Actions'), + 'desc' => $this->l('Save', array(), 'Admin.Actions'), 'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name. '&token='.Tools::getAdminTokenLite('AdminModules'), ), 'back' => array( 'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'), - 'desc' => $this->trans('Back to list', array(), 'Admin.Actions') + 'desc' => $this->l('Back to list', array(), 'Admin.Actions') ) ); @@ -207,72 +207,72 @@ public function displayForm() // Init Fields form array $fields_form[0]['form'] = array( 'legend' => array( - 'title' => $this->trans('Settings', array(),'Admin.Global'), + 'title' => $this->l('Settings', array(),'Admin.Global'), ), 'input' => array( array( 'type' => 'text', - 'label' => $this->trans('Google Analytics Tracking ID', array(), 'Modules.GAnalytics.Admin'), + 'label' => $this->l('Google Analytics Tracking ID', array(), 'Modules.GAnalytics.Admin'), 'name' => 'GA_ACCOUNT_ID', 'size' => 20, 'required' => true, - 'hint' => $this->trans('This information is available in your Google Analytics account', array(), 'Modules.GAnalytics.Admin') + 'hint' => $this->l('This information is available in your Google Analytics account', array(), 'Modules.GAnalytics.Admin') ), array( 'type' => 'switch', - 'label' => $this->trans('Enable User ID tracking', array(), 'Modules.GAnalytics.Admin'), + 'label' => $this->l('Enable User ID tracking', array(), 'Modules.GAnalytics.Admin'), 'name' => 'GA_USERID_ENABLED', 'values' => array( array( 'id' => 'ga_userid_enabled', 'value' => 1, - 'label' => $this->trans('Enabled', array(), 'Admin.Global') + 'label' => $this->l('Enabled', array(), 'Admin.Global') ), array( 'id' => 'ga_userid_disabled', 'value' => 0, - 'label' => $this->trans('Disabled', array(), 'Admin.Global') + 'label' => $this->l('Disabled', array(), 'Admin.Global') )) ), array( 'type' => 'switch', - 'label' => $this->trans('Anonymize IP', array(), 'Modules.GAnalytics.Admin'), + 'label' => $this->l('Anonymize IP', array(), 'Modules.GAnalytics.Admin'), 'name' => 'GA_ANONYMIZE_ENABLED', - 'hint' => $this->trans('Use this option to anonymize the visitor’s IP to comply with data privacy laws in some countries'), + 'hint' => $this->l('Use this option to anonymize the visitor’s IP to comply with data privacy laws in some countries'), 'values' => array( array( 'id' => 'ga_anonymize_enabled', 'value' => 1, - 'label' => $this->trans('Enabled', array(), 'Admin.Global') + 'label' => $this->l('Enabled', array(), 'Admin.Global') ), array( 'id' => 'ga_anonymize_disabled', 'value' => 0, - 'label' => $this->trans('Disabled', array(), 'Admin.Global') + 'label' => $this->l('Disabled', array(), 'Admin.Global') ), ), ), ), 'submit' => array( - 'title' => $this->trans('Save', array(), 'Admin.Actions'), + 'title' => $this->l('Save', array(), 'Admin.Actions'), ) ); if ($is_multistore_active) { $fields_form[0]['form']['input'][] = array( 'type' => 'switch', - 'label' => $this->trans('Enable Cross-Domain tracking', array(), 'Modules.GAnalytics.Admin'), + 'label' => $this->l('Enable Cross-Domain tracking', array(), 'Modules.GAnalytics.Admin'), 'name' => 'GA_CROSSDOMAIN_ENABLED', 'values' => array( array( 'id' => 'ga_crossdomain_enabled', 'value' => 1, - 'label' => $this->trans('Enabled', array(), 'Admin.Global') + 'label' => $this->l('Enabled', array(), 'Admin.Global') ), array( 'id' => 'ga_crossdomain_disabled', 'value' => 0, - 'label' => $this->trans('Disabled', array(), 'Admin.Global') + 'label' => $this->l('Disabled', array(), 'Admin.Global') ) ) ); @@ -298,24 +298,24 @@ public function getContent() if (!empty($ga_account_id)) { Configuration::updateValue('GA_ACCOUNT_ID', $ga_account_id); Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true); - $output .= $this->displayConfirmation($this->trans('Account ID updated successfully', array(), 'Modules.GAnalytics.Admin')); + $output .= $this->displayConfirmation($this->l('Account ID updated successfully', array(), 'Modules.GAnalytics.Admin')); } $ga_userid_enabled = Tools::getValue('GA_USERID_ENABLED'); if (null !== $ga_userid_enabled) { Configuration::updateValue('GA_USERID_ENABLED', (bool)$ga_userid_enabled); - $output .= $this->displayConfirmation($this->trans('Settings for User ID updated successfully', array(), 'Modules.GAnalytics.Admin')); + $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully', array(), 'Modules.GAnalytics.Admin')); } $ga_crossdomain_enabled = Tools::getValue('GA_CROSSDOMAIN_ENABLED'); if (null !== $ga_crossdomain_enabled) { Configuration::updateValue('GA_CROSSDOMAIN_ENABLED', (bool)$ga_crossdomain_enabled); - $output .= $this->displayConfirmation($this->trans('Settings for User ID updated successfully', array(), 'Modules.GAnalytics.Admin')); + $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully', array(), 'Modules.GAnalytics.Admin')); } $ga_anonymize_enabled = Tools::getValue('GA_ANONYMIZE_ENABLED'); if (null !== $ga_anonymize_enabled) { Configuration::updateValue('GA_ANONYMIZE_ENABLED', (bool)$ga_anonymize_enabled); - $output .= $this->displayConfirmation($this->trans('Settings for Anonymize IP updated successfully', array(), 'Modules.GAnalytics.Admin')); + $output .= $this->displayConfirmation($this->l('Settings for Anonymize IP updated successfully', array(), 'Modules.GAnalytics.Admin')); } } From 22cd202dd21ccf9f97679565ac4b8aa3e2c1fbcc Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 5 May 2020 16:33:27 +0200 Subject: [PATCH 09/45] Remove unused logo.gif --- logo.gif | Bin 550 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 logo.gif diff --git a/logo.gif b/logo.gif deleted file mode 100644 index 3dc44f2c8ae3becb926ec3f786d7221d3c99c787..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 550 zcmdVX%Ps>^0D$3B_n|0is?!?Ns{8$ZsmrX0C5e`e#FLP8YJo^dRO>7)Z9Rd7aSIY9 zB&ce1Iz@V<(BVZRysIqbLKfb}C_`3P9?CXb<@ zU2J__>}%KC+U22kbwqA=v>Ta3c4_p0D2GHjqRIh@$s~G5q9GbPCQ67X_f$Qh>M2#v zNGwd$2dYNM(>YCf4$^0ZzQkzamL?x*GD_c+pMRe1GeegP=0GDz_4fp1d*%x`kL2|h zS{D{2%hFOw-O55~y|^Z^&7w~-Ef&|p7RhX5y~Y~YulMkSVo#ZeZG)Y8495>cM+Ix_ zfZe$aQ$7S641Mj@(5aW10a%$?)C Date: Tue, 5 May 2020 16:56:16 +0200 Subject: [PATCH 10/45] Update hookdisplayOrderConfirmation for 1.6 --- ps_googleanalytics.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 4620683..07ba1c5 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -40,6 +40,7 @@ class Ps_Googleanalytics extends Module protected $filterable = 1; protected static $products = array(); protected $_debug = 0; + protected $psVersionIs17; public function __construct() { @@ -55,8 +56,8 @@ public function __construct() $this->displayName = $this->l('Google Analytics', array(), 'Modules.GAnalytics.Admin'); $this->description = $this->l('Gain clear insights into important metrics about your customers, using Google Analytics', array(), 'Modules.GAnalytics.Admin'); - $this->confirmUninstall = $this->l('Are you sure you want to uninstall Google Analytics? You will lose all the data related to this module.', array(), 'Modules.GAnalytics.Admin'); + $this->psVersionIs17 = (bool) version_compare(_PS_VERSION_, '1.7', '>='); } public function install() { @@ -389,7 +390,12 @@ public function wrapOrder($id_order) */ public function hookdisplayOrderConfirmation($params) { - $order = $params['order']; + if (true === $this->psVersionIs17) { + $order = $params['order']; + } else { + $order = $params['objOrder']; + } + if (Validate::isLoadedObject($order) && $order->getCurrentState() != (int)Configuration::get('PS_OS_ERROR')) { $ga_order_sent = Db::getInstance()->getValue('SELECT id_order FROM `'._DB_PREFIX_.'ganalytics` WHERE id_order = '.(int)$order->id); if ($ga_order_sent === false) { From e1dbda6e63dfe34f04a97c844cb3ee164c0cd1b0 Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 5 May 2020 17:16:03 +0200 Subject: [PATCH 11/45] Fix trans() to l() issue --- ps_googleanalytics.php | 44 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 07ba1c5..ef8b262 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -54,9 +54,9 @@ public function __construct() parent::__construct(); - $this->displayName = $this->l('Google Analytics', array(), 'Modules.GAnalytics.Admin'); - $this->description = $this->l('Gain clear insights into important metrics about your customers, using Google Analytics', array(), 'Modules.GAnalytics.Admin'); - $this->confirmUninstall = $this->l('Are you sure you want to uninstall Google Analytics? You will lose all the data related to this module.', array(), 'Modules.GAnalytics.Admin'); + $this->displayName = $this->l('Google Analytics'); + $this->description = $this->l('Gain clear insights into important metrics about your customers, using Google Analytics'); + $this->confirmUninstall = $this->l('Are you sure you want to uninstall Google Analytics? You will lose all the data related to this module.'); $this->psVersionIs17 = (bool) version_compare(_PS_VERSION_, '1.7', '>='); } public function install() @@ -194,13 +194,13 @@ public function displayForm() $helper->toolbar_btn = array( 'save' => array( - 'desc' => $this->l('Save', array(), 'Admin.Actions'), + 'desc' => $this->l('Save'), 'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name. '&token='.Tools::getAdminTokenLite('AdminModules'), ), 'back' => array( 'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'), - 'desc' => $this->l('Back to list', array(), 'Admin.Actions') + 'desc' => $this->l('Back to list') ) ); @@ -208,72 +208,72 @@ public function displayForm() // Init Fields form array $fields_form[0]['form'] = array( 'legend' => array( - 'title' => $this->l('Settings', array(),'Admin.Global'), + 'title' => $this->l('Settings'), ), 'input' => array( array( 'type' => 'text', - 'label' => $this->l('Google Analytics Tracking ID', array(), 'Modules.GAnalytics.Admin'), + 'label' => $this->l('Google Analytics Tracking ID'), 'name' => 'GA_ACCOUNT_ID', 'size' => 20, 'required' => true, - 'hint' => $this->l('This information is available in your Google Analytics account', array(), 'Modules.GAnalytics.Admin') + 'hint' => $this->l('This information is available in your Google Analytics account') ), array( 'type' => 'switch', - 'label' => $this->l('Enable User ID tracking', array(), 'Modules.GAnalytics.Admin'), + 'label' => $this->l('Enable User ID tracking'), 'name' => 'GA_USERID_ENABLED', 'values' => array( array( 'id' => 'ga_userid_enabled', 'value' => 1, - 'label' => $this->l('Enabled', array(), 'Admin.Global') + 'label' => $this->l('Enabled') ), array( 'id' => 'ga_userid_disabled', 'value' => 0, - 'label' => $this->l('Disabled', array(), 'Admin.Global') + 'label' => $this->l('Disabled') )) ), array( 'type' => 'switch', - 'label' => $this->l('Anonymize IP', array(), 'Modules.GAnalytics.Admin'), + 'label' => $this->l('Anonymize IP'), 'name' => 'GA_ANONYMIZE_ENABLED', 'hint' => $this->l('Use this option to anonymize the visitor’s IP to comply with data privacy laws in some countries'), 'values' => array( array( 'id' => 'ga_anonymize_enabled', 'value' => 1, - 'label' => $this->l('Enabled', array(), 'Admin.Global') + 'label' => $this->l('Enabled') ), array( 'id' => 'ga_anonymize_disabled', 'value' => 0, - 'label' => $this->l('Disabled', array(), 'Admin.Global') + 'label' => $this->l('Disabled') ), ), ), ), 'submit' => array( - 'title' => $this->l('Save', array(), 'Admin.Actions'), + 'title' => $this->l('Save'), ) ); if ($is_multistore_active) { $fields_form[0]['form']['input'][] = array( 'type' => 'switch', - 'label' => $this->l('Enable Cross-Domain tracking', array(), 'Modules.GAnalytics.Admin'), + 'label' => $this->l('Enable Cross-Domain tracking'), 'name' => 'GA_CROSSDOMAIN_ENABLED', 'values' => array( array( 'id' => 'ga_crossdomain_enabled', 'value' => 1, - 'label' => $this->l('Enabled', array(), 'Admin.Global') + 'label' => $this->l('Enabled') ), array( 'id' => 'ga_crossdomain_disabled', 'value' => 0, - 'label' => $this->l('Disabled', array(), 'Admin.Global') + 'label' => $this->l('Disabled') ) ) ); @@ -299,24 +299,24 @@ public function getContent() if (!empty($ga_account_id)) { Configuration::updateValue('GA_ACCOUNT_ID', $ga_account_id); Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true); - $output .= $this->displayConfirmation($this->l('Account ID updated successfully', array(), 'Modules.GAnalytics.Admin')); + $output .= $this->displayConfirmation($this->l('Account ID updated successfully')); } $ga_userid_enabled = Tools::getValue('GA_USERID_ENABLED'); if (null !== $ga_userid_enabled) { Configuration::updateValue('GA_USERID_ENABLED', (bool)$ga_userid_enabled); - $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully', array(), 'Modules.GAnalytics.Admin')); + $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully')); } $ga_crossdomain_enabled = Tools::getValue('GA_CROSSDOMAIN_ENABLED'); if (null !== $ga_crossdomain_enabled) { Configuration::updateValue('GA_CROSSDOMAIN_ENABLED', (bool)$ga_crossdomain_enabled); - $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully', array(), 'Modules.GAnalytics.Admin')); + $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully')); } $ga_anonymize_enabled = Tools::getValue('GA_ANONYMIZE_ENABLED'); if (null !== $ga_anonymize_enabled) { Configuration::updateValue('GA_ANONYMIZE_ENABLED', (bool)$ga_anonymize_enabled); - $output .= $this->displayConfirmation($this->l('Settings for Anonymize IP updated successfully', array(), 'Modules.GAnalytics.Admin')); + $output .= $this->displayConfirmation($this->l('Settings for Anonymize IP updated successfully')); } } From 90c93c6c5098761bfaf91940115e9e86a76b73c2 Mon Sep 17 00:00:00 2001 From: apacios Date: Wed, 6 May 2020 15:33:46 +0200 Subject: [PATCH 12/45] Update readme --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 634108c..0c7c532 100644 --- a/Readme.md +++ b/Readme.md @@ -14,7 +14,7 @@ Google Tag Assistant will report "No HTTP Response detected" error in Prestashop PrestaShop modules are open-source extensions to the PrestaShop e-commerce solution. Everyone is welcome and even encouraged to contribute with their own improvements. -Google Analytics is compatible with all versions of PrestaShop 1.7 +Google Analytics is compatible with all versions of PrestaShop 1.7 and 1.6 ### Requirements From f76bde496a50ae5cc7210c4a4c591e9da6007dad Mon Sep 17 00:00:00 2001 From: apacios Date: Wed, 6 May 2020 18:40:41 +0200 Subject: [PATCH 13/45] Major refacto --- .gitignore | 1 + classes/Database/Install.php | 100 ++ classes/Database/Uninstall.php | 49 + classes/Database/index.php | 35 + classes/Handler/GanalyticsDataHandler.php | 132 ++ classes/Handler/index.php | 35 + classes/Repository/CarrierRepository.php | 46 + .../Repository/GanalyticsDataRepository.php | 84 + classes/Repository/GanalyticsRepository.php | 101 ++ classes/Repository/index.php | 35 + classes/index.php | 35 + composer.json | 21 +- composer.lock | 1506 +++++++++++++++++ ps_googleanalytics.php | 335 ++-- 14 files changed, 2329 insertions(+), 186 deletions(-) create mode 100644 .gitignore create mode 100644 classes/Database/Install.php create mode 100644 classes/Database/Uninstall.php create mode 100644 classes/Database/index.php create mode 100644 classes/Handler/GanalyticsDataHandler.php create mode 100644 classes/Handler/index.php create mode 100644 classes/Repository/CarrierRepository.php create mode 100644 classes/Repository/GanalyticsDataRepository.php create mode 100644 classes/Repository/GanalyticsRepository.php create mode 100644 classes/Repository/index.php create mode 100644 classes/index.php create mode 100644 composer.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a725465 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +vendor/ \ No newline at end of file diff --git a/classes/Database/Install.php b/classes/Database/Install.php new file mode 100644 index 0000000..839f1b0 --- /dev/null +++ b/classes/Database/Install.php @@ -0,0 +1,100 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Database; + +class Install +{ + /** + * @var \Ps_Googleanalytics + */ + private $module; + + public function __construct(\Ps_Googleanalytics $module) + { + if (\Shop::isFeatureActive()) { + \Shop::setContext(\Shop::CONTEXT_ALL); + } + + $this->module = $module; + } + + /** + * installTables + * + * @return bool + */ + public function installTables() + { + $sql = array(); + + $sql[] = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'ganalytics` ( + `id_google_analytics` int(11) NOT NULL AUTO_INCREMENT, + `id_order` int(11) NOT NULL, + `id_customer` int(10) NOT NULL, + `id_shop` int(11) NOT NULL, + `sent` tinyint(1) DEFAULT NULL, + `date_add` datetime DEFAULT NULL, + PRIMARY KEY (`id_google_analytics`), + KEY `id_order` (`id_order`), + KEY `sent` (`sent`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8 AUTO_INCREMENT=1'; + + $sql[] = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'ganalytics_data` ( + `id_cart` int(11) NOT NULL, + `id_shop` int(11) NOT NULL, + `data` TEXT DEFAULT NULL, + PRIMARY KEY (`id_cart`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8'; + + foreach ($sql as $query) { + if (!\Db::getInstance()->execute($query)) { + return false; + } + } + + return true; + } + + /** + * Register Module hooks + * + * @return bool + */ + public function registerHooks() + { + return $this->module->registerHook('displayHeader') && + $this->module->registerHook('displayAdminOrder') && + $this->module->registerHook('displayFooter') && + $this->module->registerHook('displayHome') && + $this->module->registerHook('displayFooterProduct') && + $this->module->registerHook('displayOrderConfirmation') && + $this->module->registerHook('actionProductCancel') && + $this->module->registerHook('actionCartSave') && + $this->module->registerHook('displayBackOfficeHeader') && + $this->module->registerHook('actionCarrierProcess'); + } +} diff --git a/classes/Database/Uninstall.php b/classes/Database/Uninstall.php new file mode 100644 index 0000000..2c3d3ce --- /dev/null +++ b/classes/Database/Uninstall.php @@ -0,0 +1,49 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Database; + +class Uninstall +{ + /** + * uninstallTables + * + * @return bool + */ + public function uninstallTables() + { + $sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'ganalytics`'; + $sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'ganalytics_data`'; + + foreach ($sql as $query) { + if (!\Db::getInstance()->execute($query)) { + return false; + } + } + + return true; + } +} diff --git a/classes/Database/index.php b/classes/Database/index.php new file mode 100644 index 0000000..94088c7 --- /dev/null +++ b/classes/Database/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2020 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/classes/Handler/GanalyticsDataHandler.php b/classes/Handler/GanalyticsDataHandler.php new file mode 100644 index 0000000..9936074 --- /dev/null +++ b/classes/Handler/GanalyticsDataHandler.php @@ -0,0 +1,132 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Handler; + +use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsDataRepository; + +class GanalyticsDataHandler +{ + private $ganalyticsDataRepository; + private $cartId; + private $shopId; + + /** + * __construct + * + * @param int $cartId + * @param int $shopId + */ + public function __construct($cartId, $shopId) + { + $this->ganalyticsDataRepository = new GanalyticsDataRepository; + $this->cartId = (int) $cartId; + $this->shopId = (int) $shopId; + } + + /** + * manageData + * + * @param string|array $data + * @param string $action + * + * @return mixed + */ + public function manageData($data, $action) + { + if ('R' === $action) { + return $this->readData(); + } + + if ('W' === $action) { + return $this->ganalyticsDataRepository->addNewRow( + (int) $this->cartId, + (int) $this->shopId, + json_encode($data) + ); + } + + if ('A' === $action) { + return $this->appendData($data); + } + + if ('D' === $action) { + return $this->ganalyticsDataRepository->deleteRow( + $this->cartId, + $this->shopId + ); + } + + return false; + } + + /** + * readData + * + * @return array + */ + private function readData() + { + $dataRetuned = $this->ganalyticsDataRepository->findDataByCartIdAndShopId( + $this->cartId, + $this->shopId + ); + + if (false === $dataRetuned) { + return array(); + } + + return json_decode($dataRetuned, true); + } + + /** + * appendData + * + * @param string $data + * + * @return bool + */ + private function appendData($data) + { + $dataRetuned = $this->ganalyticsDataRepository->findDataByCartIdAndShopId( + $this->cartId, + $this->shopId + ); + + if (false === $dataRetuned) { + $newData = array($data); + } else { + $newData = json_decode($dataRetuned, true); + $newData[] = $data; + } + + return $this->ganalyticsDataRepository->addNewRow( + (int) $this->cartId, + (int) $this->shopId, + json_encode($newData) + ); + } +} diff --git a/classes/Handler/index.php b/classes/Handler/index.php new file mode 100644 index 0000000..94088c7 --- /dev/null +++ b/classes/Handler/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2020 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/classes/Repository/CarrierRepository.php b/classes/Repository/CarrierRepository.php new file mode 100644 index 0000000..85f7541 --- /dev/null +++ b/classes/Repository/CarrierRepository.php @@ -0,0 +1,46 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Repository; + +class CarrierRepository +{ + /** + * findByCarrierId + * + * @param int $carrierId + * + * @return string + */ + public function findByCarrierId($carrierId) + { + return \Db::getInstance()->getValue( + 'SELECT name + FROM `'._DB_PREFIX_.'carrier` + WHERE id_carrier = ' . (int) $carrierId + ); + } +} diff --git a/classes/Repository/GanalyticsDataRepository.php b/classes/Repository/GanalyticsDataRepository.php new file mode 100644 index 0000000..9c67bf8 --- /dev/null +++ b/classes/Repository/GanalyticsDataRepository.php @@ -0,0 +1,84 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Repository; + +class GanalyticsDataRepository +{ + const TABLE_NAME = 'ganalytics_data'; + + /** + * findByCartId + * + * @param int $cartId + * @param int $shopId + * + * @return mixed + */ + public function findDataByCartIdAndShopId($cartId, $shopId) + { + return \Db::getInstance()->getValue( + 'SELECT data + FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '` + WHERE id_cart = ' . (int) $cartId . ' + AND id_shop = ' . (int) $shopId + ); + } + + /** + * addNewRow + * + * @param int $cartId + * @param int $shopId + * @param array $data + * + * @return bool + */ + public function addNewRow($cartId, $shopId, $data) + { + return \Db::getInstance()->Execute( + 'INSERT INTO `' . _DB_PREFIX_ . self::TABLE_NAME . '` (id_cart, id_shop, data) + VALUES(\'' . (int) $cartId . '\',\'' . (int) $shopId . '\',\'' . pSQL($data).'\') + ON DUPLICATE KEY UPDATE data = \'' . pSQL($data) . '\';' + ); + } + + /** + * deleteRow + * + * @param int $cartId + * @param int $shopId + * + * @return void + */ + public function deleteRow($cartId, $shopId) + { + return \Db::getInstance()->delete( + self::TABLE_NAME, + 'id_cart = ' . (int) $cartId . ' AND id_shop = ' . (int) $shopId + ); + } +} diff --git a/classes/Repository/GanalyticsRepository.php b/classes/Repository/GanalyticsRepository.php new file mode 100644 index 0000000..a598101 --- /dev/null +++ b/classes/Repository/GanalyticsRepository.php @@ -0,0 +1,101 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Repository; + +class GanalyticsRepository +{ + const TABLE_NAME = 'ganalytics'; + + /** + * findGaOrderByOrderId + * + * @param int $orderId + * + * @return mixed + */ + public function findGaOrderByOrderId($orderId) + { + return \Db::getInstance()->getValue( + 'SELECT id_order + FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '` + WHERE id_order = ' . (int) $orderId); + } + + /** + * findAllByShopIdAndDateAdd + * + * @param int $shopId + * + * @return array + */ + public function findAllByShopIdAndDateAdd($shopId) + { + return \Db::getInstance()->ExecuteS( + 'SELECT * + FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '` + WHERE sent = 0 + AND id_shop = ' . (int) $shopId . ' + AND DATE_ADD(date_add, INTERVAL 30 minute) < NOW()' + ); + } + + /** + * addNewRow + * + * @param array $data + * @param \Db $type + * + * @return bool + */ + public function addNewRow(array $data, $type = \Db::INSERT_IGNORE) + { + return \Db::getInstance()->insert( + self::TABLE_NAME, + $data, + false, + true, + $type + ); + } + + /** + * updateData + * + * @param array $data + * @param string $where + * + * @return bool + */ + public function updateData($data, $where) + { + return \Db::getInstance()->update( + self::TABLE_NAME, + $data, + $where + ); + } +} \ No newline at end of file diff --git a/classes/Repository/index.php b/classes/Repository/index.php new file mode 100644 index 0000000..94088c7 --- /dev/null +++ b/classes/Repository/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2020 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/classes/index.php b/classes/index.php new file mode 100644 index 0000000..94088c7 --- /dev/null +++ b/classes/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2020 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/composer.json b/composer.json index 1c5856a..6fb4e59 100644 --- a/composer.json +++ b/composer.json @@ -9,11 +9,26 @@ "email": "contact@prestashop.com" } ], + "config": { + "preferred-install": "dist", + "optimize-autoloader": true, + "prepend-autoloader": false, + "platform": { + "php": "5.6" + } + }, "require": { - "php": ">=5.4.0" + "php": ">=5.6" }, - "config": { - "preferred-install": "dist" + "require-dev": { + "prestashop/php-dev-tools": "3.*" + }, + "autoload": { + "classmap": [ + "ps_googleanalytics.php", + "controllers", + "classes" + ] }, "type": "prestashop-module" } diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..c399c0f --- /dev/null +++ b/composer.lock @@ -0,0 +1,1506 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "0a3bd8957594bdcbb3102ba56a6cd5f5", + "packages": [], + "packages-dev": [ + { + "name": "composer/semver", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de", + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "time": "2020-01-13T12:06:48+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0", + "psr/log": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + } + ], + "time": "2020-03-01T12:26:26+00:00" + }, + { + "name": "doctrine/annotations", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2017-02-24T16:22:25+00:00" + }, + { + "name": "doctrine/lexer", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "^4.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "time": "2019-06-08T11:03:04+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v2.16.3", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "83baf823a33a1cbd5416c8626935cf3f843c10b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/83baf823a33a1cbd5416c8626935cf3f843c10b0", + "reference": "83baf823a33a1cbd5416c8626935cf3f843c10b0", + "shasum": "" + }, + "require": { + "composer/semver": "^1.4", + "composer/xdebug-handler": "^1.2", + "doctrine/annotations": "^1.2", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^5.6 || ^7.0", + "php-cs-fixer/diff": "^1.3", + "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", + "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", + "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", + "symfony/finder": "^3.0 || ^4.0 || ^5.0", + "symfony/options-resolver": "^3.0 || ^4.0 || ^5.0", + "symfony/polyfill-php70": "^1.0", + "symfony/polyfill-php72": "^1.4", + "symfony/process": "^3.0 || ^4.0 || ^5.0", + "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", + "justinrainbow/json-schema": "^5.0", + "keradus/cli-executor": "^1.2", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.1", + "php-cs-fixer/accessible-object": "^1.0", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1", + "phpunitgoodpractices/traits": "^1.8", + "symfony/phpunit-bridge": "^4.3 || ^5.0", + "symfony/yaml": "^3.0 || ^4.0 || ^5.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", + "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "classmap": [ + "tests/Test/AbstractFixerTestCase.php", + "tests/Test/AbstractIntegrationCaseFactory.php", + "tests/Test/AbstractIntegrationTestCase.php", + "tests/Test/Assert/AssertTokensTrait.php", + "tests/Test/IntegrationCase.php", + "tests/Test/IntegrationCaseFactory.php", + "tests/Test/IntegrationCaseFactoryInterface.php", + "tests/Test/InternalIntegrationCaseFactory.php", + "tests/Test/IsIdenticalConstraint.php", + "tests/TestCase.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2020-04-15T18:51:10+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v3.1.5", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", + "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "~4.0|~5.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2018-02-28T20:30:58+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.18", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "time": "2019-01-03T20:59:08+00:00" + }, + { + "name": "php-cs-fixer/diff", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "symfony/process": "^3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "SpacePossum" + } + ], + "description": "sebastian/diff v2 backport support for PHP5.6", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "time": "2018-02-15T16:58:55+00:00" + }, + { + "name": "prestashop/header-stamp", + "version": "v1.2", + "source": { + "type": "git", + "url": "https://github.com/PrestaShopCorp/header-stamp.git", + "reference": "cc83b8f315dded710aebd73b3bac2aa4a2931fe1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PrestaShopCorp/header-stamp/zipball/cc83b8f315dded710aebd73b3bac2aa4a2931fe1", + "reference": "cc83b8f315dded710aebd73b3bac2aa4a2931fe1", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^3.1", + "php": ">=5.6", + "symfony/console": "^3.4", + "symfony/finder": "^3.4" + }, + "require-dev": { + "prestashop/php-coding-standards": "dev-master" + }, + "bin": [ + "bin/header-stamp" + ], + "type": "library", + "autoload": { + "psr-4": { + "PrestaShop\\HeaderStamp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AFL-3.0" + ], + "authors": [ + { + "name": "PrestaShop SA", + "email": "contact@prestashop.com" + } + ], + "description": "Rewrite your file headers to add the license or to make them up-to-date", + "homepage": "https://github.com/PrestaShop/pimp_my_header", + "time": "2019-11-12T08:59:19+00:00" + }, + { + "name": "prestashop/php-dev-tools", + "version": "v3.3", + "source": { + "type": "git", + "url": "https://github.com/PrestaShop/php-dev-tools.git", + "reference": "837eeb0518fddb7978445170673a44af5b2a2fb2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PrestaShop/php-dev-tools/zipball/837eeb0518fddb7978445170673a44af5b2a2fb2", + "reference": "837eeb0518fddb7978445170673a44af5b2a2fb2", + "shasum": "" + }, + "require": { + "friendsofphp/php-cs-fixer": "^2.14", + "php": ">=5.6.0", + "prestashop/header-stamp": "^1.0", + "squizlabs/php_codesniffer": "^3.4", + "symfony/console": "~3.2 || ~4.0", + "symfony/filesystem": "~3.2 || ~4.0" + }, + "bin": [ + "bin/prestashop-coding-standards" + ], + "type": "library", + "autoload": { + "psr-4": { + "PrestaShop\\CodingStandards\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PrestaShop coding standards", + "time": "2020-05-04T08:19:03+00:00" + }, + { + "name": "psr/log", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2020-03-23T09:12:05+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.5.5", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/73e2e7f57d958e7228fce50dc0c61f58f017f9f6", + "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards" + ], + "time": "2020-04-17T01:09:41+00:00" + }, + { + "name": "symfony/console", + "version": "v3.4.40", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "bf60d5e606cd595391c5f82bf6b570d9573fa120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/bf60d5e606cd595391c5f82bf6b570d9573fa120", + "reference": "bf60d5e606cd595391c5f82bf6b570d9573fa120", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T17:07:22+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.4.40", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "ce9f3b5e8e1c50f849fded59b3a1b6bc3562ec29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/ce9f3b5e8e1c50f849fded59b3a1b6bc3562ec29", + "reference": "ce9f3b5e8e1c50f849fded59b3a1b6bc3562ec29", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-23T10:22:40+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.4.40", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "9d4e22943b73acc1ba50595b7de1a01fe9dbad48" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9d4e22943b73acc1ba50595b7de1a01fe9dbad48", + "reference": "9d4e22943b73acc1ba50595b7de1a01fe9dbad48", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-15T09:38:08+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v3.4.40", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "78a93e5606a19d0fb490afc3c4a9b7ecd86e1515" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/78a93e5606a19d0fb490afc3c4a9b7ecd86e1515", + "reference": "78a93e5606a19d0fb490afc3c4a9b7ecd86e1515", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-12T16:54:01+00:00" + }, + { + "name": "symfony/finder", + "version": "v3.4.40", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "5ec813ccafa8164ef21757e8c725d3a57da59200" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/5ec813ccafa8164ef21757e8c725d3a57da59200", + "reference": "5ec813ccafa8164ef21757e8c725d3a57da59200", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2020-02-14T07:34:21+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v3.4.40", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "79701529391f802604ec92080364d617f029974b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/79701529391f802604ec92080364d617f029974b", + "reference": "79701529391f802604ec92080364d617f029974b", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-06T08:30:32+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-02-27T09:26:54+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-09T19:04:49+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "2a18e37a489803559284416df58c71ccebe50bf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/2a18e37a489803559284416df58c71ccebe50bf0", + "reference": "2a18e37a489803559284416df58c71ccebe50bf0", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0|~9.99", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2020-02-27T09:26:54+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "37b0976c78b94856543260ce09b460a7bc852747" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", + "reference": "37b0976c78b94856543260ce09b460a7bc852747", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-02-27T09:26:54+00:00" + }, + { + "name": "symfony/process", + "version": "v3.4.40", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "f5104c9dcbc2cfad45d01d5150c1da9836967271" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/f5104c9dcbc2cfad45d01d5150c1da9836967271", + "reference": "f5104c9dcbc2cfad45d01d5150c1da9836967271", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-12T14:33:46+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v3.4.40", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "a7a98f40dcc382a332c3729a6d04b298ffbb8f1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/a7a98f40dcc382a332c3729a6d04b298ffbb8f1f", + "reference": "a7a98f40dcc382a332c3729a6d04b298ffbb8f1f", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-15T09:38:08+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.6" + }, + "platform-dev": [], + "platform-overrides": { + "php": "5.6" + }, + "plugin-api-version": "1.1.0" +} diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index ef8b262..9c1e032 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -28,6 +28,8 @@ exit; } +require_once __DIR__ . '/vendor/autoload.php'; + class Ps_Googleanalytics extends Module { /** @@ -59,112 +61,6 @@ public function __construct() $this->confirmUninstall = $this->l('Are you sure you want to uninstall Google Analytics? You will lose all the data related to this module.'); $this->psVersionIs17 = (bool) version_compare(_PS_VERSION_, '1.7', '>='); } - public function install() - { - if (Shop::isFeatureActive()) { - Shop::setContext(Shop::CONTEXT_ALL); - } - - $this->uninstallPrestaShop16Module(); - - if (parent::install() && - $this->registerHook('displayHeader') && - $this->registerHook('displayAdminOrder') && - $this->registerHook('displayFooter') && - $this->registerHook('displayHome') && - $this->registerHook('displayFooterProduct') && - $this->registerHook('displayOrderConfirmation') && - $this->registerHook('actionProductCancel') && - $this->registerHook('actionCartSave') && - $this->registerHook('displayBackOfficeHeader') && - $this->registerHook('actionCarrierProcess') - ) { - return $this->createTables(); - } - - return false; - } - - public function uninstall() - { - if (parent::uninstall()) { - return $this->deleteTables(); - } - - return false; - } - - /** - * Migrate data from 1.6 equivalent module (if applicable), then uninstall - */ - public function uninstallPrestaShop16Module() - { - if (!Module::isInstalled(self::PS_16_EQUIVALENT_MODULE)) { - return false; - } - $oldModule = Module::getInstanceByName(self::PS_16_EQUIVALENT_MODULE); - if ($oldModule) { - if (method_exists($oldModule, 'uninstallTab')) { - $oldModule->uninstallTab(); - } - // This closure calls the parent class to prevent data to be erased - // It allows the new module to be configured without migration - $parentUninstallClosure = function() { - return parent::uninstall(); - }; - $parentUninstallClosure = $parentUninstallClosure->bindTo($oldModule, get_class($oldModule)); - $parentUninstallClosure(); - } - return true; - } - - /** - * Creates tables - */ - protected function createTables() - { - if ((bool)Db::getInstance()->execute(' - CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'ganalytics` ( - `id_google_analytics` int(11) NOT NULL AUTO_INCREMENT, - `id_order` int(11) NOT NULL, - `id_customer` int(10) NOT NULL, - `id_shop` int(11) NOT NULL, - `sent` tinyint(1) DEFAULT NULL, - `date_add` datetime DEFAULT NULL, - PRIMARY KEY (`id_google_analytics`), - KEY `id_order` (`id_order`), - KEY `sent` (`sent`) - ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 - ') && (bool)Db::getInstance()->execute(' - CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'ganalytics_data` ( - `id_cart` int(11) NOT NULL, - `id_shop` int(11) NOT NULL, - `data` TEXT DEFAULT NULL, - PRIMARY KEY (`id_cart`) - ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8 - ')) - { - return true; - } - - return false; - } - - /** - * deletes tables - */ - protected function deleteTables() - { - if ((bool)Db::getInstance()->execute(' - DROP TABLE IF EXISTS `'._DB_PREFIX_.'ganalytics` - ') && (bool)Db::getInstance()->execute(' - DROP TABLE IF EXISTS `'._DB_PREFIX_.'ganalytics_data` - ')) { - return true; - } - - return false; - } public function displayForm() { @@ -397,18 +293,28 @@ public function hookdisplayOrderConfirmation($params) } if (Validate::isLoadedObject($order) && $order->getCurrentState() != (int)Configuration::get('PS_OS_ERROR')) { - $ga_order_sent = Db::getInstance()->getValue('SELECT id_order FROM `'._DB_PREFIX_.'ganalytics` WHERE id_order = '.(int)$order->id); - if ($ga_order_sent === false) { - Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'ganalytics` (id_order, id_shop, sent, date_add) VALUES ('.(int)$order->id.', '.(int)$this->context->shop->id.', 0, NOW())'); + $ganalyticsRepository = new PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository(); + $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) $order->id); + + if (false === $gaOrderSent) { + $ganalyticsRepository->addNewRow( + array( + 'id_order' => (int) $order->id, + 'id_shop' => (int) $this->context->shop->id, + 'sent' => 0, + 'date_add' => 'NOW()', + ) + ); + if ($order->id_customer == $this->context->cookie->id_customer) { - $order_products = array(); + $orderProducts = array(); $cart = new Cart($order->id_cart); + foreach ($cart->getProducts() as $order_product) { - $order_products[] = $this->wrapProduct($order_product, array(), 0, true); + $orderProducts[] = $this->wrapProduct($order_product, array(), 0, true); } - $ga_scripts = 'MBG.addCheckoutOption(3,\''.$order->payment.'\');'; - + $gaScripts = 'MBG.addCheckoutOption(3,\''.$order->payment.'\');'; $transaction = array( 'id' => $order->id, 'affiliation' => (Shop::isFeatureActive()) ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), @@ -417,10 +323,10 @@ public function hookdisplayOrderConfirmation($params) 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, 'url' => $this->context->link->getModuleLink('ps_googleanalytics', 'ajax', array(), true), 'customer' => $order->id_customer); - $ga_scripts .= $this->addTransaction($order_products, $transaction); + $gaScripts .= $this->addTransaction($orderProducts, $transaction); $this->js_state = 1; - return $this->_runJs($ga_scripts); + return $this->_runJs($gaScripts); } } } @@ -431,10 +337,16 @@ public function hookdisplayOrderConfirmation($params) */ public function hookdisplayFooter() { - $ga_scripts = ''; + $ganalyticsDataHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler( + $this->context->cart->id, + $this->context->shop->id + ); + + $gaScripts = ''; $this->js_state = 0; - $gacarts = $this->_manageData("", "R"); + $gacarts = $ganalyticsDataHandler->manageData('', 'R'); $controller_name = Tools::getValue('controller'); + if (count($gacarts)>0 && $controller_name!='product') { $this->filterable = 0; @@ -442,16 +354,17 @@ public function hookdisplayFooter() if (isset($gacart['quantity'])) { if ($gacart['quantity'] > 0) { - $ga_scripts .= 'MBG.addToCart('.json_encode($gacart).');'; + $gaScripts .= 'MBG.addToCart('.json_encode($gacart).');'; } elseif ($gacart['quantity'] < 0) { $gacart['quantity'] = abs($gacart['quantity']); - $ga_scripts .= 'MBG.removeFromCart('.json_encode($gacart).');'; + $gaScripts .= 'MBG.removeFromCart('.json_encode($gacart).');'; } } else { - $ga_scripts .= $gacart; + $gaScripts .= $gacart; } } - $gacarts = $this->_manageData("", "D"); + + $ganalyticsDataHandler->manageData('', 'D'); } $listing = $this->context->smarty->getTemplateVars('listing'); @@ -464,8 +377,8 @@ public function hookdisplayFooter() if (empty($step)) { $step = 0; } - $ga_scripts .= $this->addProductFromCheckout($products, $step); - $ga_scripts .= 'MBG.addCheckout(\''.(int)$step.'\');'; + $gaScripts .= $this->addProductFromCheckout($products, $step); + $gaScripts .= 'MBG.addCheckout(\''.(int)$step.'\');'; } $confirmation_hook_id = (int)Hook::getIdByName('displayOrderConfirmation'); @@ -475,21 +388,21 @@ public function hookdisplayFooter() if (isset($products) && count($products) && $controller_name != 'index') { if ($this->eligible == 0) { - $ga_scripts .= $this->addProductImpression($products); + $gaScripts .= $this->addProductImpression($products); } - $ga_scripts .= $this->addProductClick($products); + $gaScripts .= $this->addProductClick($products); } - return $this->_runJs($ga_scripts); + return $this->_runJs($gaScripts); } - protected function filter($ga_scripts) + protected function filter($gaScripts) { if ($this->filterable = 1) { - return implode(';', array_unique(explode(';', $ga_scripts))); + return implode(';', array_unique(explode(';', $gaScripts))); } - return $ga_scripts; + return $gaScripts; } /** @@ -497,7 +410,7 @@ protected function filter($ga_scripts) */ public function hookdisplayHome() { - $ga_scripts = ''; + $gaScripts = ''; // Home featured products if ($this->isModuleEnabled('ps_featuredproducts')) { @@ -512,11 +425,11 @@ public function hookdisplayHome() array(), true ); - $ga_scripts .= $this->addProductImpression($home_featured_products).$this->addProductClick($home_featured_products); + $gaScripts .= $this->addProductImpression($home_featured_products).$this->addProductClick($home_featured_products); } $this->js_state = 1; - return $this->_runJs($this->filter($ga_scripts)); + return $this->_runJs($this->filter($gaScripts)); } /** @@ -758,38 +671,6 @@ protected function _runJs($js_code, $backoffice = 0) } } - /** - * Manage data - * @param string $action "R" read data from DB, "W" write data, "A" append data, D" delete data - * @return array dans le cas du R, sinon true - */ - protected function _manageData($data, $action) - { - if ($action == 'R') { - $dataretour = Db::getInstance()->getValue('SELECT data FROM `'._DB_PREFIX_.'ganalytics_data` WHERE id_cart = \''.(int)$this->context->cart->id.'\' AND id_shop = \''.(int)$this->context->shop->id.'\''); - if ($dataretour === false) - return array(); - else - return json_decode($dataretour,true); - } - if ($action == 'W') { - return Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'ganalytics_data` (id_cart, id_shop, data) VALUES(\''.(int)$this->context->cart->id.'\',\''.(int)$this->context->shop->id.'\',\''.pSQL(json_encode($data)).'\') ON DUPLICATE KEY UPDATE data =\''.pSQL(json_encode($data)).'\' ;'); - } - if ($action == 'A') { - $dataretour = Db::getInstance()->getValue('SELECT data FROM `'._DB_PREFIX_.'ganalytics_data` WHERE id_cart = \''.(int)$this->context->cart->id.'\' AND id_shop = \''.(int)$this->context->shop->id.'\''); - if ($dataretour === false) - $datanew = array($data); - else { - $datanew = json_decode($dataretour,true); - $datanew[] = $data; - } - return Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'ganalytics_data` (id_cart, id_shop, data) VALUES(\''.(int)$this->context->cart->id.'\',\''.(int)$this->context->shop->id.'\',\''.pSQL(json_encode($datanew)).'\') ON DUPLICATE KEY UPDATE data =\''.pSQL(json_encode($datanew)).'\' ;'); - } - if ($action == 'D') { - Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'ganalytics_data` WHERE id_cart = \''.(int)$this->context->cart->id.'\' AND id_shop = \''.(int)$this->context->shop->id.'\''); - } - } - /** * Hook admin order to send transactions and refunds details */ @@ -816,35 +697,50 @@ public function hookdisplayBackOfficeHeader() $this->context->smarty->assign('GA_ACCOUNT_ID', $ga_account_id); - $ga_scripts = ''; + $gaScripts = ''; if ($this->context->controller->controller_name == 'AdminOrders') { + $ganalyticsRepository = new PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository(); + if (Tools::getValue('id_order')) { $order = new Order((int)Tools::getValue('id_order')); if (Validate::isLoadedObject($order) && strtotime('+1 day', strtotime($order->date_add)) > time()) { - $ga_order_sent = Db::getInstance()->getValue('SELECT id_order FROM `'._DB_PREFIX_.'ganalytics` WHERE id_order = '.(int)Tools::getValue('id_order')); - if ($ga_order_sent === false) { - Db::getInstance()->Execute('INSERT IGNORE INTO `'._DB_PREFIX_.'ganalytics` (id_order, id_shop, sent, date_add) VALUES ('.(int)Tools::getValue('id_order').', '.(int)$this->context->shop->id.', 0, NOW())'); + $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) Tools::getValue('id_order')); + if ($gaOrderSent === false) { + $ganalyticsRepository->addNewRow( + array( + 'id_order' => (int) Tools::getValue('id_order'), + 'id_shop' => (int) $this->context->shop->id, + 'sent' => 0, + 'date_add' => 'NOW()', + ) + ); } } } else { - $ga_order_records = Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_.'ganalytics` WHERE sent = 0 AND id_shop = \''.(int)$this->context->shop->id.'\' AND DATE_ADD(date_add, INTERVAL 30 minute) < NOW()'); + $gaOrderRecords = $ganalyticsRepository->findAllByShopIdAndDateAdd((int) $this->context->shop->id); - if ($ga_order_records) { - foreach ($ga_order_records as $row) { + if ($gaOrderRecords) { + foreach ($gaOrderRecords as $row) { $transaction = $this->wrapOrder($row['id_order']); if (!empty($transaction)) { - Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'ganalytics` SET date_add = NOW(), sent = 1 WHERE id_order = '.(int)$row['id_order'].' AND id_shop = \''.(int)$this->context->shop->id.'\''); + $ganalyticsRepository->updateData( + array( + 'date_add' => 'NOW()', + 'sent' => 1 + ), + 'id_order = ' . (int) $row['id_order'] . ' AND id_shop = ' . (int) $this->context->shop->id + ); $transaction = json_encode($transaction); - $ga_scripts .= 'MBG.addTransaction('.$transaction.');'; + $gaScripts .= 'MBG.addTransaction('.$transaction.');'; } } } } } - return $js.$this->hookdisplayHeader(null, true).$this->_runJs($ga_scripts, 1); - } else { - return $js; + return $js.$this->hookdisplayHeader(null, true).$this->_runJs($gaScripts, 1); } + + return $js; } /** @@ -853,18 +749,18 @@ public function hookdisplayBackOfficeHeader() public function hookactionProductCancel($params) { $qty_refunded = Tools::getValue('cancelQuantity'); - $ga_scripts = ''; + $gaScripts = ''; foreach ($qty_refunded as $orderdetail_id => $qty) { // Display GA refund product $order_detail = new OrderDetail($orderdetail_id); - $ga_scripts .= 'MBG.add('.json_encode( + $gaScripts .= 'MBG.add('.json_encode( array( 'id' => empty($order_detail->product_attribute_id)?$order_detail->product_id:$order_detail->product_id.'-'.$order_detail->product_attribute_id, 'quantity' => $qty) ) .');'; } - $this->context->cookie->ga_admin_refund = $ga_scripts.'MBG.refundByProduct('.json_encode(array('id' => $params['order']->id)).');'; + $this->context->cookie->ga_admin_refund = $gaScripts.'MBG.refundByProduct('.json_encode(array('id' => $params['order']->id)).');'; } /** @@ -918,6 +814,11 @@ public function hookactionCartSave() } if (isset($add_product) && !in_array((int)Tools::getValue('id_product'), self::$products)) { + $ganalyticsDataHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler( + $this->context->cart->id, + $this->context->shop->id + ); + self::$products[] = (int)Tools::getValue('id_product'); $ga_products = $this->wrapProduct($add_product, $cart, 0, true); @@ -927,7 +828,7 @@ public function hookactionCartSave() $id_product = Tools::getValue('id_product'); } - $gacart = $this->_manageData("", "R"); + $gacart = $ganalyticsDataHandler->manageData('', 'R'); if ($cart['removeAction'] == 'delete') { $ga_products['quantity'] = -1; @@ -944,15 +845,21 @@ public function hookactionCartSave() } $gacart[$id_product] = $ga_products; - $this->_manageData($gacart, 'W'); + $ganalyticsDataHandler->manageData($gacart, 'W'); } } public function hookactionCarrierProcess($params) { if (isset($params['cart']->id_carrier)) { - $carrier_name = Db::getInstance()->getValue('SELECT name FROM `'._DB_PREFIX_.'carrier` WHERE id_carrier = '.(int)$params['cart']->id_carrier); - $this->_manageData('MBG.addCheckoutOption(2,\''.$carrier_name.'\');', 'A'); + $carrierRepository = new PrestaShop\Module\Ps_Googleanalytics\Repository\CarrierRepository(); + $ganalyticsDataHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler( + $this->context->cart->id, + $this->context->shop->id + ); + + $carrierName = $carrierRepository->findByCarrierId((int) $params['cart']->id_carrier); + $ganalyticsDataHandler->manageData('MBG.addCheckoutOption(2,\''.$carrierName.'\');', 'A'); } } @@ -968,4 +875,66 @@ protected function _debugLog($function, $log) fwrite($fh, print_r($log, true)."\n\n"); fclose($fh); } + + /** + * This method is trigger at the installation of the module + * - install all module tables + * - register hook used by the module. + * + * @return bool + */ + public function install() + { + $this->uninstallPrestaShop16Module(); + $database = new PrestaShop\Module\Ps_Googleanalytics\Database\Install($this); + + return parent::install() && + $database->registerHooks() && + $database->installTables(); + } + + /** + * Triggered at the uninstall of the module + * - erase tables + * + * @return bool + */ + public function uninstall() + { + $database = new PrestaShop\Module\Ps_Googleanalytics\Database\Uninstall(); + + return parent::uninstall() && + $database->uninstallTables(); + } + + /** + * Migrate data from 1.6 equivalent module (if applicable), then uninstall + * + * @return bool + */ + public function uninstallPrestaShop16Module() + { + if (!Module::isInstalled(self::PS_16_EQUIVALENT_MODULE)) { + return false; + } + + $oldModule = Module::getInstanceByName(self::PS_16_EQUIVALENT_MODULE); + + if ($oldModule) { + if (method_exists($oldModule, 'uninstallTab')) { + $oldModule->uninstallTab(); + } + + // This closure calls the parent class to prevent data to be erased + // It allows the new module to be configured without migration + $parentUninstallClosure = function() { + return parent::uninstall(); + }; + + $parentUninstallClosure = $parentUninstallClosure->bindTo($oldModule, get_class($oldModule)); + $parentUninstallClosure(); + } + + return true; + } } From 64e70ae357e090cc37e40643e33aae30daa216d0 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 11:43:32 +0200 Subject: [PATCH 14/45] Push hooks in separate files --- classes/Handler/ModuleHandler.php | 90 +++ classes/Hook/HookActionCarrierProcess.php | 71 +++ classes/Hook/HookActionCartSave.php | 129 +++++ classes/Hook/HookActionProductCancel.php | 74 +++ classes/Hook/HookDisplayBackOfficeHeader.php | 107 ++++ classes/Hook/HookDisplayFooter.php | 107 ++++ classes/Hook/HookDisplayFooterProduct.php | 79 +++ classes/Hook/HookDisplayHeader.php | 105 ++++ classes/Hook/HookDisplayHome.php | 72 +++ classes/Hook/HookDisplayOrderConfirmation.php | 104 ++++ classes/Hook/HookInterface.php | 37 ++ classes/Hook/index.php | 35 ++ classes/Repository/GanalyticsRepository.php | 2 +- ps_googleanalytics.php | 527 +++--------------- 14 files changed, 1101 insertions(+), 438 deletions(-) create mode 100644 classes/Handler/ModuleHandler.php create mode 100644 classes/Hook/HookActionCarrierProcess.php create mode 100644 classes/Hook/HookActionCartSave.php create mode 100644 classes/Hook/HookActionProductCancel.php create mode 100644 classes/Hook/HookDisplayBackOfficeHeader.php create mode 100644 classes/Hook/HookDisplayFooter.php create mode 100644 classes/Hook/HookDisplayFooterProduct.php create mode 100644 classes/Hook/HookDisplayHeader.php create mode 100644 classes/Hook/HookDisplayHome.php create mode 100644 classes/Hook/HookDisplayOrderConfirmation.php create mode 100644 classes/Hook/HookInterface.php create mode 100644 classes/Hook/index.php diff --git a/classes/Handler/ModuleHandler.php b/classes/Handler/ModuleHandler.php new file mode 100644 index 0000000..5baf07b --- /dev/null +++ b/classes/Handler/ModuleHandler.php @@ -0,0 +1,90 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Handler; + +class ModuleHandler +{ + /** + * isModuleEnabled + * + * @param string $moduleName + * + * @return bool + */ + public function isModuleEnabled($moduleName) + { + $module = \Module::getInstanceByName($moduleName); + + if (false === $module) { + return false; + } + + if (false === \Module::isInstalled($moduleName)) { + return false; + } + + if (false === $module->active) { + return false; + } + + return $module->registerHook('displayHome'); + } + + /** + * uninstallModule + * + * @param string $moduleName + * + * @return bool + */ + public function uninstallModule($moduleName) + { + if (false === \Module::isInstalled($moduleName)) { + return false; + } + + $oldModule = \Module::getInstanceByName($moduleName); + + if (false === $oldModule) { + return false; + } + + if (method_exists($oldModule, 'uninstallTab')) { + $oldModule->uninstallTab(); + } + + // This closure calls the parent class to prevent data to be erased + $parentUninstallClosure = function() { + return parent::uninstall(); + }; + + $parentUninstallClosure = $parentUninstallClosure->bindTo($oldModule, get_class($oldModule)); + $parentUninstallClosure(); + + return true; + } +} diff --git a/classes/Hook/HookActionCarrierProcess.php b/classes/Hook/HookActionCarrierProcess.php new file mode 100644 index 0000000..5c66e41 --- /dev/null +++ b/classes/Hook/HookActionCarrierProcess.php @@ -0,0 +1,71 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\Repository\CarrierRepository; +use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; + +class HookActionCarrierProcess implements HookInterface +{ + private $module; + private $context; + private $params; + + public function __construct($module, $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * manageHook + * + * @return void + */ + public function manageHook() + { + if (isset($this->params['cart']->id_carrier)) { + $carrierRepository = new CarrierRepository(); + $ganalyticsDataHandler = new GanalyticsDataHandler( + $this->context->cart->id, + $this->context->shop->id + ); + + $carrierName = $carrierRepository->findByCarrierId((int) $this->params['cart']->id_carrier); + $ganalyticsDataHandler->manageData('MBG.addCheckoutOption(2,\''.$carrierName.'\');', 'A'); + } + } + + /** + * setParams + * + * @param array $params + */ + public function setParams($params) { + $this->params = $params; + } +} diff --git a/classes/Hook/HookActionCartSave.php b/classes/Hook/HookActionCartSave.php new file mode 100644 index 0000000..6bd01c4 --- /dev/null +++ b/classes/Hook/HookActionCartSave.php @@ -0,0 +1,129 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; + +class HookActionCartSave implements HookInterface +{ + private $module; + private $context; + + public function __construct($module, $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * manageHook + * + * @return void + */ + public function manageHook() + { + if (!isset($this->context->cart)) { + return; + } + + if (!\Tools::getIsset('id_product')) { + return; + } + + $cart = array( + 'controller' => \Tools::getValue('controller'), + 'addAction' => \Tools::getValue('add') ? 'add' : '', + 'removeAction' => \Tools::getValue('delete') ? 'delete' : '', + 'extraAction' => \Tools::getValue('op'), + 'qty' => (int)\Tools::getValue('qty', 1) + ); + + $cartProducts = $this->context->cart->getProducts(); + if (isset($cartProducts) && count($cartProducts)) { + foreach ($cartProducts as $cartProduct) { + if ($cartProduct['id_product'] == \Tools::getValue('id_product')) { + $addProduct = $cartProduct; + } + } + } + + if ($cart['removeAction'] == 'delete') { + $addProductObject = new Product((int)\Tools::getValue('id_product'), true, (int)\Configuration::get('PS_LANG_DEFAULT')); + if (\Validate::isLoadedObject($addProductObject)) { + $addProduct['name'] = $addProductObject->name; + $addProduct['manufacturer_name'] = $addProductObject->manufacturer_name; + $addProduct['category'] = $addProductObject->category; + $addProduct['reference'] = $addProductObject->reference; + $addProduct['link_rewrite'] = $addProductObject->link_rewrite; + $addProduct['link'] = $addProductObject->link_rewrite; + $addProduct['price'] = $addProductObject->price; + $addProduct['ean13'] = $addProductObject->ean13; + $addProduct['id_product'] = \Tools::getValue('id_product'); + $addProduct['id_category_default'] = $addProductObject->id_category_default; + $addProduct['out_of_stock'] = $addProductObject->out_of_stock; + $addProduct['minimal_quantity'] = 1; + $addProduct['unit_price_ratio'] = 0; + $addProduct = \Product::getProductProperties((int)\Configuration::get('PS_LANG_DEFAULT'), $addProduct); + } + } + + if (isset($addProduct) && !in_array((int)\Tools::getValue('id_product'), $this->module->products)) { + $ganalyticsDataHandler = new GanalyticsDataHandler( + $this->context->cart->id, + $this->context->shop->id + ); + + $this->module->products[] = (int)\Tools::getValue('id_product'); + $gaProducts = $this->module->wrapProduct($addProduct, $cart, 0, true); + + if (array_key_exists('id_product_attribute', $gaProducts) && $gaProducts['id_product_attribute'] != '' && $gaProducts['id_product_attribute'] != 0) { + $productId = $gaProducts['id_product_attribute']; + } else { + $productId = \Tools::getValue('id_product'); + } + + $gaCart = $ganalyticsDataHandler->manageData('', 'R'); + + if ($cart['removeAction'] == 'delete') { + $gaProducts['quantity'] = -1; + } elseif ($cart['extraAction'] == 'down') { + if (array_key_exists($productId, $gaCart)) { + $gaProducts['quantity'] = $gaCart[$productId]['quantity'] - $cart['qty']; + } else { + $gaProducts['quantity'] = $cart['qty'] * -1; + } + } elseif (\Tools::getValue('step') <= 0) { // Sometimes cartsave is called in checkout + if (array_key_exists($productId, $gaCart)) { + $gaProducts['quantity'] = $gaCart[$productId]['quantity'] + $cart['qty']; + } + } + + $gaCart[$productId] = $gaProducts; + $ganalyticsDataHandler->manageData($gaCart, 'W'); + } + } +} diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php new file mode 100644 index 0000000..4be6eb0 --- /dev/null +++ b/classes/Hook/HookActionProductCancel.php @@ -0,0 +1,74 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; + +class HookActionProductCancel implements HookInterface +{ + private $module; + private $context; + private $params; + + public function __construct($module, $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * manageHook + * + * @return string + */ + public function manageHook() + { + $quantityRefunded = \Tools::getValue('cancelQuantity'); + $gaScripts = ''; + + foreach ($quantityRefunded as $orderDetailId => $quantity) { + // Display GA refund product + $orderDetail = new \OrderDetail($orderDetailId); + $gaScripts .= 'MBG.add('.json_encode( + array( + 'id' => empty($orderDetail->product_attribute_id)?$orderDetail->product_id:$orderDetail->product_id.'-'.$orderDetail->product_attribute_id, + 'quantity' => $quantity) + ) + .');'; + } + + $this->context->cookie->ga_admin_refund = $gaScripts.'MBG.refundByProduct('.json_encode(array('id' => $this->params['order']->id)).');'; + } + + /** + * setParams + * + * @param array $params + */ + public function setParams($params) { + $this->params = $params; + } +} diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php new file mode 100644 index 0000000..9ac6eb6 --- /dev/null +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -0,0 +1,107 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; + +class HookDisplayBackOfficeHeader implements HookInterface +{ + private $module; + private $context; + + public function __construct($module, $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * manageHook + * + * @return string + */ + public function manageHook() + { + $js = ''; + if (strcmp(\Tools::getValue('configure'), $this->module->name) === 0) { + $this->context->controller->addCSS($this->module->getPathUri().'views/css/ganalytics.css'); + } + + $ga_account_id = \Configuration::get('GA_ACCOUNT_ID'); + + if (!empty($ga_account_id) && $this->module->active) { + $this->context->controller->addJs($this->module->getPathUri().'views/js/GoogleAnalyticActionLib.js'); + + $this->context->smarty->assign('GA_ACCOUNT_ID', $ga_account_id); + + $gaScripts = ''; + if ($this->context->controller->controller_name == 'AdminOrders') { + $ganalyticsRepository = new GanalyticsRepository(); + + if (\Tools::getValue('id_order')) { + $order = new Order((int)\Tools::getValue('id_order')); + if (\Validate::isLoadedObject($order) && strtotime('+1 day', strtotime($order->date_add)) > time()) { + $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) \Tools::getValue('id_order')); + if ($gaOrderSent === false) { + $ganalyticsRepository->addNewRow( + array( + 'id_order' => (int) \Tools::getValue('id_order'), + 'id_shop' => (int) $this->context->shop->id, + 'sent' => 0, + 'date_add' => 'NOW()', + ) + ); + } + } + } else { + $gaOrderRecords = $ganalyticsRepository->findAllByShopIdAndDateAdd((int) $this->context->shop->id); + + if ($gaOrderRecords) { + foreach ($gaOrderRecords as $row) { + $transaction = $this->module->wrapOrder($row['id_order']); + if (!empty($transaction)) { + $ganalyticsRepository->updateData( + array( + 'date_add' => 'NOW()', + 'sent' => 1 + ), + 'id_order = ' . (int) $row['id_order'] . ' AND id_shop = ' . (int) $this->context->shop->id + ); + $transaction = json_encode($transaction); + $gaScripts .= 'MBG.addTransaction('.$transaction.');'; + } + } + } + } + } + + return $js.$this->module->hookdisplayHeader(null, true).$this->module->_runJs($gaScripts, 1); + } + + return $js; + } +} diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php new file mode 100644 index 0000000..4f9a95b --- /dev/null +++ b/classes/Hook/HookDisplayFooter.php @@ -0,0 +1,107 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; + +class HookDisplayFooter implements HookInterface +{ + private $module; + private $context; + + public function __construct($module, $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * manageHook + * + * @return string + */ + public function manageHook() + { + $ganalyticsDataHandler = new GanalyticsDataHandler( + $this->context->cart->id, + $this->context->shop->id + ); + + $gaScripts = ''; + $this->module->js_state = 0; + $gacarts = $ganalyticsDataHandler->manageData('', 'R'); + $controller_name = \Tools::getValue('controller'); + + if (count($gacarts)>0 && $controller_name!='product') { + $this->module->filterable = 0; + + foreach ($gacarts as $gacart) { + if (isset($gacart['quantity'])) + { + if ($gacart['quantity'] > 0) { + $gaScripts .= 'MBG.addToCart('.json_encode($gacart).');'; + } elseif ($gacart['quantity'] < 0) { + $gacart['quantity'] = abs($gacart['quantity']); + $gaScripts .= 'MBG.removeFromCart('.json_encode($gacart).');'; + } + } else { + $gaScripts .= $gacart; + } + } + + $ganalyticsDataHandler->manageData('', 'D'); + } + + $listing = $this->context->smarty->getTemplateVars('listing'); + $products = $this->module->wrapProducts($listing['products'], array(), true); + + if ($controller_name == 'order' || $controller_name == 'orderopc') { + $this->module->js_state = 1; + $this->module->eligible = 1; + $step = \Tools::getValue('step'); + if (empty($step)) { + $step = 0; + } + $gaScripts .= $this->module->addProductFromCheckout($products, $step); + $gaScripts .= 'MBG.addCheckout(\''.(int)$step.'\');'; + } + + $confirmation_hook_id = (int)\Hook::getIdByName('displayOrderConfirmation'); + if (isset(\Hook::$executed_hooks[$confirmation_hook_id])) { + $this->module->eligible = 1; + } + + if (isset($products) && count($products) && $controller_name != 'index') { + if ($this->module->eligible == 0) { + $gaScripts .= $this->module->addProductImpression($products); + } + $gaScripts .= $this->module->addProductClick($products); + } + + return $this->module->_runJs($gaScripts); + } +} diff --git a/classes/Hook/HookDisplayFooterProduct.php b/classes/Hook/HookDisplayFooterProduct.php new file mode 100644 index 0000000..4d6dfd3 --- /dev/null +++ b/classes/Hook/HookDisplayFooterProduct.php @@ -0,0 +1,79 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; + +class HookDisplayFooterProduct implements HookInterface +{ + private $module; + private $context; + private $params; + + public function __construct($module, $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * manageHook + * + * @return string + */ + public function manageHook() + { + $controllerName = \Tools::getValue('controller'); + + if ('product' !== $controllerName) { + return ''; + } + + if ($this->params['product'] instanceof \Product) { + $this->params['product'] = (array) $this->params['product']; + } + // Add product view + $gaProduct = $this->module->wrapProduct($this->params['product'], null, 0, true); + $js = 'MBG.addProductDetailView('.json_encode($gaProduct).');'; + + if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) > 0) { + $js .= $this->module->addProductClickByHttpReferal(array($gaProduct)); + } + + $this->module->js_state = 1; + + return $this->module->_runJs($js); + } + + /** + * setParams + * + * @param array $params + */ + public function setParams($params) { + $this->params = $params; + } +} diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php new file mode 100644 index 0000000..0f0e4c1 --- /dev/null +++ b/classes/Hook/HookDisplayHeader.php @@ -0,0 +1,105 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; + +class HookDisplayHeader implements HookInterface +{ + private $module; + private $context; + private $params; + private $backOffice; + + public function __construct($module, $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * manageHook + * + * @return string + */ + public function manageHook() + { + if (\Configuration::get('GA_ACCOUNT_ID')) { + $this->context->controller->addJs($this->module->getPathUri().'views/js/GoogleAnalyticActionLib.js'); + + $shops = \Shop::getShops(); + $isMultistoreActive = \Shop::isFeatureActive(); + $currentShopId = (int)\Context::getContext()->shop->id; + $userId = null; + $gaCrossdomainEnabled = false; + + if (\Configuration::get('GA_USERID_ENABLED') && + $this->context->customer && $this->context->customer->isLogged() + ) { + $userId = (int)$this->context->customer->id; + } + + $gaAnonymizeEnabled = \Configuration::get('GA_ANONYMIZE_ENABLED'); + + if ((int)\Configuration::get('GA_CROSSDOMAIN_ENABLED') && $isMultistoreActive && count($shops) > 1) { + $gaCrossdomainEnabled = true; + } + + $this->context->smarty->assign( + array( + 'backOffice' => $this->backOffice, + 'currentShopId' => $currentShopId, + 'userId' => $userId, + 'gaAccountId' => \Tools::safeOutput(\Configuration::get('GA_ACCOUNT_ID')), + 'shops' => $shops, + 'gaCrossdomainEnabled' => $gaCrossdomainEnabled, + 'gaAnonymizeEnabled' => $gaAnonymizeEnabled, + 'useSecureMode' => \Configuration::get('PS_SSL_ENABLED') + ) + ); + + return $this->module->display(__FILE__, 'ps_googleanalytics.tpl'); + } + } + + /** + * setParams + * + * @param array $params + */ + public function setParams($params) { + $this->module->params = $params; + } + + /** + * setBackOffice + * + * @param array $backOffice + */ + public function setBackOffice($backOffice) { + $this->module->backOffice = $backOffice; + } +} diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php new file mode 100644 index 0000000..1fc08db --- /dev/null +++ b/classes/Hook/HookDisplayHome.php @@ -0,0 +1,72 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\Handler\ModuleHandler; + +class HookDisplayHome implements HookInterface +{ + private $module; + private $context; + + public function __construct($module, $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * manageHook + * + * @return string + */ + public function manageHook() + { + $moduleHandler = new ModuleHandler(); + $gaScripts = ''; + + // Home featured products + if ($moduleHandler->isModuleEnabled('ps_featuredproducts')) { + $category = new Category($this->context->shop->getCategory(), $this->context->language->id); + $homeFeaturedProducts = $this->module->wrapProducts( + $category->getProducts( + (int) $this->context->language->id, + 1, + (\Configuration::get('HOME_FEATURED_NBR') ? (int)\Configuration::get('HOME_FEATURED_NBR') : 8), + 'position' + ), + array(), + true + ); + $gaScripts .= $this->module->addProductImpression($homeFeaturedProducts).$this->module->addProductClick($homeFeaturedProducts); + } + + $this->js_state = 1; + + return $this->module->_runJs($this->module->filter($gaScripts)); + } +} diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php new file mode 100644 index 0000000..cd1feb9 --- /dev/null +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -0,0 +1,104 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; + +class HookDisplayOrderConfirmation implements HookInterface +{ + private $module; + private $context; + private $params; + + public function __construct($module, $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * manageHook + * + * @return string + */ + public function manageHook() + { + if (true === $this->module->psVersionIs17) { + $order = $this->params['order']; + } else { + $order = $this->params['objOrder']; + } + + if (\Validate::isLoadedObject($order) && $order->getCurrentState() != (int)\Configuration::get('PS_OS_ERROR')) { + $ganalyticsRepository = new GanalyticsRepository(); + $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) $order->id); + + if (false === $gaOrderSent) { + $ganalyticsRepository->addNewRow( + array( + 'id_order' => (int) $order->id, + 'id_shop' => (int) $this->context->shop->id, + 'sent' => 0, + 'date_add' => 'NOW()', + ) + ); + + if ($order->id_customer == $this->context->cookie->id_customer) { + $orderProducts = array(); + $cart = new Cart($order->id_cart); + + foreach ($cart->getProducts() as $order_product) { + $orderProducts[] = $this->module->wrapProduct($order_product, array(), 0, true); + } + + $gaScripts = 'MBG.addCheckoutOption(3,\''.$order->payment.'\');'; + $transaction = array( + 'id' => $order->id, + 'affiliation' => (\Shop::isFeatureActive()) ? $this->context->shop->name : \Configuration::get('PS_SHOP_NAME'), + 'revenue' => $order->total_paid, + 'shipping' => $order->total_shipping, + 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, + 'url' => $this->context->link->getModuleLink('ps_googleanalytics', 'ajax', array(), true), + 'customer' => $order->id_customer); + $gaScripts .= $this->module->addTransaction($orderProducts, $transaction); + + $this->module->js_state = 1; + return $this->module->_runJs($gaScripts); + } + } + } + } + + /** + * setParams + * + * @param array $params + */ + public function setParams($params) { + $this->params = $params; + } +} diff --git a/classes/Hook/HookInterface.php b/classes/Hook/HookInterface.php new file mode 100644 index 0000000..8e03c50 --- /dev/null +++ b/classes/Hook/HookInterface.php @@ -0,0 +1,37 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +interface HookInterface { + /** + * @param \Ps_Googleanalytics $module + * @param \Context $context + */ + public function __construct(\Ps_Googleanalytics $module, \Context $context); + + public function manageHook(); +} diff --git a/classes/Hook/index.php b/classes/Hook/index.php new file mode 100644 index 0000000..94088c7 --- /dev/null +++ b/classes/Hook/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2020 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/classes/Repository/GanalyticsRepository.php b/classes/Repository/GanalyticsRepository.php index a598101..58838fd 100644 --- a/classes/Repository/GanalyticsRepository.php +++ b/classes/Repository/GanalyticsRepository.php @@ -98,4 +98,4 @@ public function updateData($data, $where) $where ); } -} \ No newline at end of file +} diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 9c1e032..ae0106c 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -37,12 +37,12 @@ class Ps_Googleanalytics extends Module */ const PS_16_EQUIVALENT_MODULE = 'ganalytics'; - protected $js_state = 0; - protected $eligible = 0; - protected $filterable = 1; - protected static $products = array(); - protected $_debug = 0; - protected $psVersionIs17; + public $js_state = 0; + public $eligible = 0; + public $filterable = 1; + public $products = array(); + public $_debug = 0; + public $psVersionIs17; public function __construct() { @@ -223,225 +223,123 @@ public function getContent() public function hookdisplayHeader($params, $back_office = false) { - if (Configuration::get('GA_ACCOUNT_ID')) { - $this->context->controller->addJs($this->_path.'views/js/GoogleAnalyticActionLib.js'); - - $shops = Shop::getShops(); - $is_multistore_active = Shop::isFeatureActive(); - - $current_shop_id = (int)Context::getContext()->shop->id; - - $user_id = null; - $ga_crossdomain_enabled = false; - - if (Configuration::get('GA_USERID_ENABLED') && - $this->context->customer && $this->context->customer->isLogged() - ) { - $user_id = (int)$this->context->customer->id; - } - - $ga_anonymize_enabled = Configuration::get('GA_ANONYMIZE_ENABLED'); - - if ((int)Configuration::get('GA_CROSSDOMAIN_ENABLED') && $is_multistore_active && count($shops) > 1) { - $ga_crossdomain_enabled = true; - } - - $this->smarty->assign( - array( - 'backOffice' => $back_office, - 'currentShopId' => $current_shop_id, - 'userId' => $user_id, - 'gaAccountId' => Tools::safeOutput(Configuration::get('GA_ACCOUNT_ID')), - 'shops' => $shops, - 'gaCrossdomainEnabled' => $ga_crossdomain_enabled, - 'gaAnonymizeEnabled' => $ga_anonymize_enabled, - 'useSecureMode' => Configuration::get('PS_SSL_ENABLED') - ) - ); - return $this->display(__FILE__, 'ps_googleanalytics.tpl'); - } + $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayHeader($this, $this->context); + $hook->setBackOffice($back_office); + return $hook->manageHook(); } /** - * Return a detailed transaction for Google Analytics + * To track transactions */ - public function wrapOrder($id_order) + public function hookdisplayOrderConfirmation($params) { - $order = new Order((int)$id_order); + $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayOrderConfirmation($this, $this->context); + $hook->setParams($params); - if (Validate::isLoadedObject($order)) { - return array( - 'id' => $id_order, - 'affiliation' => Shop::isFeatureActive() ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), - 'revenue' => $order->total_paid, - 'shipping' => $order->total_shipping, - 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, - 'url' => $this->context->link->getAdminLink('AdminGanalyticsAjax'), - 'customer' => $order->id_customer); - } + return $hook->manageHook(); } /** - * To track transactions + * hook footer to load JS script for standards actions such as product clicks */ - public function hookdisplayOrderConfirmation($params) + public function hookdisplayFooter() { - if (true === $this->psVersionIs17) { - $order = $params['order']; - } else { - $order = $params['objOrder']; - } - - if (Validate::isLoadedObject($order) && $order->getCurrentState() != (int)Configuration::get('PS_OS_ERROR')) { - $ganalyticsRepository = new PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository(); - $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) $order->id); - - if (false === $gaOrderSent) { - $ganalyticsRepository->addNewRow( - array( - 'id_order' => (int) $order->id, - 'id_shop' => (int) $this->context->shop->id, - 'sent' => 0, - 'date_add' => 'NOW()', - ) - ); - - if ($order->id_customer == $this->context->cookie->id_customer) { - $orderProducts = array(); - $cart = new Cart($order->id_cart); - - foreach ($cart->getProducts() as $order_product) { - $orderProducts[] = $this->wrapProduct($order_product, array(), 0, true); - } - - $gaScripts = 'MBG.addCheckoutOption(3,\''.$order->payment.'\');'; - $transaction = array( - 'id' => $order->id, - 'affiliation' => (Shop::isFeatureActive()) ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), - 'revenue' => $order->total_paid, - 'shipping' => $order->total_shipping, - 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, - 'url' => $this->context->link->getModuleLink('ps_googleanalytics', 'ajax', array(), true), - 'customer' => $order->id_customer); - $gaScripts .= $this->addTransaction($orderProducts, $transaction); - - $this->js_state = 1; - return $this->_runJs($gaScripts); - } - } - } + $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayFooter($this, $this->context); + return $hook->manageHook(); } /** - * hook footer to load JS script for standards actions such as product clicks + * hook home to display generate the product list associated to home featured, news products and best sellers Modules */ - public function hookdisplayFooter() + public function hookdisplayHome() { - $ganalyticsDataHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler( - $this->context->cart->id, - $this->context->shop->id - ); + $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayHome($this, $this->context); + return $hook->manageHook(); + } - $gaScripts = ''; - $this->js_state = 0; - $gacarts = $ganalyticsDataHandler->manageData('', 'R'); - $controller_name = Tools::getValue('controller'); - - if (count($gacarts)>0 && $controller_name!='product') { - $this->filterable = 0; - - foreach ($gacarts as $gacart) { - if (isset($gacart['quantity'])) - { - if ($gacart['quantity'] > 0) { - $gaScripts .= 'MBG.addToCart('.json_encode($gacart).');'; - } elseif ($gacart['quantity'] < 0) { - $gacart['quantity'] = abs($gacart['quantity']); - $gaScripts .= 'MBG.removeFromCart('.json_encode($gacart).');'; - } - } else { - $gaScripts .= $gacart; - } - } + /** + * hook product page footer to load JS for product details view + */ + public function hookdisplayFooterProduct($params) + { + $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayFooterProduct($this, $this->context); + $hook->setParams($params); - $ganalyticsDataHandler->manageData('', 'D'); - } + return $hook->manageHook(); + } - $listing = $this->context->smarty->getTemplateVars('listing'); - $products = $this->wrapProducts($listing['products'], array(), true); + /** + * Hook admin order to send transactions and refunds details + */ + public function hookdisplayAdminOrder() + { + echo $this->_runJs($this->context->cookie->ga_admin_refund, 1); + unset($this->context->cookie->ga_admin_refund); + } - if ($controller_name == 'order' || $controller_name == 'orderopc') { - $this->js_state = 1; - $this->eligible = 1; - $step = Tools::getValue('step'); - if (empty($step)) { - $step = 0; - } - $gaScripts .= $this->addProductFromCheckout($products, $step); - $gaScripts .= 'MBG.addCheckout(\''.(int)$step.'\');'; - } + /** + * admin office header to add google analytics js + */ + public function hookdisplayBackOfficeHeader() + { + $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayBackOfficeHeader($this, $this->context); + return $hook->manageHook(); + } - $confirmation_hook_id = (int)Hook::getIdByName('displayOrderConfirmation'); - if (isset(Hook::$executed_hooks[$confirmation_hook_id])) { - $this->eligible = 1; - } + /** + * Hook admin office header to add google analytics js + */ + public function hookactionProductCancel($params) + { + $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionProductCancel($this, $this->context); + $hook->setParams($params); - if (isset($products) && count($products) && $controller_name != 'index') { - if ($this->eligible == 0) { - $gaScripts .= $this->addProductImpression($products); - } - $gaScripts .= $this->addProductClick($products); - } + return $hook->manageHook(); + } - return $this->_runJs($gaScripts); + /** + * hook save cart event to implement addtocart and remove from cart functionality + */ + public function hookactionCartSave() + { + $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCartSave($this, $this->context); + return $hook->manageHook(); } - protected function filter($gaScripts) + public function hookactionCarrierProcess($params) { - if ($this->filterable = 1) { - return implode(';', array_unique(explode(';', $gaScripts))); - } + $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCarrierProcess($this, $this->context); + $hook->setParams($params); - return $gaScripts; + return $hook->manageHook(); } /** - * hook home to display generate the product list associated to home featured, news products and best sellers Modules + * Return a detailed transaction for Google Analytics */ - public function hookdisplayHome() + public function wrapOrder($id_order) { - $gaScripts = ''; - - // Home featured products - if ($this->isModuleEnabled('ps_featuredproducts')) { - $category = new Category($this->context->shop->getCategory(), $this->context->language->id); - $home_featured_products = $this->wrapProducts( - $category->getProducts( - (int)Context::getContext()->language->id, - 1, - (Configuration::get('HOME_FEATURED_NBR') ? (int)Configuration::get('HOME_FEATURED_NBR') : 8), - 'position' - ), - array(), - true - ); - $gaScripts .= $this->addProductImpression($home_featured_products).$this->addProductClick($home_featured_products); - } + $order = new Order((int)$id_order); - $this->js_state = 1; - return $this->_runJs($this->filter($gaScripts)); + if (Validate::isLoadedObject($order)) { + return array( + 'id' => $id_order, + 'affiliation' => Shop::isFeatureActive() ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), + 'revenue' => $order->total_paid, + 'shipping' => $order->total_shipping, + 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, + 'url' => $this->context->link->getAdminLink('AdminGanalyticsAjax'), + 'customer' => $order->id_customer); + } } - /** - * hook home to display generate the product list associated to home featured, news products and best sellers Modules - */ - public function isModuleEnabled($module_name) + protected function filter($gaScripts) { - if (($module = Module::getInstanceByName($module_name)) !== false && - Module::isInstalled($module_name) && - $module->active) { - return $module->registerHook('displayHome'); + if ($this->filterable = 1) { + return implode(';', array_unique(explode(';', $gaScripts))); } + + return $gaScripts; } /** @@ -618,34 +516,10 @@ public function addProductFromCheckout($products) return $js; } - /** - * hook product page footer to load JS for product details view - */ - public function hookdisplayFooterProduct($params) - { - $controller_name = Tools::getValue('controller'); - if ($controller_name == 'product') - { - if ($params['product'] instanceof Product) { - $params['product'] = (array) $params['product']; - } - // Add product view - $ga_product = $this->wrapProduct($params['product'], null, 0, true); - $js = 'MBG.addProductDetailView('.json_encode($ga_product).');'; - - if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) > 0) { - $js .= $this->addProductClickByHttpReferal(array($ga_product)); - } - - $this->js_state = 1; - return $this->_runJs($js); - } - } - /** * Generate Google Analytics js */ - protected function _runJs($js_code, $backoffice = 0) + public function _runJs($js_code, $backoffice = 0) { if (Configuration::get('GA_ACCOUNT_ID')) { $runjs_code = ''; @@ -671,198 +545,6 @@ protected function _runJs($js_code, $backoffice = 0) } } - /** - * Hook admin order to send transactions and refunds details - */ - public function hookdisplayAdminOrder() - { - echo $this->_runJs($this->context->cookie->ga_admin_refund, 1); - unset($this->context->cookie->ga_admin_refund); - } - - /** - * admin office header to add google analytics js - */ - public function hookdisplayBackOfficeHeader() - { - $js = ''; - if (strcmp(Tools::getValue('configure'), $this->name) === 0) { - $this->context->controller->addCSS($this->_path.'views/css/ganalytics.css'); - } - - $ga_account_id = Configuration::get('GA_ACCOUNT_ID'); - - if (!empty($ga_account_id) && $this->active) { - $this->context->controller->addJs($this->_path.'views/js/GoogleAnalyticActionLib.js'); - - $this->context->smarty->assign('GA_ACCOUNT_ID', $ga_account_id); - - $gaScripts = ''; - if ($this->context->controller->controller_name == 'AdminOrders') { - $ganalyticsRepository = new PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository(); - - if (Tools::getValue('id_order')) { - $order = new Order((int)Tools::getValue('id_order')); - if (Validate::isLoadedObject($order) && strtotime('+1 day', strtotime($order->date_add)) > time()) { - $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) Tools::getValue('id_order')); - if ($gaOrderSent === false) { - $ganalyticsRepository->addNewRow( - array( - 'id_order' => (int) Tools::getValue('id_order'), - 'id_shop' => (int) $this->context->shop->id, - 'sent' => 0, - 'date_add' => 'NOW()', - ) - ); - } - } - } else { - $gaOrderRecords = $ganalyticsRepository->findAllByShopIdAndDateAdd((int) $this->context->shop->id); - - if ($gaOrderRecords) { - foreach ($gaOrderRecords as $row) { - $transaction = $this->wrapOrder($row['id_order']); - if (!empty($transaction)) { - $ganalyticsRepository->updateData( - array( - 'date_add' => 'NOW()', - 'sent' => 1 - ), - 'id_order = ' . (int) $row['id_order'] . ' AND id_shop = ' . (int) $this->context->shop->id - ); - $transaction = json_encode($transaction); - $gaScripts .= 'MBG.addTransaction('.$transaction.');'; - } - } - } - } - } - return $js.$this->hookdisplayHeader(null, true).$this->_runJs($gaScripts, 1); - } - - return $js; - } - - /** - * Hook admin office header to add google analytics js - */ - public function hookactionProductCancel($params) - { - $qty_refunded = Tools::getValue('cancelQuantity'); - $gaScripts = ''; - foreach ($qty_refunded as $orderdetail_id => $qty) { - // Display GA refund product - $order_detail = new OrderDetail($orderdetail_id); - $gaScripts .= 'MBG.add('.json_encode( - array( - 'id' => empty($order_detail->product_attribute_id)?$order_detail->product_id:$order_detail->product_id.'-'.$order_detail->product_attribute_id, - 'quantity' => $qty) - ) - .');'; - } - $this->context->cookie->ga_admin_refund = $gaScripts.'MBG.refundByProduct('.json_encode(array('id' => $params['order']->id)).');'; - } - - /** - * hook save cart event to implement addtocart and remove from cart functionality - */ - public function hookactionCartSave() - { - if (!isset($this->context->cart)) { - return; - } - - if (!Tools::getIsset('id_product')) { - return; - } - - $cart = array( - 'controller' => Tools::getValue('controller'), - 'addAction' => Tools::getValue('add') ? 'add' : '', - 'removeAction' => Tools::getValue('delete') ? 'delete' : '', - 'extraAction' => Tools::getValue('op'), - 'qty' => (int)Tools::getValue('qty', 1) - ); - - $cart_products = $this->context->cart->getProducts(); - if (isset($cart_products) && count($cart_products)) { - foreach ($cart_products as $cart_product) { - if ($cart_product['id_product'] == Tools::getValue('id_product')) { - $add_product = $cart_product; - } - } - } - - if ($cart['removeAction'] == 'delete') { - $add_product_object = new Product((int)Tools::getValue('id_product'), true, (int)Configuration::get('PS_LANG_DEFAULT')); - if (Validate::isLoadedObject($add_product_object)) { - $add_product['name'] = $add_product_object->name; - $add_product['manufacturer_name'] = $add_product_object->manufacturer_name; - $add_product['category'] = $add_product_object->category; - $add_product['reference'] = $add_product_object->reference; - $add_product['link_rewrite'] = $add_product_object->link_rewrite; - $add_product['link'] = $add_product_object->link_rewrite; - $add_product['price'] = $add_product_object->price; - $add_product['ean13'] = $add_product_object->ean13; - $add_product['id_product'] = Tools::getValue('id_product'); - $add_product['id_category_default'] = $add_product_object->id_category_default; - $add_product['out_of_stock'] = $add_product_object->out_of_stock; - $add_product['minimal_quantity'] = 1; - $add_product['unit_price_ratio'] = 0; - $add_product = Product::getProductProperties((int)Configuration::get('PS_LANG_DEFAULT'), $add_product); - } - } - - if (isset($add_product) && !in_array((int)Tools::getValue('id_product'), self::$products)) { - $ganalyticsDataHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler( - $this->context->cart->id, - $this->context->shop->id - ); - - self::$products[] = (int)Tools::getValue('id_product'); - $ga_products = $this->wrapProduct($add_product, $cart, 0, true); - - if (array_key_exists('id_product_attribute', $ga_products) && $ga_products['id_product_attribute'] != '' && $ga_products['id_product_attribute'] != 0) { - $id_product = $ga_products['id_product_attribute']; - } else { - $id_product = Tools::getValue('id_product'); - } - - $gacart = $ganalyticsDataHandler->manageData('', 'R'); - - if ($cart['removeAction'] == 'delete') { - $ga_products['quantity'] = -1; - } elseif ($cart['extraAction'] == 'down') { - if (array_key_exists($id_product, $gacart)) { - $ga_products['quantity'] = $gacart[$id_product]['quantity'] - $cart['qty']; - } else { - $ga_products['quantity'] = $cart['qty'] * -1; - } - } elseif (Tools::getValue('step') <= 0) { // Sometimes cartsave is called in checkout - if (array_key_exists($id_product, $gacart)) { - $ga_products['quantity'] = $gacart[$id_product]['quantity'] + $cart['qty']; - } - } - - $gacart[$id_product] = $ga_products; - $ganalyticsDataHandler->manageData($gacart, 'W'); - } - } - - public function hookactionCarrierProcess($params) - { - if (isset($params['cart']->id_carrier)) { - $carrierRepository = new PrestaShop\Module\Ps_Googleanalytics\Repository\CarrierRepository(); - $ganalyticsDataHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler( - $this->context->cart->id, - $this->context->shop->id - ); - - $carrierName = $carrierRepository->findByCarrierId((int) $params['cart']->id_carrier); - $ganalyticsDataHandler->manageData('MBG.addCheckoutOption(2,\''.$carrierName.'\');', 'A'); - } - } - protected function _debugLog($function, $log) { if (!$this->_debug) { @@ -885,9 +567,11 @@ protected function _debugLog($function, $log) */ public function install() { - $this->uninstallPrestaShop16Module(); + $moduleHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\ModuleHandler(); $database = new PrestaShop\Module\Ps_Googleanalytics\Database\Install($this); + $moduleHandler->uninstallModule(self::PS_16_EQUIVALENT_MODULE); + return parent::install() && $database->registerHooks() && $database->installTables(); @@ -906,35 +590,4 @@ public function uninstall() return parent::uninstall() && $database->uninstallTables(); } - - /** - * Migrate data from 1.6 equivalent module (if applicable), then uninstall - * - * @return bool - */ - public function uninstallPrestaShop16Module() - { - if (!Module::isInstalled(self::PS_16_EQUIVALENT_MODULE)) { - return false; - } - - $oldModule = Module::getInstanceByName(self::PS_16_EQUIVALENT_MODULE); - - if ($oldModule) { - if (method_exists($oldModule, 'uninstallTab')) { - $oldModule->uninstallTab(); - } - - // This closure calls the parent class to prevent data to be erased - // It allows the new module to be configured without migration - $parentUninstallClosure = function() { - return parent::uninstall(); - }; - - $parentUninstallClosure = $parentUninstallClosure->bindTo($oldModule, get_class($oldModule)); - $parentUninstallClosure(); - } - - return true; - } } From 6ea527c770e178df2fc85552938bb56caca30c19 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 11:49:39 +0200 Subject: [PATCH 15/45] Push configuration form into his file --- classes/Form/ConfigurationForm.php | 164 +++++++++++++++++++++++++++++ classes/Form/index.php | 35 ++++++ ps_googleanalytics.php | 120 +-------------------- 3 files changed, 201 insertions(+), 118 deletions(-) create mode 100644 classes/Form/ConfigurationForm.php create mode 100644 classes/Form/index.php diff --git a/classes/Form/ConfigurationForm.php b/classes/Form/ConfigurationForm.php new file mode 100644 index 0000000..8616096 --- /dev/null +++ b/classes/Form/ConfigurationForm.php @@ -0,0 +1,164 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Form; + + +class ConfigurationForm +{ + private $module; + + public function __construct(\Ps_Googleanalytics $module) { + $this->module = $module; + } + + /** + * generateForm + * + * @return string + */ + public function generateForm() + { + // Check if multistore is active + $is_multistore_active = \Shop::isFeatureActive(); + + // Get default language + $default_lang = (int)\Configuration::get('PS_LANG_DEFAULT'); + + $helper = new \HelperForm(); + + // Module, token and currentIndex + $helper->module = $this->module; + $helper->name_controller = $this->module->name; + $helper->token = \Tools::getAdminTokenLite('AdminModules'); + $helper->currentIndex = \AdminController::$currentIndex.'&configure='.$this->module->name; + + // Language + $helper->default_form_language = $default_lang; + $helper->allow_employee_form_lang = $default_lang; + + // Title and toolbar + $helper->title = $this->module->displayName; + $helper->show_toolbar = true; // false -> remove toolbar + $helper->toolbar_scroll = true; // yes - > Toolbar is always visible on the top of the screen. + $helper->submit_action = 'submit'.$this->module->name; + $helper->toolbar_btn = array( + 'save' => + array( + 'desc' => $this->module->l('Save'), + 'href' => \AdminController::$currentIndex.'&configure='.$this->module->name.'&save'.$this->module->name. + '&token='.\Tools::getAdminTokenLite('AdminModules'), + ), + 'back' => array( + 'href' => \AdminController::$currentIndex.'&token='.\Tools::getAdminTokenLite('AdminModules'), + 'desc' => $this->module->l('Back to list') + ) + ); + + $fields_form = array(); + // Init Fields form array + $fields_form[0]['form'] = array( + 'legend' => array( + 'title' => $this->module->l('Settings'), + ), + 'input' => array( + array( + 'type' => 'text', + 'label' => $this->module->l('Google Analytics Tracking ID'), + 'name' => 'GA_ACCOUNT_ID', + 'size' => 20, + 'required' => true, + 'hint' => $this->module->l('This information is available in your Google Analytics account') + ), + array( + 'type' => 'switch', + 'label' => $this->module->l('Enable User ID tracking'), + 'name' => 'GA_USERID_ENABLED', + 'values' => array( + array( + 'id' => 'ga_userid_enabled', + 'value' => 1, + 'label' => $this->module->l('Enabled') + ), + array( + 'id' => 'ga_userid_disabled', + 'value' => 0, + 'label' => $this->module->l('Disabled') + )) + ), + array( + 'type' => 'switch', + 'label' => $this->module->l('Anonymize IP'), + 'name' => 'GA_ANONYMIZE_ENABLED', + 'hint' => $this->module->l('Use this option to anonymize the visitor’s IP to comply with data privacy laws in some countries'), + 'values' => array( + array( + 'id' => 'ga_anonymize_enabled', + 'value' => 1, + 'label' => $this->module->l('Enabled') + ), + array( + 'id' => 'ga_anonymize_disabled', + 'value' => 0, + 'label' => $this->module->l('Disabled') + ), + ), + ), + ), + 'submit' => array( + 'title' => $this->module->l('Save'), + ) + ); + + if ($is_multistore_active) { + $fields_form[0]['form']['input'][] = array( + 'type' => 'switch', + 'label' => $this->module->l('Enable Cross-Domain tracking'), + 'name' => 'GA_CROSSDOMAIN_ENABLED', + 'values' => array( + array( + 'id' => 'ga_crossdomain_enabled', + 'value' => 1, + 'label' => $this->module->l('Enabled') + ), + array( + 'id' => 'ga_crossdomain_disabled', + 'value' => 0, + 'label' => $this->module->l('Disabled') + ) + ) + ); + } + + // Load current value + $helper->fields_value['GA_ACCOUNT_ID'] = \Configuration::get('GA_ACCOUNT_ID'); + $helper->fields_value['GA_USERID_ENABLED'] = \Configuration::get('GA_USERID_ENABLED'); + $helper->fields_value['GA_CROSSDOMAIN_ENABLED'] = \Configuration::get('GA_CROSSDOMAIN_ENABLED'); + $helper->fields_value['GA_ANONYMIZE_ENABLED'] = \Configuration::get('GA_ANONYMIZE_ENABLED'); + + return $helper->generateForm($fields_form); + } +} diff --git a/classes/Form/index.php b/classes/Form/index.php new file mode 100644 index 0000000..94088c7 --- /dev/null +++ b/classes/Form/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2020 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index ae0106c..2d1e223 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -64,124 +64,8 @@ public function __construct() public function displayForm() { - // Check if multistore is active - $is_multistore_active = Shop::isFeatureActive(); - - // Get default language - $default_lang = (int)Configuration::get('PS_LANG_DEFAULT'); - - $helper = new HelperForm(); - - // Module, token and currentIndex - $helper->module = $this; - $helper->name_controller = $this->name; - $helper->token = Tools::getAdminTokenLite('AdminModules'); - $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name; - - // Language - $helper->default_form_language = $default_lang; - $helper->allow_employee_form_lang = $default_lang; - - // Title and toolbar - $helper->title = $this->displayName; - $helper->show_toolbar = true; // false -> remove toolbar - $helper->toolbar_scroll = true; // yes - > Toolbar is always visible on the top of the screen. - $helper->submit_action = 'submit'.$this->name; - $helper->toolbar_btn = array( - 'save' => - array( - 'desc' => $this->l('Save'), - 'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name. - '&token='.Tools::getAdminTokenLite('AdminModules'), - ), - 'back' => array( - 'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'), - 'desc' => $this->l('Back to list') - ) - ); - - $fields_form = array(); - // Init Fields form array - $fields_form[0]['form'] = array( - 'legend' => array( - 'title' => $this->l('Settings'), - ), - 'input' => array( - array( - 'type' => 'text', - 'label' => $this->l('Google Analytics Tracking ID'), - 'name' => 'GA_ACCOUNT_ID', - 'size' => 20, - 'required' => true, - 'hint' => $this->l('This information is available in your Google Analytics account') - ), - array( - 'type' => 'switch', - 'label' => $this->l('Enable User ID tracking'), - 'name' => 'GA_USERID_ENABLED', - 'values' => array( - array( - 'id' => 'ga_userid_enabled', - 'value' => 1, - 'label' => $this->l('Enabled') - ), - array( - 'id' => 'ga_userid_disabled', - 'value' => 0, - 'label' => $this->l('Disabled') - )) - ), - array( - 'type' => 'switch', - 'label' => $this->l('Anonymize IP'), - 'name' => 'GA_ANONYMIZE_ENABLED', - 'hint' => $this->l('Use this option to anonymize the visitor’s IP to comply with data privacy laws in some countries'), - 'values' => array( - array( - 'id' => 'ga_anonymize_enabled', - 'value' => 1, - 'label' => $this->l('Enabled') - ), - array( - 'id' => 'ga_anonymize_disabled', - 'value' => 0, - 'label' => $this->l('Disabled') - ), - ), - ), - ), - 'submit' => array( - 'title' => $this->l('Save'), - ) - ); - - if ($is_multistore_active) { - $fields_form[0]['form']['input'][] = array( - 'type' => 'switch', - 'label' => $this->l('Enable Cross-Domain tracking'), - 'name' => 'GA_CROSSDOMAIN_ENABLED', - 'values' => array( - array( - 'id' => 'ga_crossdomain_enabled', - 'value' => 1, - 'label' => $this->l('Enabled') - ), - array( - 'id' => 'ga_crossdomain_disabled', - 'value' => 0, - 'label' => $this->l('Disabled') - ) - ) - ); - } - - // Load current value - $helper->fields_value['GA_ACCOUNT_ID'] = Configuration::get('GA_ACCOUNT_ID'); - $helper->fields_value['GA_USERID_ENABLED'] = Configuration::get('GA_USERID_ENABLED'); - $helper->fields_value['GA_CROSSDOMAIN_ENABLED'] = Configuration::get('GA_CROSSDOMAIN_ENABLED'); - $helper->fields_value['GA_ANONYMIZE_ENABLED'] = Configuration::get('GA_ANONYMIZE_ENABLED'); - - return $helper->generateForm($fields_form); + $configurationForm = new PrestaShop\Module\Ps_Googleanalytics\Form\ConfigurationForm($this); + return $configurationForm->generateForm(); } /** From 7487a26448628b3e0e90cd57dc72343f8ec79f79 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 12:16:52 +0200 Subject: [PATCH 16/45] Put all GA methods in one class --- classes/Form/ConfigurationForm.php | 1 - classes/GoogleAnalyticsTools.php | 187 ++++++++++++++++++ classes/Hook/HookDisplayBackOfficeHeader.php | 11 +- classes/Hook/HookDisplayFooter.php | 14 +- classes/Hook/HookDisplayFooterProduct.php | 10 +- classes/Hook/HookDisplayHome.php | 10 +- classes/Hook/HookDisplayOrderConfirmation.php | 10 +- ps_googleanalytics.php | 126 +----------- 8 files changed, 238 insertions(+), 131 deletions(-) create mode 100644 classes/GoogleAnalyticsTools.php diff --git a/classes/Form/ConfigurationForm.php b/classes/Form/ConfigurationForm.php index 8616096..bacac03 100644 --- a/classes/Form/ConfigurationForm.php +++ b/classes/Form/ConfigurationForm.php @@ -26,7 +26,6 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Form; - class ConfigurationForm { private $module; diff --git a/classes/GoogleAnalyticsTools.php b/classes/GoogleAnalyticsTools.php new file mode 100644 index 0000000..b2eda46 --- /dev/null +++ b/classes/GoogleAnalyticsTools.php @@ -0,0 +1,187 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics; + +class GoogleAnalyticsTools +{ + /** + * filter + * + * @param string $gaScripts + * @param int $filterable + * + * @return string + */ + public function filter($gaScripts, $filterable) + { + if (1 == $filterable) { + return implode(';', array_unique(explode(';', $gaScripts))); + } + + return $gaScripts; + } + + /** + * add order transaction + * + * @param array $products + * @param array $order + * + * @return string|void + */ + public function addTransaction($products, $order) + { + if (!is_array($products)) { + return; + } + + $js = ''; + foreach ($products as $product) { + $js .= 'MBG.add('.json_encode($product).');'; + } + + return $js.'MBG.addTransaction('.json_encode($order).');'; + } + + /** + * add product impression js and product click js + * + * @param array ixed $products + * + * @return string|void + */ + public function addProductImpression($products) + { + if (!is_array($products)) { + return; + } + + $js = ''; + foreach ($products as $product) { + $js .= 'MBG.add('.json_encode($product).",'',true);"; + } + + return $js; + } + + /** + * addProductClick + * + * @param array $products + * + * @return string|void + */ + public function addProductClick($products) + { + if (!is_array($products)) { + return; + } + + $js = ''; + foreach ($products as $product) { + $js .= 'MBG.addProductClick('.json_encode($product).');'; + } + + return $js; + } + + /** + * addProductClickByHttpReferal + * + * @param array $products + * + * @return string|void + */ + public function addProductClickByHttpReferal($products) + { + if (!is_array($products)) { + return; + } + + $js = ''; + foreach ($products as $product) { + $js .= 'MBG.addProductClickByHttpReferal('.json_encode($product).');'; + } + + return $js; + } + + /** + * Add product checkout info + * + * @param array $products + * + * @return string|void + */ + public function addProductFromCheckout($products) + { + if (!is_array($products)) { + return; + } + + $js = ''; + foreach ($products as $product) { + $js .= 'MBG.add('.json_encode($product).');'; + } + + return $js; + } + + /** + * Generate Google Analytics js + * + * @param string $jsCode + * @param int $backoffice + * + * @return string + */ + public function generateJs($jsState, $isoCode, $jsCode, $backoffice = 0) + { + if (\Configuration::get('GA_ACCOUNT_ID')) { + $generateJsCode = ''; + if (!empty($jsCode)) { + $generateJsCode .= ' + '; + } + + if (($jsState) != 1 && ($backoffice == 0)) { + $generateJsCode .= ' + '; + } + + return $generateJsCode; + } + } +} diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php index 9ac6eb6..f1561bf 100644 --- a/classes/Hook/HookDisplayBackOfficeHeader.php +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -27,6 +27,7 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; class HookDisplayBackOfficeHeader implements HookInterface @@ -54,6 +55,7 @@ public function manageHook() $ga_account_id = \Configuration::get('GA_ACCOUNT_ID'); if (!empty($ga_account_id) && $this->module->active) { + $gaTools = new GoogleAnalyticsTools(); $this->context->controller->addJs($this->module->getPathUri().'views/js/GoogleAnalyticActionLib.js'); $this->context->smarty->assign('GA_ACCOUNT_ID', $ga_account_id); @@ -99,7 +101,14 @@ public function manageHook() } } - return $js.$this->module->hookdisplayHeader(null, true).$this->module->_runJs($gaScripts, 1); + $generatedJs = $gaTools->generateJs( + $this->module->js_state, + $this->context->currency->iso_code, + $gaScripts, + 1 + ); + + return $js.$this->module->hookdisplayHeader(null, true).$generatedJs; } return $js; diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php index 4f9a95b..52faeab 100644 --- a/classes/Hook/HookDisplayFooter.php +++ b/classes/Hook/HookDisplayFooter.php @@ -27,6 +27,7 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; class HookDisplayFooter implements HookInterface @@ -46,6 +47,7 @@ public function __construct($module, $context) { */ public function manageHook() { + $gaTools = new GoogleAnalyticsTools(); $ganalyticsDataHandler = new GanalyticsDataHandler( $this->context->cart->id, $this->context->shop->id @@ -86,7 +88,7 @@ public function manageHook() if (empty($step)) { $step = 0; } - $gaScripts .= $this->module->addProductFromCheckout($products, $step); + $gaScripts .= $gaTools->addProductFromCheckout($products, $step); $gaScripts .= 'MBG.addCheckout(\''.(int)$step.'\');'; } @@ -97,11 +99,15 @@ public function manageHook() if (isset($products) && count($products) && $controller_name != 'index') { if ($this->module->eligible == 0) { - $gaScripts .= $this->module->addProductImpression($products); + $gaScripts .= $gaTools->addProductImpression($products); } - $gaScripts .= $this->module->addProductClick($products); + $gaScripts .= $gaTools->addProductClick($products); } - return $this->module->_runJs($gaScripts); + return $gaTools->generateJs( + $this->module->js_state, + $this->context->currency->iso_code, + $gaScripts + ); } } diff --git a/classes/Hook/HookDisplayFooterProduct.php b/classes/Hook/HookDisplayFooterProduct.php index 4d6dfd3..8d5d177 100644 --- a/classes/Hook/HookDisplayFooterProduct.php +++ b/classes/Hook/HookDisplayFooterProduct.php @@ -27,6 +27,7 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; class HookDisplayFooterProduct implements HookInterface { @@ -46,6 +47,7 @@ public function __construct($module, $context) { */ public function manageHook() { + $gaTools = new GoogleAnalyticsTools(); $controllerName = \Tools::getValue('controller'); if ('product' !== $controllerName) { @@ -60,12 +62,16 @@ public function manageHook() $js = 'MBG.addProductDetailView('.json_encode($gaProduct).');'; if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) > 0) { - $js .= $this->module->addProductClickByHttpReferal(array($gaProduct)); + $js .= $gaTools->addProductClickByHttpReferal(array($gaProduct)); } $this->module->js_state = 1; - return $this->module->_runJs($js); + return $gaTools->generateJs( + $this->module->js_state, + $this->context->currency->iso_code, + $js + ); } /** diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php index 1fc08db..09baa8e 100644 --- a/classes/Hook/HookDisplayHome.php +++ b/classes/Hook/HookDisplayHome.php @@ -27,6 +27,7 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Handler\ModuleHandler; class HookDisplayHome implements HookInterface @@ -47,6 +48,7 @@ public function __construct($module, $context) { public function manageHook() { $moduleHandler = new ModuleHandler(); + $gaTools = new GoogleAnalyticsTools(); $gaScripts = ''; // Home featured products @@ -62,11 +64,15 @@ public function manageHook() array(), true ); - $gaScripts .= $this->module->addProductImpression($homeFeaturedProducts).$this->module->addProductClick($homeFeaturedProducts); + $gaScripts .= $gaTools->addProductImpression($homeFeaturedProducts).$gaTools->addProductClick($homeFeaturedProducts); } $this->js_state = 1; - return $this->module->_runJs($this->module->filter($gaScripts)); + return $gaTools->generateJs( + $this->module->js_state, + $this->context->currency->iso_code, + $gaTools->filter($gaScripts, $this->module->filterable) + ); } } diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php index cd1feb9..ae61e20 100644 --- a/classes/Hook/HookDisplayOrderConfirmation.php +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -27,6 +27,7 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; class HookDisplayOrderConfirmation implements HookInterface @@ -70,6 +71,7 @@ public function manageHook() if ($order->id_customer == $this->context->cookie->id_customer) { $orderProducts = array(); $cart = new Cart($order->id_cart); + $gaTools = new GoogleAnalyticsTools(); foreach ($cart->getProducts() as $order_product) { $orderProducts[] = $this->module->wrapProduct($order_product, array(), 0, true); @@ -84,10 +86,14 @@ public function manageHook() 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, 'url' => $this->context->link->getModuleLink('ps_googleanalytics', 'ajax', array(), true), 'customer' => $order->id_customer); - $gaScripts .= $this->module->addTransaction($orderProducts, $transaction); + $gaScripts .= $gaTools->addTransaction($orderProducts, $transaction); $this->module->js_state = 1; - return $this->module->_runJs($gaScripts); + return $gaTools->generateJs( + $this->module->js_state, + $this->context->currency->iso_code, + $gaScripts + ); } } } diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 2d1e223..4596d50 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -157,7 +157,13 @@ public function hookdisplayFooterProduct($params) */ public function hookdisplayAdminOrder() { - echo $this->_runJs($this->context->cookie->ga_admin_refund, 1); + $gaTools = new PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools(); + echo $gaTools->generateJs( + $this->js_state, + $this->context->currency->iso_code, + $this->context->cookie->ga_admin_refund, + 1 + ); unset($this->context->cookie->ga_admin_refund); } @@ -177,7 +183,6 @@ public function hookactionProductCancel($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionProductCancel($this, $this->context); $hook->setParams($params); - return $hook->manageHook(); } @@ -217,15 +222,6 @@ public function wrapOrder($id_order) } } - protected function filter($gaScripts) - { - if ($this->filterable = 1) { - return implode(';', array_unique(explode(';', $gaScripts))); - } - - return $gaScripts; - } - /** * wrap products to provide a standard products information for google analytics script */ @@ -321,114 +317,6 @@ public function wrapProduct($product, $extras, $index = 0, $full = false) return $ga_product; } - /** - * add order transaction - */ - public function addTransaction($products, $order) - { - if (!is_array($products)) { - return; - } - - $js = ''; - foreach ($products as $product) { - $js .= 'MBG.add('.json_encode($product).');'; - } - - return $js.'MBG.addTransaction('.json_encode($order).');'; - } - - /** - * add product impression js and product click js - */ - public function addProductImpression($products) - { - if (!is_array($products)) { - return; - } - - $js = ''; - foreach ($products as $product) { - $js .= 'MBG.add('.json_encode($product).",'',true);"; - } - - return $js; - } - - public function addProductClick($products) - { - if (!is_array($products)) { - return; - } - - $js = ''; - foreach ($products as $product) { - $js .= 'MBG.addProductClick('.json_encode($product).');'; - } - - return $js; - } - - public function addProductClickByHttpReferal($products) - { - if (!is_array($products)) { - return; - } - - $js = ''; - foreach ($products as $product) { - $js .= 'MBG.addProductClickByHttpReferal('.json_encode($product).');'; - } - - return $js; - } - - /** - * Add product checkout info - */ - public function addProductFromCheckout($products) - { - if (!is_array($products)) { - return; - } - - $js = ''; - foreach ($products as $product) { - $js .= 'MBG.add('.json_encode($product).');'; - } - - return $js; - } - - /** - * Generate Google Analytics js - */ - public function _runJs($js_code, $backoffice = 0) - { - if (Configuration::get('GA_ACCOUNT_ID')) { - $runjs_code = ''; - if (!empty($js_code)) { - $runjs_code .= ' - '; - } - - if (($this->js_state) != 1 && ($backoffice == 0)) { - $runjs_code .= ' - '; - } - - return $runjs_code; - } - } - protected function _debugLog($function, $log) { if (!$this->_debug) { From 67879c0abb2e7e0afbde38686e3250534520c4a5 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 12:32:38 +0200 Subject: [PATCH 17/45] Create Wrapper classes --- classes/Hook/HookActionCartSave.php | 6 +- classes/Hook/HookDisplayBackOfficeHeader.php | 6 +- classes/Hook/HookDisplayFooter.php | 4 +- classes/Hook/HookDisplayFooterProduct.php | 4 +- classes/Hook/HookDisplayHome.php | 6 +- classes/Hook/HookDisplayOrderConfirmation.php | 6 +- classes/Wrapper/OrderWrapper.php | 58 ++++++++ classes/Wrapper/ProductWrapper.php | 134 ++++++++++++++++++ classes/Wrapper/WrapperInterface.php | 34 +++++ classes/Wrapper/index.php | 35 +++++ ps_googleanalytics.php | 115 --------------- 11 files changed, 283 insertions(+), 125 deletions(-) create mode 100644 classes/Wrapper/OrderWrapper.php create mode 100644 classes/Wrapper/ProductWrapper.php create mode 100644 classes/Wrapper/WrapperInterface.php create mode 100644 classes/Wrapper/index.php diff --git a/classes/Hook/HookActionCartSave.php b/classes/Hook/HookActionCartSave.php index 6bd01c4..48e429f 100644 --- a/classes/Hook/HookActionCartSave.php +++ b/classes/Hook/HookActionCartSave.php @@ -27,6 +27,7 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; class HookActionCartSave implements HookInterface @@ -72,7 +73,7 @@ public function manageHook() } if ($cart['removeAction'] == 'delete') { - $addProductObject = new Product((int)\Tools::getValue('id_product'), true, (int)\Configuration::get('PS_LANG_DEFAULT')); + $addProductObject = new \Product((int)\Tools::getValue('id_product'), true, (int)\Configuration::get('PS_LANG_DEFAULT')); if (\Validate::isLoadedObject($addProductObject)) { $addProduct['name'] = $addProductObject->name; $addProduct['manufacturer_name'] = $addProductObject->manufacturer_name; @@ -98,7 +99,8 @@ public function manageHook() ); $this->module->products[] = (int)\Tools::getValue('id_product'); - $gaProducts = $this->module->wrapProduct($addProduct, $cart, 0, true); + $productWrapper = new ProductWrapper($this->context); + $gaProducts = $productWrapper->wrapProduct($addProduct, $cart, 0, true); if (array_key_exists('id_product_attribute', $gaProducts) && $gaProducts['id_product_attribute'] != '' && $gaProducts['id_product_attribute'] != 0) { $productId = $gaProducts['id_product_attribute']; diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php index f1561bf..750d2e3 100644 --- a/classes/Hook/HookDisplayBackOfficeHeader.php +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -28,6 +28,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\OrderWrapper; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; class HookDisplayBackOfficeHeader implements HookInterface @@ -65,7 +66,7 @@ public function manageHook() $ganalyticsRepository = new GanalyticsRepository(); if (\Tools::getValue('id_order')) { - $order = new Order((int)\Tools::getValue('id_order')); + $order = new \Order((int)\Tools::getValue('id_order')); if (\Validate::isLoadedObject($order) && strtotime('+1 day', strtotime($order->date_add)) > time()) { $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) \Tools::getValue('id_order')); if ($gaOrderSent === false) { @@ -83,8 +84,9 @@ public function manageHook() $gaOrderRecords = $ganalyticsRepository->findAllByShopIdAndDateAdd((int) $this->context->shop->id); if ($gaOrderRecords) { + $orderWrapper = new OrderWrapper($this->context); foreach ($gaOrderRecords as $row) { - $transaction = $this->module->wrapOrder($row['id_order']); + $transaction = $orderWrapper->wrapOrder($row['id_order']); if (!empty($transaction)) { $ganalyticsRepository->updateData( array( diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php index 52faeab..3fa96ba 100644 --- a/classes/Hook/HookDisplayFooter.php +++ b/classes/Hook/HookDisplayFooter.php @@ -28,6 +28,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; class HookDisplayFooter implements HookInterface @@ -79,7 +80,8 @@ public function manageHook() } $listing = $this->context->smarty->getTemplateVars('listing'); - $products = $this->module->wrapProducts($listing['products'], array(), true); + $productWrapper = new ProductWrapper($this->context); + $products = $productWrapper->wrapProductList($listing['products'], array(), true); if ($controller_name == 'order' || $controller_name == 'orderopc') { $this->module->js_state = 1; diff --git a/classes/Hook/HookDisplayFooterProduct.php b/classes/Hook/HookDisplayFooterProduct.php index 8d5d177..b713d36 100644 --- a/classes/Hook/HookDisplayFooterProduct.php +++ b/classes/Hook/HookDisplayFooterProduct.php @@ -28,6 +28,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; class HookDisplayFooterProduct implements HookInterface { @@ -58,7 +59,8 @@ public function manageHook() $this->params['product'] = (array) $this->params['product']; } // Add product view - $gaProduct = $this->module->wrapProduct($this->params['product'], null, 0, true); + $productWrapper = new ProductWrapper($this->context); + $gaProduct = $productWrapper->wrapProduct($this->params['product'], null, 0, true); $js = 'MBG.addProductDetailView('.json_encode($gaProduct).');'; if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) > 0) { diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php index 09baa8e..0645d32 100644 --- a/classes/Hook/HookDisplayHome.php +++ b/classes/Hook/HookDisplayHome.php @@ -29,6 +29,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Handler\ModuleHandler; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; class HookDisplayHome implements HookInterface { @@ -53,8 +54,9 @@ public function manageHook() // Home featured products if ($moduleHandler->isModuleEnabled('ps_featuredproducts')) { - $category = new Category($this->context->shop->getCategory(), $this->context->language->id); - $homeFeaturedProducts = $this->module->wrapProducts( + $category = new \Category($this->context->shop->getCategory(), $this->context->language->id); + $productWrapper = new ProductWrapper($this->context); + $homeFeaturedProducts = $productWrapper->wrapProductList( $category->getProducts( (int) $this->context->language->id, 1, diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php index ae61e20..0b19f07 100644 --- a/classes/Hook/HookDisplayOrderConfirmation.php +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -28,6 +28,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; class HookDisplayOrderConfirmation implements HookInterface @@ -70,11 +71,12 @@ public function manageHook() if ($order->id_customer == $this->context->cookie->id_customer) { $orderProducts = array(); - $cart = new Cart($order->id_cart); + $cart = new \Cart($order->id_cart); $gaTools = new GoogleAnalyticsTools(); + $productWrapper = new ProductWrapper($this->context); foreach ($cart->getProducts() as $order_product) { - $orderProducts[] = $this->module->wrapProduct($order_product, array(), 0, true); + $orderProducts[] = $productWrapper->wrapProduct($order_product, array(), 0, true); } $gaScripts = 'MBG.addCheckoutOption(3,\''.$order->payment.'\');'; diff --git a/classes/Wrapper/OrderWrapper.php b/classes/Wrapper/OrderWrapper.php new file mode 100644 index 0000000..5107e21 --- /dev/null +++ b/classes/Wrapper/OrderWrapper.php @@ -0,0 +1,58 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Wrapper; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\WrapperInterface; + +class OrderWrapper implements WrapperInterface +{ + private $context; + + public function __construct($context) + { + $this->context = $context; + } + + /** + * Return a detailed transaction for Google Analytics + */ + public function wrapOrder($id_order) + { + $order = new \Order((int)$id_order); + + if (\Validate::isLoadedObject($order)) { + return array( + 'id' => $id_order, + 'affiliation' => \Shop::isFeatureActive() ? $this->context->shop->name : \Configuration::get('PS_SHOP_NAME'), + 'revenue' => $order->total_paid, + 'shipping' => $order->total_shipping, + 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, + 'url' => $this->context->link->getAdminLink('AdminGanalyticsAjax'), + 'customer' => $order->id_customer); + } + } +} \ No newline at end of file diff --git a/classes/Wrapper/ProductWrapper.php b/classes/Wrapper/ProductWrapper.php new file mode 100644 index 0000000..1cc9f71 --- /dev/null +++ b/classes/Wrapper/ProductWrapper.php @@ -0,0 +1,134 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Wrapper; + +use PrestaShop\Module\Ps_Googleanalytics\Hooks\WrapperInterface; + +class ProductWrapper implements WrapperInterface +{ + private $context; + + public function __construct($context) + { + $this->context = $context; + } + + /** + * wrap products to provide a standard products information for google analytics script + */ + public function wrapProductList($products, $extras = array(), $full = false) + { + $result_products = array(); + if (!is_array($products)) { + return; + } + + $currency = new \Currency($this->context->currency->id); + $usetax = (\Product::getTaxCalculationMethod((int)$this->context->customer->id) != PS_TAX_EXC); + + if (count($products) > 20) { + $full = false; + } else { + $full = true; + } + + foreach ($products as $index => $product) { + if ($product instanceof Product) { + $product = (array)$product; + } + + if (!isset($product['price'])) { + $product['price'] = (float)\Tools::displayPrice(\Product::getPriceStatic((int)$product['id_product'], $usetax), $currency); + } + $result_products[] = $this->wrapProduct($product, $extras, $index, $full); + } + + return $result_products; + } + + /** + * wrap product to provide a standard product information for google analytics script + */ + public function wrapProduct($product, $extras, $index = 0, $full = false) + { + $ga_product = ''; + + $variant = null; + if (isset($product['attributes_small'])) { + $variant = $product['attributes_small']; + } elseif (isset($extras['attributes_small'])) { + $variant = $extras['attributes_small']; + } + + $product_qty = 1; + if (isset($extras['qty'])) { + $product_qty = $extras['qty']; + } elseif (isset($product['cart_quantity'])) { + $product_qty = $product['cart_quantity']; + } + + $product_id = 0; + if (!empty($product['id_product'])) { + $product_id = $product['id_product']; + } elseif (!empty($product['id'])) { + $product_id = $product['id']; + } + + if (!empty($product['id_product_attribute'])) { + $product_id .= '-'. $product['id_product_attribute']; + } + + $product_type = 'typical'; + if (isset($product['pack']) && $product['pack'] == 1) { + $product_type = 'pack'; + } elseif (isset($product['virtual']) && $product['virtual'] == 1) { + $product_type = 'virtual'; + } + + if ($full) { + $ga_product = array( + 'id' => $product_id, + 'name' => \Tools::str2url($product['name']), + 'category' => \Tools::str2url($product['category']), + 'brand' => isset($product['manufacturer_name']) ? \Tools::str2url($product['manufacturer_name']) : '', + 'variant' => \Tools::str2url($variant), + 'type' => $product_type, + 'position' => $index ? $index : '0', + 'quantity' => $product_qty, + 'list' => \Tools::getValue('controller'), + 'url' => isset($product['link']) ? urlencode($product['link']) : '', + 'price' => $product['price'] + ); + } else { + $ga_product = array( + 'id' => $product_id, + 'name' => \Tools::str2url($product['name']) + ); + } + return $ga_product; + } +} \ No newline at end of file diff --git a/classes/Wrapper/WrapperInterface.php b/classes/Wrapper/WrapperInterface.php new file mode 100644 index 0000000..7ded9a9 --- /dev/null +++ b/classes/Wrapper/WrapperInterface.php @@ -0,0 +1,34 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; + +interface WrapperInterface { + /** + * @param \Context $context + */ + public function __construct(\Context $context); +} diff --git a/classes/Wrapper/index.php b/classes/Wrapper/index.php new file mode 100644 index 0000000..94088c7 --- /dev/null +++ b/classes/Wrapper/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2020 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 4596d50..e6ae3a9 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -119,7 +119,6 @@ public function hookdisplayOrderConfirmation($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayOrderConfirmation($this, $this->context); $hook->setParams($params); - return $hook->manageHook(); } @@ -203,120 +202,6 @@ public function hookactionCarrierProcess($params) return $hook->manageHook(); } - /** - * Return a detailed transaction for Google Analytics - */ - public function wrapOrder($id_order) - { - $order = new Order((int)$id_order); - - if (Validate::isLoadedObject($order)) { - return array( - 'id' => $id_order, - 'affiliation' => Shop::isFeatureActive() ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), - 'revenue' => $order->total_paid, - 'shipping' => $order->total_shipping, - 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, - 'url' => $this->context->link->getAdminLink('AdminGanalyticsAjax'), - 'customer' => $order->id_customer); - } - } - - /** - * wrap products to provide a standard products information for google analytics script - */ - public function wrapProducts($products, $extras = array(), $full = false) - { - $result_products = array(); - if (!is_array($products)) { - return; - } - - $currency = new Currency($this->context->currency->id); - $usetax = (Product::getTaxCalculationMethod((int)$this->context->customer->id) != PS_TAX_EXC); - - if (count($products) > 20) { - $full = false; - } else { - $full = true; - } - - foreach ($products as $index => $product) { - if ($product instanceof Product) { - $product = (array)$product; - } - - if (!isset($product['price'])) { - $product['price'] = (float)Tools::displayPrice(Product::getPriceStatic((int)$product['id_product'], $usetax), $currency); - } - $result_products[] = $this->wrapProduct($product, $extras, $index, $full); - } - - return $result_products; - } - - /** - * wrap product to provide a standard product information for google analytics script - */ - public function wrapProduct($product, $extras, $index = 0, $full = false) - { - $ga_product = ''; - - $variant = null; - if (isset($product['attributes_small'])) { - $variant = $product['attributes_small']; - } elseif (isset($extras['attributes_small'])) { - $variant = $extras['attributes_small']; - } - - $product_qty = 1; - if (isset($extras['qty'])) { - $product_qty = $extras['qty']; - } elseif (isset($product['cart_quantity'])) { - $product_qty = $product['cart_quantity']; - } - - $product_id = 0; - if (!empty($product['id_product'])) { - $product_id = $product['id_product']; - } elseif (!empty($product['id'])) { - $product_id = $product['id']; - } - - if (!empty($product['id_product_attribute'])) { - $product_id .= '-'. $product['id_product_attribute']; - } - - $product_type = 'typical'; - if (isset($product['pack']) && $product['pack'] == 1) { - $product_type = 'pack'; - } elseif (isset($product['virtual']) && $product['virtual'] == 1) { - $product_type = 'virtual'; - } - - if ($full) { - $ga_product = array( - 'id' => $product_id, - 'name' => Tools::str2url($product['name']), - 'category' => Tools::str2url($product['category']), - 'brand' => isset($product['manufacturer_name']) ? Tools::str2url($product['manufacturer_name']) : '', - 'variant' => Tools::str2url($variant), - 'type' => $product_type, - 'position' => $index ? $index : '0', - 'quantity' => $product_qty, - 'list' => Tools::getValue('controller'), - 'url' => isset($product['link']) ? urlencode($product['link']) : '', - 'price' => $product['price'] - ); - } else { - $ga_product = array( - 'id' => $product_id, - 'name' => Tools::str2url($product['name']) - ); - } - return $ga_product; - } - protected function _debugLog($function, $log) { if (!$this->_debug) { From 63a11a723dda9298ef49834198bbb77cb513fe55 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 12:44:36 +0200 Subject: [PATCH 18/45] Rename manageHook method into run --- classes/Hook/HookActionCarrierProcess.php | 4 ++-- classes/Hook/HookActionCartSave.php | 4 ++-- classes/Hook/HookActionProductCancel.php | 4 ++-- classes/Hook/HookDisplayBackOfficeHeader.php | 4 ++-- classes/Hook/HookDisplayFooter.php | 4 ++-- classes/Hook/HookDisplayFooterProduct.php | 4 ++-- classes/Hook/HookDisplayHeader.php | 4 ++-- classes/Hook/HookDisplayHome.php | 4 ++-- classes/Hook/HookDisplayOrderConfirmation.php | 4 ++-- classes/Hook/HookInterface.php | 2 +- ps_googleanalytics.php | 19 +++++++++---------- 11 files changed, 28 insertions(+), 29 deletions(-) diff --git a/classes/Hook/HookActionCarrierProcess.php b/classes/Hook/HookActionCarrierProcess.php index 5c66e41..f7fc081 100644 --- a/classes/Hook/HookActionCarrierProcess.php +++ b/classes/Hook/HookActionCarrierProcess.php @@ -42,11 +42,11 @@ public function __construct($module, $context) { } /** - * manageHook + * run * * @return void */ - public function manageHook() + public function run() { if (isset($this->params['cart']->id_carrier)) { $carrierRepository = new CarrierRepository(); diff --git a/classes/Hook/HookActionCartSave.php b/classes/Hook/HookActionCartSave.php index 48e429f..1903ea1 100644 --- a/classes/Hook/HookActionCartSave.php +++ b/classes/Hook/HookActionCartSave.php @@ -41,11 +41,11 @@ public function __construct($module, $context) { } /** - * manageHook + * run * * @return void */ - public function manageHook() + public function run() { if (!isset($this->context->cart)) { return; diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php index 4be6eb0..48dc54d 100644 --- a/classes/Hook/HookActionProductCancel.php +++ b/classes/Hook/HookActionProductCancel.php @@ -40,11 +40,11 @@ public function __construct($module, $context) { } /** - * manageHook + * run * * @return string */ - public function manageHook() + public function run() { $quantityRefunded = \Tools::getValue('cancelQuantity'); $gaScripts = ''; diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php index 750d2e3..02d748a 100644 --- a/classes/Hook/HookDisplayBackOfficeHeader.php +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -42,11 +42,11 @@ public function __construct($module, $context) { } /** - * manageHook + * run * * @return string */ - public function manageHook() + public function run() { $js = ''; if (strcmp(\Tools::getValue('configure'), $this->module->name) === 0) { diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php index 3fa96ba..0208a0c 100644 --- a/classes/Hook/HookDisplayFooter.php +++ b/classes/Hook/HookDisplayFooter.php @@ -42,11 +42,11 @@ public function __construct($module, $context) { } /** - * manageHook + * run * * @return string */ - public function manageHook() + public function run() { $gaTools = new GoogleAnalyticsTools(); $ganalyticsDataHandler = new GanalyticsDataHandler( diff --git a/classes/Hook/HookDisplayFooterProduct.php b/classes/Hook/HookDisplayFooterProduct.php index b713d36..ac81701 100644 --- a/classes/Hook/HookDisplayFooterProduct.php +++ b/classes/Hook/HookDisplayFooterProduct.php @@ -42,11 +42,11 @@ public function __construct($module, $context) { } /** - * manageHook + * run * * @return string */ - public function manageHook() + public function run() { $gaTools = new GoogleAnalyticsTools(); $controllerName = \Tools::getValue('controller'); diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index 0f0e4c1..21e2f35 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -41,11 +41,11 @@ public function __construct($module, $context) { } /** - * manageHook + * run * * @return string */ - public function manageHook() + public function run() { if (\Configuration::get('GA_ACCOUNT_ID')) { $this->context->controller->addJs($this->module->getPathUri().'views/js/GoogleAnalyticActionLib.js'); diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php index 0645d32..cb892be 100644 --- a/classes/Hook/HookDisplayHome.php +++ b/classes/Hook/HookDisplayHome.php @@ -42,11 +42,11 @@ public function __construct($module, $context) { } /** - * manageHook + * run * * @return string */ - public function manageHook() + public function run() { $moduleHandler = new ModuleHandler(); $gaTools = new GoogleAnalyticsTools(); diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php index 0b19f07..bc5f2c4 100644 --- a/classes/Hook/HookDisplayOrderConfirmation.php +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -43,11 +43,11 @@ public function __construct($module, $context) { } /** - * manageHook + * run * * @return string */ - public function manageHook() + public function run() { if (true === $this->module->psVersionIs17) { $order = $this->params['order']; diff --git a/classes/Hook/HookInterface.php b/classes/Hook/HookInterface.php index 8e03c50..63b6e26 100644 --- a/classes/Hook/HookInterface.php +++ b/classes/Hook/HookInterface.php @@ -33,5 +33,5 @@ interface HookInterface { */ public function __construct(\Ps_Googleanalytics $module, \Context $context); - public function manageHook(); + public function run(); } diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index e6ae3a9..449c894 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -109,7 +109,7 @@ public function hookdisplayHeader($params, $back_office = false) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayHeader($this, $this->context); $hook->setBackOffice($back_office); - return $hook->manageHook(); + return $hook->run(); } /** @@ -119,7 +119,7 @@ public function hookdisplayOrderConfirmation($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayOrderConfirmation($this, $this->context); $hook->setParams($params); - return $hook->manageHook(); + return $hook->run(); } /** @@ -128,7 +128,7 @@ public function hookdisplayOrderConfirmation($params) public function hookdisplayFooter() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayFooter($this, $this->context); - return $hook->manageHook(); + return $hook->run(); } /** @@ -137,7 +137,7 @@ public function hookdisplayFooter() public function hookdisplayHome() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayHome($this, $this->context); - return $hook->manageHook(); + return $hook->run(); } /** @@ -147,8 +147,7 @@ public function hookdisplayFooterProduct($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayFooterProduct($this, $this->context); $hook->setParams($params); - - return $hook->manageHook(); + return $hook->run(); } /** @@ -172,7 +171,7 @@ public function hookdisplayAdminOrder() public function hookdisplayBackOfficeHeader() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayBackOfficeHeader($this, $this->context); - return $hook->manageHook(); + return $hook->run(); } /** @@ -182,7 +181,7 @@ public function hookactionProductCancel($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionProductCancel($this, $this->context); $hook->setParams($params); - return $hook->manageHook(); + return $hook->run(); } /** @@ -191,7 +190,7 @@ public function hookactionProductCancel($params) public function hookactionCartSave() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCartSave($this, $this->context); - return $hook->manageHook(); + return $hook->run(); } public function hookactionCarrierProcess($params) @@ -199,7 +198,7 @@ public function hookactionCarrierProcess($params) $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCarrierProcess($this, $this->context); $hook->setParams($params); - return $hook->manageHook(); + return $hook->run(); } protected function _debugLog($function, $log) From b37d2bb591b73812e8e1ee8e108b41fe36941a89 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 12:46:13 +0200 Subject: [PATCH 19/45] Rename manageHook method into run --- ps_googleanalytics.php | 1 - 1 file changed, 1 deletion(-) diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 449c894..4c74d6c 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -197,7 +197,6 @@ public function hookactionCarrierProcess($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCarrierProcess($this, $this->context); $hook->setParams($params); - return $hook->run(); } From e8fb706d100c4c1457a6a102d85973bc2fab9249 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 14:49:40 +0200 Subject: [PATCH 20/45] Treat the form data in configurationForm class --- classes/Form/ConfigurationForm.php | 43 +++++++++++++++++++++++++++--- classes/Wrapper/OrderWrapper.php | 2 +- classes/Wrapper/ProductWrapper.php | 2 +- ps_googleanalytics.php | 38 +++++--------------------- 4 files changed, 48 insertions(+), 37 deletions(-) diff --git a/classes/Form/ConfigurationForm.php b/classes/Form/ConfigurationForm.php index bacac03..c24acb4 100644 --- a/classes/Form/ConfigurationForm.php +++ b/classes/Form/ConfigurationForm.php @@ -35,11 +35,11 @@ public function __construct(\Ps_Googleanalytics $module) { } /** - * generateForm + * generate * * @return string */ - public function generateForm() + public function generate() { // Check if multistore is active $is_multistore_active = \Shop::isFeatureActive(); @@ -158,6 +158,43 @@ public function generateForm() $helper->fields_value['GA_CROSSDOMAIN_ENABLED'] = \Configuration::get('GA_CROSSDOMAIN_ENABLED'); $helper->fields_value['GA_ANONYMIZE_ENABLED'] = \Configuration::get('GA_ANONYMIZE_ENABLED'); - return $helper->generateForm($fields_form); + return $helper->generate($fields_form); + } + + /** + * treat the form datas if submited + * + * @return string + */ + public function treat() + { + $treatmentResult = ''; + $gaAccountId = \Tools::getValue('GA_ACCOUNT_ID'); + $gaUserIdEnabled = \Tools::getValue('GA_USERID_ENABLED'); + $gaCrossdomainEnabled = \Tools::getValue('GA_CROSSDOMAIN_ENABLED'); + $gaAnonymizeEnabled = \Tools::getValue('GA_ANONYMIZE_ENABLED'); + + if (!empty($gaAccountId)) { + \Configuration::updateValue('GA_ACCOUNT_ID', $gaAccountId); + \Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true); + $treatmentResult .= $this->module->displayConfirmation($this->module->l('Account ID updated successfully')); + } + + if (null !== $gaUserIdEnabled) { + \Configuration::updateValue('GA_USERID_ENABLED', (bool) $gaUserIdEnabled); + $treatmentResult .= $this->module->displayConfirmation($this->module->l('Settings for User ID updated successfully')); + } + + if (null !== $gaCrossdomainEnabled) { + \Configuration::updateValue('GA_CROSSDOMAIN_ENABLED', (bool) $gaCrossdomainEnabled); + $treatmentResult .= $this->module->displayConfirmation($this->module->l('Settings for User ID updated successfully')); + } + + if (null !== $gaAnonymizeEnabled) { + \Configuration::updateValue('GA_ANONYMIZE_ENABLED', (bool) $gaAnonymizeEnabled); + $treatmentResult .= $this->module->displayConfirmation($this->module->l('Settings for Anonymize IP updated successfully')); + } + + return $treatmentResult; } } diff --git a/classes/Wrapper/OrderWrapper.php b/classes/Wrapper/OrderWrapper.php index 5107e21..b6c4435 100644 --- a/classes/Wrapper/OrderWrapper.php +++ b/classes/Wrapper/OrderWrapper.php @@ -55,4 +55,4 @@ public function wrapOrder($id_order) 'customer' => $order->id_customer); } } -} \ No newline at end of file +} diff --git a/classes/Wrapper/ProductWrapper.php b/classes/Wrapper/ProductWrapper.php index 1cc9f71..6626363 100644 --- a/classes/Wrapper/ProductWrapper.php +++ b/classes/Wrapper/ProductWrapper.php @@ -131,4 +131,4 @@ public function wrapProduct($product, $extras, $index = 0, $full = false) } return $ga_product; } -} \ No newline at end of file +} diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 4c74d6c..e7b15db 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -62,47 +62,21 @@ public function __construct() $this->psVersionIs17 = (bool) version_compare(_PS_VERSION_, '1.7', '>='); } - public function displayForm() - { - $configurationForm = new PrestaShop\Module\Ps_Googleanalytics\Form\ConfigurationForm($this); - return $configurationForm->generateForm(); - } - /** * back office module configuration page content */ public function getContent() { - $output = ''; + $configurationForm = new PrestaShop\Module\Ps_Googleanalytics\Form\ConfigurationForm($this); + $formOutput = ''; + if (Tools::isSubmit('submit'.$this->name)) { - $ga_account_id = Tools::getValue('GA_ACCOUNT_ID'); - if (!empty($ga_account_id)) { - Configuration::updateValue('GA_ACCOUNT_ID', $ga_account_id); - Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true); - $output .= $this->displayConfirmation($this->l('Account ID updated successfully')); - } - $ga_userid_enabled = Tools::getValue('GA_USERID_ENABLED'); - if (null !== $ga_userid_enabled) { - Configuration::updateValue('GA_USERID_ENABLED', (bool)$ga_userid_enabled); - $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully')); - } - - $ga_crossdomain_enabled = Tools::getValue('GA_CROSSDOMAIN_ENABLED'); - if (null !== $ga_crossdomain_enabled) { - Configuration::updateValue('GA_CROSSDOMAIN_ENABLED', (bool)$ga_crossdomain_enabled); - $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully')); - } - - $ga_anonymize_enabled = Tools::getValue('GA_ANONYMIZE_ENABLED'); - if (null !== $ga_anonymize_enabled) { - Configuration::updateValue('GA_ANONYMIZE_ENABLED', (bool)$ga_anonymize_enabled); - $output .= $this->displayConfirmation($this->l('Settings for Anonymize IP updated successfully')); - } + $formOutput = $configurationForm->treat(); } - $output .= $this->displayForm(); + $formOutput .= $configurationForm->generate(); - return $this->display(__FILE__, './views/templates/admin/configuration.tpl').$output; + return $this->display(__FILE__, './views/templates/admin/configuration.tpl') . $formOutput; } public function hookdisplayHeader($params, $back_office = false) From 52fcb7a6341a209e816a926a2d126588fa5a19ca Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 15:15:48 +0200 Subject: [PATCH 21/45] Fix generatedForm helper method calling --- classes/Form/ConfigurationForm.php | 2 +- classes/Hook/HookDisplayHeader.php | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/classes/Form/ConfigurationForm.php b/classes/Form/ConfigurationForm.php index c24acb4..1518a2d 100644 --- a/classes/Form/ConfigurationForm.php +++ b/classes/Form/ConfigurationForm.php @@ -158,7 +158,7 @@ public function generate() $helper->fields_value['GA_CROSSDOMAIN_ENABLED'] = \Configuration::get('GA_CROSSDOMAIN_ENABLED'); $helper->fields_value['GA_ANONYMIZE_ENABLED'] = \Configuration::get('GA_ANONYMIZE_ENABLED'); - return $helper->generate($fields_form); + return $helper->generateForm($fields_form); } /** diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index 21e2f35..63d945e 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -81,7 +81,10 @@ public function run() ) ); - return $this->module->display(__FILE__, 'ps_googleanalytics.tpl'); + return $this->module->display( + $this->module->getLocalPath().$this->module->name, + 'ps_googleanalytics.tpl' + ); } } From 5cdeace46650c4f5fbecbd8a6c4dd22162fc9dca Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 15:19:27 +0200 Subject: [PATCH 22/45] Update license header --- views/templates/admin/configuration.tpl | 4 ++-- views/templates/hook/ps_googleanalytics.tpl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/views/templates/admin/configuration.tpl b/views/templates/admin/configuration.tpl index b428243..227c147 100755 --- a/views/templates/admin/configuration.tpl +++ b/views/templates/admin/configuration.tpl @@ -1,5 +1,5 @@ {* - * 2007-2017 PrestaShop + * 2007-2020 PrestaShop * * NOTICE OF LICENSE * @@ -18,7 +18,7 @@ * needs please refer to http://www.prestashop.com for more information. * * @author PrestaShop SA - * @copyright 2007-2017 PrestaShop SA + * @copyright 2007-2020 PrestaShop SA * @version Release: $Revision:7040 $ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA diff --git a/views/templates/hook/ps_googleanalytics.tpl b/views/templates/hook/ps_googleanalytics.tpl index f4b8821..fe7c007 100755 --- a/views/templates/hook/ps_googleanalytics.tpl +++ b/views/templates/hook/ps_googleanalytics.tpl @@ -1,5 +1,5 @@ {* - * 2007-2017 PrestaShop + * 2007-2020 PrestaShop * * NOTICE OF LICENSE * @@ -18,7 +18,7 @@ * needs please refer to http://www.prestashop.com for more information. * * @author PrestaShop SA - * @copyright 2007-2017 PrestaShop SA + * @copyright 2007-2020 PrestaShop SA * @version Release: $Revision:7040 $ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA From 3ec09fea2a1afa02c55402e5426eef4796c78d59 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 15:51:37 +0200 Subject: [PATCH 23/45] GA Script generated with smarty --- classes/Form/ConfigurationForm.php | 6 +- classes/GoogleAnalyticsTools.php | 34 ---------- classes/Handler/GanalyticsJsHandler.php | 66 +++++++++++++++++++ classes/Hook/HookActionCarrierProcess.php | 2 +- classes/Hook/HookActionProductCancel.php | 2 +- classes/Hook/HookDisplayBackOfficeHeader.php | 15 ++--- classes/Hook/HookDisplayFooter.php | 8 +-- classes/Hook/HookDisplayFooterProduct.php | 8 +-- classes/Hook/HookDisplayHeader.php | 2 +- classes/Hook/HookDisplayHome.php | 8 +-- classes/Hook/HookDisplayOrderConfirmation.php | 11 ++-- ps_googleanalytics.php | 13 ++-- views/templates/hook/ga_tag.tpl | 45 +++++++++++++ 13 files changed, 142 insertions(+), 78 deletions(-) create mode 100644 classes/Handler/GanalyticsJsHandler.php create mode 100644 views/templates/hook/ga_tag.tpl diff --git a/classes/Form/ConfigurationForm.php b/classes/Form/ConfigurationForm.php index 1518a2d..a5b0578 100644 --- a/classes/Form/ConfigurationForm.php +++ b/classes/Form/ConfigurationForm.php @@ -53,7 +53,7 @@ public function generate() $helper->module = $this->module; $helper->name_controller = $this->module->name; $helper->token = \Tools::getAdminTokenLite('AdminModules'); - $helper->currentIndex = \AdminController::$currentIndex.'&configure='.$this->module->name; + $helper->currentIndex = \AdminController::$currentIndex.'&configure=' . $this->module->name; // Language $helper->default_form_language = $default_lang; @@ -63,12 +63,12 @@ public function generate() $helper->title = $this->module->displayName; $helper->show_toolbar = true; // false -> remove toolbar $helper->toolbar_scroll = true; // yes - > Toolbar is always visible on the top of the screen. - $helper->submit_action = 'submit'.$this->module->name; + $helper->submit_action = 'submit' . $this->module->name; $helper->toolbar_btn = array( 'save' => array( 'desc' => $this->module->l('Save'), - 'href' => \AdminController::$currentIndex.'&configure='.$this->module->name.'&save'.$this->module->name. + 'href' => \AdminController::$currentIndex.'&configure=' . $this->module->name.'&save' . $this->module->name. '&token='.\Tools::getAdminTokenLite('AdminModules'), ), 'back' => array( diff --git a/classes/GoogleAnalyticsTools.php b/classes/GoogleAnalyticsTools.php index b2eda46..fc185f4 100644 --- a/classes/GoogleAnalyticsTools.php +++ b/classes/GoogleAnalyticsTools.php @@ -150,38 +150,4 @@ public function addProductFromCheckout($products) return $js; } - - /** - * Generate Google Analytics js - * - * @param string $jsCode - * @param int $backoffice - * - * @return string - */ - public function generateJs($jsState, $isoCode, $jsCode, $backoffice = 0) - { - if (\Configuration::get('GA_ACCOUNT_ID')) { - $generateJsCode = ''; - if (!empty($jsCode)) { - $generateJsCode .= ' - '; - } - - if (($jsState) != 1 && ($backoffice == 0)) { - $generateJsCode .= ' - '; - } - - return $generateJsCode; - } - } } diff --git a/classes/Handler/GanalyticsJsHandler.php b/classes/Handler/GanalyticsJsHandler.php new file mode 100644 index 0000000..1a291aa --- /dev/null +++ b/classes/Handler/GanalyticsJsHandler.php @@ -0,0 +1,66 @@ + + * @copyright 2007-2020 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\Module\Ps_Googleanalytics\Handler; + +class GanalyticsJsHandler +{ + private $module; + private $context; + + public function __construct(\Ps_googleanalytics $module, \Context $context) { + $this->module = $module; + $this->context = $context; + } + + /** + * Generate Google Analytics js + * + * @param string $jsCode + * @param int $isBackoffice + * + * @return string + */ + public function generate($jsCode, $isBackoffice = 0) + { + if (\Configuration::get('GA_ACCOUNT_ID')) { + $this->context->smarty->assign( + array( + 'jsCode' => $jsCode, + 'isoCode' => \Tools::safeOutput($this->context->currency->iso_code), + 'jsState' => $this->module->js_state, + 'isBackoffice' => $isBackoffice, + ) + ); + + return $this->module->display( + $this->module->getLocalPath() . $this->module->name, + 'ga_tag.tpl' + ); + } + } + +} \ No newline at end of file diff --git a/classes/Hook/HookActionCarrierProcess.php b/classes/Hook/HookActionCarrierProcess.php index f7fc081..7e26cd3 100644 --- a/classes/Hook/HookActionCarrierProcess.php +++ b/classes/Hook/HookActionCarrierProcess.php @@ -56,7 +56,7 @@ public function run() ); $carrierName = $carrierRepository->findByCarrierId((int) $this->params['cart']->id_carrier); - $ganalyticsDataHandler->manageData('MBG.addCheckoutOption(2,\''.$carrierName.'\');', 'A'); + $ganalyticsDataHandler->manageData('MBG.addCheckoutOption(2,\'' . $carrierName.'\');', 'A'); } } diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php index 48dc54d..36cb1fc 100644 --- a/classes/Hook/HookActionProductCancel.php +++ b/classes/Hook/HookActionProductCancel.php @@ -54,7 +54,7 @@ public function run() $orderDetail = new \OrderDetail($orderDetailId); $gaScripts .= 'MBG.add('.json_encode( array( - 'id' => empty($orderDetail->product_attribute_id)?$orderDetail->product_id:$orderDetail->product_id.'-'.$orderDetail->product_attribute_id, + 'id' => empty($orderDetail->product_attribute_id)?$orderDetail->product_id:$orderDetail->product_id.'-' . $orderDetail->product_attribute_id, 'quantity' => $quantity) ) .');'; diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php index 02d748a..ca5d2c7 100644 --- a/classes/Hook/HookDisplayBackOfficeHeader.php +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -27,8 +27,8 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; -use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\OrderWrapper; +use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; class HookDisplayBackOfficeHeader implements HookInterface @@ -56,7 +56,7 @@ public function run() $ga_account_id = \Configuration::get('GA_ACCOUNT_ID'); if (!empty($ga_account_id) && $this->module->active) { - $gaTools = new GoogleAnalyticsTools(); + $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); $this->context->controller->addJs($this->module->getPathUri().'views/js/GoogleAnalyticActionLib.js'); $this->context->smarty->assign('GA_ACCOUNT_ID', $ga_account_id); @@ -96,21 +96,14 @@ public function run() 'id_order = ' . (int) $row['id_order'] . ' AND id_shop = ' . (int) $this->context->shop->id ); $transaction = json_encode($transaction); - $gaScripts .= 'MBG.addTransaction('.$transaction.');'; + $gaScripts .= 'MBG.addTransaction(' . $transaction.');'; } } } } } - $generatedJs = $gaTools->generateJs( - $this->module->js_state, - $this->context->currency->iso_code, - $gaScripts, - 1 - ); - - return $js.$this->module->hookdisplayHeader(null, true).$generatedJs; + return $js . $this->module->hookdisplayHeader(null, true) . $gaTagHandler->generate($gaScripts, 1); } return $js; diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php index 0208a0c..438f530 100644 --- a/classes/Hook/HookDisplayFooter.php +++ b/classes/Hook/HookDisplayFooter.php @@ -29,6 +29,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; +use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; class HookDisplayFooter implements HookInterface @@ -49,6 +50,7 @@ public function __construct($module, $context) { public function run() { $gaTools = new GoogleAnalyticsTools(); + $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); $ganalyticsDataHandler = new GanalyticsDataHandler( $this->context->cart->id, $this->context->shop->id @@ -106,10 +108,6 @@ public function run() $gaScripts .= $gaTools->addProductClick($products); } - return $gaTools->generateJs( - $this->module->js_state, - $this->context->currency->iso_code, - $gaScripts - ); + return $gaTagHandler->generate($gaScripts); } } diff --git a/classes/Hook/HookDisplayFooterProduct.php b/classes/Hook/HookDisplayFooterProduct.php index ac81701..930fc01 100644 --- a/classes/Hook/HookDisplayFooterProduct.php +++ b/classes/Hook/HookDisplayFooterProduct.php @@ -29,6 +29,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; +use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; class HookDisplayFooterProduct implements HookInterface { @@ -49,6 +50,7 @@ public function __construct($module, $context) { public function run() { $gaTools = new GoogleAnalyticsTools(); + $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); $controllerName = \Tools::getValue('controller'); if ('product' !== $controllerName) { @@ -69,11 +71,7 @@ public function run() $this->module->js_state = 1; - return $gaTools->generateJs( - $this->module->js_state, - $this->context->currency->iso_code, - $js - ); + return $gaTagHandler->generate($js); } /** diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index 63d945e..ec8a9b1 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -82,7 +82,7 @@ public function run() ); return $this->module->display( - $this->module->getLocalPath().$this->module->name, + $this->module->getLocalPath() . $this->module->name, 'ps_googleanalytics.tpl' ); } diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php index cb892be..1f19896 100644 --- a/classes/Hook/HookDisplayHome.php +++ b/classes/Hook/HookDisplayHome.php @@ -28,6 +28,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; +use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Handler\ModuleHandler; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; @@ -50,6 +51,7 @@ public function run() { $moduleHandler = new ModuleHandler(); $gaTools = new GoogleAnalyticsTools(); + $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); $gaScripts = ''; // Home featured products @@ -66,14 +68,12 @@ public function run() array(), true ); - $gaScripts .= $gaTools->addProductImpression($homeFeaturedProducts).$gaTools->addProductClick($homeFeaturedProducts); + $gaScripts .= $gaTools->addProductImpression($homeFeaturedProducts) . $gaTools->addProductClick($homeFeaturedProducts); } $this->js_state = 1; - return $gaTools->generateJs( - $this->module->js_state, - $this->context->currency->iso_code, + return $gaTagHandler->generate( $gaTools->filter($gaScripts, $this->module->filterable) ); } diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php index bc5f2c4..9a1a78a 100644 --- a/classes/Hook/HookDisplayOrderConfirmation.php +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -29,6 +29,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; +use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; class HookDisplayOrderConfirmation implements HookInterface @@ -73,13 +74,14 @@ public function run() $orderProducts = array(); $cart = new \Cart($order->id_cart); $gaTools = new GoogleAnalyticsTools(); + $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); $productWrapper = new ProductWrapper($this->context); foreach ($cart->getProducts() as $order_product) { $orderProducts[] = $productWrapper->wrapProduct($order_product, array(), 0, true); } - $gaScripts = 'MBG.addCheckoutOption(3,\''.$order->payment.'\');'; + $gaScripts = 'MBG.addCheckoutOption(3,\'' . $order->payment.'\');'; $transaction = array( 'id' => $order->id, 'affiliation' => (\Shop::isFeatureActive()) ? $this->context->shop->name : \Configuration::get('PS_SHOP_NAME'), @@ -91,11 +93,8 @@ public function run() $gaScripts .= $gaTools->addTransaction($orderProducts, $transaction); $this->module->js_state = 1; - return $gaTools->generateJs( - $this->module->js_state, - $this->context->currency->iso_code, - $gaScripts - ); + + return $gaTagHandler->generate($gaScripts); } } } diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index e7b15db..1475477 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -70,7 +70,7 @@ public function getContent() $configurationForm = new PrestaShop\Module\Ps_Googleanalytics\Form\ConfigurationForm($this); $formOutput = ''; - if (Tools::isSubmit('submit'.$this->name)) { + if (Tools::isSubmit('submit' . $this->name)) { $formOutput = $configurationForm->treat(); } @@ -129,10 +129,9 @@ public function hookdisplayFooterProduct($params) */ public function hookdisplayAdminOrder() { - $gaTools = new PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools(); - echo $gaTools->generateJs( - $this->js_state, - $this->context->currency->iso_code, + $gaTagHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler($this, $this->context); + + echo $gaTagHandler->generate( $this->context->cookie->ga_admin_refund, 1 ); @@ -180,9 +179,9 @@ protected function _debugLog($function, $log) return true; } - $myFile = _PS_MODULE_DIR_.$this->name.'/logs/analytics.log'; + $myFile = _PS_MODULE_DIR_ . $this->name.'/logs/analytics.log'; $fh = fopen($myFile, 'a'); - fwrite($fh, date('F j, Y, g:i a').' '.$function."\n"); + fwrite($fh, date('F j, Y, g:i a').' ' . $function."\n"); fwrite($fh, print_r($log, true)."\n\n"); fclose($fh); } diff --git a/views/templates/hook/ga_tag.tpl b/views/templates/hook/ga_tag.tpl new file mode 100644 index 0000000..e979522 --- /dev/null +++ b/views/templates/hook/ga_tag.tpl @@ -0,0 +1,45 @@ +{* + * 2007-2020 PrestaShop + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License (AFL 3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://opensource.org/licenses/afl-3.0.php + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade PrestaShop to newer + * versions in the future. If you wish to customize PrestaShop for your + * needs please refer to http://www.prestashop.com for more information. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA + * @version Release: $Revision:7040 $ + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + *} + +{if (!empty($jsCode))} + {literal} + + {/literal} +{/if} + +{if ($jsState != 1 && $isBackoffice == 0)} + {literal} + + {/literal} +{/if} From a399ea0bab4bf7b91be154b5dbf6a4cab7bbf7f1 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 15:52:26 +0200 Subject: [PATCH 24/45] GA Script generated with smarty --- classes/Handler/GanalyticsJsHandler.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/classes/Handler/GanalyticsJsHandler.php b/classes/Handler/GanalyticsJsHandler.php index 1a291aa..938259a 100644 --- a/classes/Handler/GanalyticsJsHandler.php +++ b/classes/Handler/GanalyticsJsHandler.php @@ -62,5 +62,4 @@ public function generate($jsCode, $isBackoffice = 0) ); } } - -} \ No newline at end of file +} From 6c3112f6ac9b56940a6d9f0d746c0ec6a55ed13e Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 16:05:29 +0200 Subject: [PATCH 25/45] Update all headers --- classes/Database/Install.php | 20 ++---- classes/Database/Uninstall.php | 20 ++---- classes/Database/index.php | 42 +++++------- classes/Form/ConfigurationForm.php | 20 ++---- classes/Form/index.php | 42 +++++------- classes/GoogleAnalyticsTools.php | 20 ++---- classes/Handler/GanalyticsDataHandler.php | 20 ++---- classes/Handler/GanalyticsJsHandler.php | 20 ++---- classes/Handler/ModuleHandler.php | 20 ++---- classes/Handler/index.php | 42 +++++------- classes/Hook/HookActionCarrierProcess.php | 20 ++---- classes/Hook/HookActionCartSave.php | 20 ++---- classes/Hook/HookActionProductCancel.php | 20 ++---- classes/Hook/HookDisplayBackOfficeHeader.php | 20 ++---- classes/Hook/HookDisplayFooter.php | 20 ++---- classes/Hook/HookDisplayFooterProduct.php | 20 ++---- classes/Hook/HookDisplayHeader.php | 20 ++---- classes/Hook/HookDisplayHome.php | 20 ++---- classes/Hook/HookDisplayOrderConfirmation.php | 20 ++---- classes/Hook/HookInterface.php | 20 ++---- classes/Hook/index.php | 42 +++++------- classes/Repository/CarrierRepository.php | 20 ++---- .../Repository/GanalyticsDataRepository.php | 20 ++---- classes/Repository/GanalyticsRepository.php | 20 ++---- classes/Repository/index.php | 42 +++++------- classes/Wrapper/OrderWrapper.php | 20 ++---- classes/Wrapper/ProductWrapper.php | 20 ++---- classes/Wrapper/WrapperInterface.php | 20 ++---- classes/Wrapper/index.php | 42 +++++------- classes/index.php | 42 +++++------- composer.json | 67 ++++++++++--------- controllers/admin/AdminGanalyticsAjax.php | 40 +++++------ controllers/admin/index.php | 42 +++++------- controllers/front/ajax.php | 40 +++++------ controllers/front/index.php | 42 +++++------- controllers/index.php | 42 +++++------- index.php | 42 +++++------- ps_googleanalytics.php | 20 ++---- translations/de.php | 19 ++++++ translations/en.php | 19 ++++++ translations/es.php | 19 ++++++ translations/fr.php | 19 ++++++ translations/index.php | 42 +++++------- translations/it.php | 19 ++++++ upgrade/index.php | 42 +++++------- upgrade/install-3.1.0.php | 40 +++++------ views/css/ganalytics-nobootstrap.css | 40 +++++------ views/css/ganalytics.css | 18 +++++ views/css/index.php | 42 +++++------- views/img/index.php | 42 +++++------- views/index.php | 42 +++++------- views/js/GoogleAnalyticActionLib.js | 22 +++--- views/js/index.php | 42 +++++------- views/templates/admin/configuration.tpl | 23 +++---- views/templates/admin/index.php | 42 +++++------- views/templates/hook/ga_tag.tpl | 23 +++---- views/templates/hook/index.php | 42 +++++------- views/templates/hook/ps_googleanalytics.tpl | 23 +++---- views/templates/index.php | 42 +++++------- 59 files changed, 775 insertions(+), 976 deletions(-) diff --git a/classes/Database/Install.php b/classes/Database/Install.php index 839f1b0..48b45c5 100644 --- a/classes/Database/Install.php +++ b/classes/Database/Install.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Database; diff --git a/classes/Database/Uninstall.php b/classes/Database/Uninstall.php index 2c3d3ce..29f50f4 100644 --- a/classes/Database/Uninstall.php +++ b/classes/Database/Uninstall.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Database; diff --git a/classes/Database/index.php b/classes/Database/index.php index 94088c7..438906f 100644 --- a/classes/Database/index.php +++ b/classes/Database/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/classes/Form/ConfigurationForm.php b/classes/Form/ConfigurationForm.php index a5b0578..4f35e84 100644 --- a/classes/Form/ConfigurationForm.php +++ b/classes/Form/ConfigurationForm.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Form; diff --git a/classes/Form/index.php b/classes/Form/index.php index 94088c7..438906f 100644 --- a/classes/Form/index.php +++ b/classes/Form/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/classes/GoogleAnalyticsTools.php b/classes/GoogleAnalyticsTools.php index fc185f4..55eabbe 100644 --- a/classes/GoogleAnalyticsTools.php +++ b/classes/GoogleAnalyticsTools.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics; diff --git a/classes/Handler/GanalyticsDataHandler.php b/classes/Handler/GanalyticsDataHandler.php index 9936074..03b08c6 100644 --- a/classes/Handler/GanalyticsDataHandler.php +++ b/classes/Handler/GanalyticsDataHandler.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Handler; diff --git a/classes/Handler/GanalyticsJsHandler.php b/classes/Handler/GanalyticsJsHandler.php index 938259a..9452ab5 100644 --- a/classes/Handler/GanalyticsJsHandler.php +++ b/classes/Handler/GanalyticsJsHandler.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Handler; diff --git a/classes/Handler/ModuleHandler.php b/classes/Handler/ModuleHandler.php index 5baf07b..d43eeb1 100644 --- a/classes/Handler/ModuleHandler.php +++ b/classes/Handler/ModuleHandler.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Handler; diff --git a/classes/Handler/index.php b/classes/Handler/index.php index 94088c7..438906f 100644 --- a/classes/Handler/index.php +++ b/classes/Handler/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/classes/Hook/HookActionCarrierProcess.php b/classes/Hook/HookActionCarrierProcess.php index 7e26cd3..40332b6 100644 --- a/classes/Hook/HookActionCarrierProcess.php +++ b/classes/Hook/HookActionCarrierProcess.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/HookActionCartSave.php b/classes/Hook/HookActionCartSave.php index 1903ea1..2f17d74 100644 --- a/classes/Hook/HookActionCartSave.php +++ b/classes/Hook/HookActionCartSave.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php index 36cb1fc..38d0f55 100644 --- a/classes/Hook/HookActionProductCancel.php +++ b/classes/Hook/HookActionProductCancel.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php index ca5d2c7..32cf429 100644 --- a/classes/Hook/HookDisplayBackOfficeHeader.php +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php index 438f530..b465eeb 100644 --- a/classes/Hook/HookDisplayFooter.php +++ b/classes/Hook/HookDisplayFooter.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/HookDisplayFooterProduct.php b/classes/Hook/HookDisplayFooterProduct.php index 930fc01..b3913ed 100644 --- a/classes/Hook/HookDisplayFooterProduct.php +++ b/classes/Hook/HookDisplayFooterProduct.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index ec8a9b1..78aad52 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php index 1f19896..33e5472 100644 --- a/classes/Hook/HookDisplayHome.php +++ b/classes/Hook/HookDisplayHome.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php index 9a1a78a..4ddfbe3 100644 --- a/classes/Hook/HookDisplayOrderConfirmation.php +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/HookInterface.php b/classes/Hook/HookInterface.php index 63b6e26..d74b922 100644 --- a/classes/Hook/HookInterface.php +++ b/classes/Hook/HookInterface.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Hook/index.php b/classes/Hook/index.php index 94088c7..438906f 100644 --- a/classes/Hook/index.php +++ b/classes/Hook/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/classes/Repository/CarrierRepository.php b/classes/Repository/CarrierRepository.php index 85f7541..e3e601f 100644 --- a/classes/Repository/CarrierRepository.php +++ b/classes/Repository/CarrierRepository.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Repository; diff --git a/classes/Repository/GanalyticsDataRepository.php b/classes/Repository/GanalyticsDataRepository.php index 9c67bf8..0df71fb 100644 --- a/classes/Repository/GanalyticsDataRepository.php +++ b/classes/Repository/GanalyticsDataRepository.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Repository; diff --git a/classes/Repository/GanalyticsRepository.php b/classes/Repository/GanalyticsRepository.php index 58838fd..b05bacd 100644 --- a/classes/Repository/GanalyticsRepository.php +++ b/classes/Repository/GanalyticsRepository.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Repository; diff --git a/classes/Repository/index.php b/classes/Repository/index.php index 94088c7..438906f 100644 --- a/classes/Repository/index.php +++ b/classes/Repository/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/classes/Wrapper/OrderWrapper.php b/classes/Wrapper/OrderWrapper.php index b6c4435..26b1aaa 100644 --- a/classes/Wrapper/OrderWrapper.php +++ b/classes/Wrapper/OrderWrapper.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Wrapper; diff --git a/classes/Wrapper/ProductWrapper.php b/classes/Wrapper/ProductWrapper.php index 6626363..07928ba 100644 --- a/classes/Wrapper/ProductWrapper.php +++ b/classes/Wrapper/ProductWrapper.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Wrapper; diff --git a/classes/Wrapper/WrapperInterface.php b/classes/Wrapper/WrapperInterface.php index 7ded9a9..baa0e74 100644 --- a/classes/Wrapper/WrapperInterface.php +++ b/classes/Wrapper/WrapperInterface.php @@ -1,27 +1,21 @@ - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; diff --git a/classes/Wrapper/index.php b/classes/Wrapper/index.php index 94088c7..438906f 100644 --- a/classes/Wrapper/index.php +++ b/classes/Wrapper/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/classes/index.php b/classes/index.php index 94088c7..438906f 100644 --- a/classes/index.php +++ b/classes/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/composer.json b/composer.json index 6fb4e59..be2ddd2 100644 --- a/composer.json +++ b/composer.json @@ -1,34 +1,35 @@ { - "name": "prestashop/ps_googleanalytics", - "description": "PrestaShop module ps_googleanalytics", - "homepage": "https://github.com/PrestaShop/ps_googleanalytics", - "license": "AFL - Academic Free License (AFL 3.0)", - "authors": [ - { - "name": "PrestaShop SA", - "email": "contact@prestashop.com" - } - ], - "config": { - "preferred-install": "dist", - "optimize-autoloader": true, - "prepend-autoloader": false, - "platform": { - "php": "5.6" - } - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "prestashop/php-dev-tools": "3.*" - }, - "autoload": { - "classmap": [ - "ps_googleanalytics.php", - "controllers", - "classes" - ] - }, - "type": "prestashop-module" -} + "name": "prestashop/ps_googleanalytics", + "description": "PrestaShop module ps_googleanalytics", + "homepage": "https://github.com/PrestaShop/ps_googleanalytics", + "license": "AFL-3.0", + "authors": [ + { + "name": "PrestaShop SA", + "email": "contact@prestashop.com" + } + ], + "config": { + "preferred-install": "dist", + "optimize-autoloader": true, + "prepend-autoloader": false, + "platform": { + "php": "5.6" + } + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "prestashop/php-dev-tools": "3.*" + }, + "autoload": { + "classmap": [ + "ps_googleanalytics.php", + "controllers", + "classes" + ] + }, + "type": "prestashop-module", + "author": "PrestaShop" +} \ No newline at end of file diff --git a/controllers/admin/AdminGanalyticsAjax.php b/controllers/admin/AdminGanalyticsAjax.php index 02cd066..e111a95 100644 --- a/controllers/admin/AdminGanalyticsAjax.php +++ b/controllers/admin/AdminGanalyticsAjax.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ class AdminGanalyticsAjaxController extends ModuleAdminController { diff --git a/controllers/admin/index.php b/controllers/admin/index.php index 94088c7..438906f 100644 --- a/controllers/admin/index.php +++ b/controllers/admin/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/controllers/front/ajax.php b/controllers/front/ajax.php index b127d66..ebab034 100755 --- a/controllers/front/ajax.php +++ b/controllers/front/ajax.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ class ps_GoogleanalyticsAjaxModuleFrontController extends ModuleFrontController { diff --git a/controllers/front/index.php b/controllers/front/index.php index 94088c7..438906f 100755 --- a/controllers/front/index.php +++ b/controllers/front/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/controllers/index.php b/controllers/index.php index 94088c7..438906f 100755 --- a/controllers/index.php +++ b/controllers/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/index.php b/index.php index 94088c7..438906f 100755 --- a/index.php +++ b/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 1475477..1398b12 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -1,27 +1,21 @@ - * @copyright 2007-2018 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA */ if (!defined('_PS_VERSION_')) { diff --git a/translations/de.php b/translations/de.php index 5e06b55..235c286 100644 --- a/translations/de.php +++ b/translations/de.php @@ -1,4 +1,23 @@ + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + global $_MODULE; $_MODULE = array(); diff --git a/translations/en.php b/translations/en.php index 168c8f7..c6a195a 100644 --- a/translations/en.php +++ b/translations/en.php @@ -1,4 +1,23 @@ + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + global $_MODULE; $_MODULE = array(); diff --git a/translations/es.php b/translations/es.php index c927a64..a8d8982 100644 --- a/translations/es.php +++ b/translations/es.php @@ -1,4 +1,23 @@ + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + global $_MODULE; $_MODULE = array(); diff --git a/translations/fr.php b/translations/fr.php index 18e3779..9675fca 100644 --- a/translations/fr.php +++ b/translations/fr.php @@ -1,4 +1,23 @@ + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + global $_MODULE; $_MODULE = array(); diff --git a/translations/index.php b/translations/index.php index 94088c7..438906f 100755 --- a/translations/index.php +++ b/translations/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/translations/it.php b/translations/it.php index 32aeab2..c269f53 100644 --- a/translations/it.php +++ b/translations/it.php @@ -1,4 +1,23 @@ + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + global $_MODULE; $_MODULE = array(); diff --git a/upgrade/index.php b/upgrade/index.php index 94088c7..438906f 100644 --- a/upgrade/index.php +++ b/upgrade/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/upgrade/install-3.1.0.php b/upgrade/install-3.1.0.php index b8a09b3..7fe207f 100644 --- a/upgrade/install-3.1.0.php +++ b/upgrade/install-3.1.0.php @@ -1,28 +1,22 @@ -* @copyright 2007-2018 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ if (!defined('_PS_VERSION_')) exit; function upgrade_module_3_1_0($object) diff --git a/views/css/ganalytics-nobootstrap.css b/views/css/ganalytics-nobootstrap.css index 1a73f00..7ee05f9 100644 --- a/views/css/ganalytics-nobootstrap.css +++ b/views/css/ganalytics-nobootstrap.css @@ -1,27 +1,21 @@ /** -* 2007-2020 PrestaShop -* -* NOTICE OF LICENSE -* -* This source file is subject to the Academic Free License (AFL 3.0) -* that is bundled with this package in the file LICENSE.txt. -* It is also available through the world-wide-web at this URL: -* http://opensource.org/licenses/afl-3.0.php -* If you did not receive a copy of the license and are unable to -* obtain it through the world-wide-web, please send an email -* to license@prestashop.com so we can send you a copy immediately. -* -* DISCLAIMER -* -* Do not edit or add to this file if you wish to upgrade PrestaShop to newer -* versions in the future. If you wish to customize PrestaShop for your -* needs please refer to http://www.prestashop.com for more information. -* -* @author PrestaShop SA -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ #content .panel { border-radius: 5px; diff --git a/views/css/ganalytics.css b/views/css/ganalytics.css index 569db0d..f442edf 100644 --- a/views/css/ganalytics.css +++ b/views/css/ganalytics.css @@ -1,3 +1,21 @@ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ #content .panel { margin: 0 auto; max-width: 1300px; diff --git a/views/css/index.php b/views/css/index.php index 94088c7..438906f 100644 --- a/views/css/index.php +++ b/views/css/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/views/img/index.php b/views/img/index.php index 94088c7..438906f 100755 --- a/views/img/index.php +++ b/views/img/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/views/index.php b/views/index.php index 94088c7..438906f 100755 --- a/views/index.php +++ b/views/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/views/js/GoogleAnalyticActionLib.js b/views/js/GoogleAnalyticActionLib.js index d7026b1..9fbd505 100755 --- a/views/js/GoogleAnalyticActionLib.js +++ b/views/js/GoogleAnalyticActionLib.js @@ -1,27 +1,21 @@ /** - * 2007-2020 PrestaShop + * 2007-2020 PrestaShop and Contributors * * NOTICE OF LICENSE * - * This source file is subject to the Academic Free License (AFL 3.0) + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) * that is bundled with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: - * http://opensource.org/licenses/afl-3.0.php + * https://opensource.org/licenses/AFL-3.0 * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@prestashop.com so we can send you a copy immediately. * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to http://www.prestashop.com for more information. - * - * @author PrestaShop SA - * @copyright 2007-2020 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA - **/ + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ /* globals $, ga, jQuery */ diff --git a/views/js/index.php b/views/js/index.php index 94088c7..438906f 100755 --- a/views/js/index.php +++ b/views/js/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/views/templates/admin/configuration.tpl b/views/templates/admin/configuration.tpl index 227c147..e842914 100755 --- a/views/templates/admin/configuration.tpl +++ b/views/templates/admin/configuration.tpl @@ -1,27 +1,20 @@ -{* - * 2007-2020 PrestaShop +{** + * 2007-2020 PrestaShop and Contributors * * NOTICE OF LICENSE * - * This source file is subject to the Academic Free License (AFL 3.0) + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) * that is bundled with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: - * http://opensource.org/licenses/afl-3.0.php + * https://opensource.org/licenses/AFL-3.0 * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@prestashop.com so we can send you a copy immediately. * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to http://www.prestashop.com for more information. - * - * @author PrestaShop SA - * @copyright 2007-2020 PrestaShop SA - * @version Release: $Revision:7040 $ - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA *}

diff --git a/views/templates/admin/index.php b/views/templates/admin/index.php index 94088c7..438906f 100755 --- a/views/templates/admin/index.php +++ b/views/templates/admin/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/views/templates/hook/ga_tag.tpl b/views/templates/hook/ga_tag.tpl index e979522..46e1c8b 100644 --- a/views/templates/hook/ga_tag.tpl +++ b/views/templates/hook/ga_tag.tpl @@ -1,27 +1,20 @@ -{* - * 2007-2020 PrestaShop +{** + * 2007-2020 PrestaShop and Contributors * * NOTICE OF LICENSE * - * This source file is subject to the Academic Free License (AFL 3.0) + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) * that is bundled with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: - * http://opensource.org/licenses/afl-3.0.php + * https://opensource.org/licenses/AFL-3.0 * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@prestashop.com so we can send you a copy immediately. * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to http://www.prestashop.com for more information. - * - * @author PrestaShop SA - * @copyright 2007-2020 PrestaShop SA - * @version Release: $Revision:7040 $ - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA *} {if (!empty($jsCode))} diff --git a/views/templates/hook/index.php b/views/templates/hook/index.php index 94088c7..438906f 100755 --- a/views/templates/hook/index.php +++ b/views/templates/hook/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); diff --git a/views/templates/hook/ps_googleanalytics.tpl b/views/templates/hook/ps_googleanalytics.tpl index fe7c007..59798a6 100755 --- a/views/templates/hook/ps_googleanalytics.tpl +++ b/views/templates/hook/ps_googleanalytics.tpl @@ -1,27 +1,20 @@ -{* - * 2007-2020 PrestaShop +{** + * 2007-2020 PrestaShop and Contributors * * NOTICE OF LICENSE * - * This source file is subject to the Academic Free License (AFL 3.0) + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) * that is bundled with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: - * http://opensource.org/licenses/afl-3.0.php + * https://opensource.org/licenses/AFL-3.0 * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@prestashop.com so we can send you a copy immediately. * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to http://www.prestashop.com for more information. - * - * @author PrestaShop SA - * @copyright 2007-2020 PrestaShop SA - * @version Release: $Revision:7040 $ - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) - * International Registered Trademark & Property of PrestaShop SA + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA *} {literal} diff --git a/views/templates/index.php b/views/templates/index.php index 94088c7..438906f 100755 --- a/views/templates/index.php +++ b/views/templates/index.php @@ -1,28 +1,22 @@ -* @copyright 2007-2020 PrestaShop SA -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ +/** + * 2007-2020 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); From bc3b2eacb3db1b9cb92214e275548ba7f8bf6f50 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 16:08:52 +0200 Subject: [PATCH 26/45] Fix PHP CS issues --- .php_cs.cache | 1 + .php_cs.dist | 11 ++ .prettyci.composer.json | 5 + classes/Database/Install.php | 10 +- classes/Database/Uninstall.php | 4 +- classes/Database/index.php | 3 +- classes/Form/ConfigurationForm.php | 112 +++++++++--------- classes/Form/index.php | 3 +- classes/GoogleAnalyticsTools.php | 12 +- classes/Handler/GanalyticsDataHandler.php | 10 +- classes/Handler/GanalyticsJsHandler.php | 7 +- classes/Handler/ModuleHandler.php | 2 +- classes/Handler/index.php | 3 +- classes/Hook/HookActionCarrierProcess.php | 11 +- classes/Hook/HookActionCartSave.php | 20 ++-- classes/Hook/HookActionProductCancel.php | 20 ++-- classes/Hook/HookDisplayBackOfficeHeader.php | 24 ++-- classes/Hook/HookDisplayFooter.php | 23 ++-- classes/Hook/HookDisplayFooterProduct.php | 13 +- classes/Hook/HookDisplayHeader.php | 25 ++-- classes/Hook/HookDisplayHome.php | 8 +- classes/Hook/HookDisplayOrderConfirmation.php | 27 +++-- classes/Hook/HookInterface.php | 3 +- classes/Hook/index.php | 3 +- classes/Repository/CarrierRepository.php | 2 +- .../Repository/GanalyticsDataRepository.php | 2 +- classes/Repository/index.php | 3 +- classes/Wrapper/OrderWrapper.php | 6 +- classes/Wrapper/ProductWrapper.php | 25 ++-- classes/Wrapper/WrapperInterface.php | 3 +- classes/Wrapper/index.php | 3 +- classes/index.php | 3 +- controllers/admin/AdminGanalyticsAjax.php | 8 +- controllers/admin/index.php | 3 +- controllers/front/ajax.php | 8 +- controllers/front/index.php | 3 +- controllers/index.php | 3 +- index.php | 3 +- ps_googleanalytics.php | 20 +++- tests/index.php | 28 +++++ tests/phpstan/index.php | 28 +++++ tests/phpstan/phpstan.neon | 11 ++ translations/de.php | 4 +- translations/en.php | 5 +- translations/es.php | 4 +- translations/fr.php | 4 +- translations/index.php | 3 +- translations/it.php | 4 +- upgrade/index.php | 3 +- upgrade/install-3.1.0.php | 10 +- views/css/index.php | 3 +- views/img/index.php | 3 +- views/index.php | 3 +- views/js/index.php | 3 +- views/templates/admin/index.php | 3 +- views/templates/hook/index.php | 3 +- views/templates/index.php | 3 +- 57 files changed, 324 insertions(+), 256 deletions(-) create mode 100644 .php_cs.cache create mode 100644 .php_cs.dist create mode 100644 .prettyci.composer.json create mode 100644 tests/index.php create mode 100644 tests/phpstan/index.php create mode 100644 tests/phpstan/phpstan.neon diff --git a/.php_cs.cache b/.php_cs.cache new file mode 100644 index 0000000..9b079db --- /dev/null +++ b/.php_cs.cache @@ -0,0 +1 @@ +{"php":"7.4.5","version":"2.16.3:v2.16.3#83baf823a33a1cbd5416c8626935cf3f843c10b0","indent":" ","lineEnding":"\n","rules":{"array_syntax":{"syntax":"short"},"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_before_statement":{"statements":["return"]},"braces":{"allow_single_line_closure":true},"cast_spaces":{"space":"single"},"class_attributes_separation":{"elements":["method"]},"class_definition":{"single_line":true},"concat_space":{"spacing":"one"},"declare_equal_normalize":true,"function_typehint_space":true,"include":true,"increment_style":true,"lowercase_cast":true,"lowercase_static_reference":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":true,"native_function_casing":true,"native_function_type_declaration_casing":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["curly_brace_block","extra","parenthesis_brace_block","square_brace_block","throw","use"]},"no_leading_import_slash":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":true,"no_unneeded_curly_braces":{"namespaces":true},"no_unused_imports":true,"no_whitespace_before_comma_in_array":true,"no_whitespace_in_blank_line":true,"normalize_index_brace":true,"object_operator_without_whitespace":true,"ordered_imports":true,"php_unit_fqcn_annotation":true,"phpdoc_align":{"align":"left"},"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":true,"phpdoc_single_line_var_spacing":true,"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"return_type_declaration":true,"semicolon_after_instruction":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_class_element_per_statement":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_trait_insert_per_statement":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"ternary_operator_spaces":true,"trailing_comma_in_multiline_array":true,"trim_array_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"blank_line_after_namespace":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true,"error_suppression":{"mute_deprecation_error":false,"noise_remaining_usages":false,"noise_remaining_usages_exclude":[]},"non_printable_character":true},"hashes":{"tests\/index.php":2657184305,"tests\/phpstan\/index.php":2657184305,"index.php":2657184305,"classes\/Form\/index.php":2657184305,"classes\/Form\/ConfigurationForm.php":2607320067,"classes\/index.php":2657184305,"classes\/GoogleAnalyticsTools.php":2454784637,"classes\/Hook\/HookDisplayHome.php":1331593200,"classes\/Hook\/index.php":2657184305,"classes\/Hook\/HookDisplayFooter.php":1377542451,"classes\/Hook\/HookDisplayBackOfficeHeader.php":62664555,"classes\/Hook\/HookDisplayHeader.php":3203340892,"classes\/Hook\/HookDisplayOrderConfirmation.php":3859977258,"classes\/Hook\/HookActionCarrierProcess.php":4156494746,"classes\/Hook\/HookActionProductCancel.php":1967332341,"classes\/Hook\/HookInterface.php":2350618434,"classes\/Hook\/HookDisplayFooterProduct.php":1036485983,"classes\/Hook\/HookActionCartSave.php":4029443709,"classes\/Handler\/index.php":2657184305,"classes\/Handler\/ModuleHandler.php":1844907236,"classes\/Handler\/GanalyticsJsHandler.php":2208903929,"classes\/Handler\/GanalyticsDataHandler.php":2818967706,"classes\/Database\/index.php":2657184305,"classes\/Database\/Uninstall.php":3995443079,"classes\/Database\/Install.php":1503889843,"classes\/Wrapper\/index.php":2657184305,"classes\/Wrapper\/ProductWrapper.php":3918842904,"classes\/Wrapper\/WrapperInterface.php":3165813164,"classes\/Wrapper\/OrderWrapper.php":2236500015,"classes\/Repository\/GanalyticsDataRepository.php":693896051,"classes\/Repository\/index.php":2657184305,"classes\/Repository\/GanalyticsRepository.php":396338050,"classes\/Repository\/CarrierRepository.php":595599245,"ps_googleanalytics.php":2237291875,"views\/img\/index.php":2657184305,"views\/index.php":2657184305,"views\/css\/index.php":2657184305,"views\/js\/index.php":2657184305,"views\/templates\/hook\/index.php":2657184305,"views\/templates\/index.php":2657184305,"views\/templates\/admin\/index.php":2657184305,"upgrade\/install-3.1.0.php":4083663024,"upgrade\/index.php":2657184305,"translations\/index.php":2657184305,"translations\/fr.php":4138912457,"translations\/de.php":4244071240,"translations\/it.php":1991868331,"translations\/es.php":2343815783,"translations\/en.php":1605709074,"controllers\/index.php":2657184305,"controllers\/front\/index.php":2657184305,"controllers\/front\/ajax.php":1920076942,"controllers\/admin\/index.php":2657184305,"controllers\/admin\/AdminGanalyticsAjax.php":1493758573}} \ No newline at end of file diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 0000000..9830d6d --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,11 @@ +setUsingCache(true) + ->getFinder() + ->in(__DIR__) + ->exclude('vendor'); + +return $config; diff --git a/.prettyci.composer.json b/.prettyci.composer.json new file mode 100644 index 0000000..a4d7689 --- /dev/null +++ b/.prettyci.composer.json @@ -0,0 +1,5 @@ +{ + "require-dev": { + "prestashop/php-dev-tools": "^2.0" + } +} diff --git a/classes/Database/Install.php b/classes/Database/Install.php index 48b45c5..fd698f5 100644 --- a/classes/Database/Install.php +++ b/classes/Database/Install.php @@ -43,9 +43,9 @@ public function __construct(\Ps_Googleanalytics $module) */ public function installTables() { - $sql = array(); + $sql = []; - $sql[] = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'ganalytics` ( + $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ganalytics` ( `id_google_analytics` int(11) NOT NULL AUTO_INCREMENT, `id_order` int(11) NOT NULL, `id_customer` int(10) NOT NULL, @@ -55,14 +55,14 @@ public function installTables() PRIMARY KEY (`id_google_analytics`), KEY `id_order` (`id_order`), KEY `sent` (`sent`) - ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8 AUTO_INCREMENT=1'; + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8 AUTO_INCREMENT=1'; - $sql[] = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'ganalytics_data` ( + $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ganalytics_data` ( `id_cart` int(11) NOT NULL, `id_shop` int(11) NOT NULL, `data` TEXT DEFAULT NULL, PRIMARY KEY (`id_cart`) - ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8'; + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8'; foreach ($sql as $query) { if (!\Db::getInstance()->execute($query)) { diff --git a/classes/Database/Uninstall.php b/classes/Database/Uninstall.php index 29f50f4..2ced082 100644 --- a/classes/Database/Uninstall.php +++ b/classes/Database/Uninstall.php @@ -29,8 +29,8 @@ class Uninstall */ public function uninstallTables() { - $sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'ganalytics`'; - $sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'ganalytics_data`'; + $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'ganalytics`'; + $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'ganalytics_data`'; foreach ($sql as $query) { if (!\Db::getInstance()->execute($query)) { diff --git a/classes/Database/index.php b/classes/Database/index.php index 438906f..004c891 100644 --- a/classes/Database/index.php +++ b/classes/Database/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/classes/Form/ConfigurationForm.php b/classes/Form/ConfigurationForm.php index 4f35e84..96b95f2 100644 --- a/classes/Form/ConfigurationForm.php +++ b/classes/Form/ConfigurationForm.php @@ -24,7 +24,8 @@ class ConfigurationForm { private $module; - public function __construct(\Ps_Googleanalytics $module) { + public function __construct(\Ps_Googleanalytics $module) + { $this->module = $module; } @@ -39,7 +40,7 @@ public function generate() $is_multistore_active = \Shop::isFeatureActive(); // Get default language - $default_lang = (int)\Configuration::get('PS_LANG_DEFAULT'); + $default_lang = (int) \Configuration::get('PS_LANG_DEFAULT'); $helper = new \HelperForm(); @@ -47,7 +48,7 @@ public function generate() $helper->module = $this->module; $helper->name_controller = $this->module->name; $helper->token = \Tools::getAdminTokenLite('AdminModules'); - $helper->currentIndex = \AdminController::$currentIndex.'&configure=' . $this->module->name; + $helper->currentIndex = \AdminController::$currentIndex . '&configure=' . $this->module->name; // Language $helper->default_form_language = $default_lang; @@ -58,92 +59,91 @@ public function generate() $helper->show_toolbar = true; // false -> remove toolbar $helper->toolbar_scroll = true; // yes - > Toolbar is always visible on the top of the screen. $helper->submit_action = 'submit' . $this->module->name; - $helper->toolbar_btn = array( - 'save' => - array( + $helper->toolbar_btn = [ + 'save' => [ 'desc' => $this->module->l('Save'), - 'href' => \AdminController::$currentIndex.'&configure=' . $this->module->name.'&save' . $this->module->name. - '&token='.\Tools::getAdminTokenLite('AdminModules'), - ), - 'back' => array( - 'href' => \AdminController::$currentIndex.'&token='.\Tools::getAdminTokenLite('AdminModules'), - 'desc' => $this->module->l('Back to list') - ) - ); - - $fields_form = array(); + 'href' => \AdminController::$currentIndex . '&configure=' . $this->module->name . '&save' . $this->module->name . + '&token=' . \Tools::getAdminTokenLite('AdminModules'), + ], + 'back' => [ + 'href' => \AdminController::$currentIndex . '&token=' . \Tools::getAdminTokenLite('AdminModules'), + 'desc' => $this->module->l('Back to list'), + ], + ]; + + $fields_form = []; // Init Fields form array - $fields_form[0]['form'] = array( - 'legend' => array( + $fields_form[0]['form'] = [ + 'legend' => [ 'title' => $this->module->l('Settings'), - ), - 'input' => array( - array( + ], + 'input' => [ + [ 'type' => 'text', 'label' => $this->module->l('Google Analytics Tracking ID'), 'name' => 'GA_ACCOUNT_ID', 'size' => 20, 'required' => true, - 'hint' => $this->module->l('This information is available in your Google Analytics account') - ), - array( + 'hint' => $this->module->l('This information is available in your Google Analytics account'), + ], + [ 'type' => 'switch', 'label' => $this->module->l('Enable User ID tracking'), 'name' => 'GA_USERID_ENABLED', - 'values' => array( - array( + 'values' => [ + [ 'id' => 'ga_userid_enabled', 'value' => 1, - 'label' => $this->module->l('Enabled') - ), - array( + 'label' => $this->module->l('Enabled'), + ], + [ 'id' => 'ga_userid_disabled', 'value' => 0, - 'label' => $this->module->l('Disabled') - )) - ), - array( + 'label' => $this->module->l('Disabled'), + ], ], + ], + [ 'type' => 'switch', 'label' => $this->module->l('Anonymize IP'), 'name' => 'GA_ANONYMIZE_ENABLED', 'hint' => $this->module->l('Use this option to anonymize the visitor’s IP to comply with data privacy laws in some countries'), - 'values' => array( - array( + 'values' => [ + [ 'id' => 'ga_anonymize_enabled', 'value' => 1, - 'label' => $this->module->l('Enabled') - ), - array( + 'label' => $this->module->l('Enabled'), + ], + [ 'id' => 'ga_anonymize_disabled', 'value' => 0, - 'label' => $this->module->l('Disabled') - ), - ), - ), - ), - 'submit' => array( + 'label' => $this->module->l('Disabled'), + ], + ], + ], + ], + 'submit' => [ 'title' => $this->module->l('Save'), - ) - ); + ], + ]; if ($is_multistore_active) { - $fields_form[0]['form']['input'][] = array( + $fields_form[0]['form']['input'][] = [ 'type' => 'switch', 'label' => $this->module->l('Enable Cross-Domain tracking'), 'name' => 'GA_CROSSDOMAIN_ENABLED', - 'values' => array( - array( + 'values' => [ + [ 'id' => 'ga_crossdomain_enabled', 'value' => 1, - 'label' => $this->module->l('Enabled') - ), - array( + 'label' => $this->module->l('Enabled'), + ], + [ 'id' => 'ga_crossdomain_disabled', 'value' => 0, - 'label' => $this->module->l('Disabled') - ) - ) - ); + 'label' => $this->module->l('Disabled'), + ], + ], + ]; } // Load current value diff --git a/classes/Form/index.php b/classes/Form/index.php index 438906f..004c891 100644 --- a/classes/Form/index.php +++ b/classes/Form/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/classes/GoogleAnalyticsTools.php b/classes/GoogleAnalyticsTools.php index 55eabbe..489ff6e 100644 --- a/classes/GoogleAnalyticsTools.php +++ b/classes/GoogleAnalyticsTools.php @@ -55,10 +55,10 @@ public function addTransaction($products, $order) $js = ''; foreach ($products as $product) { - $js .= 'MBG.add('.json_encode($product).');'; + $js .= 'MBG.add(' . json_encode($product) . ');'; } - return $js.'MBG.addTransaction('.json_encode($order).');'; + return $js . 'MBG.addTransaction(' . json_encode($order) . ');'; } /** @@ -76,7 +76,7 @@ public function addProductImpression($products) $js = ''; foreach ($products as $product) { - $js .= 'MBG.add('.json_encode($product).",'',true);"; + $js .= 'MBG.add(' . json_encode($product) . ",'',true);"; } return $js; @@ -97,7 +97,7 @@ public function addProductClick($products) $js = ''; foreach ($products as $product) { - $js .= 'MBG.addProductClick('.json_encode($product).');'; + $js .= 'MBG.addProductClick(' . json_encode($product) . ');'; } return $js; @@ -118,7 +118,7 @@ public function addProductClickByHttpReferal($products) $js = ''; foreach ($products as $product) { - $js .= 'MBG.addProductClickByHttpReferal('.json_encode($product).');'; + $js .= 'MBG.addProductClickByHttpReferal(' . json_encode($product) . ');'; } return $js; @@ -139,7 +139,7 @@ public function addProductFromCheckout($products) $js = ''; foreach ($products as $product) { - $js .= 'MBG.add('.json_encode($product).');'; + $js .= 'MBG.add(' . json_encode($product) . ');'; } return $js; diff --git a/classes/Handler/GanalyticsDataHandler.php b/classes/Handler/GanalyticsDataHandler.php index 03b08c6..98626e8 100644 --- a/classes/Handler/GanalyticsDataHandler.php +++ b/classes/Handler/GanalyticsDataHandler.php @@ -36,9 +36,9 @@ class GanalyticsDataHandler */ public function __construct($cartId, $shopId) { - $this->ganalyticsDataRepository = new GanalyticsDataRepository; - $this->cartId = (int) $cartId; - $this->shopId = (int) $shopId; + $this->ganalyticsDataRepository = new GanalyticsDataRepository(); + $this->cartId = (int) $cartId; + $this->shopId = (int) $shopId; } /** @@ -90,7 +90,7 @@ private function readData() ); if (false === $dataRetuned) { - return array(); + return []; } return json_decode($dataRetuned, true); @@ -111,7 +111,7 @@ private function appendData($data) ); if (false === $dataRetuned) { - $newData = array($data); + $newData = [$data]; } else { $newData = json_decode($dataRetuned, true); $newData[] = $data; diff --git a/classes/Handler/GanalyticsJsHandler.php b/classes/Handler/GanalyticsJsHandler.php index 9452ab5..9ea97a0 100644 --- a/classes/Handler/GanalyticsJsHandler.php +++ b/classes/Handler/GanalyticsJsHandler.php @@ -25,7 +25,8 @@ class GanalyticsJsHandler private $module; private $context; - public function __construct(\Ps_googleanalytics $module, \Context $context) { + public function __construct(\Ps_googleanalytics $module, \Context $context) + { $this->module = $module; $this->context = $context; } @@ -42,12 +43,12 @@ public function generate($jsCode, $isBackoffice = 0) { if (\Configuration::get('GA_ACCOUNT_ID')) { $this->context->smarty->assign( - array( + [ 'jsCode' => $jsCode, 'isoCode' => \Tools::safeOutput($this->context->currency->iso_code), 'jsState' => $this->module->js_state, 'isBackoffice' => $isBackoffice, - ) + ] ); return $this->module->display( diff --git a/classes/Handler/ModuleHandler.php b/classes/Handler/ModuleHandler.php index d43eeb1..dc97fd0 100644 --- a/classes/Handler/ModuleHandler.php +++ b/classes/Handler/ModuleHandler.php @@ -72,7 +72,7 @@ public function uninstallModule($moduleName) } // This closure calls the parent class to prevent data to be erased - $parentUninstallClosure = function() { + $parentUninstallClosure = function () { return parent::uninstall(); }; diff --git a/classes/Handler/index.php b/classes/Handler/index.php index 438906f..004c891 100644 --- a/classes/Handler/index.php +++ b/classes/Handler/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/classes/Hook/HookActionCarrierProcess.php b/classes/Hook/HookActionCarrierProcess.php index 40332b6..9f54060 100644 --- a/classes/Hook/HookActionCarrierProcess.php +++ b/classes/Hook/HookActionCarrierProcess.php @@ -20,9 +20,8 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; -use PrestaShop\Module\Ps_Googleanalytics\Repository\CarrierRepository; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; +use PrestaShop\Module\Ps_Googleanalytics\Repository\CarrierRepository; class HookActionCarrierProcess implements HookInterface { @@ -30,7 +29,8 @@ class HookActionCarrierProcess implements HookInterface private $context; private $params; - public function __construct($module, $context) { + public function __construct($module, $context) + { $this->module = $module; $this->context = $context; } @@ -50,7 +50,7 @@ public function run() ); $carrierName = $carrierRepository->findByCarrierId((int) $this->params['cart']->id_carrier); - $ganalyticsDataHandler->manageData('MBG.addCheckoutOption(2,\'' . $carrierName.'\');', 'A'); + $ganalyticsDataHandler->manageData('MBG.addCheckoutOption(2,\'' . $carrierName . '\');', 'A'); } } @@ -59,7 +59,8 @@ public function run() * * @param array $params */ - public function setParams($params) { + public function setParams($params) + { $this->params = $params; } } diff --git a/classes/Hook/HookActionCartSave.php b/classes/Hook/HookActionCartSave.php index 2f17d74..14f5099 100644 --- a/classes/Hook/HookActionCartSave.php +++ b/classes/Hook/HookActionCartSave.php @@ -20,16 +20,16 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; -use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; class HookActionCartSave implements HookInterface { private $module; private $context; - public function __construct($module, $context) { + public function __construct($module, $context) + { $this->module = $module; $this->context = $context; } @@ -49,13 +49,13 @@ public function run() return; } - $cart = array( + $cart = [ 'controller' => \Tools::getValue('controller'), 'addAction' => \Tools::getValue('add') ? 'add' : '', 'removeAction' => \Tools::getValue('delete') ? 'delete' : '', 'extraAction' => \Tools::getValue('op'), - 'qty' => (int)\Tools::getValue('qty', 1) - ); + 'qty' => (int) \Tools::getValue('qty', 1), + ]; $cartProducts = $this->context->cart->getProducts(); if (isset($cartProducts) && count($cartProducts)) { @@ -67,7 +67,7 @@ public function run() } if ($cart['removeAction'] == 'delete') { - $addProductObject = new \Product((int)\Tools::getValue('id_product'), true, (int)\Configuration::get('PS_LANG_DEFAULT')); + $addProductObject = new \Product((int) \Tools::getValue('id_product'), true, (int) \Configuration::get('PS_LANG_DEFAULT')); if (\Validate::isLoadedObject($addProductObject)) { $addProduct['name'] = $addProductObject->name; $addProduct['manufacturer_name'] = $addProductObject->manufacturer_name; @@ -82,17 +82,17 @@ public function run() $addProduct['out_of_stock'] = $addProductObject->out_of_stock; $addProduct['minimal_quantity'] = 1; $addProduct['unit_price_ratio'] = 0; - $addProduct = \Product::getProductProperties((int)\Configuration::get('PS_LANG_DEFAULT'), $addProduct); + $addProduct = \Product::getProductProperties((int) \Configuration::get('PS_LANG_DEFAULT'), $addProduct); } } - if (isset($addProduct) && !in_array((int)\Tools::getValue('id_product'), $this->module->products)) { + if (isset($addProduct) && !in_array((int) \Tools::getValue('id_product'), $this->module->products)) { $ganalyticsDataHandler = new GanalyticsDataHandler( $this->context->cart->id, $this->context->shop->id ); - $this->module->products[] = (int)\Tools::getValue('id_product'); + $this->module->products[] = (int) \Tools::getValue('id_product'); $productWrapper = new ProductWrapper($this->context); $gaProducts = $productWrapper->wrapProduct($addProduct, $cart, 0, true); diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php index 38d0f55..177f65b 100644 --- a/classes/Hook/HookActionProductCancel.php +++ b/classes/Hook/HookActionProductCancel.php @@ -20,15 +20,14 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; - class HookActionProductCancel implements HookInterface { private $module; private $context; private $params; - public function __construct($module, $context) { + public function __construct($module, $context) + { $this->module = $module; $this->context = $context; } @@ -46,15 +45,15 @@ public function run() foreach ($quantityRefunded as $orderDetailId => $quantity) { // Display GA refund product $orderDetail = new \OrderDetail($orderDetailId); - $gaScripts .= 'MBG.add('.json_encode( - array( - 'id' => empty($orderDetail->product_attribute_id)?$orderDetail->product_id:$orderDetail->product_id.'-' . $orderDetail->product_attribute_id, - 'quantity' => $quantity) + $gaScripts .= 'MBG.add(' . json_encode( + [ + 'id' => empty($orderDetail->product_attribute_id) ? $orderDetail->product_id : $orderDetail->product_id . '-' . $orderDetail->product_attribute_id, + 'quantity' => $quantity, ] ) - .');'; + . ');'; } - $this->context->cookie->ga_admin_refund = $gaScripts.'MBG.refundByProduct('.json_encode(array('id' => $this->params['order']->id)).');'; + $this->context->cookie->ga_admin_refund = $gaScripts . 'MBG.refundByProduct(' . json_encode(['id' => $this->params['order']->id]) . ');'; } /** @@ -62,7 +61,8 @@ public function run() * * @param array $params */ - public function setParams($params) { + public function setParams($params) + { $this->params = $params; } } diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php index 32cf429..33b1ba5 100644 --- a/classes/Hook/HookDisplayBackOfficeHeader.php +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -20,17 +20,17 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; -use PrestaShop\Module\Ps_Googleanalytics\Wrapper\OrderWrapper; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\OrderWrapper; class HookDisplayBackOfficeHeader implements HookInterface { private $module; private $context; - public function __construct($module, $context) { + public function __construct($module, $context) + { $this->module = $module; $this->context = $context; } @@ -44,14 +44,14 @@ public function run() { $js = ''; if (strcmp(\Tools::getValue('configure'), $this->module->name) === 0) { - $this->context->controller->addCSS($this->module->getPathUri().'views/css/ganalytics.css'); + $this->context->controller->addCSS($this->module->getPathUri() . 'views/css/ganalytics.css'); } $ga_account_id = \Configuration::get('GA_ACCOUNT_ID'); if (!empty($ga_account_id) && $this->module->active) { $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); - $this->context->controller->addJs($this->module->getPathUri().'views/js/GoogleAnalyticActionLib.js'); + $this->context->controller->addJs($this->module->getPathUri() . 'views/js/GoogleAnalyticActionLib.js'); $this->context->smarty->assign('GA_ACCOUNT_ID', $ga_account_id); @@ -60,17 +60,17 @@ public function run() $ganalyticsRepository = new GanalyticsRepository(); if (\Tools::getValue('id_order')) { - $order = new \Order((int)\Tools::getValue('id_order')); + $order = new \Order((int) \Tools::getValue('id_order')); if (\Validate::isLoadedObject($order) && strtotime('+1 day', strtotime($order->date_add)) > time()) { $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) \Tools::getValue('id_order')); if ($gaOrderSent === false) { $ganalyticsRepository->addNewRow( - array( + [ 'id_order' => (int) \Tools::getValue('id_order'), 'id_shop' => (int) $this->context->shop->id, 'sent' => 0, 'date_add' => 'NOW()', - ) + ] ); } } @@ -83,14 +83,14 @@ public function run() $transaction = $orderWrapper->wrapOrder($row['id_order']); if (!empty($transaction)) { $ganalyticsRepository->updateData( - array( + [ 'date_add' => 'NOW()', - 'sent' => 1 - ), + 'sent' => 1, + ], 'id_order = ' . (int) $row['id_order'] . ' AND id_shop = ' . (int) $this->context->shop->id ); $transaction = json_encode($transaction); - $gaScripts .= 'MBG.addTransaction(' . $transaction.');'; + $gaScripts .= 'MBG.addTransaction(' . $transaction . ');'; } } } diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php index b465eeb..1cf7444 100644 --- a/classes/Hook/HookDisplayFooter.php +++ b/classes/Hook/HookDisplayFooter.php @@ -20,18 +20,18 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; -use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; -use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; +use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; class HookDisplayFooter implements HookInterface { private $module; private $context; - public function __construct($module, $context) { + public function __construct($module, $context) + { $this->module = $module; $this->context = $context; } @@ -55,17 +55,16 @@ public function run() $gacarts = $ganalyticsDataHandler->manageData('', 'R'); $controller_name = \Tools::getValue('controller'); - if (count($gacarts)>0 && $controller_name!='product') { + if (count($gacarts) > 0 && $controller_name != 'product') { $this->module->filterable = 0; foreach ($gacarts as $gacart) { - if (isset($gacart['quantity'])) - { + if (isset($gacart['quantity'])) { if ($gacart['quantity'] > 0) { - $gaScripts .= 'MBG.addToCart('.json_encode($gacart).');'; + $gaScripts .= 'MBG.addToCart(' . json_encode($gacart) . ');'; } elseif ($gacart['quantity'] < 0) { $gacart['quantity'] = abs($gacart['quantity']); - $gaScripts .= 'MBG.removeFromCart('.json_encode($gacart).');'; + $gaScripts .= 'MBG.removeFromCart(' . json_encode($gacart) . ');'; } } else { $gaScripts .= $gacart; @@ -77,7 +76,7 @@ public function run() $listing = $this->context->smarty->getTemplateVars('listing'); $productWrapper = new ProductWrapper($this->context); - $products = $productWrapper->wrapProductList($listing['products'], array(), true); + $products = $productWrapper->wrapProductList($listing['products'], [], true); if ($controller_name == 'order' || $controller_name == 'orderopc') { $this->module->js_state = 1; @@ -87,10 +86,10 @@ public function run() $step = 0; } $gaScripts .= $gaTools->addProductFromCheckout($products, $step); - $gaScripts .= 'MBG.addCheckout(\''.(int)$step.'\');'; + $gaScripts .= 'MBG.addCheckout(\'' . (int) $step . '\');'; } - $confirmation_hook_id = (int)\Hook::getIdByName('displayOrderConfirmation'); + $confirmation_hook_id = (int) \Hook::getIdByName('displayOrderConfirmation'); if (isset(\Hook::$executed_hooks[$confirmation_hook_id])) { $this->module->eligible = 1; } diff --git a/classes/Hook/HookDisplayFooterProduct.php b/classes/Hook/HookDisplayFooterProduct.php index b3913ed..4a89a89 100644 --- a/classes/Hook/HookDisplayFooterProduct.php +++ b/classes/Hook/HookDisplayFooterProduct.php @@ -20,10 +20,9 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; -use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; class HookDisplayFooterProduct implements HookInterface { @@ -31,7 +30,8 @@ class HookDisplayFooterProduct implements HookInterface private $context; private $params; - public function __construct($module, $context) { + public function __construct($module, $context) + { $this->module = $module; $this->context = $context; } @@ -57,10 +57,10 @@ public function run() // Add product view $productWrapper = new ProductWrapper($this->context); $gaProduct = $productWrapper->wrapProduct($this->params['product'], null, 0, true); - $js = 'MBG.addProductDetailView('.json_encode($gaProduct).');'; + $js = 'MBG.addProductDetailView(' . json_encode($gaProduct) . ');'; if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) > 0) { - $js .= $gaTools->addProductClickByHttpReferal(array($gaProduct)); + $js .= $gaTools->addProductClickByHttpReferal([$gaProduct]); } $this->module->js_state = 1; @@ -73,7 +73,8 @@ public function run() * * @param array $params */ - public function setParams($params) { + public function setParams($params) + { $this->params = $params; } } diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index 78aad52..9230014 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -20,8 +20,6 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; - class HookDisplayHeader implements HookInterface { private $module; @@ -29,7 +27,8 @@ class HookDisplayHeader implements HookInterface private $params; private $backOffice; - public function __construct($module, $context) { + public function __construct($module, $context) + { $this->module = $module; $this->context = $context; } @@ -42,28 +41,28 @@ public function __construct($module, $context) { public function run() { if (\Configuration::get('GA_ACCOUNT_ID')) { - $this->context->controller->addJs($this->module->getPathUri().'views/js/GoogleAnalyticActionLib.js'); + $this->context->controller->addJs($this->module->getPathUri() . 'views/js/GoogleAnalyticActionLib.js'); $shops = \Shop::getShops(); $isMultistoreActive = \Shop::isFeatureActive(); - $currentShopId = (int)\Context::getContext()->shop->id; + $currentShopId = (int) \Context::getContext()->shop->id; $userId = null; $gaCrossdomainEnabled = false; if (\Configuration::get('GA_USERID_ENABLED') && $this->context->customer && $this->context->customer->isLogged() ) { - $userId = (int)$this->context->customer->id; + $userId = (int) $this->context->customer->id; } $gaAnonymizeEnabled = \Configuration::get('GA_ANONYMIZE_ENABLED'); - if ((int)\Configuration::get('GA_CROSSDOMAIN_ENABLED') && $isMultistoreActive && count($shops) > 1) { + if ((int) \Configuration::get('GA_CROSSDOMAIN_ENABLED') && $isMultistoreActive && count($shops) > 1) { $gaCrossdomainEnabled = true; } $this->context->smarty->assign( - array( + [ 'backOffice' => $this->backOffice, 'currentShopId' => $currentShopId, 'userId' => $userId, @@ -71,8 +70,8 @@ public function run() 'shops' => $shops, 'gaCrossdomainEnabled' => $gaCrossdomainEnabled, 'gaAnonymizeEnabled' => $gaAnonymizeEnabled, - 'useSecureMode' => \Configuration::get('PS_SSL_ENABLED') - ) + 'useSecureMode' => \Configuration::get('PS_SSL_ENABLED'), + ] ); return $this->module->display( @@ -87,7 +86,8 @@ public function run() * * @param array $params */ - public function setParams($params) { + public function setParams($params) + { $this->module->params = $params; } @@ -96,7 +96,8 @@ public function setParams($params) { * * @param array $backOffice */ - public function setBackOffice($backOffice) { + public function setBackOffice($backOffice) + { $this->module->backOffice = $backOffice; } } diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php index 33e5472..a378c9f 100644 --- a/classes/Hook/HookDisplayHome.php +++ b/classes/Hook/HookDisplayHome.php @@ -20,7 +20,6 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Handler\ModuleHandler; @@ -31,7 +30,8 @@ class HookDisplayHome implements HookInterface private $module; private $context; - public function __construct($module, $context) { + public function __construct($module, $context) + { $this->module = $module; $this->context = $context; } @@ -56,10 +56,10 @@ public function run() $category->getProducts( (int) $this->context->language->id, 1, - (\Configuration::get('HOME_FEATURED_NBR') ? (int)\Configuration::get('HOME_FEATURED_NBR') : 8), + (\Configuration::get('HOME_FEATURED_NBR') ? (int) \Configuration::get('HOME_FEATURED_NBR') : 8), 'position' ), - array(), + [], true ); $gaScripts .= $gaTools->addProductImpression($homeFeaturedProducts) . $gaTools->addProductClick($homeFeaturedProducts); diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php index 4ddfbe3..1607c19 100644 --- a/classes/Hook/HookDisplayOrderConfirmation.php +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -20,11 +20,10 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -use PrestaShop\Module\Ps_Googleanalytics\Hooks\HookInterface; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; -use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; +use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; class HookDisplayOrderConfirmation implements HookInterface { @@ -32,7 +31,8 @@ class HookDisplayOrderConfirmation implements HookInterface private $context; private $params; - public function __construct($module, $context) { + public function __construct($module, $context) + { $this->module = $module; $this->context = $context; } @@ -50,40 +50,40 @@ public function run() $order = $this->params['objOrder']; } - if (\Validate::isLoadedObject($order) && $order->getCurrentState() != (int)\Configuration::get('PS_OS_ERROR')) { + if (\Validate::isLoadedObject($order) && $order->getCurrentState() != (int) \Configuration::get('PS_OS_ERROR')) { $ganalyticsRepository = new GanalyticsRepository(); $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) $order->id); if (false === $gaOrderSent) { $ganalyticsRepository->addNewRow( - array( + [ 'id_order' => (int) $order->id, 'id_shop' => (int) $this->context->shop->id, 'sent' => 0, 'date_add' => 'NOW()', - ) + ] ); if ($order->id_customer == $this->context->cookie->id_customer) { - $orderProducts = array(); + $orderProducts = []; $cart = new \Cart($order->id_cart); $gaTools = new GoogleAnalyticsTools(); $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); $productWrapper = new ProductWrapper($this->context); foreach ($cart->getProducts() as $order_product) { - $orderProducts[] = $productWrapper->wrapProduct($order_product, array(), 0, true); + $orderProducts[] = $productWrapper->wrapProduct($order_product, [], 0, true); } - $gaScripts = 'MBG.addCheckoutOption(3,\'' . $order->payment.'\');'; - $transaction = array( + $gaScripts = 'MBG.addCheckoutOption(3,\'' . $order->payment . '\');'; + $transaction = [ 'id' => $order->id, 'affiliation' => (\Shop::isFeatureActive()) ? $this->context->shop->name : \Configuration::get('PS_SHOP_NAME'), 'revenue' => $order->total_paid, 'shipping' => $order->total_shipping, 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, - 'url' => $this->context->link->getModuleLink('ps_googleanalytics', 'ajax', array(), true), - 'customer' => $order->id_customer); + 'url' => $this->context->link->getModuleLink('ps_googleanalytics', 'ajax', [], true), + 'customer' => $order->id_customer, ]; $gaScripts .= $gaTools->addTransaction($orderProducts, $transaction); $this->module->js_state = 1; @@ -99,7 +99,8 @@ public function run() * * @param array $params */ - public function setParams($params) { + public function setParams($params) + { $this->params = $params; } } diff --git a/classes/Hook/HookInterface.php b/classes/Hook/HookInterface.php index d74b922..1099b33 100644 --- a/classes/Hook/HookInterface.php +++ b/classes/Hook/HookInterface.php @@ -20,7 +20,8 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -interface HookInterface { +interface HookInterface +{ /** * @param \Ps_Googleanalytics $module * @param \Context $context diff --git a/classes/Hook/index.php b/classes/Hook/index.php index 438906f..004c891 100644 --- a/classes/Hook/index.php +++ b/classes/Hook/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/classes/Repository/CarrierRepository.php b/classes/Repository/CarrierRepository.php index e3e601f..8cab772 100644 --- a/classes/Repository/CarrierRepository.php +++ b/classes/Repository/CarrierRepository.php @@ -33,7 +33,7 @@ public function findByCarrierId($carrierId) { return \Db::getInstance()->getValue( 'SELECT name - FROM `'._DB_PREFIX_.'carrier` + FROM `' . _DB_PREFIX_ . 'carrier` WHERE id_carrier = ' . (int) $carrierId ); } diff --git a/classes/Repository/GanalyticsDataRepository.php b/classes/Repository/GanalyticsDataRepository.php index 0df71fb..577f005 100644 --- a/classes/Repository/GanalyticsDataRepository.php +++ b/classes/Repository/GanalyticsDataRepository.php @@ -55,7 +55,7 @@ public function addNewRow($cartId, $shopId, $data) { return \Db::getInstance()->Execute( 'INSERT INTO `' . _DB_PREFIX_ . self::TABLE_NAME . '` (id_cart, id_shop, data) - VALUES(\'' . (int) $cartId . '\',\'' . (int) $shopId . '\',\'' . pSQL($data).'\') + VALUES(\'' . (int) $cartId . '\',\'' . (int) $shopId . '\',\'' . pSQL($data) . '\') ON DUPLICATE KEY UPDATE data = \'' . pSQL($data) . '\';' ); } diff --git a/classes/Repository/index.php b/classes/Repository/index.php index 438906f..004c891 100644 --- a/classes/Repository/index.php +++ b/classes/Repository/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/classes/Wrapper/OrderWrapper.php b/classes/Wrapper/OrderWrapper.php index 26b1aaa..c79578c 100644 --- a/classes/Wrapper/OrderWrapper.php +++ b/classes/Wrapper/OrderWrapper.php @@ -36,17 +36,17 @@ public function __construct($context) */ public function wrapOrder($id_order) { - $order = new \Order((int)$id_order); + $order = new \Order((int) $id_order); if (\Validate::isLoadedObject($order)) { - return array( + return [ 'id' => $id_order, 'affiliation' => \Shop::isFeatureActive() ? $this->context->shop->name : \Configuration::get('PS_SHOP_NAME'), 'revenue' => $order->total_paid, 'shipping' => $order->total_shipping, 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, 'url' => $this->context->link->getAdminLink('AdminGanalyticsAjax'), - 'customer' => $order->id_customer); + 'customer' => $order->id_customer, ]; } } } diff --git a/classes/Wrapper/ProductWrapper.php b/classes/Wrapper/ProductWrapper.php index 07928ba..569c097 100644 --- a/classes/Wrapper/ProductWrapper.php +++ b/classes/Wrapper/ProductWrapper.php @@ -34,15 +34,15 @@ public function __construct($context) /** * wrap products to provide a standard products information for google analytics script */ - public function wrapProductList($products, $extras = array(), $full = false) + public function wrapProductList($products, $extras = [], $full = false) { - $result_products = array(); + $result_products = []; if (!is_array($products)) { return; } $currency = new \Currency($this->context->currency->id); - $usetax = (\Product::getTaxCalculationMethod((int)$this->context->customer->id) != PS_TAX_EXC); + $usetax = (\Product::getTaxCalculationMethod((int) $this->context->customer->id) != PS_TAX_EXC); if (count($products) > 20) { $full = false; @@ -52,11 +52,11 @@ public function wrapProductList($products, $extras = array(), $full = false) foreach ($products as $index => $product) { if ($product instanceof Product) { - $product = (array)$product; + $product = (array) $product; } if (!isset($product['price'])) { - $product['price'] = (float)\Tools::displayPrice(\Product::getPriceStatic((int)$product['id_product'], $usetax), $currency); + $product['price'] = (float) \Tools::displayPrice(\Product::getPriceStatic((int) $product['id_product'], $usetax), $currency); } $result_products[] = $this->wrapProduct($product, $extras, $index, $full); } @@ -93,7 +93,7 @@ public function wrapProduct($product, $extras, $index = 0, $full = false) } if (!empty($product['id_product_attribute'])) { - $product_id .= '-'. $product['id_product_attribute']; + $product_id .= '-' . $product['id_product_attribute']; } $product_type = 'typical'; @@ -104,7 +104,7 @@ public function wrapProduct($product, $extras, $index = 0, $full = false) } if ($full) { - $ga_product = array( + $ga_product = [ 'id' => $product_id, 'name' => \Tools::str2url($product['name']), 'category' => \Tools::str2url($product['category']), @@ -115,14 +115,15 @@ public function wrapProduct($product, $extras, $index = 0, $full = false) 'quantity' => $product_qty, 'list' => \Tools::getValue('controller'), 'url' => isset($product['link']) ? urlencode($product['link']) : '', - 'price' => $product['price'] - ); + 'price' => $product['price'], + ]; } else { - $ga_product = array( + $ga_product = [ 'id' => $product_id, - 'name' => \Tools::str2url($product['name']) - ); + 'name' => \Tools::str2url($product['name']), + ]; } + return $ga_product; } } diff --git a/classes/Wrapper/WrapperInterface.php b/classes/Wrapper/WrapperInterface.php index baa0e74..371acb4 100644 --- a/classes/Wrapper/WrapperInterface.php +++ b/classes/Wrapper/WrapperInterface.php @@ -20,7 +20,8 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; -interface WrapperInterface { +interface WrapperInterface +{ /** * @param \Context $context */ diff --git a/classes/Wrapper/index.php b/classes/Wrapper/index.php index 438906f..004c891 100644 --- a/classes/Wrapper/index.php +++ b/classes/Wrapper/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/classes/index.php b/classes/index.php index 438906f..004c891 100644 --- a/classes/index.php +++ b/classes/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/controllers/admin/AdminGanalyticsAjax.php b/controllers/admin/AdminGanalyticsAjax.php index e111a95..3ba6a77 100644 --- a/controllers/admin/AdminGanalyticsAjax.php +++ b/controllers/admin/AdminGanalyticsAjax.php @@ -17,19 +17,17 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - class AdminGanalyticsAjaxController extends ModuleAdminController { public $ssl = true; public function init() { - $order = new Order((int)Tools::getValue('orderid')); + $order = new Order((int) Tools::getValue('orderid')); $context = Context::getContext(); - if (Validate::isLoadedObject($order) && (isset($context->employee->id) && $context->employee->id)) - { + if (Validate::isLoadedObject($order) && (isset($context->employee->id) && $context->employee->id)) { Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'ganalytics` SET sent = 1, date_add = NOW() WHERE id_order = '.(int)Tools::getValue('orderid') + UPDATE `' . _DB_PREFIX_ . 'ganalytics` SET sent = 1, date_add = NOW() WHERE id_order = ' . (int) Tools::getValue('orderid') ); die('OK'); } diff --git a/controllers/admin/index.php b/controllers/admin/index.php index 438906f..004c891 100644 --- a/controllers/admin/index.php +++ b/controllers/admin/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/controllers/front/ajax.php b/controllers/front/ajax.php index ebab034..1557d3a 100755 --- a/controllers/front/ajax.php +++ b/controllers/front/ajax.php @@ -17,10 +17,10 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - class ps_GoogleanalyticsAjaxModuleFrontController extends ModuleFrontController { public $ssl = true; + /* * @see FrontController::initContent() */ @@ -28,12 +28,12 @@ public function initContent() { parent::initContent(); - $order = new Order((int)Tools::getValue('orderid')); - if (!Validate::isLoadedObject($order) || $order->id_customer != (int)Tools::getValue('customer')) { + $order = new Order((int) Tools::getValue('orderid')); + if (!Validate::isLoadedObject($order) || $order->id_customer != (int) Tools::getValue('customer')) { die; } Db::getInstance()->execute( - 'UPDATE `'._DB_PREFIX_.'ganalytics` SET sent = 1, date_add = NOW() WHERE id_order = '.(int)Tools::getValue('orderid').' LIMIT 1' + 'UPDATE `' . _DB_PREFIX_ . 'ganalytics` SET sent = 1, date_add = NOW() WHERE id_order = ' . (int) Tools::getValue('orderid') . ' LIMIT 1' ); die; } diff --git a/controllers/front/index.php b/controllers/front/index.php index 438906f..004c891 100755 --- a/controllers/front/index.php +++ b/controllers/front/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/controllers/index.php b/controllers/index.php index 438906f..004c891 100755 --- a/controllers/index.php +++ b/controllers/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/index.php b/index.php index 438906f..004c891 100755 --- a/index.php +++ b/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 1398b12..e1a3cec 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -17,7 +17,6 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - if (!defined('_PS_VERSION_')) { exit; } @@ -34,7 +33,7 @@ class Ps_Googleanalytics extends Module public $js_state = 0; public $eligible = 0; public $filterable = 1; - public $products = array(); + public $products = []; public $_debug = 0; public $psVersionIs17; @@ -43,7 +42,7 @@ public function __construct() $this->name = 'ps_googleanalytics'; $this->tab = 'analytics_stats'; $this->version = '3.2.0'; - $this->ps_versions_compliancy = array('min' => '1.6', 'max' => _PS_VERSION_); + $this->ps_versions_compliancy = ['min' => '1.6', 'max' => _PS_VERSION_]; $this->author = 'PrestaShop'; $this->module_key = 'fd2aaefea84ac1bb512e6f1878d990b8'; $this->bootstrap = true; @@ -77,6 +76,7 @@ public function hookdisplayHeader($params, $back_office = false) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayHeader($this, $this->context); $hook->setBackOffice($back_office); + return $hook->run(); } @@ -87,6 +87,7 @@ public function hookdisplayOrderConfirmation($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayOrderConfirmation($this, $this->context); $hook->setParams($params); + return $hook->run(); } @@ -96,6 +97,7 @@ public function hookdisplayOrderConfirmation($params) public function hookdisplayFooter() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayFooter($this, $this->context); + return $hook->run(); } @@ -105,6 +107,7 @@ public function hookdisplayFooter() public function hookdisplayHome() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayHome($this, $this->context); + return $hook->run(); } @@ -115,6 +118,7 @@ public function hookdisplayFooterProduct($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayFooterProduct($this, $this->context); $hook->setParams($params); + return $hook->run(); } @@ -138,6 +142,7 @@ public function hookdisplayAdminOrder() public function hookdisplayBackOfficeHeader() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayBackOfficeHeader($this, $this->context); + return $hook->run(); } @@ -148,6 +153,7 @@ public function hookactionProductCancel($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionProductCancel($this, $this->context); $hook->setParams($params); + return $hook->run(); } @@ -157,6 +163,7 @@ public function hookactionProductCancel($params) public function hookactionCartSave() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCartSave($this, $this->context); + return $hook->run(); } @@ -164,6 +171,7 @@ public function hookactionCarrierProcess($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCarrierProcess($this, $this->context); $hook->setParams($params); + return $hook->run(); } @@ -173,10 +181,10 @@ protected function _debugLog($function, $log) return true; } - $myFile = _PS_MODULE_DIR_ . $this->name.'/logs/analytics.log'; + $myFile = _PS_MODULE_DIR_ . $this->name . '/logs/analytics.log'; $fh = fopen($myFile, 'a'); - fwrite($fh, date('F j, Y, g:i a').' ' . $function."\n"); - fwrite($fh, print_r($log, true)."\n\n"); + fwrite($fh, date('F j, Y, g:i a') . ' ' . $function . "\n"); + fwrite($fh, print_r($log, true) . "\n\n"); fclose($fh); } diff --git a/tests/index.php b/tests/index.php new file mode 100644 index 0000000..004c891 --- /dev/null +++ b/tests/index.php @@ -0,0 +1,28 @@ + + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ +header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/tests/phpstan/index.php b/tests/phpstan/index.php new file mode 100644 index 0000000..004c891 --- /dev/null +++ b/tests/phpstan/index.php @@ -0,0 +1,28 @@ + + * @copyright 2007-2020 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + * International Registered Trademark & Property of PrestaShop SA + */ +header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit; diff --git a/tests/phpstan/phpstan.neon b/tests/phpstan/phpstan.neon new file mode 100644 index 0000000..e87f706 --- /dev/null +++ b/tests/phpstan/phpstan.neon @@ -0,0 +1,11 @@ +includes: + - %currentWorkingDirectory%/vendor/prestashop/php-dev-tools/phpstan/extension.neon + +parameters: + paths: + # We consider that the extension file will be stored the folder test/phpstan + # From Phpstan 0.12, paths are relative to the .neon file. + # - ../../classes + # - ../../controllers + reportUnmatchedIgnoredErrors: false + level: max diff --git a/translations/de.php b/translations/de.php index 235c286..51830fd 100644 --- a/translations/de.php +++ b/translations/de.php @@ -17,10 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - - global $_MODULE; -$_MODULE = array(); +$_MODULE = []; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_7510f8b22dd3e10476096425f78d4239'] = 'Sie haben Ihre Google Analytics-ID noch nicht eingestellt'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_aba1a7971f85c725ba4aed21343eeb4b'] = 'Integrieren Sie das Google Analytics-Skript in Ihren Shop'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_fa214007826415a21a8456e3e09f999d'] = 'Sie sind sicher, dass Sie Ihre Daten löschen wollen?'; diff --git a/translations/en.php b/translations/en.php index c6a195a..52440e2 100644 --- a/translations/en.php +++ b/translations/en.php @@ -17,10 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - - global $_MODULE; -$_MODULE = array(); +$_MODULE = []; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_7510f8b22dd3e10476096425f78d4239'] = 'You have not yet set your Google Analytics ID'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_aba1a7971f85c725ba4aed21343eeb4b'] = 'Integrate Google Analytics script into your shop'; @@ -69,5 +67,4 @@ $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_6a47891ae07f45802bc948d2618e36f5'] = 'Check the \'Case sensitive\' option'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_d2d0c38d112e1d775057388122ae7545'] = 'Save this new goal'; - return $_MODULE; diff --git a/translations/es.php b/translations/es.php index a8d8982..85fd998 100644 --- a/translations/es.php +++ b/translations/es.php @@ -17,10 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - - global $_MODULE; -$_MODULE = array(); +$_MODULE = []; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_7510f8b22dd3e10476096425f78d4239'] = 'Aún no ha configurado su ID de Google Analytics'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_aba1a7971f85c725ba4aed21343eeb4b'] = 'Integrar el script de Google Analytics en su tienda'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_fa214007826415a21a8456e3e09f999d'] = '¿Está seguro de querer eliminar todos sus datos?'; diff --git a/translations/fr.php b/translations/fr.php index 9675fca..07d14aa 100644 --- a/translations/fr.php +++ b/translations/fr.php @@ -17,10 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - - global $_MODULE; -$_MODULE = array(); +$_MODULE = []; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_7510f8b22dd3e10476096425f78d4239'] = 'Vous n\'avez pas encore renseigné votre ID Google Analytics'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_aba1a7971f85c725ba4aed21343eeb4b'] = 'Intègre le script de Google Analytics à votre boutique'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_fa214007826415a21a8456e3e09f999d'] = 'Etes-vous sûr de vouloir supprimer vos paramètres ?'; diff --git a/translations/index.php b/translations/index.php index 438906f..004c891 100755 --- a/translations/index.php +++ b/translations/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/translations/it.php b/translations/it.php index c269f53..7154e56 100644 --- a/translations/it.php +++ b/translations/it.php @@ -17,10 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - - global $_MODULE; -$_MODULE = array(); +$_MODULE = []; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_7510f8b22dd3e10476096425f78d4239'] = 'Non hai ancora impostato l\'ID di Google Analytics'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_aba1a7971f85c725ba4aed21343eeb4b'] = 'Integra lo script di Google Analytics nel tuo negozio'; $_MODULE['<{ps_googleanalytics}prestashop>ps_googleanalytics_fa214007826415a21a8456e3e09f999d'] = 'Sei sicuro di voler cancellare i tuoi dati?'; diff --git a/upgrade/index.php b/upgrade/index.php index 438906f..004c891 100644 --- a/upgrade/index.php +++ b/upgrade/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/upgrade/install-3.1.0.php b/upgrade/install-3.1.0.php index 7fe207f..a5f8ddb 100644 --- a/upgrade/install-3.1.0.php +++ b/upgrade/install-3.1.0.php @@ -17,16 +17,18 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ -if (!defined('_PS_VERSION_')) - exit; +if (!defined('_PS_VERSION_')) { + exit; +} function upgrade_module_3_1_0($object) { Configuration::updateValue('GANALYTICS', '3.1.0'); + return Db::getInstance()->execute(' - CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'ganalytics_data` ( + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ganalytics_data` ( `id_cart` int(11) NOT NULL, `id_shop` int(11) NOT NULL, `data` TEXT DEFAULT NULL, PRIMARY KEY (`id_cart`) - ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8'); + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8'); } diff --git a/views/css/index.php b/views/css/index.php index 438906f..004c891 100644 --- a/views/css/index.php +++ b/views/css/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/views/img/index.php b/views/img/index.php index 438906f..004c891 100755 --- a/views/img/index.php +++ b/views/img/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/views/index.php b/views/index.php index 438906f..004c891 100755 --- a/views/index.php +++ b/views/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/views/js/index.php b/views/js/index.php index 438906f..004c891 100755 --- a/views/js/index.php +++ b/views/js/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/views/templates/admin/index.php b/views/templates/admin/index.php index 438906f..004c891 100755 --- a/views/templates/admin/index.php +++ b/views/templates/admin/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/views/templates/hook/index.php b/views/templates/hook/index.php index 438906f..004c891 100755 --- a/views/templates/hook/index.php +++ b/views/templates/hook/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); diff --git a/views/templates/index.php b/views/templates/index.php index 438906f..004c891 100755 --- a/views/templates/index.php +++ b/views/templates/index.php @@ -17,9 +17,8 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ - header('Expires: Mon, 26 Jul 1998 05:00:00 GMT'); -header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); From 368707dee36367703400fc97d220532a469eb4a2 Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 16:38:50 +0200 Subject: [PATCH 27/45] Fix many PHPSTAN issues --- classes/GoogleAnalyticsTools.php | 2 +- classes/Handler/GanalyticsJsHandler.php | 4 ++-- classes/Hook/HookActionProductCancel.php | 2 +- classes/Hook/HookDisplayFooter.php | 2 +- classes/Hook/HookDisplayHeader.php | 2 +- classes/Hook/HookDisplayHome.php | 2 +- classes/Hook/HookDisplayOrderConfirmation.php | 2 ++ .../Repository/GanalyticsDataRepository.php | 4 ++-- classes/Repository/GanalyticsRepository.php | 2 +- classes/Wrapper/ProductWrapper.php | 2 +- ps_googleanalytics.php | 19 +++++++++++++------ tests/phpstan/phpstan.neon | 19 +++++++++++-------- 12 files changed, 37 insertions(+), 25 deletions(-) diff --git a/classes/GoogleAnalyticsTools.php b/classes/GoogleAnalyticsTools.php index 489ff6e..8e8fe27 100644 --- a/classes/GoogleAnalyticsTools.php +++ b/classes/GoogleAnalyticsTools.php @@ -64,7 +64,7 @@ public function addTransaction($products, $order) /** * add product impression js and product click js * - * @param array ixed $products + * @param array $products * * @return string|void */ diff --git a/classes/Handler/GanalyticsJsHandler.php b/classes/Handler/GanalyticsJsHandler.php index 9ea97a0..544561e 100644 --- a/classes/Handler/GanalyticsJsHandler.php +++ b/classes/Handler/GanalyticsJsHandler.php @@ -25,7 +25,7 @@ class GanalyticsJsHandler private $module; private $context; - public function __construct(\Ps_googleanalytics $module, \Context $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; @@ -37,7 +37,7 @@ public function __construct(\Ps_googleanalytics $module, \Context $context) * @param string $jsCode * @param int $isBackoffice * - * @return string + * @return void|string */ public function generate($jsCode, $isBackoffice = 0) { diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php index 177f65b..1687473 100644 --- a/classes/Hook/HookActionProductCancel.php +++ b/classes/Hook/HookActionProductCancel.php @@ -35,7 +35,7 @@ public function __construct($module, $context) /** * run * - * @return string + * @return void */ public function run() { diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php index 1cf7444..0a600be 100644 --- a/classes/Hook/HookDisplayFooter.php +++ b/classes/Hook/HookDisplayFooter.php @@ -85,7 +85,7 @@ public function run() if (empty($step)) { $step = 0; } - $gaScripts .= $gaTools->addProductFromCheckout($products, $step); + $gaScripts .= $gaTools->addProductFromCheckout($products); $gaScripts .= 'MBG.addCheckout(\'' . (int) $step . '\');'; } diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index 9230014..facef1b 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -36,7 +36,7 @@ public function __construct($module, $context) /** * run * - * @return string + * @return void|string */ public function run() { diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php index a378c9f..14baa98 100644 --- a/classes/Hook/HookDisplayHome.php +++ b/classes/Hook/HookDisplayHome.php @@ -65,7 +65,7 @@ public function run() $gaScripts .= $gaTools->addProductImpression($homeFeaturedProducts) . $gaTools->addProductClick($homeFeaturedProducts); } - $this->js_state = 1; + $this->module->js_state = 1; return $gaTagHandler->generate( $gaTools->filter($gaScripts, $this->module->filterable) diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php index 1607c19..ba0dedd 100644 --- a/classes/Hook/HookDisplayOrderConfirmation.php +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -92,6 +92,8 @@ public function run() } } } + + return ''; } /** diff --git a/classes/Repository/GanalyticsDataRepository.php b/classes/Repository/GanalyticsDataRepository.php index 577f005..72ca2ec 100644 --- a/classes/Repository/GanalyticsDataRepository.php +++ b/classes/Repository/GanalyticsDataRepository.php @@ -47,7 +47,7 @@ public function findDataByCartIdAndShopId($cartId, $shopId) * * @param int $cartId * @param int $shopId - * @param array $data + * @param string $data * * @return bool */ @@ -66,7 +66,7 @@ public function addNewRow($cartId, $shopId, $data) * @param int $cartId * @param int $shopId * - * @return void + * @return bool */ public function deleteRow($cartId, $shopId) { diff --git a/classes/Repository/GanalyticsRepository.php b/classes/Repository/GanalyticsRepository.php index b05bacd..5f65387 100644 --- a/classes/Repository/GanalyticsRepository.php +++ b/classes/Repository/GanalyticsRepository.php @@ -61,7 +61,7 @@ public function findAllByShopIdAndDateAdd($shopId) * addNewRow * * @param array $data - * @param \Db $type + * @param int $type * * @return bool */ diff --git a/classes/Wrapper/ProductWrapper.php b/classes/Wrapper/ProductWrapper.php index 569c097..e4e4905 100644 --- a/classes/Wrapper/ProductWrapper.php +++ b/classes/Wrapper/ProductWrapper.php @@ -51,7 +51,7 @@ public function wrapProductList($products, $extras = [], $full = false) } foreach ($products as $index => $product) { - if ($product instanceof Product) { + if ($product instanceof \Product) { $product = (array) $product; } diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index e1a3cec..aad8e92 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -30,6 +30,16 @@ class Ps_Googleanalytics extends Module */ const PS_16_EQUIVALENT_MODULE = 'ganalytics'; + public $name; + public $tab; + public $version; + public $ps_versions_compliancy; + public $author; + public $module_key; + public $bootstrap; + public $displayName; + public $description; + public $confirmUninstall; public $js_state = 0; public $eligible = 0; public $filterable = 1; @@ -153,8 +163,7 @@ public function hookactionProductCancel($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionProductCancel($this, $this->context); $hook->setParams($params); - - return $hook->run(); + $hook->run(); } /** @@ -163,16 +172,14 @@ public function hookactionProductCancel($params) public function hookactionCartSave() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCartSave($this, $this->context); - - return $hook->run(); + $hook->run(); } public function hookactionCarrierProcess($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCarrierProcess($this, $this->context); $hook->setParams($params); - - return $hook->run(); + $hook->run(); } protected function _debugLog($function, $log) diff --git a/tests/phpstan/phpstan.neon b/tests/phpstan/phpstan.neon index e87f706..976ff60 100644 --- a/tests/phpstan/phpstan.neon +++ b/tests/phpstan/phpstan.neon @@ -1,11 +1,14 @@ includes: - - %currentWorkingDirectory%/vendor/prestashop/php-dev-tools/phpstan/extension.neon + - %currentWorkingDirectory%/vendor/prestashop/php-dev-tools/phpstan/ps-module-extension.neon parameters: - paths: - # We consider that the extension file will be stored the folder test/phpstan - # From Phpstan 0.12, paths are relative to the .neon file. - # - ../../classes - # - ../../controllers - reportUnmatchedIgnoredErrors: false - level: max + paths: + - ../../classes + - ../../controllers + - ../../ps_googleanalytics.php + reportUnmatchedIgnoredErrors: false + ignoreErrors: + - '#Property Ps_Googleanalytics::\$version \(float\) does not accept string.#' + - '#Strict comparison using === between false and Module will always evaluate to false.#' + + level: 5 From 69c25575761acb74b9a949003db8ef7cfaf3940b Mon Sep 17 00:00:00 2001 From: apacios Date: Thu, 7 May 2020 16:48:28 +0200 Subject: [PATCH 28/45] Improve controllers --- classes/Repository/GanalyticsRepository.php | 6 ++++-- controllers/admin/AdminGanalyticsAjax.php | 24 +++++++++++++++------ controllers/front/ajax.php | 22 ++++++++++++++----- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/classes/Repository/GanalyticsRepository.php b/classes/Repository/GanalyticsRepository.php index 5f65387..99b40fe 100644 --- a/classes/Repository/GanalyticsRepository.php +++ b/classes/Repository/GanalyticsRepository.php @@ -81,15 +81,17 @@ public function addNewRow(array $data, $type = \Db::INSERT_IGNORE) * * @param array $data * @param string $where + * @param int $limit * * @return bool */ - public function updateData($data, $where) + public function updateData($data, $where, $limit = 0) { return \Db::getInstance()->update( self::TABLE_NAME, $data, - $where + $where, + $limit ); } } diff --git a/controllers/admin/AdminGanalyticsAjax.php b/controllers/admin/AdminGanalyticsAjax.php index 3ba6a77..c15373f 100644 --- a/controllers/admin/AdminGanalyticsAjax.php +++ b/controllers/admin/AdminGanalyticsAjax.php @@ -17,20 +17,30 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ + +use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; + class AdminGanalyticsAjaxController extends ModuleAdminController { public $ssl = true; public function init() { - $order = new Order((int) Tools::getValue('orderid')); - $context = Context::getContext(); - if (Validate::isLoadedObject($order) && (isset($context->employee->id) && $context->employee->id)) { - Db::getInstance()->execute(' - UPDATE `' . _DB_PREFIX_ . 'ganalytics` SET sent = 1, date_add = NOW() WHERE id_order = ' . (int) Tools::getValue('orderid') + $orderId = (int) Tools::getValue('orderid'); + $order = new Order($orderId); + + if (Validate::isLoadedObject($order) && (isset($this->context->employee->id) && $this->context->employee->id)) { + (new GanalyticsRepository())->updateData( + [ + 'sent' => 1, + 'date_add' => 'NOW()', + ], + 'id_order = ' . $orderId ); - die('OK'); + + $this->ajaxDie('OK'); } - die('KO'); + + $this->ajaxDie('KO'); } } diff --git a/controllers/front/ajax.php b/controllers/front/ajax.php index 1557d3a..0e3042c 100755 --- a/controllers/front/ajax.php +++ b/controllers/front/ajax.php @@ -17,6 +17,9 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) * International Registered Trademark & Property of PrestaShop SA */ + +use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; + class ps_GoogleanalyticsAjaxModuleFrontController extends ModuleFrontController { public $ssl = true; @@ -28,13 +31,22 @@ public function initContent() { parent::initContent(); - $order = new Order((int) Tools::getValue('orderid')); + $orderId = (int) Tools::getValue('orderid'); + $order = new Order($orderId); + if (!Validate::isLoadedObject($order) || $order->id_customer != (int) Tools::getValue('customer')) { - die; + $this->ajaxDie('KO'); } - Db::getInstance()->execute( - 'UPDATE `' . _DB_PREFIX_ . 'ganalytics` SET sent = 1, date_add = NOW() WHERE id_order = ' . (int) Tools::getValue('orderid') . ' LIMIT 1' + + (new GanalyticsRepository())->updateData( + [ + 'sent' => 1, + 'date_add' => 'NOW()', + ], + 'id_order = ' . $orderId, + 1 ); - die; + + $this->ajaxDie('OK'); } } From 44d55e7f25be923e4505f16b2c8b36d91c7d5d56 Mon Sep 17 00:00:00 2001 From: apacios Date: Mon, 11 May 2020 12:07:39 +0200 Subject: [PATCH 29/45] Add object name in hook constructors --- classes/Hook/HookActionCarrierProcess.php | 2 +- classes/Hook/HookActionCartSave.php | 2 +- classes/Hook/HookActionProductCancel.php | 2 +- classes/Hook/HookDisplayBackOfficeHeader.php | 2 +- classes/Hook/HookDisplayFooter.php | 2 +- classes/Hook/HookDisplayFooterProduct.php | 2 +- classes/Hook/HookDisplayHeader.php | 2 +- classes/Hook/HookDisplayHome.php | 2 +- classes/Hook/HookDisplayOrderConfirmation.php | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/classes/Hook/HookActionCarrierProcess.php b/classes/Hook/HookActionCarrierProcess.php index 9f54060..f30a6f1 100644 --- a/classes/Hook/HookActionCarrierProcess.php +++ b/classes/Hook/HookActionCarrierProcess.php @@ -29,7 +29,7 @@ class HookActionCarrierProcess implements HookInterface private $context; private $params; - public function __construct($module, $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; diff --git a/classes/Hook/HookActionCartSave.php b/classes/Hook/HookActionCartSave.php index 14f5099..c38d584 100644 --- a/classes/Hook/HookActionCartSave.php +++ b/classes/Hook/HookActionCartSave.php @@ -28,7 +28,7 @@ class HookActionCartSave implements HookInterface private $module; private $context; - public function __construct($module, $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php index 1687473..05467b3 100644 --- a/classes/Hook/HookActionProductCancel.php +++ b/classes/Hook/HookActionProductCancel.php @@ -26,7 +26,7 @@ class HookActionProductCancel implements HookInterface private $context; private $params; - public function __construct($module, $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php index 33b1ba5..ac6f2a6 100644 --- a/classes/Hook/HookDisplayBackOfficeHeader.php +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -29,7 +29,7 @@ class HookDisplayBackOfficeHeader implements HookInterface private $module; private $context; - public function __construct($module, $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php index 0a600be..b17b0a5 100644 --- a/classes/Hook/HookDisplayFooter.php +++ b/classes/Hook/HookDisplayFooter.php @@ -30,7 +30,7 @@ class HookDisplayFooter implements HookInterface private $module; private $context; - public function __construct($module, $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; diff --git a/classes/Hook/HookDisplayFooterProduct.php b/classes/Hook/HookDisplayFooterProduct.php index 4a89a89..4df6b51 100644 --- a/classes/Hook/HookDisplayFooterProduct.php +++ b/classes/Hook/HookDisplayFooterProduct.php @@ -30,7 +30,7 @@ class HookDisplayFooterProduct implements HookInterface private $context; private $params; - public function __construct($module, $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index facef1b..89de8c4 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -27,7 +27,7 @@ class HookDisplayHeader implements HookInterface private $params; private $backOffice; - public function __construct($module, $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php index 14baa98..8ca389b 100644 --- a/classes/Hook/HookDisplayHome.php +++ b/classes/Hook/HookDisplayHome.php @@ -30,7 +30,7 @@ class HookDisplayHome implements HookInterface private $module; private $context; - public function __construct($module, $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php index ba0dedd..cfc6b89 100644 --- a/classes/Hook/HookDisplayOrderConfirmation.php +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -31,7 +31,7 @@ class HookDisplayOrderConfirmation implements HookInterface private $context; private $params; - public function __construct($module, $context) + public function __construct(\Ps_Googleanalytics $module, \Context $context) { $this->module = $module; $this->context = $context; From 7005ce83638f1a9bb447e0ee52235d1e8fa1f17c Mon Sep 17 00:00:00 2001 From: apacios Date: Mon, 11 May 2020 12:12:53 +0200 Subject: [PATCH 30/45] Add object name in wrappers constructor --- classes/Wrapper/OrderWrapper.php | 2 +- classes/Wrapper/ProductWrapper.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/Wrapper/OrderWrapper.php b/classes/Wrapper/OrderWrapper.php index c79578c..9e8f280 100644 --- a/classes/Wrapper/OrderWrapper.php +++ b/classes/Wrapper/OrderWrapper.php @@ -26,7 +26,7 @@ class OrderWrapper implements WrapperInterface { private $context; - public function __construct($context) + public function __construct(\Context $context) { $this->context = $context; } diff --git a/classes/Wrapper/ProductWrapper.php b/classes/Wrapper/ProductWrapper.php index e4e4905..408b53f 100644 --- a/classes/Wrapper/ProductWrapper.php +++ b/classes/Wrapper/ProductWrapper.php @@ -26,7 +26,7 @@ class ProductWrapper implements WrapperInterface { private $context; - public function __construct($context) + public function __construct(\Context $context) { $this->context = $context; } From 1ad38c41095c27e9da72d69b76fd483456fd748e Mon Sep 17 00:00:00 2001 From: apacios Date: Mon, 11 May 2020 12:22:36 +0200 Subject: [PATCH 31/45] Update carrier repository by adding the table name as a const --- classes/Repository/CarrierRepository.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/classes/Repository/CarrierRepository.php b/classes/Repository/CarrierRepository.php index 8cab772..6ddd93d 100644 --- a/classes/Repository/CarrierRepository.php +++ b/classes/Repository/CarrierRepository.php @@ -22,6 +22,8 @@ class CarrierRepository { + const TABLE_NAME = 'carrier'; + /** * findByCarrierId * @@ -33,7 +35,7 @@ public function findByCarrierId($carrierId) { return \Db::getInstance()->getValue( 'SELECT name - FROM `' . _DB_PREFIX_ . 'carrier` + FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '` WHERE id_carrier = ' . (int) $carrierId ); } From 17a4eb76ba8b9cbd47c542994e4480f14a14485b Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 12 May 2020 15:13:26 +0200 Subject: [PATCH 32/45] Fix lastest PHPSTAN issues --- classes/Handler/ModuleHandler.php | 3 +-- classes/Hook/HookActionProductCancel.php | 6 +++++- ps_googleanalytics.php | 4 ++-- tests/phpstan/phpstan.neon | 4 ++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/classes/Handler/ModuleHandler.php b/classes/Handler/ModuleHandler.php index dc97fd0..9725c15 100644 --- a/classes/Handler/ModuleHandler.php +++ b/classes/Handler/ModuleHandler.php @@ -77,8 +77,7 @@ public function uninstallModule($moduleName) }; $parentUninstallClosure = $parentUninstallClosure->bindTo($oldModule, get_class($oldModule)); - $parentUninstallClosure(); - return true; + return $parentUninstallClosure(); } } diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php index 05467b3..7b9931c 100644 --- a/classes/Hook/HookActionProductCancel.php +++ b/classes/Hook/HookActionProductCancel.php @@ -53,7 +53,11 @@ public function run() . ');'; } - $this->context->cookie->ga_admin_refund = $gaScripts . 'MBG.refundByProduct(' . json_encode(['id' => $this->params['order']->id]) . ');'; + $this->context->cookie->__set( + 'ga_admin_refund', + $gaScripts . 'MBG.refundByProduct(' . json_encode(['id' => $this->params['order']->id]) . ');' + ); + $this->context->cookie->write(); } /** diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index aad8e92..f1cdb84 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -140,10 +140,10 @@ public function hookdisplayAdminOrder() $gaTagHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler($this, $this->context); echo $gaTagHandler->generate( - $this->context->cookie->ga_admin_refund, + $this->context->cookie->__get('ga_admin_refund'), 1 ); - unset($this->context->cookie->ga_admin_refund); + $this->context->cookie->__unset('ga_admin_refund'); } /** diff --git a/tests/phpstan/phpstan.neon b/tests/phpstan/phpstan.neon index 976ff60..8ef6362 100644 --- a/tests/phpstan/phpstan.neon +++ b/tests/phpstan/phpstan.neon @@ -10,5 +10,9 @@ parameters: ignoreErrors: - '#Property Ps_Googleanalytics::\$version \(float\) does not accept string.#' - '#Strict comparison using === between false and Module will always evaluate to false.#' + - + message: '#Access to an undefined property object::\$shop.#' + path: ../../classes/Hook/HookActionCartSave.php + - '#PrestaShop.Module.Ps_Googleanalytics.Handler.ModuleHandler::uninstallModule\(\) calls parent::uninstall\(\) but PrestaShop.Module.Ps_Googleanalytics.Handler.ModuleHandler does not extend any class.#' level: 5 From 3e827d9aee1378d1702c55292fedd147cdca197c Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 12 May 2020 15:16:52 +0200 Subject: [PATCH 33/45] Add Github action to check PHPSTAN and PHP CS results --- .github/workflows/php.yml | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/php.yml diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..0b2b714 --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,47 @@ +name: PHP tests +on: [push, pull_request] +jobs: + php-linter: + name: PHP Syntax check 5.6|7.2|7.3 + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2.0.0 + - name: PHP syntax checker 5.6 + uses: prestashop/github-action-php-lint/5.6@master + - name: PHP syntax checker 7.2 + uses: prestashop/github-action-php-lint/7.2@master + - name: PHP syntax checker 7.3 + uses: prestashop/github-action-php-lint/7.3@master + php-cs-fixer: + name: PHP-CS-Fixer + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2.0.0 + - name: Run PHP-CS-Fixer + uses: prestashopcorp/github-action-php-cs-fixer@master + phpstan: + name: PHPStan + runs-on: ubuntu-latest + strategy: + matrix: + presta-versions: ['1.7.0.3', 'latest'] + steps: + - name: Checkout + uses: actions/checkout@v2.0.0 + - name: Cache vendor folder + uses: actions/cache@v1 + with: + path: vendor + key: php-${{ hashFiles('composer.lock') }} + - name: Cache composer folder + uses: actions/cache@v1 + with: + path: ~/.composer/cache + key: php-composer-cache + - run: composer install + - name: Pull PrestaShop files (Tag ${{ matrix.presta-versions }}) + run: docker run -tid --rm -v ps-volume:/var/www/html --name temp-ps prestashop/prestashop:${{ matrix.presta-versions }} + - name : Run PHPStan + run: docker run --rm --volumes-from temp-ps -v $PWD:/web/module -e _PS_ROOT_DIR_=/var/www/html --workdir=/web/module phpstan/phpstan:0.12 analyse --configuration=/web/module/tests/phpstan/phpstan.neon From f43d5884e2e6758edf367bc1df02ae0aa823c990 Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 12 May 2020 16:06:02 +0200 Subject: [PATCH 34/45] Apply PSR2 standard in ps_googleanalytics methods --- .gitignore | 4 +++- .gitmodules | 1 - .php_cs.cache | 1 - ps_googleanalytics.php | 20 ++++++++++---------- 4 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 .gitmodules delete mode 100644 .php_cs.cache diff --git a/.gitignore b/.gitignore index a725465..ca027f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -vendor/ \ No newline at end of file +vendor/ +.php_cs.cache +.prettyci.composer.json \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8b13789..0000000 --- a/.gitmodules +++ /dev/null @@ -1 +0,0 @@ - diff --git a/.php_cs.cache b/.php_cs.cache deleted file mode 100644 index 9b079db..0000000 --- a/.php_cs.cache +++ /dev/null @@ -1 +0,0 @@ -{"php":"7.4.5","version":"2.16.3:v2.16.3#83baf823a33a1cbd5416c8626935cf3f843c10b0","indent":" ","lineEnding":"\n","rules":{"array_syntax":{"syntax":"short"},"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_before_statement":{"statements":["return"]},"braces":{"allow_single_line_closure":true},"cast_spaces":{"space":"single"},"class_attributes_separation":{"elements":["method"]},"class_definition":{"single_line":true},"concat_space":{"spacing":"one"},"declare_equal_normalize":true,"function_typehint_space":true,"include":true,"increment_style":true,"lowercase_cast":true,"lowercase_static_reference":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":true,"native_function_casing":true,"native_function_type_declaration_casing":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["curly_brace_block","extra","parenthesis_brace_block","square_brace_block","throw","use"]},"no_leading_import_slash":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":true,"no_unneeded_curly_braces":{"namespaces":true},"no_unused_imports":true,"no_whitespace_before_comma_in_array":true,"no_whitespace_in_blank_line":true,"normalize_index_brace":true,"object_operator_without_whitespace":true,"ordered_imports":true,"php_unit_fqcn_annotation":true,"phpdoc_align":{"align":"left"},"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":true,"phpdoc_single_line_var_spacing":true,"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"return_type_declaration":true,"semicolon_after_instruction":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_class_element_per_statement":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_trait_insert_per_statement":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"ternary_operator_spaces":true,"trailing_comma_in_multiline_array":true,"trim_array_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"blank_line_after_namespace":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true,"error_suppression":{"mute_deprecation_error":false,"noise_remaining_usages":false,"noise_remaining_usages_exclude":[]},"non_printable_character":true},"hashes":{"tests\/index.php":2657184305,"tests\/phpstan\/index.php":2657184305,"index.php":2657184305,"classes\/Form\/index.php":2657184305,"classes\/Form\/ConfigurationForm.php":2607320067,"classes\/index.php":2657184305,"classes\/GoogleAnalyticsTools.php":2454784637,"classes\/Hook\/HookDisplayHome.php":1331593200,"classes\/Hook\/index.php":2657184305,"classes\/Hook\/HookDisplayFooter.php":1377542451,"classes\/Hook\/HookDisplayBackOfficeHeader.php":62664555,"classes\/Hook\/HookDisplayHeader.php":3203340892,"classes\/Hook\/HookDisplayOrderConfirmation.php":3859977258,"classes\/Hook\/HookActionCarrierProcess.php":4156494746,"classes\/Hook\/HookActionProductCancel.php":1967332341,"classes\/Hook\/HookInterface.php":2350618434,"classes\/Hook\/HookDisplayFooterProduct.php":1036485983,"classes\/Hook\/HookActionCartSave.php":4029443709,"classes\/Handler\/index.php":2657184305,"classes\/Handler\/ModuleHandler.php":1844907236,"classes\/Handler\/GanalyticsJsHandler.php":2208903929,"classes\/Handler\/GanalyticsDataHandler.php":2818967706,"classes\/Database\/index.php":2657184305,"classes\/Database\/Uninstall.php":3995443079,"classes\/Database\/Install.php":1503889843,"classes\/Wrapper\/index.php":2657184305,"classes\/Wrapper\/ProductWrapper.php":3918842904,"classes\/Wrapper\/WrapperInterface.php":3165813164,"classes\/Wrapper\/OrderWrapper.php":2236500015,"classes\/Repository\/GanalyticsDataRepository.php":693896051,"classes\/Repository\/index.php":2657184305,"classes\/Repository\/GanalyticsRepository.php":396338050,"classes\/Repository\/CarrierRepository.php":595599245,"ps_googleanalytics.php":2237291875,"views\/img\/index.php":2657184305,"views\/index.php":2657184305,"views\/css\/index.php":2657184305,"views\/js\/index.php":2657184305,"views\/templates\/hook\/index.php":2657184305,"views\/templates\/index.php":2657184305,"views\/templates\/admin\/index.php":2657184305,"upgrade\/install-3.1.0.php":4083663024,"upgrade\/index.php":2657184305,"translations\/index.php":2657184305,"translations\/fr.php":4138912457,"translations\/de.php":4244071240,"translations\/it.php":1991868331,"translations\/es.php":2343815783,"translations\/en.php":1605709074,"controllers\/index.php":2657184305,"controllers\/front\/index.php":2657184305,"controllers\/front\/ajax.php":1920076942,"controllers\/admin\/index.php":2657184305,"controllers\/admin\/AdminGanalyticsAjax.php":1493758573}} \ No newline at end of file diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index f1cdb84..9afc5ff 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -82,7 +82,7 @@ public function getContent() return $this->display(__FILE__, './views/templates/admin/configuration.tpl') . $formOutput; } - public function hookdisplayHeader($params, $back_office = false) + public function hookDisplayHeader($params, $back_office = false) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayHeader($this, $this->context); $hook->setBackOffice($back_office); @@ -93,7 +93,7 @@ public function hookdisplayHeader($params, $back_office = false) /** * To track transactions */ - public function hookdisplayOrderConfirmation($params) + public function hookDisplayOrderConfirmation($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayOrderConfirmation($this, $this->context); $hook->setParams($params); @@ -104,7 +104,7 @@ public function hookdisplayOrderConfirmation($params) /** * hook footer to load JS script for standards actions such as product clicks */ - public function hookdisplayFooter() + public function hookDisplayFooter() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayFooter($this, $this->context); @@ -114,7 +114,7 @@ public function hookdisplayFooter() /** * hook home to display generate the product list associated to home featured, news products and best sellers Modules */ - public function hookdisplayHome() + public function hookDisplayHome() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayHome($this, $this->context); @@ -124,7 +124,7 @@ public function hookdisplayHome() /** * hook product page footer to load JS for product details view */ - public function hookdisplayFooterProduct($params) + public function hookDisplayFooterProduct($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayFooterProduct($this, $this->context); $hook->setParams($params); @@ -135,7 +135,7 @@ public function hookdisplayFooterProduct($params) /** * Hook admin order to send transactions and refunds details */ - public function hookdisplayAdminOrder() + public function hookDisplayAdminOrder() { $gaTagHandler = new PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler($this, $this->context); @@ -149,7 +149,7 @@ public function hookdisplayAdminOrder() /** * admin office header to add google analytics js */ - public function hookdisplayBackOfficeHeader() + public function hookDisplayBackOfficeHeader() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookDisplayBackOfficeHeader($this, $this->context); @@ -159,7 +159,7 @@ public function hookdisplayBackOfficeHeader() /** * Hook admin office header to add google analytics js */ - public function hookactionProductCancel($params) + public function hookActionProductCancel($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionProductCancel($this, $this->context); $hook->setParams($params); @@ -169,13 +169,13 @@ public function hookactionProductCancel($params) /** * hook save cart event to implement addtocart and remove from cart functionality */ - public function hookactionCartSave() + public function hookActionCartSave() { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCartSave($this, $this->context); $hook->run(); } - public function hookactionCarrierProcess($params) + public function hookActionCarrierProcess($params) { $hook = new PrestaShop\Module\Ps_Googleanalytics\Hooks\HookActionCarrierProcess($this, $this->context); $hook->setParams($params); From 389150d9b69de1db8fb28ee3c63f9f35a1445e07 Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 12 May 2020 16:28:05 +0200 Subject: [PATCH 35/45] Improve PHPStan error handled --- .gitignore | 2 +- .prettyci.composer.json | 5 ----- classes/Hook/HookActionCartSave.php | 6 +++++- classes/Hook/HookActionProductCancel.php | 4 ++-- tests/phpstan/phpstan.neon | 3 --- 5 files changed, 8 insertions(+), 12 deletions(-) delete mode 100644 .prettyci.composer.json diff --git a/.gitignore b/.gitignore index ca027f8..caadd0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ vendor/ .php_cs.cache -.prettyci.composer.json \ No newline at end of file +.prettyci.composer.json diff --git a/.prettyci.composer.json b/.prettyci.composer.json deleted file mode 100644 index a4d7689..0000000 --- a/.prettyci.composer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "require-dev": { - "prestashop/php-dev-tools": "^2.0" - } -} diff --git a/classes/Hook/HookActionCartSave.php b/classes/Hook/HookActionCartSave.php index c38d584..b48ec1d 100644 --- a/classes/Hook/HookActionCartSave.php +++ b/classes/Hook/HookActionCartSave.php @@ -26,6 +26,10 @@ class HookActionCartSave implements HookInterface { private $module; + + /** + * @var \Context + */ private $context; public function __construct(\Ps_Googleanalytics $module, \Context $context) @@ -58,7 +62,7 @@ public function run() ]; $cartProducts = $this->context->cart->getProducts(); - if (isset($cartProducts) && count($cartProducts)) { + if (!empty($cartProducts)) { foreach ($cartProducts as $cartProduct) { if ($cartProduct['id_product'] == \Tools::getValue('id_product')) { $addProduct = $cartProduct; diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php index 7b9931c..8c01575 100644 --- a/classes/Hook/HookActionProductCancel.php +++ b/classes/Hook/HookActionProductCancel.php @@ -48,8 +48,8 @@ public function run() $gaScripts .= 'MBG.add(' . json_encode( [ 'id' => empty($orderDetail->product_attribute_id) ? $orderDetail->product_id : $orderDetail->product_id . '-' . $orderDetail->product_attribute_id, - 'quantity' => $quantity, ] - ) + 'quantity' => $quantity, + ]) . ');'; } diff --git a/tests/phpstan/phpstan.neon b/tests/phpstan/phpstan.neon index 8ef6362..7c7bf3c 100644 --- a/tests/phpstan/phpstan.neon +++ b/tests/phpstan/phpstan.neon @@ -10,9 +10,6 @@ parameters: ignoreErrors: - '#Property Ps_Googleanalytics::\$version \(float\) does not accept string.#' - '#Strict comparison using === between false and Module will always evaluate to false.#' - - - message: '#Access to an undefined property object::\$shop.#' - path: ../../classes/Hook/HookActionCartSave.php - '#PrestaShop.Module.Ps_Googleanalytics.Handler.ModuleHandler::uninstallModule\(\) calls parent::uninstall\(\) but PrestaShop.Module.Ps_Googleanalytics.Handler.ModuleHandler does not extend any class.#' level: 5 From 548e98ec52785c6b0440270b97f5fddb38ebff70 Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 12 May 2020 16:35:32 +0200 Subject: [PATCH 36/45] Bump to 4.0.0 --- config.xml | 2 +- ps_googleanalytics.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.xml b/config.xml index 866d46f..158b509 100644 --- a/config.xml +++ b/config.xml @@ -2,7 +2,7 @@ ps_googleanalytics - + diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index 9afc5ff..cd730bf 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -51,7 +51,7 @@ public function __construct() { $this->name = 'ps_googleanalytics'; $this->tab = 'analytics_stats'; - $this->version = '3.2.0'; + $this->version = '4.0.0'; $this->ps_versions_compliancy = ['min' => '1.6', 'max' => _PS_VERSION_]; $this->author = 'PrestaShop'; $this->module_key = 'fd2aaefea84ac1bb512e6f1878d990b8'; From ce5360b4c98470147fa01d0b4210368e9398b3cd Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 12 May 2020 16:35:52 +0200 Subject: [PATCH 37/45] Add Github action release builder --- .github/release-drafter.yml | 13 ++++++ .github/workflows/build-release.yml | 66 +++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/build-release.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..d56af65 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,13 @@ +name-template: v$NEXT_PATCH_VERSION +tag-template: v$NEXT_PATCH_VERSION +categories: + - title: 🔨 Improvements + label: enhancement + - title: 🐛 Bug Fixes + label: bug + - title: 🚀 New Features + label: Feature +change-template: '- #$NUMBER: $TITLE by @$AUTHOR' +template: | + # Changes + $CHANGES diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000..894dcec --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,66 @@ +name: Build & Release draft + +on: + pull_request: + push: + branches: + - master + +jobs: + deploy: + name: build dependencies & create artifact + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2.0.0 + - name: Build JS dependencies + uses: PrestaShopCorp/github-action-build-js/12@v1.0 + with: + cmd: npm + path: ./_dev + - name: Install composer dependencies + run: composer install --no-dev -o + - name: Clean-up project + uses: PrestaShopCorp/github-action-clean-before-deploy@v1.0 + - name: Create & upload artifact + uses: actions/upload-artifact@v1 + with: + name: ${{ github.event.repository.name }} + path: ../ + update_release_draft: + runs-on: ubuntu-latest + needs: [deploy] + if: github.event_name == 'push' + steps: + - name: Download artifact + uses: actions/download-artifact@v1 + with: + name: ${{ github.event.repository.name }} + - id: release_info + uses: toolmantim/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Prepare for Release + run: | + cd ${{ github.event.repository.name }} + zip -r ${{ github.event.repository.name }}.zip ${{ github.event.repository.name }} + - name: Clean existing assets + shell: bash + run: | + curl -fsSL https://github.com/github/hub/raw/master/script/get | bash -s 2.14.1 + assets=`bin/hub api -t repos/${{ github.repository }}/releases/${{ steps.release_info.outputs.id }}/assets | awk '/\].url/ { print $2 }'` + for asset in $assets + do + bin/hub api -X DELETE $asset + done + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Publish to GitHub Release + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.release_info.outputs.upload_url }} + asset_path: ./${{ github.event.repository.name }}/${{ github.event.repository.name }}.zip + asset_name: ${{ github.event.repository.name }}.zip + asset_content_type: application/zip From beed6e1de84dc68288efd2eeb92423da94cd2e50 Mon Sep 17 00:00:00 2001 From: apacios Date: Tue, 12 May 2020 16:57:34 +0200 Subject: [PATCH 38/45] Add PHPSTAN for 1.6 too --- .github/workflows/php.yml | 15 +++++++++++++-- tests/phpstan/phpstan-PS-1.6.neon | 14 ++++++++++++++ .../phpstan/{phpstan.neon => phpstan-PS-1.7.neon} | 0 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/phpstan/phpstan-PS-1.6.neon rename tests/phpstan/{phpstan.neon => phpstan-PS-1.7.neon} (100%) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 0b2b714..c061640 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -26,22 +26,33 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - presta-versions: ['1.7.0.3', 'latest'] + presta-versions: ['latest', '1.7.0.3', '1.6.1.21','1.6.1.0'] steps: - name: Checkout uses: actions/checkout@v2.0.0 + - name: Cache vendor folder uses: actions/cache@v1 with: path: vendor key: php-${{ hashFiles('composer.lock') }} + - name: Cache composer folder uses: actions/cache@v1 with: path: ~/.composer/cache key: php-composer-cache + - run: composer install + - name: Pull PrestaShop files (Tag ${{ matrix.presta-versions }}) run: docker run -tid --rm -v ps-volume:/var/www/html --name temp-ps prestashop/prestashop:${{ matrix.presta-versions }} + + - name: Select .neon file to run with PHPStan + id: neon + run: | + PS_VERSION=$(docker exec temp-ps bash -c 'echo "$PS_VERSION"') + [[ "${PS_VERSION:0:3}" != '1.7' ]] && echo ::set-output name=filename::phpstan-PS-1.6.neon || echo ::set-output name=filename::phpstan-PS-1.7.neon + - name : Run PHPStan - run: docker run --rm --volumes-from temp-ps -v $PWD:/web/module -e _PS_ROOT_DIR_=/var/www/html --workdir=/web/module phpstan/phpstan:0.12 analyse --configuration=/web/module/tests/phpstan/phpstan.neon + run: docker run --rm --volumes-from temp-ps -v $PWD:/web/module -e _PS_ROOT_DIR_=/var/www/html --workdir=/web/module phpstan/phpstan:0.12 analyse --configuration=/web/module/tests/phpstan/${{steps.neon.outputs.filename}} diff --git a/tests/phpstan/phpstan-PS-1.6.neon b/tests/phpstan/phpstan-PS-1.6.neon new file mode 100644 index 0000000..79ae554 --- /dev/null +++ b/tests/phpstan/phpstan-PS-1.6.neon @@ -0,0 +1,14 @@ +includes: + - %currentWorkingDirectory%/vendor/prestashop/php-dev-tools/phpstan/ps-module-extension.neon + +parameters: + paths: + - ../../classes + - ../../controllers + - ../../ps_googleanalytics.php + reportUnmatchedIgnoredErrors: false + ignoreErrors: + - '#Strict comparison using === between false and Module will always evaluate to false.#' + - '#PrestaShop.Module.Ps_Googleanalytics.Handler.ModuleHandler::uninstallModule\(\) calls parent::uninstall\(\) but PrestaShop.Module.Ps_Googleanalytics.Handler.ModuleHandler does not extend any class.#' + + level: 5 diff --git a/tests/phpstan/phpstan.neon b/tests/phpstan/phpstan-PS-1.7.neon similarity index 100% rename from tests/phpstan/phpstan.neon rename to tests/phpstan/phpstan-PS-1.7.neon From 0a4de7ba16e82281378766eb6d74d6e5fb065736 Mon Sep 17 00:00:00 2001 From: Adrien Pacios <35840830+apacios@users.noreply.github.com> Date: Thu, 28 May 2020 17:09:17 +0200 Subject: [PATCH 39/45] Update classes/Hook/HookDisplayHeader.php Co-authored-by: Progi1984 --- classes/Hook/HookDisplayHeader.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index 89de8c4..eb22f5a 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -40,7 +40,9 @@ public function __construct(\Ps_Googleanalytics $module, \Context $context) */ public function run() { - if (\Configuration::get('GA_ACCOUNT_ID')) { + if (!\Configuration::get('GA_ACCOUNT_ID')) { + return; + } $this->context->controller->addJs($this->module->getPathUri() . 'views/js/GoogleAnalyticActionLib.js'); $shops = \Shop::getShops(); From 5cd124189a6357cba032152ce4309e530ca11491 Mon Sep 17 00:00:00 2001 From: apacios Date: Fri, 29 May 2020 10:37:58 +0200 Subject: [PATCH 40/45] Fix PHP error syntax --- classes/Hook/HookDisplayHeader.php | 64 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index eb22f5a..dc0cf3f 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -43,44 +43,44 @@ public function run() if (!\Configuration::get('GA_ACCOUNT_ID')) { return; } - $this->context->controller->addJs($this->module->getPathUri() . 'views/js/GoogleAnalyticActionLib.js'); - $shops = \Shop::getShops(); - $isMultistoreActive = \Shop::isFeatureActive(); - $currentShopId = (int) \Context::getContext()->shop->id; - $userId = null; - $gaCrossdomainEnabled = false; + $this->context->controller->addJs($this->module->getPathUri() . 'views/js/GoogleAnalyticActionLib.js'); - if (\Configuration::get('GA_USERID_ENABLED') && - $this->context->customer && $this->context->customer->isLogged() - ) { - $userId = (int) $this->context->customer->id; - } + $shops = \Shop::getShops(); + $isMultistoreActive = \Shop::isFeatureActive(); + $currentShopId = (int) \Context::getContext()->shop->id; + $userId = null; + $gaCrossdomainEnabled = false; - $gaAnonymizeEnabled = \Configuration::get('GA_ANONYMIZE_ENABLED'); - - if ((int) \Configuration::get('GA_CROSSDOMAIN_ENABLED') && $isMultistoreActive && count($shops) > 1) { - $gaCrossdomainEnabled = true; - } + if (\Configuration::get('GA_USERID_ENABLED') && + $this->context->customer && $this->context->customer->isLogged() + ) { + $userId = (int) $this->context->customer->id; + } - $this->context->smarty->assign( - [ - 'backOffice' => $this->backOffice, - 'currentShopId' => $currentShopId, - 'userId' => $userId, - 'gaAccountId' => \Tools::safeOutput(\Configuration::get('GA_ACCOUNT_ID')), - 'shops' => $shops, - 'gaCrossdomainEnabled' => $gaCrossdomainEnabled, - 'gaAnonymizeEnabled' => $gaAnonymizeEnabled, - 'useSecureMode' => \Configuration::get('PS_SSL_ENABLED'), - ] - ); + $gaAnonymizeEnabled = \Configuration::get('GA_ANONYMIZE_ENABLED'); - return $this->module->display( - $this->module->getLocalPath() . $this->module->name, - 'ps_googleanalytics.tpl' - ); + if ((int) \Configuration::get('GA_CROSSDOMAIN_ENABLED') && $isMultistoreActive && count($shops) > 1) { + $gaCrossdomainEnabled = true; } + + $this->context->smarty->assign( + [ + 'backOffice' => $this->backOffice, + 'currentShopId' => $currentShopId, + 'userId' => $userId, + 'gaAccountId' => \Tools::safeOutput(\Configuration::get('GA_ACCOUNT_ID')), + 'shops' => $shops, + 'gaCrossdomainEnabled' => $gaCrossdomainEnabled, + 'gaAnonymizeEnabled' => $gaAnonymizeEnabled, + 'useSecureMode' => \Configuration::get('PS_SSL_ENABLED'), + ] + ); + + return $this->module->display( + $this->module->getLocalPath() . $this->module->name, + 'ps_googleanalytics.tpl' + ); } /** From db6d80b8831f50f676132c3b347b9b03e74a0b02 Mon Sep 17 00:00:00 2001 From: apacios Date: Fri, 29 May 2020 11:27:13 +0200 Subject: [PATCH 41/45] Using namespace when using Core Classes --- classes/Database/Install.php | 14 +++-- classes/Database/Uninstall.php | 4 +- classes/Form/ConfigurationForm.php | 51 +++++++++++-------- classes/Handler/GanalyticsJsHandler.php | 11 ++-- classes/Handler/ModuleHandler.php | 10 ++-- classes/Hook/HookActionCarrierProcess.php | 3 +- classes/Hook/HookActionCartSave.php | 40 ++++++++------- classes/Hook/HookActionProductCancel.php | 11 ++-- classes/Hook/HookDisplayBackOfficeHeader.php | 22 +++++--- classes/Hook/HookDisplayFooter.php | 14 +++-- classes/Hook/HookDisplayFooterProduct.php | 10 ++-- classes/Hook/HookDisplayHeader.php | 26 ++++++---- classes/Hook/HookDisplayHome.php | 10 ++-- classes/Hook/HookDisplayOrderConfirmation.php | 14 +++-- classes/Hook/HookInterface.php | 9 ++-- classes/Repository/CarrierRepository.php | 4 +- .../Repository/GanalyticsDataRepository.php | 8 +-- classes/Repository/GanalyticsRepository.php | 12 +++-- classes/Wrapper/OrderWrapper.php | 13 +++-- classes/Wrapper/ProductWrapper.php | 26 ++++++---- classes/Wrapper/WrapperInterface.php | 6 ++- 21 files changed, 200 insertions(+), 118 deletions(-) diff --git a/classes/Database/Install.php b/classes/Database/Install.php index fd698f5..bd1ef66 100644 --- a/classes/Database/Install.php +++ b/classes/Database/Install.php @@ -20,17 +20,21 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Database; +use Db; +use Ps_Googleanalytics; +use Shop; + class Install { /** - * @var \Ps_Googleanalytics + * @var Ps_Googleanalytics */ private $module; - public function __construct(\Ps_Googleanalytics $module) + public function __construct(Ps_Googleanalytics $module) { - if (\Shop::isFeatureActive()) { - \Shop::setContext(\Shop::CONTEXT_ALL); + if (Shop::isFeatureActive()) { + Shop::setContext(Shop::CONTEXT_ALL); } $this->module = $module; @@ -65,7 +69,7 @@ public function installTables() ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8'; foreach ($sql as $query) { - if (!\Db::getInstance()->execute($query)) { + if (!Db::getInstance()->execute($query)) { return false; } } diff --git a/classes/Database/Uninstall.php b/classes/Database/Uninstall.php index 2ced082..1d010d3 100644 --- a/classes/Database/Uninstall.php +++ b/classes/Database/Uninstall.php @@ -20,6 +20,8 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Database; +use Db; + class Uninstall { /** @@ -33,7 +35,7 @@ public function uninstallTables() $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'ganalytics_data`'; foreach ($sql as $query) { - if (!\Db::getInstance()->execute($query)) { + if (!Db::getInstance()->execute($query)) { return false; } } diff --git a/classes/Form/ConfigurationForm.php b/classes/Form/ConfigurationForm.php index 96b95f2..f1de6de 100644 --- a/classes/Form/ConfigurationForm.php +++ b/classes/Form/ConfigurationForm.php @@ -20,11 +20,18 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Form; +use AdminController; +use Configuration; +use HelperForm; +use Ps_Googleanalytics; +use Shop; +use Tools; + class ConfigurationForm { private $module; - public function __construct(\Ps_Googleanalytics $module) + public function __construct(Ps_Googleanalytics $module) { $this->module = $module; } @@ -37,18 +44,18 @@ public function __construct(\Ps_Googleanalytics $module) public function generate() { // Check if multistore is active - $is_multistore_active = \Shop::isFeatureActive(); + $is_multistore_active = Shop::isFeatureActive(); // Get default language - $default_lang = (int) \Configuration::get('PS_LANG_DEFAULT'); + $default_lang = (int) Configuration::get('PS_LANG_DEFAULT'); - $helper = new \HelperForm(); + $helper = new HelperForm(); // Module, token and currentIndex $helper->module = $this->module; $helper->name_controller = $this->module->name; - $helper->token = \Tools::getAdminTokenLite('AdminModules'); - $helper->currentIndex = \AdminController::$currentIndex . '&configure=' . $this->module->name; + $helper->token = Tools::getAdminTokenLite('AdminModules'); + $helper->currentIndex = AdminController::$currentIndex . '&configure=' . $this->module->name; // Language $helper->default_form_language = $default_lang; @@ -62,11 +69,11 @@ public function generate() $helper->toolbar_btn = [ 'save' => [ 'desc' => $this->module->l('Save'), - 'href' => \AdminController::$currentIndex . '&configure=' . $this->module->name . '&save' . $this->module->name . - '&token=' . \Tools::getAdminTokenLite('AdminModules'), + 'href' => AdminController::$currentIndex . '&configure=' . $this->module->name . '&save' . $this->module->name . + '&token=' . Tools::getAdminTokenLite('AdminModules'), ], 'back' => [ - 'href' => \AdminController::$currentIndex . '&token=' . \Tools::getAdminTokenLite('AdminModules'), + 'href' => AdminController::$currentIndex . '&token=' . Tools::getAdminTokenLite('AdminModules'), 'desc' => $this->module->l('Back to list'), ], ]; @@ -147,10 +154,10 @@ public function generate() } // Load current value - $helper->fields_value['GA_ACCOUNT_ID'] = \Configuration::get('GA_ACCOUNT_ID'); - $helper->fields_value['GA_USERID_ENABLED'] = \Configuration::get('GA_USERID_ENABLED'); - $helper->fields_value['GA_CROSSDOMAIN_ENABLED'] = \Configuration::get('GA_CROSSDOMAIN_ENABLED'); - $helper->fields_value['GA_ANONYMIZE_ENABLED'] = \Configuration::get('GA_ANONYMIZE_ENABLED'); + $helper->fields_value['GA_ACCOUNT_ID'] = Configuration::get('GA_ACCOUNT_ID'); + $helper->fields_value['GA_USERID_ENABLED'] = Configuration::get('GA_USERID_ENABLED'); + $helper->fields_value['GA_CROSSDOMAIN_ENABLED'] = Configuration::get('GA_CROSSDOMAIN_ENABLED'); + $helper->fields_value['GA_ANONYMIZE_ENABLED'] = Configuration::get('GA_ANONYMIZE_ENABLED'); return $helper->generateForm($fields_form); } @@ -163,29 +170,29 @@ public function generate() public function treat() { $treatmentResult = ''; - $gaAccountId = \Tools::getValue('GA_ACCOUNT_ID'); - $gaUserIdEnabled = \Tools::getValue('GA_USERID_ENABLED'); - $gaCrossdomainEnabled = \Tools::getValue('GA_CROSSDOMAIN_ENABLED'); - $gaAnonymizeEnabled = \Tools::getValue('GA_ANONYMIZE_ENABLED'); + $gaAccountId = Tools::getValue('GA_ACCOUNT_ID'); + $gaUserIdEnabled = Tools::getValue('GA_USERID_ENABLED'); + $gaCrossdomainEnabled = Tools::getValue('GA_CROSSDOMAIN_ENABLED'); + $gaAnonymizeEnabled = Tools::getValue('GA_ANONYMIZE_ENABLED'); if (!empty($gaAccountId)) { - \Configuration::updateValue('GA_ACCOUNT_ID', $gaAccountId); - \Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true); + Configuration::updateValue('GA_ACCOUNT_ID', $gaAccountId); + Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true); $treatmentResult .= $this->module->displayConfirmation($this->module->l('Account ID updated successfully')); } if (null !== $gaUserIdEnabled) { - \Configuration::updateValue('GA_USERID_ENABLED', (bool) $gaUserIdEnabled); + Configuration::updateValue('GA_USERID_ENABLED', (bool) $gaUserIdEnabled); $treatmentResult .= $this->module->displayConfirmation($this->module->l('Settings for User ID updated successfully')); } if (null !== $gaCrossdomainEnabled) { - \Configuration::updateValue('GA_CROSSDOMAIN_ENABLED', (bool) $gaCrossdomainEnabled); + Configuration::updateValue('GA_CROSSDOMAIN_ENABLED', (bool) $gaCrossdomainEnabled); $treatmentResult .= $this->module->displayConfirmation($this->module->l('Settings for User ID updated successfully')); } if (null !== $gaAnonymizeEnabled) { - \Configuration::updateValue('GA_ANONYMIZE_ENABLED', (bool) $gaAnonymizeEnabled); + Configuration::updateValue('GA_ANONYMIZE_ENABLED', (bool) $gaAnonymizeEnabled); $treatmentResult .= $this->module->displayConfirmation($this->module->l('Settings for Anonymize IP updated successfully')); } diff --git a/classes/Handler/GanalyticsJsHandler.php b/classes/Handler/GanalyticsJsHandler.php index 544561e..a75b189 100644 --- a/classes/Handler/GanalyticsJsHandler.php +++ b/classes/Handler/GanalyticsJsHandler.php @@ -20,12 +20,17 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Handler; +use Configuration; +use Context; +use Ps_Googleanalytics; +use Tools; + class GanalyticsJsHandler { private $module; private $context; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; @@ -41,11 +46,11 @@ public function __construct(\Ps_Googleanalytics $module, \Context $context) */ public function generate($jsCode, $isBackoffice = 0) { - if (\Configuration::get('GA_ACCOUNT_ID')) { + if (Configuration::get('GA_ACCOUNT_ID')) { $this->context->smarty->assign( [ 'jsCode' => $jsCode, - 'isoCode' => \Tools::safeOutput($this->context->currency->iso_code), + 'isoCode' => Tools::safeOutput($this->context->currency->iso_code), 'jsState' => $this->module->js_state, 'isBackoffice' => $isBackoffice, ] diff --git a/classes/Handler/ModuleHandler.php b/classes/Handler/ModuleHandler.php index 9725c15..8bf56ee 100644 --- a/classes/Handler/ModuleHandler.php +++ b/classes/Handler/ModuleHandler.php @@ -20,6 +20,8 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Handler; +use Module; + class ModuleHandler { /** @@ -31,13 +33,13 @@ class ModuleHandler */ public function isModuleEnabled($moduleName) { - $module = \Module::getInstanceByName($moduleName); + $module = Module::getInstanceByName($moduleName); if (false === $module) { return false; } - if (false === \Module::isInstalled($moduleName)) { + if (false === Module::isInstalled($moduleName)) { return false; } @@ -57,11 +59,11 @@ public function isModuleEnabled($moduleName) */ public function uninstallModule($moduleName) { - if (false === \Module::isInstalled($moduleName)) { + if (false === Module::isInstalled($moduleName)) { return false; } - $oldModule = \Module::getInstanceByName($moduleName); + $oldModule = Module::getInstanceByName($moduleName); if (false === $oldModule) { return false; diff --git a/classes/Hook/HookActionCarrierProcess.php b/classes/Hook/HookActionCarrierProcess.php index f30a6f1..1c52646 100644 --- a/classes/Hook/HookActionCarrierProcess.php +++ b/classes/Hook/HookActionCarrierProcess.php @@ -22,6 +22,7 @@ use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; use PrestaShop\Module\Ps_Googleanalytics\Repository\CarrierRepository; +use Ps_Googleanalytics; class HookActionCarrierProcess implements HookInterface { @@ -29,7 +30,7 @@ class HookActionCarrierProcess implements HookInterface private $context; private $params; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; diff --git a/classes/Hook/HookActionCartSave.php b/classes/Hook/HookActionCartSave.php index b48ec1d..c06e278 100644 --- a/classes/Hook/HookActionCartSave.php +++ b/classes/Hook/HookActionCartSave.php @@ -20,19 +20,25 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Configuration; +use Context; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; +use Product; +use Ps_Googleanalytics; +use Tools; +use Validate; class HookActionCartSave implements HookInterface { private $module; /** - * @var \Context + * @var Context */ private $context; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; @@ -49,30 +55,30 @@ public function run() return; } - if (!\Tools::getIsset('id_product')) { + if (!Tools::getIsset('id_product')) { return; } $cart = [ - 'controller' => \Tools::getValue('controller'), - 'addAction' => \Tools::getValue('add') ? 'add' : '', - 'removeAction' => \Tools::getValue('delete') ? 'delete' : '', - 'extraAction' => \Tools::getValue('op'), - 'qty' => (int) \Tools::getValue('qty', 1), + 'controller' => Tools::getValue('controller'), + 'addAction' => Tools::getValue('add') ? 'add' : '', + 'removeAction' => Tools::getValue('delete') ? 'delete' : '', + 'extraAction' => Tools::getValue('op'), + 'qty' => (int) Tools::getValue('qty', 1), ]; $cartProducts = $this->context->cart->getProducts(); if (!empty($cartProducts)) { foreach ($cartProducts as $cartProduct) { - if ($cartProduct['id_product'] == \Tools::getValue('id_product')) { + if ($cartProduct['id_product'] == Tools::getValue('id_product')) { $addProduct = $cartProduct; } } } if ($cart['removeAction'] == 'delete') { - $addProductObject = new \Product((int) \Tools::getValue('id_product'), true, (int) \Configuration::get('PS_LANG_DEFAULT')); - if (\Validate::isLoadedObject($addProductObject)) { + $addProductObject = new Product((int) Tools::getValue('id_product'), true, (int) Configuration::get('PS_LANG_DEFAULT')); + if (Validate::isLoadedObject($addProductObject)) { $addProduct['name'] = $addProductObject->name; $addProduct['manufacturer_name'] = $addProductObject->manufacturer_name; $addProduct['category'] = $addProductObject->category; @@ -81,29 +87,29 @@ public function run() $addProduct['link'] = $addProductObject->link_rewrite; $addProduct['price'] = $addProductObject->price; $addProduct['ean13'] = $addProductObject->ean13; - $addProduct['id_product'] = \Tools::getValue('id_product'); + $addProduct['id_product'] = Tools::getValue('id_product'); $addProduct['id_category_default'] = $addProductObject->id_category_default; $addProduct['out_of_stock'] = $addProductObject->out_of_stock; $addProduct['minimal_quantity'] = 1; $addProduct['unit_price_ratio'] = 0; - $addProduct = \Product::getProductProperties((int) \Configuration::get('PS_LANG_DEFAULT'), $addProduct); + $addProduct = Product::getProductProperties((int) Configuration::get('PS_LANG_DEFAULT'), $addProduct); } } - if (isset($addProduct) && !in_array((int) \Tools::getValue('id_product'), $this->module->products)) { + if (isset($addProduct) && !in_array((int) Tools::getValue('id_product'), $this->module->products)) { $ganalyticsDataHandler = new GanalyticsDataHandler( $this->context->cart->id, $this->context->shop->id ); - $this->module->products[] = (int) \Tools::getValue('id_product'); + $this->module->products[] = (int) Tools::getValue('id_product'); $productWrapper = new ProductWrapper($this->context); $gaProducts = $productWrapper->wrapProduct($addProduct, $cart, 0, true); if (array_key_exists('id_product_attribute', $gaProducts) && $gaProducts['id_product_attribute'] != '' && $gaProducts['id_product_attribute'] != 0) { $productId = $gaProducts['id_product_attribute']; } else { - $productId = \Tools::getValue('id_product'); + $productId = Tools::getValue('id_product'); } $gaCart = $ganalyticsDataHandler->manageData('', 'R'); @@ -116,7 +122,7 @@ public function run() } else { $gaProducts['quantity'] = $cart['qty'] * -1; } - } elseif (\Tools::getValue('step') <= 0) { // Sometimes cartsave is called in checkout + } elseif (Tools::getValue('step') <= 0) { // Sometimes cartsave is called in checkout if (array_key_exists($productId, $gaCart)) { $gaProducts['quantity'] = $gaCart[$productId]['quantity'] + $cart['qty']; } diff --git a/classes/Hook/HookActionProductCancel.php b/classes/Hook/HookActionProductCancel.php index 8c01575..f10617d 100644 --- a/classes/Hook/HookActionProductCancel.php +++ b/classes/Hook/HookActionProductCancel.php @@ -20,13 +20,18 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Context; +use OrderDetail; +use Ps_Googleanalytics; +use Tools; + class HookActionProductCancel implements HookInterface { private $module; private $context; private $params; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; @@ -39,12 +44,12 @@ public function __construct(\Ps_Googleanalytics $module, \Context $context) */ public function run() { - $quantityRefunded = \Tools::getValue('cancelQuantity'); + $quantityRefunded = Tools::getValue('cancelQuantity'); $gaScripts = ''; foreach ($quantityRefunded as $orderDetailId => $quantity) { // Display GA refund product - $orderDetail = new \OrderDetail($orderDetailId); + $orderDetail = new OrderDetail($orderDetailId); $gaScripts .= 'MBG.add(' . json_encode( [ 'id' => empty($orderDetail->product_attribute_id) ? $orderDetail->product_id : $orderDetail->product_id . '-' . $orderDetail->product_attribute_id, diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php index ac6f2a6..17258e3 100644 --- a/classes/Hook/HookDisplayBackOfficeHeader.php +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -20,16 +20,22 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Configuration; +use Context; +use Order; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\OrderWrapper; +use Ps_Googleanalytics; +use Tools; +use Validate; class HookDisplayBackOfficeHeader implements HookInterface { private $module; private $context; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; @@ -43,11 +49,11 @@ public function __construct(\Ps_Googleanalytics $module, \Context $context) public function run() { $js = ''; - if (strcmp(\Tools::getValue('configure'), $this->module->name) === 0) { + if (strcmp(Tools::getValue('configure'), $this->module->name) === 0) { $this->context->controller->addCSS($this->module->getPathUri() . 'views/css/ganalytics.css'); } - $ga_account_id = \Configuration::get('GA_ACCOUNT_ID'); + $ga_account_id = Configuration::get('GA_ACCOUNT_ID'); if (!empty($ga_account_id) && $this->module->active) { $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); @@ -59,14 +65,14 @@ public function run() if ($this->context->controller->controller_name == 'AdminOrders') { $ganalyticsRepository = new GanalyticsRepository(); - if (\Tools::getValue('id_order')) { - $order = new \Order((int) \Tools::getValue('id_order')); - if (\Validate::isLoadedObject($order) && strtotime('+1 day', strtotime($order->date_add)) > time()) { - $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) \Tools::getValue('id_order')); + if (Tools::getValue('id_order')) { + $order = new Order((int) Tools::getValue('id_order')); + if (Validate::isLoadedObject($order) && strtotime('+1 day', strtotime($order->date_add)) > time()) { + $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) Tools::getValue('id_order')); if ($gaOrderSent === false) { $ganalyticsRepository->addNewRow( [ - 'id_order' => (int) \Tools::getValue('id_order'), + 'id_order' => (int) Tools::getValue('id_order'), 'id_shop' => (int) $this->context->shop->id, 'sent' => 0, 'date_add' => 'NOW()', diff --git a/classes/Hook/HookDisplayFooter.php b/classes/Hook/HookDisplayFooter.php index b17b0a5..b720af8 100644 --- a/classes/Hook/HookDisplayFooter.php +++ b/classes/Hook/HookDisplayFooter.php @@ -20,17 +20,21 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Context; +use Hook; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; +use Ps_Googleanalytics; +use Tools; class HookDisplayFooter implements HookInterface { private $module; private $context; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; @@ -53,7 +57,7 @@ public function run() $gaScripts = ''; $this->module->js_state = 0; $gacarts = $ganalyticsDataHandler->manageData('', 'R'); - $controller_name = \Tools::getValue('controller'); + $controller_name = Tools::getValue('controller'); if (count($gacarts) > 0 && $controller_name != 'product') { $this->module->filterable = 0; @@ -81,7 +85,7 @@ public function run() if ($controller_name == 'order' || $controller_name == 'orderopc') { $this->module->js_state = 1; $this->module->eligible = 1; - $step = \Tools::getValue('step'); + $step = Tools::getValue('step'); if (empty($step)) { $step = 0; } @@ -89,8 +93,8 @@ public function run() $gaScripts .= 'MBG.addCheckout(\'' . (int) $step . '\');'; } - $confirmation_hook_id = (int) \Hook::getIdByName('displayOrderConfirmation'); - if (isset(\Hook::$executed_hooks[$confirmation_hook_id])) { + $confirmation_hook_id = (int) Hook::getIdByName('displayOrderConfirmation'); + if (isset(Hook::$executed_hooks[$confirmation_hook_id])) { $this->module->eligible = 1; } diff --git a/classes/Hook/HookDisplayFooterProduct.php b/classes/Hook/HookDisplayFooterProduct.php index 4df6b51..6ea03b1 100644 --- a/classes/Hook/HookDisplayFooterProduct.php +++ b/classes/Hook/HookDisplayFooterProduct.php @@ -20,9 +20,13 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Context; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; +use Product; +use Ps_Googleanalytics; +use Tools; class HookDisplayFooterProduct implements HookInterface { @@ -30,7 +34,7 @@ class HookDisplayFooterProduct implements HookInterface private $context; private $params; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; @@ -45,13 +49,13 @@ public function run() { $gaTools = new GoogleAnalyticsTools(); $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); - $controllerName = \Tools::getValue('controller'); + $controllerName = Tools::getValue('controller'); if ('product' !== $controllerName) { return ''; } - if ($this->params['product'] instanceof \Product) { + if ($this->params['product'] instanceof Product) { $this->params['product'] = (array) $this->params['product']; } // Add product view diff --git a/classes/Hook/HookDisplayHeader.php b/classes/Hook/HookDisplayHeader.php index dc0cf3f..96cc3d4 100644 --- a/classes/Hook/HookDisplayHeader.php +++ b/classes/Hook/HookDisplayHeader.php @@ -20,6 +20,12 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Configuration; +use Context; +use Ps_Googleanalytics; +use Shop; +use Tools; + class HookDisplayHeader implements HookInterface { private $module; @@ -27,7 +33,7 @@ class HookDisplayHeader implements HookInterface private $params; private $backOffice; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; @@ -40,27 +46,27 @@ public function __construct(\Ps_Googleanalytics $module, \Context $context) */ public function run() { - if (!\Configuration::get('GA_ACCOUNT_ID')) { + if (!Configuration::get('GA_ACCOUNT_ID')) { return; } $this->context->controller->addJs($this->module->getPathUri() . 'views/js/GoogleAnalyticActionLib.js'); - $shops = \Shop::getShops(); - $isMultistoreActive = \Shop::isFeatureActive(); - $currentShopId = (int) \Context::getContext()->shop->id; + $shops = Shop::getShops(); + $isMultistoreActive = Shop::isFeatureActive(); + $currentShopId = (int) Context::getContext()->shop->id; $userId = null; $gaCrossdomainEnabled = false; - if (\Configuration::get('GA_USERID_ENABLED') && + if (Configuration::get('GA_USERID_ENABLED') && $this->context->customer && $this->context->customer->isLogged() ) { $userId = (int) $this->context->customer->id; } - $gaAnonymizeEnabled = \Configuration::get('GA_ANONYMIZE_ENABLED'); + $gaAnonymizeEnabled = Configuration::get('GA_ANONYMIZE_ENABLED'); - if ((int) \Configuration::get('GA_CROSSDOMAIN_ENABLED') && $isMultistoreActive && count($shops) > 1) { + if ((int) Configuration::get('GA_CROSSDOMAIN_ENABLED') && $isMultistoreActive && count($shops) > 1) { $gaCrossdomainEnabled = true; } @@ -69,11 +75,11 @@ public function run() 'backOffice' => $this->backOffice, 'currentShopId' => $currentShopId, 'userId' => $userId, - 'gaAccountId' => \Tools::safeOutput(\Configuration::get('GA_ACCOUNT_ID')), + 'gaAccountId' => Tools::safeOutput(Configuration::get('GA_ACCOUNT_ID')), 'shops' => $shops, 'gaCrossdomainEnabled' => $gaCrossdomainEnabled, 'gaAnonymizeEnabled' => $gaAnonymizeEnabled, - 'useSecureMode' => \Configuration::get('PS_SSL_ENABLED'), + 'useSecureMode' => Configuration::get('PS_SSL_ENABLED'), ] ); diff --git a/classes/Hook/HookDisplayHome.php b/classes/Hook/HookDisplayHome.php index 8ca389b..c771542 100644 --- a/classes/Hook/HookDisplayHome.php +++ b/classes/Hook/HookDisplayHome.php @@ -20,17 +20,21 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Category; +use Configuration; +use Context; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Handler\ModuleHandler; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; +use Ps_Googleanalytics; class HookDisplayHome implements HookInterface { private $module; private $context; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; @@ -50,13 +54,13 @@ public function run() // Home featured products if ($moduleHandler->isModuleEnabled('ps_featuredproducts')) { - $category = new \Category($this->context->shop->getCategory(), $this->context->language->id); + $category = new Category($this->context->shop->getCategory(), $this->context->language->id); $productWrapper = new ProductWrapper($this->context); $homeFeaturedProducts = $productWrapper->wrapProductList( $category->getProducts( (int) $this->context->language->id, 1, - (\Configuration::get('HOME_FEATURED_NBR') ? (int) \Configuration::get('HOME_FEATURED_NBR') : 8), + (Configuration::get('HOME_FEATURED_NBR') ? (int) Configuration::get('HOME_FEATURED_NBR') : 8), 'position' ), [], diff --git a/classes/Hook/HookDisplayOrderConfirmation.php b/classes/Hook/HookDisplayOrderConfirmation.php index cfc6b89..4e693c2 100644 --- a/classes/Hook/HookDisplayOrderConfirmation.php +++ b/classes/Hook/HookDisplayOrderConfirmation.php @@ -20,10 +20,16 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Cart; +use Configuration; +use Context; use PrestaShop\Module\Ps_Googleanalytics\GoogleAnalyticsTools; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler; use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository; use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper; +use Ps_Googleanalytics; +use Shop; +use Validate; class HookDisplayOrderConfirmation implements HookInterface { @@ -31,7 +37,7 @@ class HookDisplayOrderConfirmation implements HookInterface private $context; private $params; - public function __construct(\Ps_Googleanalytics $module, \Context $context) + public function __construct(Ps_Googleanalytics $module, Context $context) { $this->module = $module; $this->context = $context; @@ -50,7 +56,7 @@ public function run() $order = $this->params['objOrder']; } - if (\Validate::isLoadedObject($order) && $order->getCurrentState() != (int) \Configuration::get('PS_OS_ERROR')) { + if (Validate::isLoadedObject($order) && $order->getCurrentState() != (int) Configuration::get('PS_OS_ERROR')) { $ganalyticsRepository = new GanalyticsRepository(); $gaOrderSent = $ganalyticsRepository->findGaOrderByOrderId((int) $order->id); @@ -66,7 +72,7 @@ public function run() if ($order->id_customer == $this->context->cookie->id_customer) { $orderProducts = []; - $cart = new \Cart($order->id_cart); + $cart = new Cart($order->id_cart); $gaTools = new GoogleAnalyticsTools(); $gaTagHandler = new GanalyticsJsHandler($this->module, $this->context); $productWrapper = new ProductWrapper($this->context); @@ -78,7 +84,7 @@ public function run() $gaScripts = 'MBG.addCheckoutOption(3,\'' . $order->payment . '\');'; $transaction = [ 'id' => $order->id, - 'affiliation' => (\Shop::isFeatureActive()) ? $this->context->shop->name : \Configuration::get('PS_SHOP_NAME'), + 'affiliation' => (Shop::isFeatureActive()) ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), 'revenue' => $order->total_paid, 'shipping' => $order->total_shipping, 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, diff --git a/classes/Hook/HookInterface.php b/classes/Hook/HookInterface.php index 1099b33..4a688cd 100644 --- a/classes/Hook/HookInterface.php +++ b/classes/Hook/HookInterface.php @@ -20,13 +20,16 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Context; +use Ps_Googleanalytics; + interface HookInterface { /** - * @param \Ps_Googleanalytics $module - * @param \Context $context + * @param Ps_Googleanalytics $module + * @param Context $context */ - public function __construct(\Ps_Googleanalytics $module, \Context $context); + public function __construct(Ps_Googleanalytics $module, Context $context); public function run(); } diff --git a/classes/Repository/CarrierRepository.php b/classes/Repository/CarrierRepository.php index 6ddd93d..d9ee6f3 100644 --- a/classes/Repository/CarrierRepository.php +++ b/classes/Repository/CarrierRepository.php @@ -20,6 +20,8 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Repository; +use Db; + class CarrierRepository { const TABLE_NAME = 'carrier'; @@ -33,7 +35,7 @@ class CarrierRepository */ public function findByCarrierId($carrierId) { - return \Db::getInstance()->getValue( + return Db::getInstance()->getValue( 'SELECT name FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '` WHERE id_carrier = ' . (int) $carrierId diff --git a/classes/Repository/GanalyticsDataRepository.php b/classes/Repository/GanalyticsDataRepository.php index 72ca2ec..26f9e14 100644 --- a/classes/Repository/GanalyticsDataRepository.php +++ b/classes/Repository/GanalyticsDataRepository.php @@ -20,6 +20,8 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Repository; +use Db; + class GanalyticsDataRepository { const TABLE_NAME = 'ganalytics_data'; @@ -34,7 +36,7 @@ class GanalyticsDataRepository */ public function findDataByCartIdAndShopId($cartId, $shopId) { - return \Db::getInstance()->getValue( + return Db::getInstance()->getValue( 'SELECT data FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '` WHERE id_cart = ' . (int) $cartId . ' @@ -53,7 +55,7 @@ public function findDataByCartIdAndShopId($cartId, $shopId) */ public function addNewRow($cartId, $shopId, $data) { - return \Db::getInstance()->Execute( + return Db::getInstance()->Execute( 'INSERT INTO `' . _DB_PREFIX_ . self::TABLE_NAME . '` (id_cart, id_shop, data) VALUES(\'' . (int) $cartId . '\',\'' . (int) $shopId . '\',\'' . pSQL($data) . '\') ON DUPLICATE KEY UPDATE data = \'' . pSQL($data) . '\';' @@ -70,7 +72,7 @@ public function addNewRow($cartId, $shopId, $data) */ public function deleteRow($cartId, $shopId) { - return \Db::getInstance()->delete( + return Db::getInstance()->delete( self::TABLE_NAME, 'id_cart = ' . (int) $cartId . ' AND id_shop = ' . (int) $shopId ); diff --git a/classes/Repository/GanalyticsRepository.php b/classes/Repository/GanalyticsRepository.php index 99b40fe..873541a 100644 --- a/classes/Repository/GanalyticsRepository.php +++ b/classes/Repository/GanalyticsRepository.php @@ -20,6 +20,8 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Repository; +use Db; + class GanalyticsRepository { const TABLE_NAME = 'ganalytics'; @@ -33,7 +35,7 @@ class GanalyticsRepository */ public function findGaOrderByOrderId($orderId) { - return \Db::getInstance()->getValue( + return Db::getInstance()->getValue( 'SELECT id_order FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '` WHERE id_order = ' . (int) $orderId); @@ -48,7 +50,7 @@ public function findGaOrderByOrderId($orderId) */ public function findAllByShopIdAndDateAdd($shopId) { - return \Db::getInstance()->ExecuteS( + return Db::getInstance()->ExecuteS( 'SELECT * FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '` WHERE sent = 0 @@ -65,9 +67,9 @@ public function findAllByShopIdAndDateAdd($shopId) * * @return bool */ - public function addNewRow(array $data, $type = \Db::INSERT_IGNORE) + public function addNewRow(array $data, $type = Db::INSERT_IGNORE) { - return \Db::getInstance()->insert( + return Db::getInstance()->insert( self::TABLE_NAME, $data, false, @@ -87,7 +89,7 @@ public function addNewRow(array $data, $type = \Db::INSERT_IGNORE) */ public function updateData($data, $where, $limit = 0) { - return \Db::getInstance()->update( + return Db::getInstance()->update( self::TABLE_NAME, $data, $where, diff --git a/classes/Wrapper/OrderWrapper.php b/classes/Wrapper/OrderWrapper.php index 9e8f280..c96a571 100644 --- a/classes/Wrapper/OrderWrapper.php +++ b/classes/Wrapper/OrderWrapper.php @@ -20,13 +20,18 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Wrapper; +use Configuration; +use Context; +use Order; use PrestaShop\Module\Ps_Googleanalytics\Hooks\WrapperInterface; +use Shop; +use Validate; class OrderWrapper implements WrapperInterface { private $context; - public function __construct(\Context $context) + public function __construct(Context $context) { $this->context = $context; } @@ -36,12 +41,12 @@ public function __construct(\Context $context) */ public function wrapOrder($id_order) { - $order = new \Order((int) $id_order); + $order = new Order((int) $id_order); - if (\Validate::isLoadedObject($order)) { + if (Validate::isLoadedObject($order)) { return [ 'id' => $id_order, - 'affiliation' => \Shop::isFeatureActive() ? $this->context->shop->name : \Configuration::get('PS_SHOP_NAME'), + 'affiliation' => Shop::isFeatureActive() ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), 'revenue' => $order->total_paid, 'shipping' => $order->total_shipping, 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, diff --git a/classes/Wrapper/ProductWrapper.php b/classes/Wrapper/ProductWrapper.php index 408b53f..6565d4b 100644 --- a/classes/Wrapper/ProductWrapper.php +++ b/classes/Wrapper/ProductWrapper.php @@ -20,13 +20,17 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Wrapper; +use Context; +use Currency; use PrestaShop\Module\Ps_Googleanalytics\Hooks\WrapperInterface; +use Product; +use Tools; class ProductWrapper implements WrapperInterface { private $context; - public function __construct(\Context $context) + public function __construct(Context $context) { $this->context = $context; } @@ -41,8 +45,8 @@ public function wrapProductList($products, $extras = [], $full = false) return; } - $currency = new \Currency($this->context->currency->id); - $usetax = (\Product::getTaxCalculationMethod((int) $this->context->customer->id) != PS_TAX_EXC); + $currency = new Currency($this->context->currency->id); + $usetax = (Product::getTaxCalculationMethod((int) $this->context->customer->id) != PS_TAX_EXC); if (count($products) > 20) { $full = false; @@ -51,12 +55,12 @@ public function wrapProductList($products, $extras = [], $full = false) } foreach ($products as $index => $product) { - if ($product instanceof \Product) { + if ($product instanceof Product) { $product = (array) $product; } if (!isset($product['price'])) { - $product['price'] = (float) \Tools::displayPrice(\Product::getPriceStatic((int) $product['id_product'], $usetax), $currency); + $product['price'] = (float) Tools::displayPrice(Product::getPriceStatic((int) $product['id_product'], $usetax), $currency); } $result_products[] = $this->wrapProduct($product, $extras, $index, $full); } @@ -106,21 +110,21 @@ public function wrapProduct($product, $extras, $index = 0, $full = false) if ($full) { $ga_product = [ 'id' => $product_id, - 'name' => \Tools::str2url($product['name']), - 'category' => \Tools::str2url($product['category']), - 'brand' => isset($product['manufacturer_name']) ? \Tools::str2url($product['manufacturer_name']) : '', - 'variant' => \Tools::str2url($variant), + 'name' => Tools::str2url($product['name']), + 'category' => Tools::str2url($product['category']), + 'brand' => isset($product['manufacturer_name']) ? Tools::str2url($product['manufacturer_name']) : '', + 'variant' => Tools::str2url($variant), 'type' => $product_type, 'position' => $index ? $index : '0', 'quantity' => $product_qty, - 'list' => \Tools::getValue('controller'), + 'list' => Tools::getValue('controller'), 'url' => isset($product['link']) ? urlencode($product['link']) : '', 'price' => $product['price'], ]; } else { $ga_product = [ 'id' => $product_id, - 'name' => \Tools::str2url($product['name']), + 'name' => Tools::str2url($product['name']), ]; } diff --git a/classes/Wrapper/WrapperInterface.php b/classes/Wrapper/WrapperInterface.php index 371acb4..34f4d88 100644 --- a/classes/Wrapper/WrapperInterface.php +++ b/classes/Wrapper/WrapperInterface.php @@ -20,10 +20,12 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Context; + interface WrapperInterface { /** - * @param \Context $context + * @param Context $context */ - public function __construct(\Context $context); + public function __construct(Context $context); } From fc41951871f1ff030f3d6e5564f5159239945a7c Mon Sep 17 00:00:00 2001 From: apacios Date: Fri, 29 May 2020 11:32:38 +0200 Subject: [PATCH 42/45] Use Context namespace in HookActionCarrierProcess --- classes/Hook/HookActionCarrierProcess.php | 1 + 1 file changed, 1 insertion(+) diff --git a/classes/Hook/HookActionCarrierProcess.php b/classes/Hook/HookActionCarrierProcess.php index 1c52646..e99ae6b 100644 --- a/classes/Hook/HookActionCarrierProcess.php +++ b/classes/Hook/HookActionCarrierProcess.php @@ -20,6 +20,7 @@ namespace PrestaShop\Module\Ps_Googleanalytics\Hooks; +use Context; use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsDataHandler; use PrestaShop\Module\Ps_Googleanalytics\Repository\CarrierRepository; use Ps_Googleanalytics; From e2869817f8f41107c3c71a349479ab8107a3df2a Mon Sep 17 00:00:00 2001 From: apacios Date: Wed, 3 Jun 2020 10:50:36 +0200 Subject: [PATCH 43/45] Rename retuned to returned --- classes/Handler/GanalyticsDataHandler.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/classes/Handler/GanalyticsDataHandler.php b/classes/Handler/GanalyticsDataHandler.php index 98626e8..e24a3dc 100644 --- a/classes/Handler/GanalyticsDataHandler.php +++ b/classes/Handler/GanalyticsDataHandler.php @@ -84,16 +84,16 @@ public function manageData($data, $action) */ private function readData() { - $dataRetuned = $this->ganalyticsDataRepository->findDataByCartIdAndShopId( + $dataReturned = $this->ganalyticsDataRepository->findDataByCartIdAndShopId( $this->cartId, $this->shopId ); - if (false === $dataRetuned) { + if (false === $dataReturned) { return []; } - return json_decode($dataRetuned, true); + return json_decode($dataReturned, true); } /** @@ -105,15 +105,15 @@ private function readData() */ private function appendData($data) { - $dataRetuned = $this->ganalyticsDataRepository->findDataByCartIdAndShopId( + $dataReturned = $this->ganalyticsDataRepository->findDataByCartIdAndShopId( $this->cartId, $this->shopId ); - if (false === $dataRetuned) { + if (false === $dataReturned) { $newData = [$data]; } else { - $newData = json_decode($dataRetuned, true); + $newData = json_decode($dataReturned, true); $newData[] = $data; } From 07563209a788a151730fc2ff5f3a678d7af47bb9 Mon Sep 17 00:00:00 2001 From: apacios Date: Fri, 12 Jun 2020 15:17:24 +0200 Subject: [PATCH 44/45] Fix many issues and improve json_decode data retreiving --- classes/Form/ConfigurationForm.php | 2 +- classes/Handler/GanalyticsDataHandler.php | 23 +++++++++++++++++--- classes/Handler/GanalyticsJsHandler.php | 4 ++-- classes/Hook/HookActionCartSave.php | 1 + classes/Hook/HookDisplayBackOfficeHeader.php | 2 +- classes/Wrapper/OrderWrapper.php | 3 ++- ps_googleanalytics.php | 2 +- views/templates/hook/ga_tag.tpl | 2 +- 8 files changed, 29 insertions(+), 10 deletions(-) diff --git a/classes/Form/ConfigurationForm.php b/classes/Form/ConfigurationForm.php index f1de6de..7fdb9b4 100644 --- a/classes/Form/ConfigurationForm.php +++ b/classes/Form/ConfigurationForm.php @@ -69,7 +69,7 @@ public function generate() $helper->toolbar_btn = [ 'save' => [ 'desc' => $this->module->l('Save'), - 'href' => AdminController::$currentIndex . '&configure=' . $this->module->name . '&save' . $this->module->name . + 'href' => AdminController::$currentIndex . '&configure=' . $this->module->name . '&save=' . $this->module->name . '&token=' . Tools::getAdminTokenLite('AdminModules'), ], 'back' => [ diff --git a/classes/Handler/GanalyticsDataHandler.php b/classes/Handler/GanalyticsDataHandler.php index e24a3dc..726e0fd 100644 --- a/classes/Handler/GanalyticsDataHandler.php +++ b/classes/Handler/GanalyticsDataHandler.php @@ -93,7 +93,7 @@ private function readData() return []; } - return json_decode($dataReturned, true); + return $this->jsonDecodeValidJson($dataReturned); } /** @@ -113,8 +113,7 @@ private function appendData($data) if (false === $dataReturned) { $newData = [$data]; } else { - $newData = json_decode($dataReturned, true); - $newData[] = $data; + $newData[] = $this->jsonDecodeValidJson($dataReturned); } return $this->ganalyticsDataRepository->addNewRow( @@ -123,4 +122,22 @@ private function appendData($data) json_encode($newData) ); } + + /** + * Check if the json is valid and returns an empty array if not + * + * @param string $json + * + * @return array + */ + protected function jsonDecodeValidJson($json) + { + $array = json_decode($json, true); + + if (JSON_ERROR_NONE === json_last_error()) { + return $array; + } + + return []; + } } diff --git a/classes/Handler/GanalyticsJsHandler.php b/classes/Handler/GanalyticsJsHandler.php index a75b189..84882b0 100644 --- a/classes/Handler/GanalyticsJsHandler.php +++ b/classes/Handler/GanalyticsJsHandler.php @@ -40,11 +40,11 @@ public function __construct(Ps_Googleanalytics $module, Context $context) * Generate Google Analytics js * * @param string $jsCode - * @param int $isBackoffice + * @param bool $isBackoffice * * @return void|string */ - public function generate($jsCode, $isBackoffice = 0) + public function generate($jsCode, $isBackoffice = false) { if (Configuration::get('GA_ACCOUNT_ID')) { $this->context->smarty->assign( diff --git a/classes/Hook/HookActionCartSave.php b/classes/Hook/HookActionCartSave.php index c06e278..9eb8861 100644 --- a/classes/Hook/HookActionCartSave.php +++ b/classes/Hook/HookActionCartSave.php @@ -72,6 +72,7 @@ public function run() foreach ($cartProducts as $cartProduct) { if ($cartProduct['id_product'] == Tools::getValue('id_product')) { $addProduct = $cartProduct; + break; } } } diff --git a/classes/Hook/HookDisplayBackOfficeHeader.php b/classes/Hook/HookDisplayBackOfficeHeader.php index 17258e3..8816964 100644 --- a/classes/Hook/HookDisplayBackOfficeHeader.php +++ b/classes/Hook/HookDisplayBackOfficeHeader.php @@ -103,7 +103,7 @@ public function run() } } - return $js . $this->module->hookdisplayHeader(null, true) . $gaTagHandler->generate($gaScripts, 1); + return $js . $this->module->hookdisplayHeader(null, true) . $gaTagHandler->generate($gaScripts, true); } return $js; diff --git a/classes/Wrapper/OrderWrapper.php b/classes/Wrapper/OrderWrapper.php index c96a571..6d5b374 100644 --- a/classes/Wrapper/OrderWrapper.php +++ b/classes/Wrapper/OrderWrapper.php @@ -51,7 +51,8 @@ public function wrapOrder($id_order) 'shipping' => $order->total_shipping, 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, 'url' => $this->context->link->getAdminLink('AdminGanalyticsAjax'), - 'customer' => $order->id_customer, ]; + 'customer' => $order->id_customer, + ]; } } } diff --git a/ps_googleanalytics.php b/ps_googleanalytics.php index cd730bf..1cbd2fa 100755 --- a/ps_googleanalytics.php +++ b/ps_googleanalytics.php @@ -141,7 +141,7 @@ public function hookDisplayAdminOrder() echo $gaTagHandler->generate( $this->context->cookie->__get('ga_admin_refund'), - 1 + true ); $this->context->cookie->__unset('ga_admin_refund'); } diff --git a/views/templates/hook/ga_tag.tpl b/views/templates/hook/ga_tag.tpl index 46e1c8b..bd665a6 100644 --- a/views/templates/hook/ga_tag.tpl +++ b/views/templates/hook/ga_tag.tpl @@ -29,7 +29,7 @@ {/literal} {/if} -{if ($jsState != 1 && $isBackoffice == 0)} +{if ($jsState != 1 && $isBackoffice === true)} {literal}