<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-8936921225064570325</atom:id><lastBuildDate>Sat, 18 Oct 2025 03:07:42 +0000</lastBuildDate><category>technology</category><category>webdev</category><category>cms</category><category>gumbo-cms</category><category>mvc</category><category>opensource</category><category>php</category><category>tutorial</category><category>android</category><category>food</category><category>adsense</category><category>adsense rejected</category><category>adsense rejection</category><category>analytics</category><category>analytics not working</category><category>anaytics code not working</category><category>apply adsense</category><category>battery</category><category>battery life</category><category>breakfast</category><category>breakfast idea</category><category>brunch</category><category>brunchpot</category><category>convert tabs</category><category>cse</category><category>custom search</category><category>deruny</category><category>derunys</category><category>dranyky</category><category>dutch cuisine</category><category>fedora</category><category>fedora 19</category><category>food idea</category><category>full adsense</category><category>gnome</category><category>google adsense</category><category>google analytics</category><category>google cse</category><category>google custom search</category><category>gp5</category><category>guitar pro</category><category>guitar tab</category><category>health</category><category>home studio</category><category>how to get adsense</category><category>hutspot</category><category>idevice</category><category>international cuisine</category><category>iphone</category><category>lifestyle</category><category>linux</category><category>linux essential</category><category>linux must-have</category><category>midi</category><category>miscellaneous</category><category>outlook</category><category>pop</category><category>real-time analytics not working</category><category>recipe</category><category>root gt-s6312</category><category>samsung gt s6312</category><category>smartphone</category><category>synchronization</category><category>tab to midi</category><category>tab to midi converter</category><category>tabs to midi</category><category>thunderbird</category><category>tracking code not working</category><category>tux guitar</category><category>ukrainian cuisine</category><category>ukrainian food</category><category>ukrainian recipe</category><title>RolandC.net</title><description></description><link>http://www.rolandc.net/</link><managingEditor>noreply@blogger.com (rolandc.net)</managingEditor><generator>Blogger</generator><openSearch:totalResults>20</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-6476670572720498129</guid><pubDate>Thu, 04 Aug 2016 14:17:00 +0000</pubDate><atom:updated>2016-08-25T16:44:53.285+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">adsense</category><category domain="http://www.blogger.com/atom/ns#">adsense rejected</category><category domain="http://www.blogger.com/atom/ns#">adsense rejection</category><category domain="http://www.blogger.com/atom/ns#">apply adsense</category><category domain="http://www.blogger.com/atom/ns#">full adsense</category><category domain="http://www.blogger.com/atom/ns#">google adsense</category><category domain="http://www.blogger.com/atom/ns#">how to get adsense</category><category domain="http://www.blogger.com/atom/ns#">technology</category><title>So, how do you get fully accepted into Adsense these days? (August 2016)</title><description>Lots of people keep making videos about how you have to do this and target that so that maybe one day the Adsense people will take you in and that if you never get fully enrolled then guess what there are other ad programs out there so that Google ain&#39;t the best thing on earth afterall and so on and so on... STOP&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7hb70D57BCMg0XHpoV20lM1EqpECtIWIH87WeOwUVzl8p08tlb82wxEjCHgxq5T19d0qaCwSkgcPBETqXvUvmN6DvgCI7kXvfiaVoyDJdR49tnod69XRKd7BymT6VC8RUbGUX8rFQ9MpX/s1600/adense-rolandc.net_ok.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;199&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7hb70D57BCMg0XHpoV20lM1EqpECtIWIH87WeOwUVzl8p08tlb82wxEjCHgxq5T19d0qaCwSkgcPBETqXvUvmN6DvgCI7kXvfiaVoyDJdR49tnod69XRKd7BymT6VC8RUbGUX8rFQ9MpX/s320/adense-rolandc.net_ok.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
I thought I&#39;d write a little article that will save you some time and frustration, hopefully lightening your path a little. That supposes of course that you have applied at least twice to get full acceptance into the program and that currently you&#39;re in a state of almost wanting to bang your head against the wall because you can&#39;t figure out what it takes to get Adsense and that it makes you feel psychotic every time you start thinking about it.&lt;br /&gt;
&lt;br /&gt;
Fine, but RolandC.net, what do I need to do to get fully accepted into the Adsense program?&lt;br /&gt;
&lt;br /&gt;
The answer is both simple and short: &lt;b&gt;BE TRULY USEFUL&lt;/b&gt; to someone (not just to yourself).&lt;br /&gt;
&lt;br /&gt;
Let me clarify what I just wrote. I did not even have 18 (probably 17 or 16) articles when I applied (and got full acceptance), so don&#39;t even listen to those who recommend having at least 20-25 quality articles.&lt;br /&gt;
&lt;br /&gt;
And frankly, quality is not even the point here. Google does not need quality, it needs usefulness, hear problem solving, creating solutions.&lt;br /&gt;
&lt;br /&gt;
Before I elaborate on this, a little word about traffic, as not only I remember having had less than 18 posts for sure, I was also averaging the 30 visits a day or so, probably even less at that time. Felt lonely on my Blogger platform...well who cares, I did get accepted anyway&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
Of course, your content should reflect the fact that you&#39;ve benefited from some sort of, quote on quote, education somewhere along the way, and your website/blog&#39;s visual identity should convey your own personal spirit or your product/services&#39;s spirit or whatever, but that&#39;s not to the point, the point being that &lt;b&gt;your content should solve problems, bring solutions&lt;/b&gt; (and not just to you but to people that are actually real and that actually need your content, so that the reader went &#39;Oh, gee! thanks so much for your article, it really saved my day!&#39; or &#39;Gosh, I didn&#39;t know that! I&#39;m so happy I found your blog!&#39; or even &#39;Wow, this is so interesting, I gotta try that on my own too!&#39;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bang! there you have it, if you can help people, if you can really be useful to anyone on this planet, then you will attract people (potential customers) and then advertisers want to know about you - here&#39;s your Adsense ID coming up right now! (well actually they first give you a link to download the Adsense app :))&lt;br /&gt;
&lt;br /&gt;
Anyway, I&#39;m not going to mention anything about the &#39;you have to be passionate&#39; 
thing because it&#39;s obvious or it should obvious that if you write 
articles about something you know enough to help the community, that 
means you love what you do enough to be doing it, right? So if you have 
started creating content just to get Adsense, hoping you&#39;ll only have to 
&lt;i&gt;sit on it&lt;/i&gt; afterwards waiting for Paypal® to come thru while sipping your beverage of
 choice on some sand beach somewhere, then this article wasn&#39;t for you 
to read, sorry. &lt;br /&gt;
&lt;br /&gt;
Think about it this way, as an advertiser, would you invest money in people who don&#39;t have nothing helpful to tell (hear sell)?&amp;nbsp; Would you invest your hard-earned money in people who only keep sharing things they haven&#39;t even created themselves? Would you invest in people who&#39;re only looking to get an income from advertising while not even trying to understand what advertisers need from them? (advertisers - those are the people who will pay you money in the end) and would you invest in people that....I forgot, sorry.&lt;br /&gt;
&lt;br /&gt;
Anyway I hope you&#39;ve enjoyed reading this article, that it&#39;ll help you either getting Adsense quick and easy at last (though truthfully), or find something else (better) to do with your life (you might wanna try the &lt;a href=&quot;https://www.dmoz.org/&quot; target=&quot;_blank&quot;&gt;DMOZ directory project&lt;/a&gt; to get ideas if you don&#39;t have any - link opens in a new window and they have a brand new mascot there, green mascot actually).&lt;br /&gt;
&lt;br /&gt;
Thank you for reading and don&#39;t forget to like&amp;amp;share&amp;amp;subscribe to rss and you know...&lt;br /&gt;
&lt;br /&gt;
Stay tuned for more on RolandC.net!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;This &lt;a href=&quot;http://www.rolandc.net/2016/08/so-how-do-you-get-fully-accepted-in-google-adsense.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt; first appeared Thursday the 4th of August 2016 on &lt;a href=&quot;http://rolandc.net/&quot;&gt;RolandC.net&lt;/a&gt;.&lt;/span&gt;</description><link>http://www.rolandc.net/2016/08/so-how-do-you-get-fully-accepted-in-google-adsense_4.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7hb70D57BCMg0XHpoV20lM1EqpECtIWIH87WeOwUVzl8p08tlb82wxEjCHgxq5T19d0qaCwSkgcPBETqXvUvmN6DvgCI7kXvfiaVoyDJdR49tnod69XRKd7BymT6VC8RUbGUX8rFQ9MpX/s72-c/adense-rolandc.net_ok.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-7867347518781653122</guid><pubDate>Fri, 24 Jun 2016 06:07:00 +0000</pubDate><atom:updated>2016-06-24T09:11:42.459+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">outlook</category><category domain="http://www.blogger.com/atom/ns#">pop</category><category domain="http://www.blogger.com/atom/ns#">synchronization</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">thunderbird</category><title>Can&#39;t access your Hotmail pop email anymore? (24th June 2016) </title><description>Yesterday (23rd June 2016), I have spent all day noticing Thunderbird could not retrieve my Hotmail pop email anymore. The following dialog boxes were constantly shown&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1EQThfkqPXsI2sxc6ww2kYq0y9PKX2gYH0MWBI6-5qdbfdSPE9s4GIbvxT3P8TliqNOm6MQwz6cfs4OQ73PU6x68eklEZ_b4UcMzSJUugoY2u_PEFWTZ_6UTDwh5TS5a1ejYnyLwerp5I/s1600/hotmail_sync1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;Hotmail pop sync problems on rolandc.net&quot; border=&quot;0&quot; height=&quot;64&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1EQThfkqPXsI2sxc6ww2kYq0y9PKX2gYH0MWBI6-5qdbfdSPE9s4GIbvxT3P8TliqNOm6MQwz6cfs4OQ73PU6x68eklEZ_b4UcMzSJUugoY2u_PEFWTZ_6UTDwh5TS5a1ejYnyLwerp5I/s320/hotmail_sync1.png&quot; title=&quot;&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&amp;nbsp; &lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCQoet_dl_utmPuWPTnXJgm4Ne6mecP0YKcaCCi0JjIlIK5qsay2wfcdXiqfBU2IhQuAZjrWV3hb4oYzhNiPaRVClAu7xfcapf4FbNlSruwea7o_R91Glat6FzODsMaAlUjYTgr2fpGsKV/s1600/hotmail_sync2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;Hotmail pop sync problems on rolandc.net&quot; border=&quot;0&quot; height=&quot;108&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCQoet_dl_utmPuWPTnXJgm4Ne6mecP0YKcaCCi0JjIlIK5qsay2wfcdXiqfBU2IhQuAZjrWV3hb4oYzhNiPaRVClAu7xfcapf4FbNlSruwea7o_R91Glat6FzODsMaAlUjYTgr2fpGsKV/s320/hotmail_sync2.png&quot; title=&quot;&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
I had also noticed that Hotmail&#39;s Webmail interface got a new paint job that day, and somehow I knew this had to be connected.&lt;br /&gt;
&lt;br /&gt;
Right, it was !&lt;br /&gt;
&lt;br /&gt;
Even though I had been getting my Hotmail email from Thunderbird for years without encoutering a single issue, I just realized that the option to get Hotmail&#39;s pop email retrieved was now disabled by default!&lt;br /&gt;
&lt;br /&gt;
So, here&#39;s what I found while visiting Hotmail&#39;s Webmail option page:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjFWpHMDwJ2TMCh1QgAj__BC0QvmMR0eMkc1-bfkhK8zW58pc7kGOE5p6xn6to92uU8A05J-EfOqFmFRhJoka2oVR03ZLpuavQRfF3LR7QUxkEzb3szXmS3_hdazKDbRHc-84k3fHllAWG/s1600/hotmail_sync5+%2528copy%2529.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;Hotmail pop sync problems on rolandc.net&quot; border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjFWpHMDwJ2TMCh1QgAj__BC0QvmMR0eMkc1-bfkhK8zW58pc7kGOE5p6xn6to92uU8A05J-EfOqFmFRhJoka2oVR03ZLpuavQRfF3LR7QUxkEzb3szXmS3_hdazKDbRHc-84k3fHllAWG/s320/hotmail_sync5+%2528copy%2529.png&quot; title=&quot;&quot; width=&quot;289&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Nice paint job huh?!&lt;br /&gt;
&lt;br /&gt;
Just click on Yes and you will get your Hotmail email again as usual. No complicated two-step verification process involved or anything. That&#39;s as simple as that.&lt;br /&gt;
&lt;br /&gt;
By the way, in case you can&#39;t figure out where the dialog box above came from, read on:&lt;br /&gt;
&lt;br /&gt;
- Visit &lt;b&gt;hotmail.com&lt;/b&gt; or I guess &lt;b&gt;outlook.live.com&lt;/b&gt; is a shorter way here&lt;br /&gt;
- Click on the &lt;b&gt;Settings&lt;/b&gt; menu (as found below)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDJX91SzNbM-ZsZTtuJIq02Lm-3J3gubGnEkBOJUaJrWoIGZbDJIveXGLrxSTUCSGdESzLefzYmOCoYqdAt0STzb6oZj2e0Ou7SusidZSoupS8dhE1p0NMpvKLpyTbbBsrCbg5Hi4lxCcf/s1600/hotmail_sync3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;Hotmail pop sync problems on rolandc.net&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDJX91SzNbM-ZsZTtuJIq02Lm-3J3gubGnEkBOJUaJrWoIGZbDJIveXGLrxSTUCSGdESzLefzYmOCoYqdAt0STzb6oZj2e0Ou7SusidZSoupS8dhE1p0NMpvKLpyTbbBsrCbg5Hi4lxCcf/s1600/hotmail_sync3.png&quot; title=&quot;&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
- Click on &lt;b&gt;Options&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span id=&quot;goog_2073009894&quot;&gt;&lt;/span&gt;&lt;span id=&quot;goog_2073009895&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXPTXF1VXoyQNv3Lespsf3nrW_hGF7xlUGJ2aBbTN33rD3xMTTe4YhPPcDQh6jH1KRXR4C78rb4Q_4NqbuZldFThLlnRzR_UnBPKlXpWyLrwXxETxOp8fkYQkVt0l-KTKkyf3F7VqyNEY2/s1600/hotmail_sync4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;Hotmail pop sync problems on rolandc.net&quot; border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXPTXF1VXoyQNv3Lespsf3nrW_hGF7xlUGJ2aBbTN33rD3xMTTe4YhPPcDQh6jH1KRXR4C78rb4Q_4NqbuZldFThLlnRzR_UnBPKlXpWyLrwXxETxOp8fkYQkVt0l-KTKkyf3F7VqyNEY2/s320/hotmail_sync4.png&quot; title=&quot;&quot; width=&quot;217&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Then click on &lt;b&gt;Mail -&amp;gt; Accounts -&amp;gt; POP and IMAP&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc5o1hohcTELLziOiIuN19-ypZv7bGBK4I26OT0ZnQ4E0j1Zk0inj-rr_i8EkIGEl1rDjWoWXZ9tv3zIZYfzH8MLw0q-cG2SybimzBWugAj3gwnvWqP-VmeLGtgnkI5ujQop6m9jeUlT7s/s1600/hotmail_sync5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;Hotmail pop sync problems on rolandc.net&quot; border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc5o1hohcTELLziOiIuN19-ypZv7bGBK4I26OT0ZnQ4E0j1Zk0inj-rr_i8EkIGEl1rDjWoWXZ9tv3zIZYfzH8MLw0q-cG2SybimzBWugAj3gwnvWqP-VmeLGtgnkI5ujQop6m9jeUlT7s/s320/hotmail_sync5.png&quot; title=&quot;&quot; width=&quot;289&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;Once there, don&#39;t forget to hit &lt;b&gt;Save&lt;/b&gt; (a little above) and you&#39;re done.&lt;br /&gt;
&lt;br /&gt;
I hope this helped you save the day by getting your customers email back in your email client as usual. Stay tuned for more on RolandC.net&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;This &lt;a href=&quot;http://www.rolandc.net/2016/06/cant-access-your-hotmail-pop-email-anymore.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt; first appeared Thursday the 16th of February 2016 on &lt;a href=&quot;http://rolandc.net/&quot;&gt;RolandC.net&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;</description><link>http://www.rolandc.net/2016/06/cant-access-your-hotmail-pop-email-anymore.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1EQThfkqPXsI2sxc6ww2kYq0y9PKX2gYH0MWBI6-5qdbfdSPE9s4GIbvxT3P8TliqNOm6MQwz6cfs4OQ73PU6x68eklEZ_b4UcMzSJUugoY2u_PEFWTZ_6UTDwh5TS5a1ejYnyLwerp5I/s72-c/hotmail_sync1.png" height="72" width="72"/><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-5856840229315802319</guid><pubDate>Tue, 16 Feb 2016 07:35:00 +0000</pubDate><atom:updated>2016-02-16T09:35:47.107+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cms</category><category domain="http://www.blogger.com/atom/ns#">gumbo-cms</category><category domain="http://www.blogger.com/atom/ns#">mvc</category><category domain="http://www.blogger.com/atom/ns#">opensource</category><category domain="http://www.blogger.com/atom/ns#">php</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">tutorial</category><category domain="http://www.blogger.com/atom/ns#">webdev</category><title>How to build your own Multilingual PHP MVC CMS from scratch - Part 6 - Language/Theme Handling</title><description>Welcome to the sixth settlement of our tutorial series on how to build your own Multilingual MVC CMS from scratch and in which I will get into full detail as to how themes and languages are managed in our project.
&lt;br /&gt;
&lt;br /&gt;
During the &lt;a href=&quot;http://www.rolandc.net/2015/12/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-5.html&quot;&gt;last settlement&lt;/a&gt;, I have demonstrated a way of building a user interface that allows us to register, sign in and review/update personal data. I have also challenged you, aided by all the knowledge you have gained so far, to try and build some additional features&amp;nbsp; like forgotten password, password changing and extra settings modules on your own project, operation that you should not be so difficult by now and that should considerably enhance the interface you have started to build, if you have.
&lt;br /&gt;
&lt;br /&gt;
But today, I will be getting in a totally different aspect of the project, gathering all the previously discussed elements about theme and language handling and coming full circle by giving you a complete, synthesized overview of the matter. I will point out however, that this will not have any direct relation with the back-end language and theme management features we will be starting to create during the next settlements, as this will in fact only be a discussion about everything pertaining to theme/language handling, mostly in front end.
&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Language handling
&lt;/h2&gt;
&lt;br /&gt;
So, starting with languages and starting with the obvious, all language files, whether used in back-end or in front-end, will fall under the same directory structure as shown right bellow
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizoUdNx6z0Od8Z_qr1C9Yh5L-RH2EkR7cOIxhb3lL91ZPnCJeoWlsj0iPni4qCny4ToOJoSPMWdc_3kYry8xZWb9rjHazfW5CmWxBryWEc7B8JDyzuSaQxp5yBclfa3xPjLyqqGWSc5524/s1600/language_structure_basic2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizoUdNx6z0Od8Z_qr1C9Yh5L-RH2EkR7cOIxhb3lL91ZPnCJeoWlsj0iPni4qCny4ToOJoSPMWdc_3kYry8xZWb9rjHazfW5CmWxBryWEc7B8JDyzuSaQxp5yBclfa3xPjLyqqGWSc5524/s1600/language_structure_basic2.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
The file named encoding.php merely holds an array containing charsets on a per language basis, we will later review the method it is being useful to.
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
   
      /* languages/encoding.php */  
   
      $encod = array(  
           &#39;en&#39; =&amp;gt; &#39;utf-8&#39;,  
           &#39;fr&#39; =&amp;gt; &#39;utf-8&#39;,  
           &#39;uk&#39; =&amp;gt; &#39;utf-8&#39;,  
           &#39;ru&#39; =&amp;gt; &#39;utf-8&#39;,  
           &#39;de&#39; =&amp;gt; &#39;utf-8&#39;,  
           &#39;nl&#39; =&amp;gt; &#39;utf-8&#39;,  
           &#39;se&#39; =&amp;gt; &#39;utf-8&#39;,  
           &#39;es&#39; =&amp;gt; &#39;utf-8&#39;  
      );       
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
The file named translate.js contains a script that&#39;s needed to help localize all Javascript strings. Since I have already presented it in a previous article (&lt;a href=&quot;http://www.sitepoint.com/localizing-javascript-strings-php-mvc-framework/&quot;&gt;http://www.sitepoint.com/localizing-javascript-strings-php-mvc-framework/&lt;/a&gt;), I will not need to do so here.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
Obvious is the fact that each language folder contains its own flag, in .png format.
&lt;br /&gt;
&lt;br /&gt;
One thing that differentiates the front-end section however, is that a template folder has been added to address template needs while sending out emails, eg: confirmation of registration, contact form and new password sending. Indeed, a user who has registered using Swedish may not like to receive a confirmation all written in English or in any language other than Swedish for that matter.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYpHweWhjXDntsjZ7z3paIUVEa4TbaOp4x7wOGk_WO5ZnAY9Fgx_5cjEguF75JznKg3HnQqAek9OZE5HgCX3hKjHfLT4AkRxHqcmv5gf52fazaxNHi2ubq-7vRNJ29ou7LTv8W8vZjWG_t/s1600/language_templates.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYpHweWhjXDntsjZ7z3paIUVEa4TbaOp4x7wOGk_WO5ZnAY9Fgx_5cjEguF75JznKg3HnQqAek9OZE5HgCX3hKjHfLT4AkRxHqcmv5gf52fazaxNHi2ubq-7vRNJ29ou7LTv8W8vZjWG_t/s320/language_templates.png&quot; width=&quot;121&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Let&#39;s now take the example of registration_en.php
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php   
   
 /* languages/templates/en/registration_en.php */  
   
 $content = &quot;  
 &amp;lt;html&amp;gt;  
 &amp;lt;head&amp;gt;  
 &amp;lt;title&amp;gt;Your registration&amp;lt;/title&amp;gt;  
 &amp;lt;/head&amp;gt;  
 &amp;lt;body&amp;gt;  
 &amp;lt;p&amp;gt;Thank you for your registration on \&quot;&quot;.SITE_TITLE.&quot;\&quot;!&amp;lt;/p&amp;gt;  
 Your credentials are  
 &amp;lt;table&amp;gt;  
 &amp;lt;tr&amp;gt;  
 &amp;lt;td&amp;gt;Login : &amp;lt;/td&amp;gt;  
 &amp;lt;th&amp;gt;&quot;.$login.&quot;&amp;lt;/th&amp;gt;  
 &amp;lt;/tr&amp;gt;  
 &amp;lt;tr&amp;gt;  
 &amp;lt;td&amp;gt;Pass : &amp;lt;/td&amp;gt;  
 &amp;lt;th&amp;gt;&quot;.$password.&quot;&amp;lt;/th&amp;gt;  
 &amp;lt;/tr&amp;gt;  
 &amp;lt;/table&amp;gt;  
 &amp;lt;p&amp;gt;  
 Regards,&amp;lt;br /&amp;gt;   
 the site&#39;s team  
 &amp;lt;/body&amp;gt;  
 &amp;lt;/html&amp;gt;  
 &quot;;  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Knowing HTML, you can all imagine what the output is going to look like in your favorite email client program.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
Interesting too is the constant named CONTACT_FORM_RECEIVER_LANGUAGE, which is used to define the language in which the administrator will like to receive its feedback emails.
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; define(&quot;CONTACT_FORM_RECEIVER_LANGUAGE&quot;,&quot;en&quot;); // the code of the language in which you wish to receive the user contact messages  
&lt;/code&gt;&lt;/pre&gt;
Now for the core language files that hold most of the PHP localized strings, let&#39;s take the example of en.php
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
   
 /* from languages/en/en.php */  
   
 $t_welcome = &#39;Welcome&#39;;  
 $t_contact = &#39;Contact&#39;;  
 $t_registration = &#39;Register&#39;;  
 $t_reg = &#39;Register&#39;;  
 $t_copyright = &#39;Copyright&#39;;  
 $t_submit = &#39;Submit&#39;;       
 $t_save = &#39;Save&#39;;  
 ...  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
As an unspoken rule, localized PHP strings follow the format $t_my_word_or_sentence = &#39;My word or sentence&#39;; Thus the variable name is always lowercase, each word separated by an underscore, and t_ is appended at the beginning of each term. HTML tags may be used inside the literal string part, constants too, but best is to avoid this whenever possible, for readability at least.
&lt;br /&gt;
&lt;br /&gt;
And now, time to get into the controversial debate about global variables. Since day 1, I have chosen to use global variables to access the translation strings we have just discussed. So, for example, throughout the whole site, we may access $t_welcome by using $GLOBALS[&#39;t_welcome&#39;], which turns out to be greatly convenient, for a number of reasons.
&lt;br /&gt;
&lt;br /&gt;
Although... I can hear some of you screaming &#39;globals? DO NOT USE GLOBALS!&#39;, which is sometimes fair in its own right. But passed the fact that I myself am totally not convinced using globals for my translation system here will affect the project or anyone instantiating/using it, fact is, I have even yet to be convinced by the generally advised methods in the field, which are either about stacking everything into array from which translations are latter picked, or using POEdit/gettext which I don&#39;t feel truly answers the specific needs I have been having for this project so far.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
So that, the array method being pretty cumbersome to maintain and great only for small localization projects and also, with over 1000 translation strings for each language, unless someone comes up with a great new way to handle localization that&#39;s easily and quickly implementable, even in large-scale environments, that&#39;s the method I will keep using. Besides, not only this is the one method I am myself being the most comfortable with, this is also a method that neither ever failed me nor made waves somewhere else in the project since I have started implementing it. So, as programmers too sometimes say: &#39;Don&#39;t repair it if it ain&#39;t broken!&#39;.
&lt;br /&gt;
&lt;br /&gt;
But the truth is, within our project, there is much more to language handling than mere string localization.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
Let&#39;s start from the localization table and then go on with the language selector bar&amp;nbsp;
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php       
 --  
 -- Table structure for table `localization`  
 --  
   
 CREATE TABLE `localization` (  
  `id_localization` smallint(6) NOT NULL,  
  `localization_name` varchar(3) NOT NULL,  
  `localization_flag` varchar(8) NOT NULL,  
  `localization_enabled` enum(&#39;no&#39;,&#39;yes&#39;) NOT NULL,  
  `localization_default` enum(&#39;no&#39;,&#39;yes&#39;) NOT NULL,  
  `localization_timestamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP  
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1;  
   
 --  
 -- Dumping data for table `localization`  
 --  
   
 INSERT INTO `localization` (`id_localization`, `localization_name`, `localization_flag`, `localization_enabled`, `localization_default`, `localization_timestamp`) VALUES  
 (7, &#39;se&#39;, &#39;se.png&#39;, &#39;no&#39;, &#39;no&#39;, &#39;2014-10-24 19:17:26&#39;),  
 (8, &#39;es&#39;, &#39;es.png&#39;, &#39;no&#39;, &#39;no&#39;, &#39;2014-10-24 19:17:26&#39;),  
 (5, &#39;ru&#39;, &#39;ru.png&#39;, &#39;no&#39;, &#39;no&#39;, &#39;2014-10-24 19:17:26&#39;),  
 (6, &#39;nl&#39;, &#39;nl.png&#39;, &#39;no&#39;, &#39;no&#39;, &#39;2014-10-24 19:17:26&#39;),  
 (3, &#39;fr&#39;, &#39;fr.png&#39;, &#39;yes&#39;, &#39;yes&#39;, &#39;2014-10-24 19:17:26&#39;),  
 (4, &#39;uk&#39;, &#39;uk.png&#39;, &#39;no&#39;, &#39;no&#39;, &#39;2014-10-24 19:17:26&#39;),  
 (1, &#39;en&#39;, &#39;en.png&#39;, &#39;yes&#39;, &#39;no&#39;, &#39;2014-10-24 19:17:26&#39;),  
 (2, &#39;de&#39;, &#39;de.png&#39;, &#39;no&#39;, &#39;no&#39;, &#39;2014-10-24 19:17:26&#39;),  
 (10, &#39;dk&#39;, &#39;dk.png&#39;, &#39;no&#39;, &#39;no&#39;, &#39;2014-10-24 19:17:26&#39;);  
   
 --  
 -- Indexes for dumped tables  
 --  
   
 --  
 -- Indexes for table `localization`  
 --  
 ALTER TABLE `localization`  
 ADD PRIMARY KEY (`id_localization`);  
   
 --  
 -- AUTO_INCREMENT for dumped tables  
 --  
   
 --  
 -- AUTO_INCREMENT for table `localization`  
 --  
 ALTER TABLE `localization`  
 MODIFY `id_localization` smallint(6) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=11;  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Nothing too unexplainable about this one, although it&#39;s a good occasion to remind you the timestamp field is here only to help force MySQL perform an update of at least one row every time we perform an operation but the data hasn&#39;t changed.
&lt;br /&gt;
&lt;br /&gt;
And from this table, we can start building a language selector bar users will be able to interact with, shifting from one language to another easily.
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      /* from themes/your_style/main.php */  
   
      // we fetch an instance of the localization model   
      // and call the method that will retrieve all enabled languages  
      $lang_bar = LocModel::getInstance()-&amp;gt;getData_language_bar();  
   
      foreach($lang_bar as $ln_item){//for each language  
   
           if(GRAPHICAL_LN_BAR == true){   
                // if flag view is set, we display the flag for each language  
                // wrapped in the target url  
                // The get_ln_straight() method from bellow  
                // will take in a URL and process it to match the language   
                // on which the mouse is hovering, 
                // all while preserving the other elements the current URL contains, as well as their order  
           ?&amp;gt;   
                &amp;lt;a href=&quot;&amp;lt;?php echo LocModel::getInstance()-&amp;gt;get_ln_straight($ln_item[0]); ?&amp;gt;&quot;&amp;gt;&amp;lt;img src=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;; ?&amp;gt;languages/&amp;lt;?php echo $ln_item[0]; ?&amp;gt;/&amp;lt;?php echo $ln_item[1]; ?&amp;gt;&quot; width=&quot;&amp;lt;?php echo FLAG_WIDTH; ?&amp;gt;&quot; height=&quot;&amp;lt;?php echo FLAG_HEIGHT; ?&amp;gt;&quot; alt=&quot;&amp;lt;?php echo isset($GLOBALS[&#39;t_&#39;.$ln_item[0].&#39;_lng&#39;])?$GLOBALS[&#39;t_&#39;.$ln_item[0].&#39;_lng&#39;]:&quot;$ln_item[0]&quot;; ?&amp;gt;&quot; /&amp;gt;&amp;lt;/a&amp;gt; &amp;lt;?php   
           }  
           else{   
           ?&amp;gt;  
                &amp;lt;a href=&quot;&amp;lt;?php echo LocModel::getInstance()-&amp;gt;get_ln_straight($ln_item[0]); ?&amp;gt;&quot;&amp;gt;&amp;lt;u&amp;gt;&amp;lt;?php echo $ln_item[0]; ?&amp;gt;&amp;lt;/u&amp;gt;&amp;lt;/a&amp;gt; &amp;lt;?php   
           }  
      }  
 }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
There is not much to write about the commented above code, expect for the call to the get_ln_straight() method, which will, no matter what the current URL is, make sure the language that&#39;s being displayed in the target link matches the flag/language name on which the mouse of the user is hovering, all while preserving the other elements the current URL contains, as well as their order. But before that, let&#39;s consider the useful getData_language_bar() method
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
   
      /* from application/models/LocModel.php */  
        
      ...  
      public function getData_language_bar(){  
   
           $this-&amp;gt;locData = array();  
           $line = array();  
   
           // we first check what language directories (inside both front-end and back-end /language dirs)   
           //exist physically on server. We use PHP&#39;s glob() function for that  
           $language_directories = glob(PATH_TO_LANGUAGES . &#39;/*&#39; , GLOB_ONLYDIR);  
           $language_admin_directories = glob(&#39;admin/&#39;.PATH_TO_LANGUAGES. &#39;/*&#39; , GLOB_ONLYDIR);  
   
           $admin_language_list = array();  
   
           foreach($language_admin_directories as $admin_dir){  
                $exp_admin_dir = explode(&quot;/&quot;,$admin_dir);  
                array_push($admin_language_list, end($exp_admin_dir));  
           }  
   
           $language_list = array();  
   
           foreach($language_directories as $dir){  
   
                $exp_dir = explode(&quot;/&quot;,$dir);  
   
                // if the currently processed front-end language dir can be found in the admin language dir array,  
                // we push it inside the $language_list array, to be used later for checking   
                if( in_array( end($exp_dir), $admin_language_list)){array_push($language_list, end($exp_dir));}  
           }  
   
           //then we pull out the language list from the db  
           $q_ln_bar = &quot;SELECT id_localization, localization_name, localization_flag, localization_enabled   
                           FROM localization   
                           WHERE localization_enabled = &#39;yes&#39;  
                           ORDER BY id_localization ASC                      
                          &quot;;  
   
           MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($q_ln_bar);  
   
           while($mylnbar = MySQLModel::get_mysql_instance()-&amp;gt;getRows($q_ln_bar)){  
        
                //we now compare the each freshly pulled language row from the DB   
                //with our array of check existing physical language dirs  
                if(in_array($mylnbar[&#39;localization_name&#39;], $language_list)){   
                     // so that, basically, if the name of the currently processed language corresponds to existing language folders   
                     //in both back-end and front-end at the same time, we consider that language valid and stack it in an array,   
                     //that will be stacked with other arrays in one more array that will be returned to us as a whole   
                     array_push($line, $mylnbar[&#39;localization_name&#39;],$mylnbar[&#39;localization_flag&#39;]);  
                     array_push($this-&amp;gt;locData,$line);$line = array();  
                }  
           }  
           return $this-&amp;gt;locData;   
      }       
      ...  
   
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
And then the get_ln_straight() method we have started to discuss earlier, here to ensure the user will always fall back on its feet while changing the language from any page
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      /* from application/models/LocModel.php */  
        
      ...  
      public function get_ln_straight($lang){  
        
           //using $_SERVER[&#39;QUERY_STRING&#39;] will make our life a lot easier when it comes to dealing with clean URLs  
        
           if(empty($_SERVER[&#39;QUERY_STRING&#39;])){ // if $_SERVER[&#39;QUERY_STRING&#39;] turns out empty we use default values  
                if(DEFAULT_PLINK){$plink = &#39;&amp;amp;&#39;.PLINK.&#39;=&#39;.CURRENT_PLINK;} else{$plink = &#39;&#39;;}  
                $_SERVER[&#39;QUERY_STRING&#39;] = RLINK.&#39;=&#39;.CURRENT_RLINK.$plink.&#39;&amp;amp;&#39;.LN.&#39;=&#39;.CURRENT_LANG;  
           }   
   
           $this-&amp;gt;query_string = $_SERVER[&#39;QUERY_STRING&#39;];  
           $pat = &#39;/&#39;.LN.&#39;=[a-z]{2}/&#39;;  
           $pat2 = &#39;/language=[a-z]{2}/&#39;; //that&#39;s for Google CSE  
   
           //if we can spot a valid URL language atom, we replace it by the then-sought language, $lang,   
           //initially passed as parameter to the method  
           if(preg_match($pat, $this-&amp;gt;query_string, $matches, PREG_OFFSET_CAPTURE, 3)){  
                $rpl = LN.&#39;=&#39;.$lang;  
                $this-&amp;gt;query_string = preg_replace($pat, $rpl, $this-&amp;gt;query_string);  
           }  
   
           if(preg_match($pat2, $this-&amp;gt;query_string, $matches, PREG_OFFSET_CAPTURE, 3)){  
                $rpl2 = &#39;language=&#39;.$lang;  
                $this-&amp;gt;query_string = preg_replace($pat2, $rpl2, $this-&amp;gt;query_string);  
           }  
   
           //normally at that point, we should already be fine,  
           //but clean urls come complicating everything again...  
           if(CLEAN_URLS == true){//if clean urls are on,  
                  
                //we don&#39;t just preg_replace our query string, we explode it.  
                $exp_query_string = explode(&#39;&amp;amp;&#39;, $this-&amp;gt;query_string);  
   
                $markers = array(  
                     //and here come markers  
                     //basically, markers are something we use with clean_urls whenever thought necessary,   
                     //to avoid collision between .htaccess rewrite rules, when there are (too) many of them  
                     &#39;pn&#39; =&amp;gt; PGN,  
                     //note that &#39;pn&#39; is what will be written in .htaccess  
                     //and PGN is the constant name that normally holds the page number url variable &#39;pgn&#39;.   
                     // again, with clean urls being off, you could assign any other value than pgn to the constant PGN  
                     &#39;sn&#39; =&amp;gt; SN  
                );  
   
                $fake_string = array(); //this will hold our new url string until imploded  
   
                foreach($exp_query_string as $atom){ //for each atom of the old string (x=y)  
   
                     $exp_atom = explode(&quot;=&quot;,$atom); // we explode it (the atom)  
   
                     $needle = $exp_atom[0];     //and take its left part, called the needle in this case  
   
                     if(!preg_match(&#39;/success/&#39;,$atom)){// we avoid the word success in our algorithm for collision reasons       
                            
                          //we then search for that needle inside the haystack (marker&#39;s array)  
                            
                          if(in_array($needle, $markers) ){   
                               //if found, we will create a new clean atom with the marker in it.   
                               //because that&#39;s what markers are here for - making clear what the nature of the value they bare is  
   
                               $key = array_search($needle, $markers); //so we get its key   
   
                               $new_atom = $key.&#39;/&#39;.$exp_atom[1]; //and actually create the new &#39;clean&#39; atom using that key,   
                               //a slash sign and the value of the old one  
                          }  
                          else{$new_atom = $exp_atom[1];} // otherwise, if the atom does not need a marker, we retain only the x part of the old string  
   
                          array_push($fake_string,$new_atom); //and we stack each resulting atom in our $fake_string array  
                     }  
                }  
   
                ///and then we turn that array to a string, using &#39;/&#39; to implode all parts  
        
                $new_string = implode(&quot;/&quot;, $fake_string);  
                  
                //we quickly append our clean path constant and we&#39;re good to go  
                $this-&amp;gt;query_string = CLEAN_PATH.&#39;/&#39;.$new_string;  
   
                //not just yet though... in case of Google CSE  
                if(preg_match(&#39;/cx=(.*)&amp;amp;gs=(.*)&amp;amp;sa.x=(.*)&amp;amp;sa.y=(.*)/&#39;, $_SERVER[&#39;REQUEST_URI&#39;])){   
                     // so, if google search is on  
                     //we explode not the query string but the request URI this time  
                     $exp_q_s = explode(&#39;?&#39;,$_SERVER[&#39;REQUEST_URI&#39;]);  
                     $this-&amp;gt;query_string .= &#39;?&#39;.$exp_q_s[1]; //and retain only the parts proper to Google,   
                     //(so the user keeps seeing its search results after it has selected another language),   
                     //we concatenate them to the query string we&#39;ve just built and now we&#39;re good to go.  
                }  
           }   
           else{  
                //else, if clean urls weren&#39;t set, we only need to append $_SERVER[&#39;PHP_SELF&#39;]   
                //instead of the clean path constant before the new string we have built   
                $this-&amp;gt;query_string = $_SERVER[&#39;PHP_SELF&#39;].&#39;?&#39;.$this-&amp;gt;query_string;  
           }  
   
           return $this-&amp;gt;query_string; //we return our resulting string  
      }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Then still from the localization model, the get_encoding_straight() method, which if passed the current language as parameter, will return the corresponding charset, as defined in languages/encoding.php. This method will be used in header.php, both in front-end and back-end. By the way, we will get to the header files today while getting in the subject of theme handling, so we will have a chance to see the get_encoding_straight() method in action there.&amp;nbsp;&amp;nbsp;
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      /* from application/models/LocModel.php */  
   
      ...  
      public function get_encoding_straight($lang){  
   
           require PATH_TO_LANGUAGES.&#39;/encoding.php&#39;; // we require the file that contains the $encod array, as discussed earlier today.  
   
           if (array_key_exists($lang,$encod)){ //if we can find the current language in that array, as key  
                $this-&amp;gt;encoding = $encod[$lang]; we then use its corresponding value  
           }  
           else{$this-&amp;gt;encoding = &#39;utf-8&#39;;} //else we default to utf-8  
   
           return $this-&amp;gt;encoding; //we return what we have then  
      }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Finally, a word about the choice of character sets though: while this may feel odd to some of you that I sometimes modify the MySQL charset to Latin1 before attempting a query, from the way the database tables for this project were originally set up (latin1_swedish_ci), plus using utf-8 as HTML charset on all pages, no encoding-related display issues have yet arisen while testing the system against languages such as French, Russian or even Swedish, this on both Windows/*nix platforms. In other words, why changing what already works? But still, if you somehow end up getting encoding issues that I myself do not get, just try other values to see what works for you, although, everyone should be just fine with the settings as they currently are, regardless of the (modern) browser it&#39;s being displayed in.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;b&gt;Theme handling&lt;/b&gt;
&lt;/h2&gt;
&lt;br /&gt;
At the beginning of these series, I have presented the template engine that dynamically includes all the necessary view files, plus takes care off the passing of variables from the controller. But there&#39;s of course more to theme handling than this powerful routine. I justify:
&lt;br /&gt;
&lt;br /&gt;
Let&#39;s start by analyzing the overall structure of the theme folder
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiephO29epFTo4sjc2wzgkFiNd0IKNU5EcWzyQ8FLmapLcRbASfEetR_j8CppMizF8NP2Et-9-pQF9-DHEiHsXIIG1svwTd6hcBIjXOeEJLXnbjpWWfgBFoDEJTfOad3OznQCUMAB7cVdX-/s1600/themes_structure.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiephO29epFTo4sjc2wzgkFiNd0IKNU5EcWzyQ8FLmapLcRbASfEetR_j8CppMizF8NP2Et-9-pQF9-DHEiHsXIIG1svwTd6hcBIjXOeEJLXnbjpWWfgBFoDEJTfOad3OznQCUMAB7cVdX-/s1600/themes_structure.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Then the same structure in more detail&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkR_pw7Fc2gBqnJF-En1qhKW8YJG13TzC4gx8W9QXgi8Od0nou7SjatKGlHagq6cnPswO8b8UykyQvcbcQ1CgIkSk11L7U8Olq-10BZ9Sb1o0tI6P2Z_Hl7X5KerLwVNXr2V4oM_UVm8WQ/s1600/themes_structure_detailed.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;264&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkR_pw7Fc2gBqnJF-En1qhKW8YJG13TzC4gx8W9QXgi8Od0nou7SjatKGlHagq6cnPswO8b8UykyQvcbcQ1CgIkSk11L7U8Olq-10BZ9Sb1o0tI6P2Z_Hl7X5KerLwVNXr2V4oM_UVm8WQ/s320/themes_structure_detailed.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&amp;nbsp;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDmp7ad-9O97f4m94rciI69xmNbfv7cZ1Oeph38gZ_LFf2rbK6wXQwgQ8iSR3o_57vT__RGv0m8Mms06G_XkQzIRvH-4YDwenYkcn-vuJqzLq30OOQ_hDG62bLBpUBpHICCdfN1VNak306/s1600/themes_structure_detailed2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;264&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDmp7ad-9O97f4m94rciI69xmNbfv7cZ1Oeph38gZ_LFf2rbK6wXQwgQ8iSR3o_57vT__RGv0m8Mms06G_XkQzIRvH-4YDwenYkcn-vuJqzLq30OOQ_hDG62bLBpUBpHICCdfN1VNak306/s320/themes_structure_detailed2.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
From this picture above, you can see there are two different styles being operated, which are named skane and sparta. These folders exist in the back-end section too but vary in content.
&lt;br /&gt;
&lt;br /&gt;
At root (/themes/your_style), you can find all the view files proper to each module and not only. There lie our common template files header.php, main.php and footer.php and we can also find secondary view files like vf_image_u.php or follow.php, which respectively create an image for the captcha in registration.php and create a social media follow bar to be included wherever found stylish inside main.php. More interesting view files can be found there, like paging.php but we will get to them during the forthcoming settlements only.
&lt;br /&gt;
&lt;br /&gt;
Jumping straight into the css folder, we find name_of_the_style.css.php which is the main css file for any given theme. It bares a .php extension because on some occasions we need to use PHP variables there. We also find specific CSS files for the libraries we use like dTree, which has dtree.css sitting there and so on.
&lt;br /&gt;
&lt;br /&gt;
On the same level as the css folder, we find a folder called images. That folder holds two sub-folders called favicon and servicing. While it looks obvious what the favicon folder is here for, the servicing folder however requires a little explanation. It is a folder that holds images that will not only build on the visual identity of a given theme, but that are also dedicated to common navigation items. As an example, servicing is the folder where you will find small arrows, warning and missing page/module icons. And since menus are considered standard navigation items, you will also find two sub-folders called dtree and hmenu, which contain images for use with the menu system you already know of. In fact a third sub-folder to the servicing folder can even be found, called rss, which contains a few rss feed logo .png format images to choose from (see RSS_ICON_FILE in config.php).
&lt;br /&gt;
&lt;br /&gt;
At the root level of /themes/your_style/images, therefore at the same level where the favicon and servicing folder are, can main logo images be found.
&lt;br /&gt;
&lt;br /&gt;
Entering the /themes/your_style/css folder again, we find there&#39;s an image folder too, not to be confused with /themes/your_style/images. This will contain images for use in Javascript dialog boxes. Note these dialog boxes are also configurable via /themes/your_style/css/dialog_box.css.
&lt;br /&gt;
&amp;nbsp;
&lt;br /&gt;
Back at the root level of the themes folder reside two of our common template files. The first one is header.php
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&amp;gt;  
 &amp;lt;?php  
 // here we sort out language code names to be compatible with most standards  
 if(CURRENT_LANG){  
      if(CURRENT_LANG == &#39;uk&#39;){$tinymce_lang = &#39;uk_UA&#39;;}  
      elseif(CURRENT_LANG == &#39;fr&#39;){$tinymce_lang = &#39;fr_FR&#39;;}  
      elseif(CURRENT_LANG == &#39;se&#39;){$tinymce_lang = &#39;sv_SE&#39;;}  
      else{$tinymce_lang = CURRENT_LANG;}  
 }  
 else{$tinymce_lang = &#39;en&#39;;} // if nothing was found, we default to English  
 ?&amp;gt;  
 &amp;lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;&amp;lt;?php echo $tinymce_lang; ?&amp;gt;&quot; lang=&quot;&amp;lt;?php echo $tinymce_lang; ?&amp;gt;&quot;&amp;gt;  
 &amp;lt;head&amp;gt;  
 &amp;lt;?php   
 $header_utils = new UtilsModel();  
 $met = $header_utils-&amp;gt;get_meta_info(); //we fetch meta infos from the DB, this is being discussed further down the page.  
   
 foreach($met as $m){ //for each meta entry...   
      if($m[0] == &#39;title&#39;){ // if tag is title  
           echo &#39;&amp;lt;title&amp;gt; &#39;;   
           if(isset($page_name)){echo $page_name;}   
           else{  
                if(isset($module_name)){  
                     echo isset($GLOBALS[&#39;t_&#39;.strtolower($module_name)]) ? $GLOBALS[&#39;t_&#39;.strtolower($module_name)] : $module_name;  
                }  
           }  
           echo &#39;&amp;lt;/title&amp;gt;&#39;;   
           }  
      else{ //if not title tag, we use the rest of what we have pulled from the meta table.  
           if($m[0] == &#39;http-equiv&#39;){   
                if($m[1] == &quot;Content_Type&quot;){$m[1] = &quot;Content-Type&quot;;}   
                $char = &quot; charset = &quot;.LocModel::getInstance()-&amp;gt;get_encoding_straight(CURRENT_LANG).&quot;&quot;;  
                $m[2] .= $char;  
           }  
           echo &#39;&amp;lt;meta &#39;.$m[0].&#39;=&quot;&#39;.$m[1].&#39;&quot; content=&quot;&#39;.$m[2].&#39;&quot; /&amp;gt;&#39;;  
      }  
 }  
 ?&amp;gt;  
 &amp;lt;!-- now we link all the CSS/JS we need on our project --&amp;gt;  
 &amp;lt;link rel=&quot;shortcut icon&quot; href=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES; ?&amp;gt;/&amp;lt;?php echo CURRENT_THEME; ?&amp;gt;/images/favicon/favicon.ico&quot; type=&quot;image/x-icon&quot; /&amp;gt;  
 &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES; ?&amp;gt;/&amp;lt;?php echo CURRENT_THEME; ?&amp;gt;/css/&amp;lt;?php echo CURRENT_THEME; ?&amp;gt;.css.php&quot; type=&quot;text/css&quot; /&amp;gt;  
 &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES; ?&amp;gt;/&amp;lt;?php echo CURRENT_THEME; ?&amp;gt;/css/dtree.css&quot; /&amp;gt;  
 &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES; ?&amp;gt;/&amp;lt;?php echo CURRENT_THEME; ?&amp;gt;/css/hmenu.css&quot; /&amp;gt;  
 &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES; ?&amp;gt;/&amp;lt;?php echo CURRENT_THEME; ?&amp;gt;/css/dialog_box.css&quot; /&amp;gt;  
 &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES; ?&amp;gt;/&amp;lt;?php echo CURRENT_THEME; ?&amp;gt;/css/dtp.css&quot; /&amp;gt;  
 &amp;lt;script src=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;; ?&amp;gt;languages/&amp;lt;?php echo CURRENT_LANG; ?&amp;gt;/JS/&amp;lt;?php echo CURRENT_LANG; ?&amp;gt;.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;  
 &amp;lt;script src=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;; ?&amp;gt;languages/translate.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;  
 &amp;lt;?php  
 include &#39;public/js/main.php&#39;;  
 include &#39;public/js/dtree.php&#39;;  
 ?&amp;gt;  
 &amp;lt;script src=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;; ?&amp;gt;/public/js/hmenu.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;  
 &amp;lt;?php include &#39;public/js/dialog_box.php&#39;; ?&amp;gt;  
 &amp;lt;script src=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;; ?&amp;gt;public/js/dtp.js&quot;type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;  
 &amp;lt;!-- and in case Javascript is disabled, we use a noscript tag --&amp;gt;  
 &amp;lt;noscript&amp;gt;&amp;lt;div class=&quot;disabled_feature&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_javascript_has_been_disabled&#39;]; ?&amp;gt; &amp;lt;?php echo $GLOBALS[&#39;t_please_reenable_it&#39;]; ?&amp;gt; - &amp;lt;a href=&quot;http://www.enable-javascript.com&quot; target=&quot;_blank&quot;&amp;gt;&amp;lt;u&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_instructions_here&#39;]; ?&amp;gt;&amp;lt;/u&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;  
 &amp;lt;/noscript&amp;gt;  
 &amp;lt;/head&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
So, in header.php, we first sort out our language code names, for enhanced compatibility with the most standards officially in use and we use the freshly defined code in our html xml declaration. Then we pull all meta info from the DB table called meta, that looks as follows
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; --  
 -- Table structure for table `meta`  
 --  
   
 CREATE TABLE `meta` (  
  `id_meta` tinyint(4) NOT NULL,  
  `meta_type` varchar(12) NOT NULL,  
  `meta_name` varchar(40) NOT NULL,  
  `meta_value` text NOT NULL  
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1;  
   
 --  
 -- Dumping data for table `meta`  
 --  
   
 INSERT INTO `meta` (`id_meta`, `meta_type`, `meta_name`, `meta_value`) VALUES  
 (1, &#39;title&#39;, &#39;title&#39;, &#39;Gumbo-CMS&#39;),  
 (2, &#39;name&#39;, &#39;description&#39;, &#39;Gumbo (or Gumbo-CMS) is a Mutilingual MVC CMS written \&#39;from scratch\&#39; in PHP 5 and that requires Apache 2.2+ and MySQL 5.6+&#39;),  
 (3, &#39;name&#39;, &#39;keywords&#39;, &#39;cms, mvc, php, multilingual cms&#39;),  
 (4, &#39;name&#39;, &#39;robots&#39;, &#39;index, follow&#39;),  
 (5, &#39;name&#39;, &#39;google_bot&#39;, &#39;index, follow&#39;),  
 (6, &#39;name&#39;, &#39;google&#39;, &#39;notranslation&#39;),  
 (7, &#39;http-equiv&#39;, &#39;Content_Type&#39;, &#39;text/html;&#39;);  
   
 --  
 -- Indexes for dumped tables  
 --  
   
 --  
 -- Indexes for table `meta`  
 --  
 ALTER TABLE `meta`  
  ADD PRIMARY KEY (`id_meta`);  
   
 --  
 -- AUTO_INCREMENT for dumped tables  
 --  
   
 --  
 -- AUTO_INCREMENT for table `meta`  
 --  
 ALTER TABLE `meta`  
  MODIFY `id_meta` tinyint(4) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;  
&lt;/code&gt;&lt;/pre&gt;
and we start looping the results we have successfully fetched.&lt;br /&gt;
&lt;br /&gt;
A note about meta tags though, please visit &lt;a href=&quot;https://support.google.com/webmasters/answer/79812?hl=en&quot;&gt;https://support.google.com/webmasters/answer/79812?hl=en&lt;/a&gt; for a brief overview of the tags Google Inc. understands.&lt;br /&gt;
&lt;br /&gt;
Then we go onto including all needed CSS/Javascript files and end up using a noscript tag that will warn users in case Javascript is disabled on their viewing device. That&#39;s all there is to the header file found in the front-end. In a next settlement though, we will see the header file for the back-end contains a lot more information, primarily due to TinyMCE being initialized there. Note that header.php ends with the closing head tag.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
Meanwhile, in footer.php, the starting tag is a tr tag and the closing tag is the closing html tag
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      /* themes/your_style/footer.php */  
 ?&amp;gt;  
      &amp;lt;tr class=&quot;footer_tr&quot;&amp;gt;  
           &amp;lt;td colspan=&quot;2&quot; class=&quot;footer&quot;&amp;gt;  
                &amp;lt;?php echo $GLOBALS[&#39;t_copyright&#39;].&#39; &#39;.COPYRIGHT; ?&amp;gt;  
           &amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
 &amp;lt;/table&amp;gt;  
 &amp;lt;/div&amp;gt;  
 &amp;lt;/body&amp;gt;  
 &amp;lt;/html&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Nothing too difficult about this one as well, and that wraps up this settlement in which you have learned more about the way languages and themes are dealt with in this project, getting for now a complete overview, for at least the front-end part.&lt;br /&gt;
&lt;br /&gt;
But next time, I will take these series to yet another level, getting onto the other side of the fence by introducing you to the back-end side of our CMS, starting with the building of a really exciting language management module. Creating this new module will allow me, among other things, to tell you more about the paging and sorting features many of the back-end modules actually depend on.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;This &lt;a href=&quot;http://www.rolandc.net/2016/02/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-6.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt; first appeared Thursday the 16th of February 2016 on &lt;a href=&quot;http://rolandc.net/&quot;&gt;RolandC.net&lt;/a&gt;.&lt;/span&gt; </description><link>http://www.rolandc.net/2016/02/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-6.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizoUdNx6z0Od8Z_qr1C9Yh5L-RH2EkR7cOIxhb3lL91ZPnCJeoWlsj0iPni4qCny4ToOJoSPMWdc_3kYry8xZWb9rjHazfW5CmWxBryWEc7B8JDyzuSaQxp5yBclfa3xPjLyqqGWSc5524/s72-c/language_structure_basic2.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-1849060257417612061</guid><pubDate>Tue, 29 Dec 2015 10:10:00 +0000</pubDate><atom:updated>2016-01-07T11:54:36.098+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cms</category><category domain="http://www.blogger.com/atom/ns#">gumbo-cms</category><category domain="http://www.blogger.com/atom/ns#">mvc</category><category domain="http://www.blogger.com/atom/ns#">opensource</category><category domain="http://www.blogger.com/atom/ns#">php</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">tutorial</category><category domain="http://www.blogger.com/atom/ns#">webdev</category><title>How to build your own Multilingual PHP MVC CMS from scratch - Part 5 - Frontend (part 3): User Interface</title><description>Welcome to the fifth settlement of our tutorial series on how to build your own Multilingual MVC CMS from scratch and in which we will be building an interface for the user to sign in and update its personal data.&lt;br /&gt;&lt;br /&gt;
During the last settlements, we have created a page module that allows the user to display a specific page based on URL input and aided by some third-party scripts, we have also created a customizable menu display system that comes in two layout flavors, vertical and horizontal. In a future settlement, I will show you how to create the back-end feature that will allow you to completely edit the menus stored in the database.&lt;br /&gt;&lt;br /&gt;
But for now, we will set our interest onto building a user interface that will allow for signing into the system as well as updating personal data. Now, since in reality, the user’s experience on this project encompasses at least 6 whole modules, which are: registration, sign in, forgotten password, account, profile, settings, password changing, plus the use of authentication model as well and that we obviously cannot review all of them at once, we will only be reviewing some of them, which are the registration, signin and profile modules. And this will still be a lot, so I will be focusing only on the parts that are of interest. This makes sense since there is no point in explaining how a controller works over and over again.&lt;br /&gt;&lt;br /&gt;
Anyway, the table named ‘users’ represents the natural entry point through which we will start digging our user interface.
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; --  
 -- Table structure for table `users`  
 --  
   
 CREATE TABLE `users` (  
  `id_user` int(11) NOT NULL,  
  `user_login` varchar(12) NOT NULL,  
  `user_password` text NOT NULL,  
  `user_email` varchar(72) NOT NULL,  
  `user_country` varchar(255) NOT NULL DEFAULT &#39;uk&#39;,  
  `user_gender` enum(&#39;else&#39;,&#39;female&#39;,&#39;male&#39;) NOT NULL,  
  `user_birthdate` varchar(100) NOT NULL,  
  `user_type` enum(&#39;0&#39;,&#39;1&#39;,&#39;2&#39;) NOT NULL,  
  `user_ip` varchar(16) NOT NULL,  
  `user_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP  
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1;  
   
 --  
 -- Dumping data for table `users`  
 --  
   
 INSERT INTO `users` (`id_user`, `user_login`, `user_password`, `user_email`, `user_country`, `user_gender`, `user_birthdate`, `user_type`, `user_ip`, `user_timestamp`) VALUES  
 (1, &#39;test&#39;, &#39;c08ac56ae1145566f2ce54cbbea35fa3&#39;, &#39;test@mail.com&#39;, &#39;fidji&#39;, &#39;male&#39;, &#39;08/02/1985&#39;, &#39;1&#39;, &#39;127.0.0.1&#39;, &#39;2015-10-02 00:55:41&#39;);  
   
 --  
 -- Indexes for dumped tables  
 --  
   
 --  
 -- Indexes for table `users`  
 --  
 ALTER TABLE `users`  
  ADD PRIMARY KEY (`id_user`);  
   
 --  
 -- AUTO_INCREMENT for dumped tables  
 --  
   
 --  
 -- AUTO_INCREMENT for table `users`  
 --  
 ALTER TABLE `users`  
  MODIFY `id_user` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;  
&lt;/code&gt;&lt;/pre&gt;Relying on the users table is the table named user_settings, which I will expose while discussing the profile module. Note the user_type field will not be of much help since permission levels will not be implemented, at least not until the end of the series.&lt;br /&gt;&lt;br /&gt;
Without any transition whatsoever, let’s jump right in the registration form. There’s no point in providing a user with an interface to sign in if that user was never given the opportunity to register beforehand, right?&lt;br /&gt;
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; /* themes/your_style/registration.php */  
   
 &amp;lt;div align=&quot;center&quot;&amp;gt;  
 &amp;lt;?php  
 // we first take care off displaying feedback, if it contains error messages  
 if(isset($feedback) &amp;amp;&amp;amp; !is_string($feedback)){  
 ?&amp;gt;  
      &amp;lt;table border=&quot;0&quot; cellpadding=&quot;2&quot; cellspacing=&quot;2&quot; align=&quot;center&quot;&amp;gt;  
      &amp;lt;ul&amp;gt;  
      &amp;lt;?php  
      foreach($feedback as $err){  
           foreach($err as $er){  
                $msgs = &#39;&#39;;  
                $exp_msg = explode(&quot;#&quot;,$er[1]);  
                $cx = 0;  
                foreach($exp_msg as $em){  
                     if($cx &amp;lt; sizeof($exp_msg)-1){$trailer = &#39;; &#39;;}else{$trailer = &#39;&#39;;}  
                     $msgs .= $GLOBALS[&#39;t_&#39;.$em].$trailer;$cx++;  
                }  
                ?&amp;gt;  
                &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&quot;response_negative&quot; style=&quot;font-size:12px;&quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;?php echo ucfirst($GLOBALS[&#39;t_&#39;.$er[0]]); ?&amp;gt; -&amp;gt; &amp;lt;?php echo $msgs; ?&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;  
      &amp;lt;?php   
           }  
      }  
 ?&amp;gt;  
      &amp;lt;/ul&amp;gt;  
      &amp;lt;/table&amp;gt;  
      &amp;lt;br /&amp;gt;  
      &amp;lt;?php  
 }  
 if(isset($feedback) &amp;amp;&amp;amp; $feedback == &#39;failed&#39;){ ?&amp;gt;&amp;lt;div class=&quot;failed&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_problem_registering_you&#39;]; ?&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;?php }  
   
 //now preparing for action urls to deploy  
 if(CLEAN_URLS == true){$url = CLEAN_PATH.&#39;/&#39;.CURRENT_RLINK.&#39;/&#39;.CURRENT_LANG;}  
 else{$url = $_SERVER[&#39;PHP_SELF&#39;].&#39;?&#39;.RLINK.&#39;=&#39;.CURRENT_RLINK.&#39;&amp;amp;&#39;.LN.&#39;=&#39;.CURRENT_LANG;}  
   
 $utils = new UtilsModel();  
 $user_ip = $utils-&amp;gt;get_user_ip();  
 ?&amp;gt;  
 &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;  
 &amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;  
 &amp;lt;/div&amp;gt;  
   
 &amp;lt;!-- the actual registration form --&amp;gt;  
 &amp;lt;form id=&quot;frm_registration&quot; name=&quot;frm_registration&quot; enctype=&quot;multipart/form-data&quot; method=&quot;post&quot; action=&quot;&amp;lt;?php echo $url; ?&amp;gt;&quot; onsubmit=&quot;return Check_registration(this,&#39;off&#39;);&quot;&amp;gt;  
       
 &amp;lt;table border=&quot;0&quot; cellpadding=&quot;2&quot; cellspacing=&quot;0&quot; align=&quot;center&quot;&amp;gt;  
      &amp;lt;tr&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;std&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_login&#39;]; ?&amp;gt;*&amp;lt;/td&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;input&quot;&amp;gt;&amp;lt;input class=&quot;text&quot; type=&quot;text&quot; id=&quot;login&quot; name=&quot;login&quot; value=&quot;&amp;lt;?php echo isset($_POST[&#39;login&#39;])?htmlspecialchars($_POST[&#39;login&#39;]):&quot;&quot;; ?&amp;gt;&quot; maxlength=&quot;&amp;lt;?php echo MAX_CHARS_LOGIN; ?&amp;gt;&quot; onkeyup=&quot;check_login_exists();&quot; /&amp;gt;&amp;lt;/td&amp;gt;  
           &amp;lt;!-- pay attention to the ternary statements - if post_login is set we use it,   
           //otherwise we leave the field empty - this allows for field rememberance,   
           //so that if for any reason the form gets reloaded, the user won&#39;t have to type everything over again --&amp;gt;  
      &amp;lt;/tr&amp;gt;  
   
      &amp;lt;tr&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;std&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_password&#39;]; ?&amp;gt;*&amp;lt;/td&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;input&quot;&amp;gt;&amp;lt;input class=&quot;text&quot; type=&quot;password&quot; id=&quot;password&quot; name=&quot;password&quot; value=&quot;&amp;lt;?php echo isset($_POST[&#39;password&#39;])?htmlspecialchars($_POST[&#39;password&#39;]):&quot;&quot;; ?&amp;gt;&quot; maxlength=&quot;&amp;lt;?php echo MAX_CHARS_PWD; ?&amp;gt;&quot; /&amp;gt;&amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
   
      &amp;lt;tr&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;std&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_email&#39;]; ?&amp;gt;*&amp;lt;/td&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;input&quot;&amp;gt;&amp;lt;input class=&quot;text&quot; type=&quot;text&quot; id=&quot;email&quot; name=&quot;email&quot; value=&quot;&amp;lt;?php echo isset($_POST[&#39;email&#39;]) ? htmlspecialchars($_POST[&#39;email&#39;]):&quot;&quot;; ?&amp;gt;&quot; maxlength=&quot;&amp;lt;?php echo EMAIL_MAX_LENGTH; ?&amp;gt;&quot; onkeyup=&quot;check_email_exists();&quot; /&amp;gt;&amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
   
      &amp;lt;tr&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;std&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_country&#39;]; ?&amp;gt;*&amp;lt;/td&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;input&quot;&amp;gt;  
           &amp;lt;select name=&quot;country&quot;&amp;gt;  
           &amp;lt;?php   
           require(&#39;lists/countries/countries.php&#39;); //for clarity, we use a country list  
   
                if(isset($country_list) &amp;amp;&amp;amp; !empty($country_list)){  
   
                foreach($country_list as $country){                      
                     if(isset($_POST[&#39;country&#39;]) &amp;amp;&amp;amp; $_POST[&#39;country&#39;] == $country[1]){$sel = &#39; selected=selected&#39;;}else{$sel = &#39;&#39;;}  
                ?&amp;gt;  
                     &amp;lt;option value=&quot;&amp;lt;?php echo $country[1]; ?&amp;gt;&quot;&amp;lt;?php echo $sel; ?&amp;gt;&amp;gt;&amp;lt;?php echo $country[0]; ?&amp;gt;&amp;lt;/option&amp;gt;  
                     &amp;lt;?php  
                     }  
                }   
           ?&amp;gt;  
           &amp;lt;/select&amp;gt;  
           &amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
   
      &amp;lt;tr&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;std&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_gender&#39;]; ?&amp;gt;*&amp;lt;/td&amp;gt;  
           &amp;lt;?php  
 // if(isset($_POST[&#39;gender&#39;])){echo $_POST[&#39;gender&#39;];$checked = &#39; checked&#39;;}else{$checked = &#39;&#39;;}   
 ?&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;input&quot;&amp;gt;  
                &amp;lt;table border=&quot;0&quot; cellpadding=&quot;2&quot; cellspacing=&quot;0&quot; width=&quot;100%&quot;&amp;gt;  
                     &amp;lt;tr&amp;gt;  
                          &amp;lt;td&amp;gt;&amp;lt;input type=&quot;radio&quot; id=&quot;gender&quot; name=&quot;gender&quot; value=&quot;male&quot; checked /&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;?php echo $GLOBALS[&#39;t_male&#39;]; ?&amp;gt;&amp;lt;/td&amp;gt;  
 &amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;  
   
                     &amp;lt;td&amp;gt;&amp;lt;input type=&quot;radio&quot; id=&quot;gender&quot; name=&quot;gender&quot; value=&quot;female&quot;&amp;lt;?php if(isset($_POST[&#39;gender&#39;]) &amp;amp;&amp;amp; $_POST[&#39;gender&#39;] == &#39;female&#39;){echo &#39; checked&#39;;} ?&amp;gt; /&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;?php echo $GLOBALS[&#39;t_female&#39;]; ?&amp;gt;&amp;lt;/td&amp;gt;  
                     &amp;lt;/tr&amp;gt;  
                &amp;lt;/table&amp;gt;  
           &amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
   
      &amp;lt;tr&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;std&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_birthdate&#39;]; ?&amp;gt;*&amp;lt;/td&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;input&quot;&amp;gt;&amp;lt;input class=&quot;text&quot; type=&quot;text&quot; id=&quot;birthdate&quot; name=&quot;birthdate&quot; value=&quot;&amp;lt;?php echo isset($_POST[&#39;birthdate&#39;])?htmlspecialchars($_POST[&#39;birthdate&#39;]):&quot;&quot;; ?&amp;gt;&quot; maxlength=&quot;10&quot; onkeyup = &quot;displayDatePicker(&#39;birthdate&#39;);&quot; onclick = &quot;displayDatePicker(&#39;birthdate&#39;);&quot; /&amp;gt;&amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
        
      &amp;lt;tr&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;std&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_newsletter&#39;]; ?&amp;gt;&amp;lt;/td&amp;gt;  
           &amp;lt;td align=&quot;left&quot; class=&quot;input&quot;&amp;gt;&amp;lt;input type=&quot;checkbox&quot; name=&quot;newsletter&quot; value=&quot;&quot;&amp;lt;?php if(isset($_POST[&#39;newsletter&#39;])){$checked = &#39; checked&#39;;}else{$checked = &#39;&#39;;} echo $checked; ?&amp;gt; /&amp;gt;&amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
   
      &amp;lt;tr align=&quot;right&quot;&amp;gt;  
           &amp;lt;td align=&quot;center&quot; valign=&quot;middle&quot; class=&quot;std&quot; valign=&quot;bottom&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_verification_code&#39;]; ?&amp;gt;&amp;lt;/td&amp;gt;  
           &amp;lt;td align=&quot;center&quot;&amp;gt;  
                &amp;lt;table border=&quot;0&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; width=&quot;100%&quot;&amp;gt;  
                     &amp;lt;tr align=&quot;center&quot;&amp;gt;  
                          &amp;lt;td valign=&quot;middle&quot;&amp;gt;&amp;lt;img src=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES.&#39;/&#39;.CURRENT_THEME.&#39;/vf_image_u.php&#39;; ?&amp;gt;&quot; /&amp;gt;&amp;amp;nbsp;&amp;lt;/td&amp;gt;  
                          &amp;lt;td&amp;gt;&amp;lt;input name=&quot;vercode_u&quot; type=&quot;text&quot; id=&quot;vercode_u&quot; autocomplete=&quot;off&quot; maxlength=&quot;5&quot; style=&quot;height:;width:155px;font-size:px;background-color:#;color:#;&quot; /&amp;gt;&amp;lt;/td&amp;gt;  
                     &amp;lt;/tr&amp;gt;  
                &amp;lt;/table&amp;gt;  
           &amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
   
      &amp;lt;tr&amp;gt;  
           &amp;lt;td&amp;gt;&amp;lt;input type=&quot;hidden&quot; name=&quot;type&quot; value=&quot;1&quot; /&amp;gt;&amp;lt;/td&amp;gt;  
           &amp;lt;td&amp;gt;&amp;lt;input type=&quot;hidden&quot; name=&quot;ip&quot; value=&quot;&amp;lt;?php echo $user_ip; ?&amp;gt;&quot; /&amp;gt;&amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
   
      &amp;lt;tr&amp;gt;  
           &amp;lt;td class=&quot;std&quot; colspan=&quot;2&quot; align=&quot;center&quot;&amp;gt;  
           &amp;lt;br /&amp;gt;  
           &amp;lt;input type=&quot;submit&quot; id=&quot;submit&quot; name=&quot;submit&quot; class=&quot;submit&quot; value=&quot;&amp;lt;?php echo $GLOBALS[&#39;t_submit&#39;]; ?&amp;gt;&quot; /&amp;gt;&amp;lt;/td&amp;gt;  
      &amp;lt;/tr&amp;gt;  
 &amp;lt;/table&amp;gt;  
 &amp;lt;/form&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
There really isn’t much to the above code. It starts off displaying feedback messages, if those are containing registration errors. Then the actual registration form begins.&lt;br /&gt;&lt;br /&gt;
Ternary syntax is used for field remembering. Eg:
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;input class=&quot;text&quot; type=&quot;text&quot; id=&quot;login&quot; name=&quot;login&quot; value=&quot;&amp;lt;?php echo isset($_POST[&#39;login&#39;])?htmlspecialchars($_POST[&#39;login&#39;]):&quot;&quot;; ?&amp;gt;&quot; maxlength=&quot;&amp;lt;?php echo MAX_CHARS_LOGIN; ?&amp;gt;&quot; /&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;This tells that if a $post login is set, let’s use it, otherwise let’s leave empty.&lt;br /&gt;&lt;br /&gt;
Client-side Javascript validation is operated by a call to the Javascript Check_registration() function that can be found in public/js/main.php. In the event that the user disables Javascript, server-side validation is triggered via the check_form_errors() method as discussed further bellow.&lt;br /&gt;&lt;br /&gt;
During both onkeyup and onclick events, a dynamic Javascript/CSS-written date picker library is instantiated and appears below the birthdate field. This library will provided be as an attachment along with the corresponding CSS file. You may choose not to use this library and it will make no difference in terms of validation, as the birthdate field is validated client-side as well as server side. This third-party open-source date picker is here to operate only as an aesthetically pleasant date picker, enhancing the overall visual aspect.&lt;br /&gt;&lt;br /&gt;
A country list file and script is used for country selection.
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
 /* lists/countries/coutries.php */  
   
 require(&#39;array_countries.php&#39;);  
 $country_list = array();  
 $line = array();  
   
 foreach($array_country_list as $c){  
      array_push($line, $GLOBALS[&#39;t_&#39;.strtolower($c)], strtolower($c));  
      array_push($country_list, $line);  
      $line = array();  
 }  
 asort($country_list);  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;This snippet is pretty much self-explanatory and draws from the array bellow to create a new array containing the internationalized name of each country along with the all-lowercase formatted name of that same country.&lt;br /&gt;&lt;br /&gt;
Note the alphabetical sorting operated by the asort() function does not work too well with languages like Russian, or with html ascii codes because as of now, that’s as far as PHP’s support in that matter goes.
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
 /* lists/coutries/array_countries.php */  
   
 $array_country_list = array(  
 &#39;Afghanistan&#39;,  
 &#39;Albania&#39;,  
 &#39;Algeria&#39;,  
  ...,  
 &#39;Zimbabwe&#39;  
 );  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
The full array is available as attachment, along with the English translations, to be added to languages/en/en.php&lt;br /&gt;&lt;br /&gt;
An image containing a verification code is created by vf_image_u.php using GD, as follows&lt;br /&gt;
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
 /* themes/your_style/vf_image_u.php */  
   
 session_name(&#39;general_purpose&#39;);  
 session_start(); //we will store our random number inside a session variable   
      //so we need to declare a session here too.  
   
 $text = rand(10000,99999);   
 $_SESSION[&quot;vercode_u&quot;] = $text;   
 $height = 15;   
 $width = 45;   
    
 $image_p = imagecreate($width, $height); // check about.php in case the image does not display  
 $black = imagecolorallocate($image_p, 165, 174, 112);   
 $white = imagecolorallocate($image_p, 68, 68, 68);   
 $font_size = 14;   
    
 imagestring($image_p, $font_size, 0, 0, $text, $white);   
 imagejpeg($image_p, null, 80);   
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;This simple script is using the imagecreate() function from the GD library. If the image does not display, load the about.php script that resides at root, to find out if you have GD enabled + jpeg support. We also fabricate a random number that we store inside $_SESSION[“vercode_u”].&lt;br /&gt;&lt;br /&gt;
Of course other fields can be added, some removed, that’s up to you and your project’s business logic of course, but then you need to make sure the proper fields are correctly retrieved while processing the sent data inside RegModel.php.&lt;br /&gt;&lt;br /&gt;
But from a controller point of view, this has no importance whatsoever since we will fetch our form variables programatically. Here’s how
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
 /* application/controllers/RegController.php */  
   
 class RegController extends BaseController{  
        
      private $module_name;  
      private $form_name;  
      private $feedback;  
                  
      public function initialize(){  
             
           $this-&amp;gt;registry-&amp;gt;template-&amp;gt;module_name = $this-&amp;gt;getModuleName();  
           $this-&amp;gt;registry-&amp;gt;template-&amp;gt;form_name = $this-&amp;gt;getFormName();  
             
           // new in our initialize() method, the intercept_data() method  
           // which will catch all form variables   
           // and send it to the relevant model method  
   
           $this-&amp;gt;registry-&amp;gt;template-&amp;gt;feedback = $this-&amp;gt;intercept_data();  
           $this-&amp;gt;registry-&amp;gt;template-&amp;gt;assign_theme();  
      }  
   
      public function getModuleName(){  
           $model = RegModel::getInstance();  
           return $model-&amp;gt;get_module_name();  
      }  
        
      public function getFormName(){  
           $model = RegModel::getInstance();  
           return $model-&amp;gt;get_form_name();  
      }  
        
      public function intercept_data(){  
   
           // this method is where we intercept form variables  
           // to be then sent to the relevant model&#39;s method  
   
           $this-&amp;gt;dol_posts = array();       
   
           foreach($_POST as $key =&amp;gt; $val){//for each form variable  
   
                     // if there&#39;s a $post set of that variable,  
                     // we stack its value in the dol_posts array  
                  
                if(isset($_POST[$key])){array_push($this-&amp;gt;dol_posts,$key);}  
           }  
             
           $model = RegModel::getInstance(); //we call an instance of the reg model  
           return $model-&amp;gt;get_form_feedback($this-&amp;gt;dol_posts); // and invoke the method that will process our form variables, passing them as parameter.  
      }  
 }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
So, this controller brings us new elements that weren’t in the page controller because the logic there was different, there was no form and thus no data to be intercepted. The intercept_data() method is where we intercept form variables to be then sent to the relevant model’s method, in this case, the get_form_feedback() method belonging to the RegModel class (Singleton).
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php   
   
 /* from application/models/RegModel.php */  
   
 public function get_form_feedback($parms){  
             
           $util = new UtilsModel();  
   
           if(!empty($parms)){ //if we did receive form variables  
             
                $error_list = array();  
   
                // we check for form errors  
                $error_check_reg = self::check_reg_form_errors($parms);  
                //and whether login and email are unique  
                $is_unique_login = $util-&amp;gt;check_if_unique(&#39;users&#39;, &#39;user_login&#39;, $_POST[$parms[0]], array(&#39;login&#39;,&#39;already_in_use&#39;));  
                $is_unique_email = $util-&amp;gt;check_if_unique(&#39;users&#39;, &#39;user_email&#39;, $_POST[$parms[2]], array(&#39;email&#39;,&#39;already_in_use&#39;));  
   
                //if login is not unique, we stack that info into the error array  
                if (sizeof($is_unique_login) &amp;gt; 0){array_push($error_list,$is_unique_login);}  
   
                //same for email  
                if (CHECK_EMAIL_UNIQUE == true &amp;amp;&amp;amp; sizeof($is_unique_email) == 1){array_push($error_list,$is_unique_email);}  
   
                //and for general form errors  
                if (sizeof($error_check_reg) &amp;gt; 0){array_push($error_list,$error_check_reg);}  
   
                //if there&#39;s at least one error found, no matter what type            
                if(sizeof($error_list) &amp;gt; 0){  
                     //we return our array of errors  
                     $this-&amp;gt;form_feedback = $error_list;            
                }  
                else{  
                     //otherwise, no form errors, so we start processing the form   
                       
                     // that&#39;s the place where you make sure the form&#39;s HTML element   
                     // names you need to pull back correspond to what&#39;s in the list just bellow  
                     $field_list = array(&#39;login&#39;,&#39;password&#39;,&#39;email&#39;,&#39;country&#39;,&#39;gender&#39;,&#39;birthdate&#39;,&#39;newsletter&#39;,&#39;vercode_u&#39;,&#39;type&#39;,&#39;ip&#39;);  
                  
                     foreach($field_list as $fl){ //and for each for variable we have fetched,   
                          //if it can be found in the array above and there&#39;s a set $post variable of that name, then  
                          //we set a variable that will bare that same name, using the variable of variable ($$) syntax  
                          if(in_array($fl,$parms) &amp;amp;&amp;amp; isset($_POST[$fl])){$$fl = $_POST[$fl];}  
                     }  
   
                     //building a query to insert the new user  
                     $q_insert_user = &quot;INSERT INTO users (user_login, user_password, user_email, user_country, user_gender, user_birthdate, user_type, user_ip) VALUES (&#39;$login&#39;, md5(&#39;$password&#39;), &#39;$email&#39;, &#39;$country&#39;, &#39;$gender&#39;, &#39;$birthdate&#39;, &#39;$type&#39;, &#39;$ip&#39;)&quot;;  
   
                     // if the query executes well  
                     if(MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($q_insert_user) == 0){  
                          //we get the ID of the newly created user  
                          $q_id = &quot;SELECT id_user, user_login,user_password FROM users WHERE user_login = &#39;&quot;.$login.&quot;&#39; AND user_password = md5(&#39;&quot;.$password.&quot;&#39;)&quot;;  
                          MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($q_id);  
                          $my_id = MySQLModel::get_mysql_instance()-&amp;gt;getRows($q_id);  
   
                          if($my_id != &#39;&#39;){  
                               // and if everything went well and we have that ID we wanted  
                               $id = $my_id[&#39;id_user&#39;];  
                       
                               //let&#39;s create a settings entry in db  
                               if(CURRENT_LANG){$this-&amp;gt;lng = CURRENT_LANG;}  
                               //do not worry about the newsletter parameter because newsletters have not really been implemented  
                               //on the project. This is here only to give an effect of form completeness  
                               if(isset($newsletter)){$this-&amp;gt;nl = &#39;yes&#39;;}else{$this-&amp;gt;nl = &#39;&#39;;}  
                            
                               //we build the query to insert a settings row for that new user  
                               $q_insert_user_settings = &quot;INSERT INTO user_settings(user_settings_id_user, user_settings_language, user_settings_newsletter) VALUES (&#39;$id&#39;,&#39;$this-&amp;gt;lng&#39;,&#39;$this-&amp;gt;nl&#39;)&quot;;  
                                      
                               if(MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($q_insert_user_settings) == 0){  
   
                                         //and if everything went according to plan, we authentify the new user                      
                                         $auth = AuthModel::getInstance();  
                                         $auth-&amp;gt;stay_in($id,$login,$password);  
   
                                         //and then we take care of sending a &#39;thanks for registering - here are your credentials...&#39; email  
                                         // we will discuss email sending via PHPMailer in a future settlement   
                                         //so you can safely skip that part for now.  
                                         require_once(&#39;languages/templates/&#39;.CURRENT_LANG.&#39;/&#39;.strtolower($this-&amp;gt;module_name).&#39;_&#39;.CURRENT_LANG.&#39;.php&#39;);  
   
                                         if($regmail = $util-&amp;gt;send_mail(CONTACT_FORM_EMAIL,$email,$GLOBALS[&#39;t_reg_email_subject&#39;],$GLOBALS[&#39;t_reg_email_alt&#39;],$content)){$this-&amp;gt;form_feedback = 0 ;}  
   
                                         //In any case, our business logic requires us to relaod the page if registration turns out successful   
                                         //simply because we want the new user to automatically see a thanks for   
                                         //registering message,  
                                         //so we build a header location link and redirect the new user to that  
                                         if(DEFAULT_PLINK != &#39;&#39;){  
                                              if(CLEAN_URLS == true){$plink = &#39;/&#39;.DEFAULT_PLINK;}  
                                              else {$plink = &#39;&amp;amp;&#39;.PLINK.&#39;=&#39;.DEFAULT_PLINK;}  
                                         }  
                                         else{$plink = &#39;&#39;;}  
   
                                         if(CLEAN_URLS == true){$location = CLEAN_PATH.&#39;/&#39;.DEFAULT_RLINK.$plink.&#39;/&#39;.CURRENT_LANG.&#39;/regs&#39;;}  
                                         else{$location = $_SERVER[&#39;PHP_SELF&#39;].&#39;?&#39;.RLINK.&#39;=&#39;.DEFAULT_RLINK.$plink.&#39;&amp;amp;&#39;.LN.&#39;=&#39;.CURRENT_LANG.&#39;&amp;amp;reg=success&#39;;}  
                                         header(&#39;Location: &#39;.$location);exit();  
                                    }  
                               }  
                               else{$this-&amp;gt;form_feedback = &#39;failed&#39;;} //self-explanatory  
                          }  
                               else{$this-&amp;gt;form_feedback = &#39;failed&#39;;}  
                     }  
                }   
           return $this-&amp;gt;form_feedback;  
      }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Very simply, what that method does is, if we did intercept our form variables, we subject them to an error checking method (that we will review further bellow), we also check for the uniqueness of the login and email fields. If errors are found we stack them into an array we return to the controller, otherwise that means all came to us clean and we then start processing that data.&lt;br /&gt;&lt;br /&gt;
We insert the new user into the database and if the query went okay, we fetch its ID and insert a row into the user_settings table for that user. We log the user in, using our AuthModel class. We do send a confirmation email, but since we will discuss email sending in a future settlement only, we leave out that part for now. And since we want the new user to get a welcome message and everything, we build a location URL through which we will reload the page.&lt;br /&gt;&lt;br /&gt;
We will take a moment to review three things though, the first one being the check_reg_form_errors() method, useful to the get_form_feedback() method, as discussed above&lt;br /&gt;
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php   
   
 /* from application/models/RegModel.php */  
   
 public function check_reg_form_errors($mr){  
           $this-&amp;gt;reg_form_errors = array();  
   
           $line = array();            
             
           foreach($mr as $r){  
   
                if(preg_match(&#39;/login/&#39;,$r)){  
                     if(!preg_match(&#39;/^[a-z0-9_]{&#39;.MIN_CHARS_LOGIN.&#39;,&#39;.MAX_CHARS_LOGIN.&#39;}$/&#39;,$_POST[$r])){  
                     array_push($line,&#39;login&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;reg_form_errors,$line);}  
                     // as an example, &#39;login&#39; and &#39;not_valid&#39; are the strings we will match against   
                     //while in registration.php to help us know the encountered format for the login field is not valid  
                }  
   
                if(preg_match(&#39;/password/&#39;,$r)){  
                     if(!preg_match(&#39;/^[a-z0-9_]{&#39;.MIN_CHARS_PWD.&#39;,&#39;.MAX_CHARS_PWD.&#39;}$/&#39;,$_POST[$r])){  
                          array_push($line,&#39;password&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;reg_form_errors,$line);  
                     }       
                }  
   
                if(preg_match(&#39;/email/&#39;,$r)){  
                     if (!filter_var($_POST[$r], FILTER_VALIDATE_EMAIL)){array_push($line,&#39;email&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;reg_form_errors,$line);}  
                     if($_POST[$r] != &#39;&#39; &amp;amp;&amp;amp; mb_strlen($_POST[$r]) &amp;gt; EMAIL_MAX_LENGTH){array_push($line,&#39;email&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;reg_form_errors,$line);}   
                }  
   
                if(preg_match(&#39;/birthdate/&#39;,$r)){  
                     if(!empty($_POST[$r])){  
                          if(preg_match(&#39;/^[0-9]{2}\/[0-9]{2}\/[0-9]{4}$/&#39;,$_POST[$r])){$exp_d = explode(&#39;/&#39;,$_POST[$r]);  
                          //var_dump($exp_d);  
                          if(checkdate($exp_d[0], $exp_d[1], $exp_d[2]) != 1 || !preg_match(&#39;/^[0-9]{2}\/[0-9]{2}\/[0-9]{4}$/&#39;,$_POST[$r])){array_push($line,&#39;birthdate&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;reg_form_errors,$line);}                                     
                          }  
                          else{array_push($line,&#39;birthdate&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;reg_form_errors,$line);}  
                     }  
                     else{array_push($line,&#39;birthdate&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;reg_form_errors,$line);}  
                }  
   
                if(preg_match(&#39;/vercode_u/&#39;,$r)){  
                     if(!preg_match(&#39;/^&#39;.$_SESSION[&quot;vercode_u&quot;].&#39;$/&#39;,$_POST[$r])){array_push($line,&#39;verification_code&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;reg_form_errors,$line);}  
                }  
   
      $line = array();  
           }  
                //var_dump($this-&amp;gt;reg_form_errors);  
           return $this-&amp;gt;reg_form_errors;  
      }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Nothing special on that one, as it’s only about basic preg_match verification of field format. Not however that, the arrays we send back in case of found errors, correspond to the expected arrays we expect and parse in registration.php, before we display the registration form.&lt;br /&gt;&lt;br /&gt;
Second, to be able log the new user in following successful registration, we have used the stay_in() method of yet another Singleton class, the authentication class called AuthModel
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php   
   
 /* from application/models/AuthModel.php */  
   
 public function stay_in($id, $user, $pwd, $lang = NULL){  
      $_SESSION[&#39;c_id&#39;] = $id;  
      $_SESSION[&#39;c_login&#39;] = $user;  
      $_SESSION[&#39;c_pwd&#39;] = md5($pwd);  
      $_SESSION[&#39;c_lang&#39;] = $lang;  
 }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
There, we simply store obviously needed credentials into session variables, so the user stays logged in as long as it still hasn’t logged out.&lt;br /&gt;&lt;br /&gt;
Third, the get_user_ip() method from UtilsModel.php that’s being called in the registration form from registration.php
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
 /* from application/models/UtilsModel.php */  
   
 public function get_user_ip(){  
             
      if (!empty($_SERVER[&quot;HTTP_CLIENT_IP&quot;])){$this-&amp;gt;real_ip = $_SERVER[&quot;HTTP_CLIENT_IP&quot;];}  
      elseif(!empty($_SERVER[&quot;HTTP_X_FORWARDED_FOR&quot;])){$this-&amp;gt;real_ip = $_SERVER[&quot;HTTP_X_FORWARDED_FOR&quot;];}  
      else{$this-&amp;gt;real_ip = $_SERVER[&quot;REMOTE_ADDR&quot;];}  
             
       return $this-&amp;gt;real_ip;  
 }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Nothing complicated here, this snippet will go as far as it can to fetch the real IP of the user. Fetching someone’s IP is no perfect science, so bare with me and with this code until someone comes up with something even more efficient. Although, this here should get a fair share of users hiding behind proxies. But you got my point, it won’t be perfect in any case.&lt;br /&gt;&lt;br /&gt;
So anyway, our user can already register and upon successful registration gets logged in automatically. Now what if that same user has logged out but needs to log in again? Our system must offer that user a way to manually log in to the system. This is where the Signin module comes in handy. But again and for the sake of clarity, I will only be showing the relevant pieces of code. Indeed, the SignIn controller is identical to the RegController, except for a few occurrences where ‘Reg’ should be replaced by ‘SignIn’ and that’s all there’s is to it. So we will now get straight into the signin model’s get_form_feedback() method.&lt;br /&gt;
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
 /* from application/models/SignInModel.php */  
   
 public function get_form_feedback($parms){  
             
      if(!empty($parms)){ //if we did get our form values  
             
           $error_list = array();  
   
           //we check them against the check_signin_form_errors() method  
           $error_check_pwdc = self::check_signin_form_errors($parms);  
   
           if (sizeof($error_check_pwdc) &amp;gt; 0){array_push($error_list,$error_check_pwdc);}  
             
           if(sizeof($error_list) &amp;gt; 0){$this-&amp;gt;form_feedback = $error_list;} // if errors were found, we return them  
           //otherwise, we authenticate the user, invoking AuthModel  
           else{$this-&amp;gt;form_feedback = self::authentify($_POST[$parms[0]],$_POST[$parms[1]]);}  
           //0 for login and 1 for password  
      }  
   
      return $this-&amp;gt;form_feedback;  
 }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Basically here, we validate our form data against the check_signin_form_errors() model, which looks as follows&lt;br /&gt;
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
 /* from application/models/SignInModel.php */  
   
 public function check_signin_form_errors($mr){  
           $this-&amp;gt;signin_form_errors = array();  
   
           $line = array();            
           //var_dump($mr);  
             
           foreach($mr as $r){  
           //var_dump($r);  
                if($r == &#39;login&#39;){  
                     if(!preg_match(&#39;/^[a-z0-9_]{&#39;.MIN_CHARS_LOGIN.&#39;,&#39;.MAX_CHARS_LOGIN.&#39;}$/&#39;,$_POST[$r])){  
                          array_push($line,&#39;login&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;signin_form_errors,$line);  
                     }       
                }  
   
                if($r == &#39;password&#39;){//echo $_POST[$r];  
                     if(!preg_match(&#39;/^[a-z0-9_]{&#39;.MIN_CHARS_PWD.&#39;,&#39;.MAX_CHARS_PWD.&#39;}$/&#39;,$_POST[$r])){  
                          array_push($line,&#39;password&#39;,&#39;not_valid&#39;);array_push($this-&amp;gt;signin_form_errors,$line);  
                     }       
                }  
   
      $line = array();  
           }  
   
           return $this-&amp;gt;signin_form_errors;  
      }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
This method acts exactly the same way as check_reg_form_errors(). But if everything goes okay, we authenticate the user by calling the authentify() method from the same class.&lt;br /&gt;
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php   
   
 /* from application/models/SignInModel.php */  
   
 public function authentify($login,$password){  
      $auth = AuthModel::getInstance();  
      $this-&amp;gt;authentified = $auth-&amp;gt;identify($login,$password);  
   
      return $this-&amp;gt;authentified;  
 }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
This method called for the identify() method kept in AuthModel.php, which looks just like this bellow
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
 /* from application/models/AuthModel.php */  
   
 public function identify($user,$pwd){  
             
           $default_plink = &#39;&#39;;  
   
           if(DEFAULT_PLINK){  
                if(CLEAN_URLS == true){$default_plink = DEFAULT_PLINK.&#39;/&#39;;}  
                else{$default_plink = &#39;&amp;amp;&#39;.PLINK.&#39;=&#39;.DEFAULT_PLINK;}  
           }  
             
          $q_auth = &quot;SELECT id_user,user_login,user_password   
                     FROM users   
                     WHERE user_login = &#39;&quot;.$user.&quot;&#39;   
                     AND user_password = md5(&#39;&quot;.$pwd.&quot;&#39;)  
                &quot;;  
   
           MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($q_auth);  
           $my_auth = MySQLModel::get_mysql_instance()-&amp;gt;getRows($q_auth);  
             
           if($my_auth != &#39;&#39;){       
                  
                $id = $my_auth[&#39;id_user&#39;];  
                $this-&amp;gt;identified = &#39;yes&#39;;  
                  
                //once the user has been authenticated, we go fetch its private language settings  
                $user_lang = &quot;SELECT id_user_settings, user_settings_id_user, user_settings_language   
                        FROM user_settings   
                        WHERE user_settings_id_user = &#39;&quot;.$id.&quot;&#39;  
                        &quot;;  
                  
                MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($user_lang);  
                  
                $userlang = MySQLModel::get_mysql_instance()-&amp;gt;getRows($user_lang);  
   
                if($userlang != &#39;&#39;){$this-&amp;gt;user_lang = $userlang[&#39;user_settings_language&#39;];}  
   
                //and write the user to a session                 
                $this-&amp;gt;stay_in($id, $user, $pwd, $this-&amp;gt;user_lang);  
                  
                // IF LAST URL REMEMBERED  
                if(isset($_SESSION[&#39;last_url&#39;]) &amp;amp;&amp;amp; $_SESSION[&#39;last_url&#39;] != &#39;&#39;){  
                     $exp_url = explode(&quot;&amp;amp;&quot;,$_SESSION[&#39;last_url&#39;]);  
                     $xpr = LN.&#39;=&#39;;  
                     $parts = &#39;&#39;;  
                     $rep = LN.&#39;=&#39;.CURRENT_LANG.&#39;&amp;amp;&#39;;  
                       
                     foreach($exp_url as $xpurl){       
                          if(preg_match(&#39;/&#39;.$xpr.&#39;/&#39;,$xpurl)){  
                               if($xpurl_new = preg_replace(&#39;/&#39;.$xpr.&#39;/&#39;,$rep,$xpurl)){$parts .= $rep;}  
                          }  
                     else{$parts .= $xpurl.&#39;&amp;amp;&#39;;}  
                     }  
                       
                     if(preg_match(&#39;/signin/&#39;,$_SESSION[&#39;last_url&#39;]) || preg_match(&#39;/fpwd/&#39;,$_SESSION[&#39;last_url&#39;])){  
                          if(CLEAN_URLS == true){$location = CLEAN_PATH.&#39;/&#39;.DEFAULT_RLINK.$default_plink.&#39;/&#39;.CURRENT_LANG;}  
                          else{$location = $_SERVER[&#39;PHP_SELF&#39;].&#39;?&#39;.RLINK.&#39;=&#39;.DEFAULT_RLINK.$default_plink.&#39;&amp;amp;&#39;.LN.&#39;=&#39;.CURRENT_LANG;}                           
                     }  
                     else{$location = rtrim($parts,&quot;&amp;amp;&quot;);}  
                }  
                else{  
                     if(CLEAN_URLS == true){$location = CLEAN_PATH.&#39;/&#39;.DEFAULT_RLINK.&#39;/&#39;.$default_plink.CURRENT_LANG;}  
                     else{$location = $_SERVER[&#39;PHP_SELF&#39;].&#39;?&#39;.RLINK.&#39;=&#39;.DEFAULT_RLINK.$default_plink.&#39;&amp;amp;&#39;.LN.&#39;=&#39;.CURRENT_LANG;}  
                }  
                header(&#39;Location: &#39;.$location);exit();  
           }  
           else{$this-&amp;gt;identified = &#39;wrong_credentials&#39;;}  
             
           return $this-&amp;gt;identified;  
      }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
If you have survived these series up until now, you should not need an explanation for this last snippet, but just in case though… : once we have identified the user we go fetch its ID and language than determine the location to which we will redirect that user, based on whether a last URL was recorded and whether clean URLS are enabled or not.&lt;br /&gt;&lt;br /&gt;
Finally, now that our user has been able to log in manually, that same user may be wanting to review and update its personal data. This is just what profile module is all about. The profile module, which is alike the settings one, only differs from let’s say the signin module in that we need to get the user’s data in order to present it in the profile form.
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
      /* from application/controllers/ProfileController.php */  
   
      ...  
      // while in properties  
      private $user_data;  
      ...  
   
      ...  
      //while inside the initialize() method, after the intercept_data() method, and before assign_theme()  
      $this-&amp;gt;registry-&amp;gt;template-&amp;gt;user_data = $this-&amp;gt;getProfileFormData(); //we call the method right bellow  
      ...  
        
      private function getProfileFormData(){  
           $model = ProfileModel::getInstance();  
           return $model-&amp;gt;getProfileFormData(); //we call and returns the method we need  
      }  
      ...  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
and so, in the profile model we have
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
      /* from application/controllers/ProfileModel.php */  
   
      public function getProfileFormData(){  
             
           $this-&amp;gt;user_profile=array();  
        
           //data for the profile module resides in the users table       
           $my_query_profile = &quot;SELECT * FROM users WHERE id_user = &#39;&quot;.$_SESSION[&#39;c_id&#39;].&quot;&#39; AND user_login = &#39;&quot;.$_SESSION[&#39;c_login&#39;].&quot;&#39;&quot;;  
             
           MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($my_query_profile);  
             
                $this-&amp;gt;up = MySQLModel::get_mysql_instance()-&amp;gt;getRows($my_query_profile);  
                  
                if($this-&amp;gt;up != &#39;&#39;){  
                     array_push($this-&amp;gt;user_profile, $this-&amp;gt;up[&#39;user_login&#39;],$this-&amp;gt;up[&#39;user_email&#39;],$this-&amp;gt;up[&#39;user_country&#39;],$this-&amp;gt;up[&#39;user_birthdate&#39;]);  
                }  
                  
                return $this-&amp;gt;user_profile;  
      }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
It also differs in that once we have intercepted our form data, we need to update the database with it, and here is how we will do it
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
      /* from application/controllers/ProfileModel.php */  
   
      public function get_form_feedback($parms){  
             
           if(!empty($parms)){  
             
                $error_list = array();  
   
                $error_check_profile = self::check_profile_form_errors($parms);  
   
                if (sizeof($error_check_profile) &amp;gt; 0){array_push($error_list,$error_check_profile);}  
             
                if(sizeof($error_list) &amp;gt; 0){  
                     $this-&amp;gt;form_feedback = $error_list;            
                }  
                else{            
                     $field_list = array(&#39;login&#39;,&#39;password&#39;,&#39;email&#39;,&#39;country&#39;,&#39;birthdate&#39;);  
                  
                          foreach($field_list as $fl){  
                               if(in_array($fl,$parms) &amp;amp;&amp;amp; isset($_POST[$fl])){$$fl = $_POST[$fl];}  
                          }  
                  
                     $now = date(&quot;Y-m-d H:i:s&quot;);  
   
                     $q_update_profile = &quot;UPDATE users   
                          SET user_email = &#39;&quot;.$email.&quot;&#39;,  
                          user_country = &#39;&quot;.$country.&quot;&#39;,  
                          user_birthdate = &#39;&quot;.$birthdate.&quot;&#39;,  
                          user_timestamp = &#39;&quot;.$now.&quot;&#39;  
                          WHERE user_login = &#39;&quot;.$login.&quot;&#39;  
                          AND user_password = md5(&#39;&quot;.$password.&quot;&#39;)  
                          AND user_timestamp != &#39;&quot;.$now.&quot;&#39;  
                     &quot;;                 
   
                     if(MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($q_update_profile) == 0){  
                          if(MySQLModel::get_mysql_instance()-&amp;gt;affectedRows() == 1) {$this-&amp;gt;form_feedback = &#39;success&#39;;}  
                          else{echo MySQLModel::get_mysql_instance()-&amp;gt;mysql_fetch_errors();$this-&amp;gt;form_feedback = &#39;wrong_password&#39;;}  
                     }  
                     else{$this-&amp;gt;form_feedback = &#39;failed&#39;;}  
                }  
             
           }            
                return $this-&amp;gt;form_feedback;  
      }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
You can see it’s very similar to any other get_form_feedback() methods from other models, only here we update the data. Note the use of the field user_timestamp which exists only to force MySQL to affect something if one of the values has then changed. Otherwise, if the update is performed and no rows were affected, there’s no programatic way to know it worked and so our code could only return a failed state, or nothing at best.&lt;br /&gt;&lt;br /&gt;
Regarding the profile form in profile.php, its structure appears to be no different than the registration form. You will only have to make sure your HTML form field names correspond to what is being processed in the model.
On the model side, the check_profile_form_errors() methods is similar to check_reg_form_errors() so there’s no point repeating the same code over and over and besides, this will be a great exercise for you to test the knowledge you have acquired so far from these series.&lt;br /&gt;&lt;br /&gt;
By the way, let’s take a look at the user_settings table, because you will need it too
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; --  
 -- Table structure for table `user_settings`  
 --  
   
 CREATE TABLE `user_settings` (  
  `id_user_settings` int(11) NOT NULL,  
  `user_settings_id_user` int(11) NOT NULL,  
  `user_settings_language` enum(&#39;de&#39;,&#39;es&#39;,&#39;fr&#39;,&#39;nl&#39;,&#39;ru&#39;,&#39;se&#39;,&#39;uk&#39;,&#39;en&#39;) NOT NULL DEFAULT &#39;en&#39;,  
  `user_settings_nipp` enum(&#39;10&#39;,&#39;15&#39;,&#39;20&#39;,&#39;50&#39;,&#39;75&#39;,&#39;100&#39;,&#39;150&#39;) NOT NULL DEFAULT &#39;15&#39;,  
  `user_settings_newsletter` varchar(3) NOT NULL,  
  `user_settings_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP  
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1;  
   
 --  
 -- Dumping data for table `user_settings`  
 --  
   
 INSERT INTO `user_settings` (`id_user_settings`, `user_settings_id_user`, `user_settings_language`, `user_settings_nipp`, `user_settings_newsletter`, `user_settings_timestamp`) VALUES  
 (1, 1, &#39;fr&#39;, &#39;20&#39;, &#39;&#39;, &#39;2015-11-19 08:05:46&#39;);  
   
 --  
 -- Indexes for dumped tables  
 --  
   
 --  
 -- Indexes for table `user_settings`  
 --  
 ALTER TABLE `user_settings`  
  ADD PRIMARY KEY (`id_user_settings`);  
   
 --  
 -- AUTO_INCREMENT for dumped tables  
 --  
   
 --  
 -- AUTO_INCREMENT for table `user_settings`  
 --  
 ALTER TABLE `user_settings`  
  MODIFY `id_user_settings` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;  
&lt;/code&gt;&lt;/pre&gt;
Note the user_settings_nipp and newsletter fields are only here for demonstration, so you may remove them but as long as you modify your settings model accordingly of course.&lt;br /&gt;&lt;br /&gt;
Finally and regarding the forgotten password module or the change password module, there is nothing magical about them, the fpwd module is somehow very identical to the signin module and the pwdc module is relatively similar to the signin module too. Knowing that should get you on track faster while you attempt at building those new modules. Note that regarding the fpwd module, an email is sent with a new password, you may use the generate_new_pwd() method from UtilsModel.php.&lt;br /&gt;&lt;br /&gt;
Now we could not be coming full circle if we did not get in the subject of authentication again. Why? Simply because users should not be allowed to enter their profile data unless they are already logged in, which makes sense. So that, the first call inside the initialize() method of your profile controller will be to the get_module_auth() method, as described bellow
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
      /* from application/controllers/ProfileController/php */  
        
      public function initialize(){  
   
           $utils = new UtilsModel();            
           $this-&amp;gt;registry-&amp;gt;template-&amp;gt;module_auth = $utils-&amp;gt;get_module_auth();  
           ...  
      }       
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Now the function get_module_auth() method itself, pretty much and explicit one.
&lt;pre  style=&quot;font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;&quot;&gt;&lt;code style=&quot;color:#000000;word-wrap:normal;&quot;&gt; &amp;lt;?php  
   
      /* from application/models/UtilsModel/php */  
   
      public function get_module_auth(){  
        
           if(CURRENT_PLINK){  
                if(CLEAN_URLS == true){$pg = &#39;/&#39;.CURRENT_PLINK;} else{$pg = &#39;&amp;amp;&#39;.PLINK.&#39;=&#39;.CURRENT_PLINK;}  
           }  
           else{$pg = &#39;&#39;;}       
                       
           if(isset($_SESSION[&#39;c_login&#39;]) &amp;amp;&amp;amp; isset($_SESSION[&#39;c_pwd&#39;]) &amp;amp;&amp;amp; $_SESSION[&#39;c_login&#39;]!=&#39;&#39; &amp;amp;&amp;amp; $_SESSION[&#39;c_pwd&#39;]!=&#39;&#39;){  
                $this-&amp;gt;module_auth = 0;  
           }  
           else{  
                $this-&amp;gt;module_auth = 1;  
                if(CLEAN_URLS == true){$location = CLEAN_PATH.&#39;/signin/&#39;.CURRENT_LANG;}  
                else{$location = $_SERVER[&#39;PHP_SELF&#39;].&#39;?&#39;.RLINK.&#39;=signin&amp;amp;&#39;.LN.&#39;=&#39;.CURRENT_LANG;}  
                header(&#39;Location: &#39;.$location);exit();  
           }  
      }  
 ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
And that wraps up this fifth settlement dedicated to building a user interface that allows someone to register, sign in, review and update profile. From this, you can now easily extend what you have already learned to build a settings module, same as a profile module but that will rely on the user_settings table, a fpwd module that will handle cases of forgotten passwords and even a pwdc module, that will allow users to change password, first checking of course on the validity of the password they enter, then checking that the new and confirm new passwords are of correct format and match each other too.&lt;br /&gt;&lt;br /&gt;
Next time I will get into full detail as to the subject of theme and language handling, reviewing all aspects of what makes the visual identity of this project and also discuss a method for localizing strings.
&lt;br /&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;This &lt;a href=&quot;http://www.rolandc.net//2015/12/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-5.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt; first appeared Thursday the 7th of January 2016 on &lt;a href=&quot;http://rolandc.net/&quot;&gt;RolandC.net&lt;/a&gt;.&lt;/span&gt; &lt;br /&gt;
&lt;br /&gt;</description><link>http://www.rolandc.net/2015/12/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-5.html</link><author>noreply@blogger.com (rolandc.net)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-706541592560695184</guid><pubDate>Wed, 03 Jun 2015 12:36:00 +0000</pubDate><atom:updated>2015-06-03T17:53:36.125+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">analytics</category><category domain="http://www.blogger.com/atom/ns#">analytics not working</category><category domain="http://www.blogger.com/atom/ns#">anaytics code not working</category><category domain="http://www.blogger.com/atom/ns#">google analytics</category><category domain="http://www.blogger.com/atom/ns#">real-time analytics not working</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">tracking code not working</category><category domain="http://www.blogger.com/atom/ns#">webdev</category><title>Google Analytics tracking code not being detected anymore and/or real-time tracking not working?</title><description>Today I am going to show you a simple trick so you can avoid a few hours of head-banging while trying to fix your Google Universal Analytics code not being detected anymore and/or erase the frustration of having a real-time feature menu taking up half the left-hand panel but that always shows only one data - &amp;#39;0&amp;#39;.&lt;br&gt;
&lt;br&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHbkTcIUXG8jQ3vjK2QETiDFB-LidXQHeCuygulCNV5qBNN3dfSgJ10rZgvPM5wBKtf3TqxBhU-sXkOBilRL4hCW1xg_T92IZpeiwKtJ1Ie_b0TeDkPLAvgB05lDm5bUtbjtLVWdn8cXhv/s1600/Google_Analytics_Not_Working_RolandC.net.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHbkTcIUXG8jQ3vjK2QETiDFB-LidXQHeCuygulCNV5qBNN3dfSgJ10rZgvPM5wBKtf3TqxBhU-sXkOBilRL4hCW1xg_T92IZpeiwKtJ1Ie_b0TeDkPLAvgB05lDm5bUtbjtLVWdn8cXhv/s1600/Google_Analytics_Not_Working_RolandC.net.png&quot;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;a href=&quot;http://www.rolandc.net/2015/06/google-analytics-tracking-code-not-working-anymore-and-or-real-time-tracking-not-working.html#more&quot;&gt;More »&lt;/a&gt;</description><link>http://www.rolandc.net/2015/06/google-analytics-tracking-code-not-working-anymore-and-or-real-time-tracking-not-working.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHbkTcIUXG8jQ3vjK2QETiDFB-LidXQHeCuygulCNV5qBNN3dfSgJ10rZgvPM5wBKtf3TqxBhU-sXkOBilRL4hCW1xg_T92IZpeiwKtJ1Ie_b0TeDkPLAvgB05lDm5bUtbjtLVWdn8cXhv/s72-c/Google_Analytics_Not_Working_RolandC.net.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-7731066820429299536</guid><pubDate>Fri, 02 Jan 2015 10:16:00 +0000</pubDate><atom:updated>2016-08-02T12:01:09.523+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">convert tabs</category><category domain="http://www.blogger.com/atom/ns#">gp5</category><category domain="http://www.blogger.com/atom/ns#">guitar pro</category><category domain="http://www.blogger.com/atom/ns#">guitar tab</category><category domain="http://www.blogger.com/atom/ns#">home studio</category><category domain="http://www.blogger.com/atom/ns#">midi</category><category domain="http://www.blogger.com/atom/ns#">tab to midi</category><category domain="http://www.blogger.com/atom/ns#">tab to midi converter</category><category domain="http://www.blogger.com/atom/ns#">tabs to midi</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">tux guitar</category><title>How to convert online guitar tabs to Midi to import into Guitar Pro/Tux Guitar</title><description>&lt;div style=&quot;text-align: left;&quot;&gt;
Not earlier than last night, I was confronted to the challenge of having to convert a guitar tab I found on the internet, to MIDI format, so it could be read by &lt;a href=&quot;http://tuxguitar.herac.com.ar/&quot; target=&quot;_blank&quot;&gt;TuxGuitar&lt;/a&gt;.&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Indeed, I had just wanted to effortlessly check if the tab I was reading exactly sounded like I needed.&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Effortlessly? well not quite so, but in the end I was able to come up with a reliable method which allows one to import a tab into TuxGuitar, play it and/or then export it to whatever format is available, gp5 for example, all legally and free of charge.&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
The operation is really easy, and to prove it, let&#39;s start with, say....something that looks like this:&lt;/div&gt;
&lt;pre style=&quot;text-align: center;&quot;&gt;&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;E||------2--0-2---2-------2--0-2---2---|------3--2-3---3-------3--2-3---3---|
B||------------------------------------|----0--------0---0---0--------0---0-|
G||----2--------2---2---2--------2---2-|------------------------------------|
D||-------------------0----------------|-------------------0----------------|
A||------------------------------------|------------------------------------|
D||--0---------------------------------|--0---------------------------------|

 
------3--2-3---3-------3--2-3---3---|------2--0-2---2-------2--0-2---2---|
----2--------2---2---2--------2---2-|----3--------3---3---3--------3---2-|
------------------------------------|------------------------------------|
-------------------0----------------|-------------------0----------------|
------------------------------------|------------------------------------|
--0---------------------------------|--0---------------------------------|&lt;/span&gt;&lt;/div&gt;
&lt;/pre&gt;
&lt;pre style=&quot;text-align: center;&quot;&gt;&lt;/pre&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Tab junkies should have already recognized the piece, Bach&#39;s Cello Suite №1 Prelude.
The simple trick I found after three hours playing with converters that never worked, was to use this &lt;a href=&quot;http://www.8notes.com/tab/default.asp&quot;&gt;free online tab converter&lt;/a&gt; . I did not even have to register and it&#39;s completely free.&lt;/div&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;u&gt;Step 1&lt;/u&gt;&lt;/b&gt;&lt;/pre&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqmYBzPo83DvJfVoMD3FPoRJTw5C9TUd-G37nc7T9sAiOFyRtX6JEP4k8_9_tIUruN74H_uCejZD9iFZNo8l31dPTXdwL7ghgY9RS3xngYccLtbVe2ojLAnu_X0EIRbC9Cy1IHPN2B2Urz/s1600/Selection_003.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;228&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqmYBzPo83DvJfVoMD3FPoRJTw5C9TUd-G37nc7T9sAiOFyRtX6JEP4k8_9_tIUruN74H_uCejZD9iFZNo8l31dPTXdwL7ghgY9RS3xngYccLtbVe2ojLAnu_X0EIRbC9Cy1IHPN2B2Urz/s1600/Selection_003.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Just copy-paste the tab you have found in the second box. I would not recommend importing directly as a file, as you might get a parsing error. Click on upload.&lt;/div&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;u&gt;Step 2&lt;/u&gt;&lt;/b&gt;&lt;/pre&gt;
&lt;pre style=&quot;text-align: center;&quot;&gt;&lt;/pre&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3t9ZsEoG52VcJYaSYSmXLBM0e1JXCXDnnJWJE-tEAS2rd0Th3ueQhnkrlWE1MKOJCm_W6UwF8pRcWc2QlZruPssdnadQIqQjWbwzhoSbzejcvtU8sXZi0azB2npn1ze_SttBfKC118wYo/s1600/Selection_004.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;190&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3t9ZsEoG52VcJYaSYSmXLBM0e1JXCXDnnJWJE-tEAS2rd0Th3ueQhnkrlWE1MKOJCm_W6UwF8pRcWc2QlZruPssdnadQIqQjWbwzhoSbzejcvtU8sXZi0azB2npn1ze_SttBfKC118wYo/s1600/Selection_004.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Now time to select an option or two. For my piece, Medium (default option) was too slow so choosing Medium fast did the trick. Also, I have had to select the option Ignore spacing and play notes equally so it rendered times normally. Once done then click on Go at the bottom right corner &lt;/div&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;u&gt;Step 3&lt;/u&gt;&lt;/b&gt;&lt;/pre&gt;
&lt;pre style=&quot;text-align: center;&quot;&gt;&lt;/pre&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuQQeD2wu7f-SbH99E3cDAYoqy-qUEdlyKdNgc2pwYjT0jmvoN1D_wITwEY1Y8b-fHDclp4-xraWuom8JO-oHHrTWj1dUvyn9nJrshDwT7g9hwqPPsp88QhONCjVNJyuM4PC9_5fS36ojA/s1600/Selection_005.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;271&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuQQeD2wu7f-SbH99E3cDAYoqy-qUEdlyKdNgc2pwYjT0jmvoN1D_wITwEY1Y8b-fHDclp4-xraWuom8JO-oHHrTWj1dUvyn9nJrshDwT7g9hwqPPsp88QhONCjVNJyuM4PC9_5fS36ojA/s1600/Selection_005.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;pre style=&quot;text-align: center;&quot;&gt;&lt;/pre&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
At this point, all you have to do is to click the blue arrow you can see on the top right corner, this will have you save your data into a .pl file.&lt;/div&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;u&gt;Step 4&lt;/u&gt;&lt;/b&gt;&lt;/pre&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Assuming you are using TuxGuitar (should not be too different for Guitar Pro users), go to File-&amp;gt;Import-&amp;gt;Import Midi and select the .pl file you have just downloaded.&amp;nbsp;&lt;/div&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
By now, you should be able to play your tab, do any necessary modifications and re-import if necessary, and, you should be able to save or export your tab to any format you like, eg .gp3,&amp;nbsp;.gp4,&amp;nbsp;.gp5, .tg, you name it.&amp;nbsp;&lt;/div&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;b&gt;Note&lt;/b&gt;: In case you have trouble playing the tab because you get an error that the Jack audio server cannot be started, don&#39;t go disabling pulseaudio or do anything that will impair sounds on your system. Just go to your settings menu (Tools-&amp;gt;Settings-&amp;gt;Sound), and select a difference audio server interface.&lt;/div&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
In TuxGuitar, it would look like this:&lt;/div&gt;
&lt;pre style=&quot;text-align: center;&quot;&gt;&lt;/pre&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFxta2m8waJqw4qPoNbgllqDNwVIsUKvKWXsFDwcp2yPjSwtluIslsPZfebyi2Uf-1CpNqu9FkLx_0ArpJN8IeIcf67puq5d_OQ2L1OAyUrcKkyh0xqswZCrCvBQD77x8MbUMCxrmsNnW6/s1600/Selection_006.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFxta2m8waJqw4qPoNbgllqDNwVIsUKvKWXsFDwcp2yPjSwtluIslsPZfebyi2Uf-1CpNqu9FkLx_0ArpJN8IeIcf67puq5d_OQ2L1OAyUrcKkyh0xqswZCrCvBQD77x8MbUMCxrmsNnW6/s1600/Selection_006.png&quot; width=&quot;277&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;
&lt;span style=&quot;text-align: left;&quot;&gt;All you would have to do would be to choose the (default) TG Fuidsynth port and Tux Guitar Sequencer or anything else that&#39;s available on your system.&lt;/span&gt;&lt;/div&gt;
&lt;pre style=&quot;text-align: left;&quot;&gt;&lt;/pre&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
So now, you should be able to (legally, effortlessly&amp;nbsp;and at no cost) import guitar tabs found online to your favorite Guitar tab program. Enjoy your tabs!&lt;/div&gt;
</description><link>http://www.rolandc.net/2015/01/how-to-convert-guitar-tabs-to-midi-to-import-in-tuxguitar-or-guitarpro.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqmYBzPo83DvJfVoMD3FPoRJTw5C9TUd-G37nc7T9sAiOFyRtX6JEP4k8_9_tIUruN74H_uCejZD9iFZNo8l31dPTXdwL7ghgY9RS3xngYccLtbVe2ojLAnu_X0EIRbC9Cy1IHPN2B2Urz/s72-c/Selection_003.png" height="72" width="72"/><thr:total>12</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-5118687251216068842</guid><pubDate>Sun, 28 Dec 2014 09:00:00 +0000</pubDate><atom:updated>2014-12-28T11:31:43.337+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cms</category><category domain="http://www.blogger.com/atom/ns#">gumbo-cms</category><category domain="http://www.blogger.com/atom/ns#">mvc</category><category domain="http://www.blogger.com/atom/ns#">opensource</category><category domain="http://www.blogger.com/atom/ns#">php</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">tutorial</category><category domain="http://www.blogger.com/atom/ns#">webdev</category><title>How to build your own Multilingual PHP MVC CMS from scratch - Part 4 - Frontend (part 2): Menu System</title><description>Welcome to the fourth settlement of our tutorial series on how to build your own Multilingual MVC CMS from scratch and in which, this time, we will lay our hands on the front-end menu system.&lt;br /&gt;
&lt;br /&gt;
The &lt;a href=&quot;http://www.rolandc.net/2014/11/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-3.html&quot;&gt;last time&lt;/a&gt;, we have covered the complete building of the (front) page module, going from the database page table up to page.php, in which the required page content is displayed. In between, we have exposed and explained the page controller and its corresponding model, while also demonstrating, among other things, a way to create and instantiate a Singleton class. All of which having resulted in the making of our first complete and fully functional module that allows the user, upon manually loading the correct URL in the browser, to fetch and display on screen a requested page.&lt;br /&gt;
&lt;br /&gt;
But although doing this works perfectly well, one cannot expect nowadays to have users always &#39;hard-type&#39; the URL of the resource they seek in their browsers. That is one of the reasons why menus have generally been accepted as an extremely important feature of modern Web development. Since the aim of any CMS is by nature to make the programmer/administrator&#39;s life easier while trying to meet the most functionality that&#39;s the most frequently asked for in the Web creation industry, we&#39;re going to do just that: to implement a menu system in our CMS.&lt;br /&gt;
&lt;br /&gt;
Note however, before we begin, that each forthcoming settlement will tend to reflect the CMS&#39;s own business logic gradually more, rather than get to explain the same PHP-OOP language features over and over again. This does make sense since we have already reviewed the most part of the core object model on which our project is based.&lt;br /&gt;
&lt;br /&gt;
Our menu system will have its own back-end management module, which we will be covering in a future settlement only. But for now, we will set our focus onto its front-end side, starting from the database model and going all the way to visual display.&lt;br /&gt;
&lt;br /&gt;
So let&#39;s jump into the database model first. Basically, from a back-end point of view, the administrator will have the possibility to create, edit and delete whole menus. What this means for us on the front-end side is that, at any time, there could be any number of menu tables in the database, named menus_1, menus_2 and menus_3, etc... . Each menu table containing any number of menu rows (entries). &lt;br /&gt;
&lt;br /&gt;
Still on the front-end side, two types of graphical menu layouts will be available for now, both relying on Javascript/CSS via third-party scripts, one of which displaying a layout of vertical type (menu_tree.php) and the other one, of horizontal type (hmenu.php). Both menu layouts being multilevel, meaning they will support an unlimited number of levels. &lt;br /&gt;
&lt;br /&gt;
The Javascript files for those libraries are named respectively dtree.php and hmenu.js and reside in public/js. As for CSS, they are named dtree.css and hmenu.css and can be found in themes/your_style/css. Theme icons for the dtree library will be set inside its Javascript file, but will be found in themes/your_style/servicing/dtree. Icons for the hmenu library will be found in themes/your_style/servicing/hmenu.&lt;br /&gt;
&lt;br /&gt;
Now, In config.php, we will have to manually determine which menu table is to be assigned to either of those two graphical menu types, as shown bellow 
&lt;br /&gt;
&lt;pre style=&quot;background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;&quot;&gt;&lt;code&gt;define(&quot;MENU_TREE_MENU_TABLE&quot;,&quot;1&quot;);
define(&quot;HMENU_MENU_TABLE&quot;,&quot;2&quot;);
&lt;/code&gt;&lt;/pre&gt;
As an example, the value &quot;1&quot; simply corresponds to the table named menus_1 and &quot;2&quot; to menus_2.&lt;br /&gt;
&lt;br /&gt;
There is definitely room for new graphical layout types or more of the existing ones, as long you declare the corresponding constants in config.php, at least to keep everything clean there. That said, in nowadays internet, Websites displaying more than two graphical menus at once on the same page are not that frequently encountered. &lt;br /&gt;
&lt;br /&gt;
In any case, now is a good time to reveal the structure of our menu_# tables
&lt;br /&gt;
&lt;pre style=&quot;background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;&quot;&gt;&lt;code&gt;--
    -- Table structure for table `menus_1`
    --
    
    CREATE TABLE IF NOT EXISTS `menus_1` (
    `id_menu_1` smallint(6) NOT NULL,
      `menu_1_rank` varchar(12) NOT NULL,
      `menu_1_type` varchar(60) NOT NULL,
      `menu_1_page` varchar(60) DEFAULT NULL,
      `menu_1_position` smallint(6) NOT NULL,
      `menu_1_display` enum(&#39;no&#39;,&#39;yes&#39;) DEFAULT NULL
    ) ENGINE=MyISAM AUTO_INCREMENT=43 DEFAULT CHARSET=latin1;
    
    --
    -- Dumping data for table `menus_1`
    --
    
    INSERT INTO `menus_1` (`id_menu_1`, `menu_1_rank`, `menu_1_type`, `menu_1_page`, `menu_1_position`, `menu_1_display`) VALUES
    (1, &#39;1#0&#39;, &#39;page&#39;, &#39;hours&#39;, 1, &#39;yes&#39;),
    (2, &#39;2#0&#39;, &#39;page&#39;, &#39;products&#39;, 2, &#39;yes&#39;),
    (3, &#39;3#0&#39;, &#39;page&#39;, &#39;about_us&#39;, 3, &#39;yes&#39;),
    (4, &#39;4#0&#39;, &#39;page&#39;, &#39;directions&#39;, 4, &#39;yes&#39;);
    
    --
    -- Indexes for dumped tables
    --
    
    --
    -- Indexes for table `menus_1`
    --
    ALTER TABLE `menus_1`
     ADD PRIMARY KEY (`id_menu_1`);
    
    --
    -- AUTO_INCREMENT for dumped tables
    --
    
    --
    -- AUTO_INCREMENT for table `menus_1`
    --
    ALTER TABLE `menus_1`
    MODIFY `id_menu_1` smallint(6) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=5;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;We can tell by the menus_#_display field that each menu row can either be chosen for display displayed or not. &lt;/li&gt;
&lt;li&gt;You may already be recognizing the menu_#_type field, which corresponds the module type since our menu system will only be linking modules.&lt;/li&gt;
&lt;li&gt;Row ranks (menu_#_rank) hold a special format [1 to 2 figures + # + 1 to 2 figures], and a row rank with 0 as right side value means it is a top-level row. No menu_rank entry can hold a 0 on its left side. &lt;/li&gt;
&lt;li&gt;A parent-kid relation is obtained by having the right side of the kid&#39;s rank being equal to the left side of the parent&#39;s rank.  &lt;/li&gt;
&lt;li&gt;Modules other than the page or the elink module will have 0 as menu_#_page value since they don&#39;t need a page or elink. Elink will be the module that takes care of External links, used in case we need to link an external URL to a menu row.&lt;/li&gt;
&lt;/ul&gt;
That is all we need to know for now regarding the menu model at database level. So now we will be turning to MenuModel.php, and see how we will format our database menu table data to properly supply both our tree and horizontal menu layouts the feed formats they require.&lt;br /&gt;
&lt;pre style=&quot;background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;&quot;&gt;&lt;code&gt;&amp;lt;?php
    /* application/models/MenuModel.php */    

    class MenuModel{ 
        //Instantiating our class which appears to be a Singleton one again
    
        static $instance;    //declaring properties
        private $menu_items;
        private $module_name;
        private $items_translation_line;    
        private $items_translations;
    

        public static function getInstance(){ 
            //creating and returning a single static instance of the class for when invoked
            if(self::$instance ==  null)
                self::$instance = new self();
            return self::$instance;
        }
    
        public function __construct(){} //disallowing the creation of a new instance
        private function __clone(){} // disallowing cloning
    
        public function get_module_name(){ //returns the module name
            return $this-&amp;gt;module_name &lt;/code&gt;= &#39;menu&#39;;&lt;/pre&gt;
&lt;pre style=&quot;background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;&quot;&gt;&lt;code&gt;        }    
    
        public function getMenuItemsData($mtp, $direction = NULL){ //$mtp is the mumber of the menu we seek
            //this method fetches and prepares the database data needed to feed the menu layout  
            
            $util = new UtilsModel();
    
            // we first get all the exist menu tables
            $get_all_tables = $util-&amp;gt;show_tables(DBNAME,&#39;menus&#39;); 
    
            if(in_array($mtp,$get_all_tables)){ // if the menu we seek exists 
    
                $mtp = &quot;_&quot;.$mtp;
                $myitems = &#39;&#39;;
    
                $menu_label_translations = self::getTreeMenuItemTranslations(CURRENT_LANG,&#39;page&#39;); // we fetch all page name translations for the current language (this will be useful when the module name is &#39;page&#39;)
                $menu_label_translations_elink = self::getTreeMenuItemTranslations(&lt;span style=&quot;font-family: &#39;Andale Mono&#39;, &#39;Lucida Console&#39;, Monaco, fixed, monospace;&quot;&gt;CURRENT_LANG&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Andale Mono&#39;, &#39;Lucida Console&#39;, Monaco, fixed, monospace;&quot;&gt;,&#39;elink&#39;); // same for elinks but we do not need to worry about it for now&lt;/span&gt;
    
                //we select all the rows meant for display, order them by position, ascending 
                $my_menu_items = &quot;SELECT id_menu&quot;.$mtp.&quot;, menu&quot;.$mtp.&quot;_rank, menu&quot;.$mtp.&quot;_type, menu&quot;.$mtp.&quot;_page 
                                     FROM menus&quot;.$mtp.&quot; 
                                     WHERE menu&quot;.$mtp.&quot;_display = &#39;yes&#39; 
                                     ORDER BY menu&quot;.$mtp.&quot;_position ASC
                                   &quot;;        
                
                MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($my_menu_items);
            
                if(MySQLModel::get_mysql_instance()-&amp;gt;affectedRows() &amp;gt; 0){ //if we found at least one row

                    $ct_top_menus = 0;
    
                    while($myitems = MySQLModel::get_mysql_instance()-&amp;gt;getRows($my_menu_items)){    
                        //while we pull and stack each found row in $myitems
    
                        if($myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;] == &#39;page&#39;){//if row type equals page
                            
                            $mt = trim($myitems[&#39;menu&#39;.$mtp.&#39;_page&#39;]); //we fetch its formatted page name
                            $cv = 0;                
    
                            foreach($menu_label_translations as $mlt){
    
                                if(!in_array($mt,$mlt)){$cv++;}
                                else{
                                        //and fetch its non-formatted internationalized name to stack it in $menu_text, 
                                        //which corresponds to the menu label that will be displayed on screen
                                        if($mlt[1] == &lt;span style=&quot;font-family: &#39;Andale Mono&#39;, &#39;Lucida Console&#39;, Monaco, fixed, monospace;&quot;&gt;CURRENT_LANG&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Andale Mono&#39;, &#39;Lucida Console&#39;, Monaco, fixed, monospace;&quot;&gt;){$menu_text = $mlt[2];&lt;/span&gt;
                                }
                            }
                        } 
    
                            if($cv == sizeof($menu_label_translations)){ // if no non-formatted name has been found
                                if(isset($mt) &amp;amp;&amp;amp; $mt != &#39;&#39;){$mt = $mt.&#39; - &#39;;} // if there was a page
                                else{$mt = &#39;&#39;;}
        
                                // we assign a missing page string to $menu_text
                                $menu_text = $mt.$GLOBALS[&#39;t_page_missing&#39;];
                            }
                        }
    
                        elseif($myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;] == &#39;elink&#39;){    // same for elinks but again, we do not need to worry about it for now
    
                            $mt = trim($myitems[&#39;menu&#39;.$mtp.&#39;_page&#39;]);
    
                            $cv = 0;                
    
                            foreach($menu_label_translations_elink as $mlt){
    
                                if(!in_array($mt,$mlt)){$cv++;}    
                                else{if($mlt[1]== &lt;span style=&quot;font-family: &#39;Andale Mono&#39;, &#39;Lucida Console&#39;, Monaco, fixed, monospace;&quot;&gt;CURRENT_LANG&lt;/span&gt;&lt;span style=&quot;font-family: &#39;Andale Mono&#39;, &#39;Lucida Console&#39;, Monaco, fixed, monospace;&quot;&gt;){$menu_text = $mlt[2];}&lt;/span&gt;
                            }
                        } 
    
                            if($cv == sizeof($menu_label_translations_elink)){
                                if(isset($mt) &amp;amp;&amp;amp; $mt != &#39;&#39;){$mt = $mt.&#39; - &#39;;}else{$mt = &#39;&#39;;}
                                $menu_text = $mt.$GLOBALS[&#39;t_page_missing&#39;];
                            }
                        }
    
                        elseif($myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;] == &#39;sign&#39;){ 
                                // if row type equals sign (this is the sign-in/out module)
    
                            //if the user is looged in
                            if(isset($_SESSION[&#39;c_login&#39;]) &amp;amp;&amp;amp; isset($_SESSION[&#39;c_pwd&#39;]) &amp;amp;&amp;amp; $_SESSION[&#39;c_login&#39;] != &#39;&#39; &amp;amp;&amp;amp; $_SESSION[&#39;c_pwd&#39;] != &#39;&#39;){
                                // we assign &#39;signout&#39;, internationalized as menu label
                                $menu_text = $GLOBALS[&#39;t_&#39;.$myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;].&#39;out&#39;];
                            }
                            else{
                                    //otherwise, if not logged in, we assign &#39;signin&#39;, internationalized
                                    $menu_text = $GLOBALS[&#39;t_&#39;.$myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;].&#39;in&#39;];}
                    }
                    else{ //otherwise, for any other module type

                        if(isset($GLOBALS[&#39;t_&#39;.$myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;]])){
                            // if it exists, we assign the internationalized name of the module to $menu_text
                            $menu_text = $GLOBALS[&#39;t_&#39;.$myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;]];

                        }
                        else{
                                //otherwise we assign a missing module string
                                if($myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;] != &#39;&#39;){
                                    $menu_text = $myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;].&#39; - &#39;.$GLOBALS[&#39;t_module_missing&#39;];
                                }
                                else{$menu_text = $GLOBALS[&#39;t_module_missing&#39;];}
                        }
                    } 

                    // if row type equals to registration and the user is logged in, we do nothing
                    if($myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;] == &#39;reg&#39; &amp;amp;&amp;amp; isset($_SESSION[&#39;c_login&#39;])){} 

                    else{
                            //otherwise, we append the elements (separated by #) of each row (separated by @@) to $this-&amp;gt;menu_items, 
                            //so that our feed will be string, not an array.
                            $this-&amp;gt;menu_items .= $myitems[&#39;menu&#39;.$mtp.&#39;_rank&#39;].&#39;#&#39;.html_entity_decode($menu_text).&#39;#&#39;.$myitems[&#39;menu&#39;.$mtp.&#39;_type&#39;].&#39;#&#39;.$myitems[&#39;menu&#39;.$mtp.&#39;_page&#39;].&#39;@@&#39;;
                            //at this point, our feed is ready for use in menu_tree.php
                    }

                    if(preg_match(&#39;/[0-9]#0#/&#39;,$this-&amp;gt;menu_items)){ //if this menu row corresponds to a top-level menu entry
                        $ct_top_menus++; //we increment the top menu counter by 1
                    }
                }

                if($ct_top_menus == 0){//if no top menus found
                    return $this-&amp;gt;menu_items = &#39;no_top_menu&#39;; //we return just that
                }

                if(isset($mtp) &amp;amp;&amp;amp; isset($this-&amp;gt;menu_items) &amp;amp;&amp;amp; isset($direction) &amp;amp;&amp;amp; $direction == &#39;hmenu&#39;){ 
                    // if the call to this method was issued from hmenu.php, therefore, 
                    // if we need to be able to process our feed in our horizontal layout, 
                    // we first transform it to an array of menu rows

                    $hmenu_exp = explode(&quot;@@&quot;,rtrim($this-&amp;gt;menu_items,&quot;@@&quot;));

                    $menu = array();
                    $line = array();

                    foreach($hmenu_exp as $he){ // and then for each menu row

                        $exp_he = explode(&quot;#&quot;,$he); //we explode the row

                        array_push($line,$exp_he[0],$exp_he[1],$exp_he[2],$exp_he[3],$exp_he[4]); //we stack everything in a $line array
                        array_push($menu,$line); //and stack each $line in a $menu array
                        $exp_he = array(); //we unset $exp_he and $line
                        $line = array();
                    }
                    krsort($menu); // we sort the $menu array by key
                    $this-&amp;gt;menu_items = self::rebuild_menu($menu); //we call the rebuild_menu() method.
                }
            }
            else{    
                    // if menu exists but no row was found in it
                    $this-&amp;gt;menu_items = &#39;menu_empty&#39;;
            }
        }
        else{
                //if the menu we seek does not exist
                $this-&amp;gt;menu_items = &#39;menu_missing&#39;;
            }
        return $this-&amp;gt;menu_items;
        }
    
        public function getTreeMenuItemTranslations($ln, $pfx){ //passing current language and table name as paramaters
    
            MySQLModel::get_mysql_instance()-&amp;gt;set_char(&#39;latin1&#39;);
    
            // creating a query to retrieve all pages or elinks whose language corresponds to the current one
            $q_retrieve_pages = &quot;SELECT * FROM &quot;.$pfx.&quot;s 
                                 WHERE &quot;.$pfx.&quot;_language = &#39;&quot;.$ln.&quot;&#39;
                                &quot;;
    
            $this-&amp;gt;items_translation_line = array();
            $this-&amp;gt;items_translations = array();
    
            MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($q_retrieve_pages);
    
                while($mypage = MySQLModel::get_mysql_instance()-&amp;gt;getRows($q_retrieve_pages)){ // while we have results
                    array_push($this-&amp;gt;items_translation_line, $mypage[$pfx.&#39;_name_formatted&#39;], $mypage[$pfx.&#39;_language&#39;], $mypage[$pfx.&#39;_name&#39;]); //we stack them in the $this-&amp;gt;items_translation_line array
                    array_push($this-&amp;gt;items_translations, $this-&amp;gt;items_translation_line); //and stack $this-&amp;gt;items_translation_line into $this-&amp;gt;items_translations
                    $this-&amp;gt;items_translation_line = array(); 
                }
            return $this-&amp;gt;items_translations; //and return all processed rows
        }
    
        public function rebuild_menu($menu){ 
            //called inside the getMenuItemsData() method

            $new_menu = array();
            $new_m = array();
    
            foreach($menu as $m){ //foreach menu row
    
                //we rebuild/duplicate the row in a brand new row array called new_m
                array_push($new_m,$m[0],$m[1],$m[2],$m[3],$m[4]);
    
                $kids = self::check_if_kids($m[0],$menu); //and we check if the current row has kids 
    
                //if we found some, we add them to the new row array ($new_m)
                if(sizeof($kids) &amp;gt; 0){array_push($new_m,$kids);}
     
                array_push($new_menu, $new_m); //we stack the new row array into the $new_menu array
                $new_m =  array(); // we empty each new row to avoid redundancy
            }
            return $new_menu; //we return the complete menu set, ready for use in hmenu.php
        }
        
        public function check_if_kids($id, $menu){

            $kids = array();
            $kids_line = array();
    
            foreach($menu as $v){    
                if($id == $v[1]){ //if the current row is a kid to someone, we check whether that kid is not itself a parent 
                    if(sizeof(self::check_if_kids($v[0], $menu)) &amp;gt; 0){ 
                        //if it turns out that that kid indeed has kids
                        ksort($menu); //sorting
                        array_push($kids_line,$v[0],$v[1],$v[2],$v[3],$v[4],self::check_if_kids($v[0], $menu)); // here we use recursion:
                        //we add them to the $kids_line array we&#39;re building
                    }
                    else{
                        //otherwise we build our kid line but without adding kids because that kid hasn&#39;t any    
                        array_push($kids_line,$v[0],$v[1],$v[2],$v[3],$v[4]);
                    }
                    //and we stack each kid line in the $kid array
                    array_push($kids,$kids_line);    
                    
                    $kids_line = array(); // we override the $kids_line array()
                }
            }
            return $kids; //we return all kids 
        }
    }
    ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
So basically, the MenuModel class first checks whether the menu we seek does exist, then fetches all its displayable rows (if found any) and builds a thread for each one, based on row type (menu_type). That thread includes, but not limited to, either the localized module name, or, if the module equals to page, the localized non-formatted page name that has been assigned to it. The &#39;#&#39; sign is used to separate row items and &#39;@@&#39; is used to separate rows.&lt;br /&gt;
&lt;br /&gt;
At that point, our feed is ready for use in menu_tree.php, but if the model is being called from hmenu.php, meaning, if we need to be able to process our feed in our horizontal menu, we then first transform it to an array of menu rows, and turn each row to an array of row items, then stack all items into a new $line array which itself is being stacked into a new $menu array. We then rebuild the $menu array using the rebuild_menu() method. This is the method that will restore hierarchy between all rows. It takes a $menu array as sole parameter and for each row traversed, calls upon the check_if_kids() method to simply check if the then-menu row does have kids.&lt;br /&gt;
&lt;br /&gt;
The check_if_kids() method is a recursive one, which, if it finds that the kid it then traverses has kids of its own, adds them to a new row array it is  building, all while iterating through those new kids and so on. Otherwise, it does not add anything and is building a regular row array with no kids in it. A resulting kid array is returned to the rebuild_menu() method which again returns us a feed we can use, but in hmenu.php this time.  &lt;br /&gt;
&lt;br /&gt;
The show_tables() method allows, based on a database name and a table name pattern, to spot any table that follows that pattern. It is called in getMenuItemsData() and we use it to retrieve all existing menu tables. It is part of the UtilsModel class and appears to be pretty much self-explanatory.&lt;br /&gt;
&lt;pre style=&quot;background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;&quot;&gt;&lt;code&gt;/* from application/UtilsModel.php */

    Class UtilsModel....

    public function show_tables($db_name, $pattern){

        $all_menu_tables = &quot;SHOW TABLES FROM &quot;.$db_name;

        MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($all_menu_tables);

        $this-&amp;gt;all_table_indexes = array();

        while($tables = MySQLModel::get_mysql_instance()-&amp;gt;getRows($all_menu_tables)){
            $table_name = $tables[&#39;Tables_in_&#39;.$db_name];    
            if(preg_match(&#39;/^&#39;.$pattern.&#39;_.*$/&#39;,$table_name)){
                //note the carret sign at the end of the regex, so we don&#39;t pick admin_menu_tables alongthe way.
                $exp_table_name = explode(&quot;_&quot;,$table_name);
                $index = end($exp_table_name);    
                array_push($this-&amp;gt;all_table_indexes, $index);
            }        
        }
    return $this-&amp;gt;all_table_indexes;
    }
&lt;/code&gt;&lt;/pre&gt;
Now is a great time to uncover our tree menu layout, located in menu_tree.php. As stated earlier, it relies on an open-source Javascript/CSS script, which in fact is called dTree, written by Geirs Landrö and attached to this settlement. But between our feed and this script, there is a lot to be done to ensure our tree menu layout is correctly being handled.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s take a look at menu_tree.php

&lt;br /&gt;
&lt;pre style=&quot;background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;&quot;&gt;&lt;code&gt;&amp;lt;?php
$menu_tree = new MenuModel();
$dmenu = $menu_tree-&amp;gt;getMenuItemsData(MENU_TREE_MENU_TABLE,&#39;&#39;); //&#39;1&#39;,&#39;2&#39;,&#39;3&#39; - leave parameter #2 to avoid extra processing - this type of menu does not need it

if(isset($dmenu)){ 
    
    if($dmenu == &#39;menu_missing&#39;){
        ?&amp;gt;
        &amp;lt;div class=&quot;failure&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_menu_nb&#39;].MENU_TREE_MENU_TABLE; ?&amp;gt; &amp;lt;?php echo $GLOBALS[&#39;t_is_missing_from_db&#39;]; ?&amp;gt;&amp;lt;/div&amp;gt; 
    &amp;lt;?php }
    
    elseif($dmenu == &#39;no_top_menu&#39;){ 
    ?&amp;gt;
    &amp;lt;div class=&quot;failure&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_menu_nb&#39;].MENU_TREE_MENU_TABLE; ?&amp;gt; - &amp;lt;?php echo $GLOBALS[&#39;t_no_top_menu_detected&#39;]; ?&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;?php }

    elseif($dmenu == &#39;menu_empty&#39;){ 
    ?&amp;gt;
    &amp;lt;div class=&quot;failure&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_menu_nb&#39;].MENU_TREE_MENU_TABLE; ?&amp;gt; &amp;lt;?php echo $GLOBALS[&#39;t_exists_but_is_empty&#39;]; ?&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;?php }

    else{
        
        if(CURRENT_LANG){$ln = CURRENT_LANG;} else{$utils = new UtilsModel(); $ln = $utils-&amp;gt;get_default_lang();}
        
        $default_plink = &#39;&#39;;

        if(CLEAN_URLS == true){
            if(DEFAULT_PLINK){$default_plink = DEFAULT_PLINK.&#39;/&#39;;}
            $home_link = CLEAN_PATH.&#39;/&#39;.DEFAULT_RLINK.&#39;/&#39;.$default_plink.$ln;
            $out = &quot;/out&quot;;
        }
        else{
            if(DEFAULT_PLINK){$default_plink = &#39;&amp;amp;&#39;.PLINK.&#39;=&#39;.DEFAULT_PLINK;}
            $home_link = $_SERVER[&#39;PHP_SELF&#39;].&#39;?&#39;.RLINK.&#39;=&#39;.DEFAULT_RLINK.$default_plink.&#39;&amp;amp;&#39;.LN.&#39;=&#39;.$ln;
            $out = &quot;&amp;amp;&quot;.LETOUT.&quot;=yes&quot;;
        }
        $home = $_SERVER[&#39;PHP_SELF&#39;];

        $rlink = RLINK;
        $plink = PLINK;
        $ln = LN;
        $cur_ln = CURRENT_LANG;
        
        if(isset($_SESSION[&#39;c_login&#39;])){$c_login = $_SESSION[&#39;c_login&#39;];}else{$c_login = &quot;&quot;;}
        ?&amp;gt;
        &amp;lt;table border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; class=&quot;dtree&quot;&amp;gt;
        &amp;lt;div class=&quot;dtree&quot;&amp;gt;
        
            &amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
            var home = &quot;&amp;lt;?php echo $home; ?&amp;gt;&quot;;
            var homelink = &quot;&amp;lt;?php echo $home_link; ?&amp;gt;&quot;; 
            var home_label = &quot;&amp;lt;?php echo $GLOBALS[&#39;t_home&#39;]; ?&amp;gt;&quot;; 
            var dmenu = &quot;&amp;lt;?php print(htmlspecialchars($dmenu)); ?&amp;gt;&quot;;
            var arosplit = dmenu.split(&quot;@@&quot;);
            var dmenu2 = &#39;&#39;;
            var rlink = &quot;&amp;lt;?php echo $rlink; ?&amp;gt;&quot;;
            var plink = &quot;&amp;lt;?php echo $plink; ?&amp;gt;&quot;;
            var ln = &quot;&amp;lt;?php echo $ln; ?&amp;gt;&quot;;
            var cur_ln = &quot;&amp;lt;?php echo $cur_ln; ?&amp;gt;&quot;;
            var c_login = &quot;&amp;lt;?php echo $c_login; ?&amp;gt;&quot;;
            var out = &quot;&amp;lt;?php echo $out; ?&amp;gt;&quot;;
            var clean_urls = &quot;&amp;lt;?php echo CLEAN_URLS; ?&amp;gt;&quot;;
            var clean_root = &quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;; ?&amp;gt;&quot;;
                &amp;lt;!--

                d = new dTree(&#39;d&#39;); //we create a new tree object called d

                d.add(0,-1,home_label,homelink);  //we add the home node/menu

                 for (i = 0;i&amp;lt;arosplit.length; i++){    //for each menu line    
                     
                    var tlink = &quot;&quot;;
                    var olink = &quot;&quot;;
                     
                    dmenu2 = arosplit[i];
                    sharpsplit = dmenu2.split(&quot;#&quot;);
                       
                    if(sharpsplit[3] == &quot;page&quot; || sharpsplit[3] == &quot;elink&quot;){ // if type = page or type = elink
                        if(clean_urls == true){tlink = &quot;/&quot; + sharpsplit[4];}
                        else{tlink = &quot;&amp;amp;&quot; + plink + &quot;=&quot; + sharpsplit[4];}
                    }
                      else{if(sharpsplit[3] == &#39;signin&#39;){if(c_login != &#39;&#39;){olink = out;}}
                    }               
                    if(clean_urls == true){
                        if(sharpsplit[3] != undefined){ //then add the other nodes
                            d.add(sharpsplit[0],sharpsplit[1],sharpsplit[2], clean_root + sharpsplit[3] + tlink + olink + &quot;/&quot; + cur_ln); 
                        }
                    }
                    else{
                        d.add(sharpsplit[0],sharpsplit[1],sharpsplit[2], home + &quot;?&quot; + rlink + &quot;=&quot; + sharpsplit[3] + tlink + olink + &quot;&amp;amp;&quot; + ln + &quot;=&quot; + cur_ln);
                    }
                  }
                      
                document.write(d); //we print out the tree

                //--&amp;gt;
            &amp;lt;/script&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;/table&amp;gt;
        &amp;lt;?php unset ($plink);
    }
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
So, from the moment we drew our menu feed to store it inside $dmenu, we first echo missing menu messages and alike, if any, then we take care off the default language, default page link and start building our home link since we&#39;re not yet looping through the menu rows we have. Note that only the tree menu will have a home link feature, proper to itself. It will of course be possible to add a home link to the horizontal menu, but it will have to be a regular type of link.
We then start gathering all the PHP variables we need and pass them to Javascript to help build the menu nodes(rows). We instantiate a tree using the dTree() method from the third-party tree menu library we use and start adding the home link by invoking the add() method of that library. Then, looping through each menu row, we dynamically build and add the rest of the nodes. Finally, we display the resulting tree on screen. Again, the proper dTree Javascript/CSS library files would be provided as attachments. Note that these libraries have been modified to support clean URL&#39;s and also so that you can bring you own icons and style along the way.&lt;br /&gt;
&lt;br /&gt;
Last, but not least, hmenu.php.

&lt;br /&gt;
&lt;pre style=&quot;background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;&quot;&gt;&lt;code&gt;&amp;lt;?php
$hmenu = new MenuModel();
$menu = $hmenu-&amp;gt;getMenuItemsData(HMENU_MENU_TABLE,&#39;hmenu&#39;); // Leave &#39;hmenu&#39; as is, to have the db result set undergo extra processing to make it fit with this kind of menu.

if(isset($menu) &amp;amp;&amp;amp; is_string($menu) &amp;amp;&amp;amp; $menu == &#39;menu_missing&#39;){ 
    ?&amp;gt;
    &amp;lt;div class=&quot;failure&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_menu_nb&#39;].HMENU_MENU_TABLE; ?&amp;gt; &amp;lt;?php echo $GLOBALS[&#39;t_is_missing_from_db&#39;]; ?&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;?php }
if(isset($menu) &amp;amp;&amp;amp; is_string($menu) &amp;amp;&amp;amp; $menu == &#39;menu_empty&#39;){ 
    ?&amp;gt;
    &amp;lt;div class=&quot;failure&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_menu_nb&#39;].HMENU_MENU_TABLE; ?&amp;gt; &amp;lt;?php echo $GLOBALS[&#39;t_exists_but_is_empty&#39;]; ?&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;?php }
if(isset($menu) &amp;amp;&amp;amp; is_string($menu) &amp;amp;&amp;amp; $menu == &#39;no_top_menu&#39;){ 
    ?&amp;gt;
    &amp;lt;div class=&quot;failure&quot;&amp;gt;&amp;lt;?php echo $GLOBALS[&#39;t_menu_nb&#39;].HMENU_MENU_TABLE; ?&amp;gt; - &amp;lt;?php echo $GLOBALS[&#39;t_no_top_menu_detected&#39;]; ?&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;?php }

if(isset($menu) &amp;amp;&amp;amp; is_array($menu)){
?&amp;gt;
&amp;lt;ul class=&quot;hmenu&quot; id=&quot;hmenu&quot;&amp;gt;
&amp;lt;?php
$letout = &#39;&#39;; 

foreach($menu as $m){
 //foreach menu line    
    $child = $m[0];
    $parent = $m[1];
    $translation = $m[2];
    $type = htmlspecialchars($m[3]);
    $page = htmlspecialchars($m[4]);

    if($type == &#39;page&#39; &amp;amp;&amp;amp; $page != &#39;0&#39;){
        if(CLEAN_URLS == true){$pg = &#39;/&#39;.$page;}
        else{$pg = &#39;&amp;amp;&#39;.PLINK.&#39;=&#39;.$page;}
    }
    elseif($type == &#39;elink&#39; &amp;amp;&amp;amp; $page != &#39;0&#39;){
        if(CLEAN_URLS == true){$pg = &#39;/&#39;.$page;}
        else{$pg = &#39;&amp;amp;&#39;.PLINK.&#39;=&#39;.$page;}
    } 
    else{$pg = &#39;&#39;;}
     
    if($type == &#39;signin&#39;){
        if(!isset($_SESSION[&#39;c_login&#39;]) || $_SESSION[&#39;c_login&#39;] == &#39;&#39;){
            //$type = &#39;signin&#39;;
            $letout = &#39;&#39;;
        }
        else{
            //$type = &#39;signin&#39;; 
            if(CLEAN_URLS == true){$letout = &#39;/out&#39;;}
            else{$letout = &#39;&amp;amp;&#39;.LETOUT.&#39;=yes&#39;;}
        }
    }
//if(isset($m[5])){sort($m[5]);}
    if($parent == &#39;0&#39;){
        //IF TOP MENU, WE DISPLAY IT
        if(CLEAN_URLS == true){$url = CLEAN_PATH.&#39;/&#39;.$type.$pg.$letout.&#39;/&#39;.CURRENT_LANG;} 
        else{$url = SITE_URL.&#39;?&#39;.RLINK.&#39;=&#39;.$type.$pg.&#39;&amp;amp;&#39;.LN.&#39;=&#39;.CURRENT_LANG.$letout;}
        ?&amp;gt;
        &amp;lt;li&amp;gt;
            &amp;lt;a href=&quot;&amp;lt;?php echo $url; ?&amp;gt;&quot; class=&quot;hmenulink&quot;&amp;gt;&amp;lt;?php echo $translation;
            
            if(isset($m[5]) &amp;amp;&amp;amp; sizeof($m[5]) &amp;gt; 0){// if the topmenu has kids, we add a decorative arrow
                ?&amp;gt;&amp;amp;nbsp;&amp;lt;img src=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES.&#39;/&#39;.CURRENT_THEME; ?&amp;gt;/images/servicing/hmenu/down-arrow.png&quot; width=&quot;9&quot; height=&quot;5&quot; /&amp;gt;
        &amp;lt;?php } ?&amp;gt;&amp;lt;/a&amp;gt;

        &amp;lt;?php
            if(isset($m[5]) &amp;amp;&amp;amp; sizeof($m[5]) &amp;gt; 0){//and we call the get_kids() function to display them
                $ml = 0;
                ksort($m[5]);                        
                echo get_kids($m[5],$child, $ml);             
            }
        ?&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;?php
        }
    }
    ?&amp;gt;
&amp;lt;/ul&amp;gt; 
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
    var hmenu = new hmenu.dd(&quot;hmenu&quot;);
    hmenu.init(&quot;hmenu&quot;,&quot;menuhover&quot;);
&amp;lt;/script&amp;gt;
&amp;lt;?php 
}

function get_kids($kid, $child, $ml){

    krsort($kid); //to test
    $letout = &#39;&#39;;
    
    $feed = &#39;&amp;lt;ul class=&quot;hmenu&quot; id=&quot;hmenu&quot;&amp;gt;&#39;;    

    foreach($kid as $k){
         //for each kid
            
        $par = $k[1];
        $ch = $k[0];
        $tr = $k[2];
        $ty = htmlspecialchars($k[3]);
        $pag = htmlspecialchars($k[4]);

        if($ty == &#39;page&#39; &amp;amp;&amp;amp; $pag != &#39;0&#39;){
            if(CLEAN_URLS == true){$pg = &#39;/&#39;.$pag;}
            else{$pg = &#39;&amp;amp;&#39;.PLINK.&#39;=&#39;.$pag;}
        }
        elseif($ty == &#39;elink&#39; &amp;amp;&amp;amp; $pag != &#39;0&#39;){
            if(CLEAN_URLS == true){$pg = &#39;/&#39;.$pag;}
            else{$pg = &#39;&amp;amp;&#39;.PLINK.&#39;=&#39;.$pag;}
        } 
        else{$pg = &#39;&#39;;}

        if($ty == &#39;signin&#39;){
            if(!isset($_SESSION[&#39;c_login&#39;]) || $_SESSION[&#39;c_login&#39;] == &#39;&#39;){
                //$ty = &#39;signin&#39;;
                $letout = &#39;&#39;;
            }
            else{
                //$ty = &#39;signin&#39;;
                if(CLEAN_URLS == true){$letout = &#39;/yes&#39;;}
                else{$letout = &#39;&amp;amp;&#39;.LETOUT.&#39;=yes&#39;;}
            }
        }
                
        if($par == $child){
            if(CLEAN_URLS == true){$url = CLEAN_PATH.&#39;/&#39;.$ty.$pg.$letout.&#39;/&#39;.CURRENT_LANG;}
            else{$url = SITE_URL.&#39;?&#39;.RLINK.&#39;=&#39;.$ty.$pg.&#39;&amp;amp;&#39;.LN.&#39;=&#39;.CURRENT_LANG.$letout;}

            $feed .= &#39;&amp;lt;li&amp;gt;&#39;;
            $feed .= &#39;&amp;lt;a href=&quot;&#39;.$url.&#39;&quot; class=&quot;shmenulink2&quot;&amp;gt;&#39;.$tr;
        
            if(isset($k[5])){// if kids, we add an arrow
                $feed .= &#39;&amp;amp;nbsp;&amp;lt;img src=&quot;&#39;.CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES.&#39;/&#39;.CURRENT_THEME.&#39;/images/servicing/hmenu/down-arrow.png&quot; width=&quot;9&quot; height=&quot;5&quot; alt=&quot;&#39;.$tr.&#39;&quot; /&amp;gt;&amp;lt;/a&amp;gt;&#39;;
            }
    
            if(isset($k[5])){$feed .= get_kids($k[5],$ch, $ml);}    //recursion
            $feed .= &#39;&amp;lt;/li&amp;gt;&#39;;
        }
    }
    $feed .= &#39;&amp;lt;/ul&amp;gt;&#39;;
    return $feed;
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
For the most part, hmenu.php, as highlighted in the comments, does just what menu_tree.php does, but there are important differences though. Its layout is partly being drawn here, as opposed to menu_tree.php, which has its layout being entirely drawn inside the dTree library. Most importantly too, this is here we will use recursion while looking for kids.&lt;br /&gt;
&lt;br /&gt;
In hmenu.php, if we find a top row, we display it, and if that top row has kids, we not only add a decorative arrow ourselves, but we do call the get_kids() function, which, since its a recursive one, will, upon finding new kids, call itself back. Once the loop is complete, we call on our third-party horizontal library to create a menu object called hmenu, passing our new structure as parameter, and initialize that object passing the relevant CSS class names as parameters.&lt;br /&gt;
&lt;br /&gt;
So now, we have a fully functional front-end menu system working for us, which offers a choice of two layouts, vertical and horizontal. Both support an infinite number of levels and are totally customizable by means of CSS and Javascript too. This menu system will now on allows us to hierarchically present our pages and modules nicely, without having the users hard-typing their URL in their browsers. In a future settlement, I will show you how to build the back-end management module that will allow the creation/edition/deletion of those menu rows as well as the menus itself.&lt;br /&gt;
&lt;br /&gt;
But next time, we will set our focus on how to handle the user experience by creating a sign module along with a user profile module, that will allow the user to get into its personal account interface and update personal data.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;This &lt;a href=&quot;http://www.rolandc.net/2014/12/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-4.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt; first appeared Sunday the 28th of December 2014 on &lt;a href=&quot;http://rolandc.net/&quot;&gt;RolandC.net&lt;/a&gt;.&lt;/span&gt; &lt;br /&gt;
&lt;br /&gt;</description><link>http://www.rolandc.net/2014/12/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-4.html</link><author>noreply@blogger.com (rolandc.net)</author><thr:total>9</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-291377940813169899</guid><pubDate>Wed, 05 Nov 2014 21:07:00 +0000</pubDate><atom:updated>2014-12-28T00:19:20.210+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cms</category><category domain="http://www.blogger.com/atom/ns#">gumbo-cms</category><category domain="http://www.blogger.com/atom/ns#">mvc</category><category domain="http://www.blogger.com/atom/ns#">opensource</category><category domain="http://www.blogger.com/atom/ns#">php</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">tutorial</category><category domain="http://www.blogger.com/atom/ns#">webdev</category><title>How to build your own Multilingual PHP MVC CMS from scratch - Part 3 - Frontend (part 1): Page Module</title><description>Welcome to the third settlement of our tutorial series on how to build your own Multilingual MVC CMS from scratch and in which we will be creating our first entire module, the page module.
&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.rolandc.net/2014/10/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-2.html&quot;&gt;Last time&lt;/a&gt;, we have set the focus on the index.php file, responsible for routing user input, calling all the needed core components that we have previously reviewed.&amp;nbsp;
&lt;br /&gt;
We have seen how the Router class, which can be assimilated as a factory, once instantiated and invoked by means of its own route() method, generates and triggers, based on user input, a new controller object that itself, aided by the __autoload() function from the index file, calls on the name-matching module class it needs in order to get all the logic and data it will use to build the needed view and it does so by relying on the $registry object that was passed onto it while being instantiated by the $router object, the $registry object containing a template object, itself containing a method to include the needed view file as well as a __set() method to store all the template variables the controller needs to pass to the view file, in order to be used by the programmer there.
&lt;br /&gt;
&lt;br /&gt;
So today, we are going to take a dive straight into the page module, see how its&amp;nbsp; controller, model and view really work on the ground. But before we start with the page controller class, let&#39;s take a brief look at the structure of the database table called &#39;pages&#39;, which contains all the data we will rely on to effectively handle pages in our CMS.
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; --  
      -- Table structure for table `pages`  
      --  
      CREATE TABLE IF NOT EXISTS `pages` (  
      `id_page` smallint(6) NOT NULL,  
       `page_data` mediumtext,  
       `page_name_formatted` varchar(60) NOT NULL,  
       `page_name` varchar(80) NOT NULL,  
       `page_language` varchar(2) NOT NULL,  
       `page_created` datetime DEFAULT CURRENT_TIMESTAMP,  
       `page_last_edited` datetime DEFAULT CURRENT_TIMESTAMP  
      ) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;  
      --  
      -- Indexes for dumped tables  
      --  
      --  
      -- Indexes for table `pages`  
      --  
      ALTER TABLE `pages`  
       ADD PRIMARY KEY (`id_page`), ADD FULLTEXT KEY `pages_data` (`page_data`);  
      --  
      -- AUTO_INCREMENT for dumped tables  
      --  
      --  
      -- AUTO_INCREMENT for table `pages`  
      --  
      ALTER TABLE `pages`  
      MODIFY `id_page` smallint(6) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=0;  
      /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;  
      /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;  
      /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;  
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Note that the field called page_name_formatted, since it corresponds to the formatted name of the page that will be either be displayed in the URL or serve inside menu links, to play it safe, will always need to contain nothing but lowercase, latin-alphabet letters and/or figures, with only underscore/minus signs allowed. Also, page_language will always have to contain two lowercase letters only.
&lt;br /&gt;
&lt;br /&gt;
Here is a dummy data sample for you to insert in your own page table
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; INSERT INTO &#39;pages&#39; (&#39;id_page&#39;, &#39;page_data&#39;, &#39;page_name_formatted&#39;, &#39;page_name&#39;, &#39;page_language&#39;, &#39;page_created&#39;, &#39;page_last_edited&#39;) VALUES  
      (1, &#39;data for page 1&#39;, &#39;page_1&#39;, &#39;Page 1&#39;, &#39;en&#39;, &#39;2014-09-06 23:05:39&#39;, &#39;2014-09-06 23:53:19&#39;),  
      (2, &#39;data for page 2&#39;, &#39;page_2&#39;, &#39;Page 2&#39;, &#39;en&#39;, &#39;2014-09-06 23:06:42&#39;, &#39;2014-09-06 23:55:28&#39;),  
      (3, &#39;data for page 3&#39;, &#39;page_3&#39;, &#39;Page 3&#39;, &#39;en&#39;, &#39;2014-09-06 23:07:18&#39;, &#39;2014-09-06 23:58:02&#39;)  
      );  
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
So now, without further waiting, here comes our PageController class
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      /* application/controllers/PageController.php */  
      class PageController extends BaseController{   
           //Declaring our PageController class  
           //This class benefits from the properties of the abstract parent class it extends, called BaseController,   
           //but even though the parent class forces us to have an initialize() method set here,   
           //it is up to us programmers to decide how we wish to implement it here.  
           //declaring our private properties  
           private $module_name; //passed to main.php to validate the including of page.php  
           private $page_data; // passed to page.php as containing the actual data stored in the DB for the particular page the user seeks   
           private $page_name; //passed to header.php to serve inside the meta title tag  
           private $page; // only this property will not be passed directly to the view,   
           //instead, it will hold the value of plink and be passed as a parameter to the model&#39;s get_data() method   
           //Note: those properties will not be passed as-is to the view, not from here at least, so we can confidently keep them as private while here.  
           public function initialize(){ // this is the method our abstract BaseController class forces us to declare  
                //Next we issue a few calls to some methods that will in turn call upon the model to get the needed data  
                //and we store the results as properties of the template object (remember the Template class contains a __set() method too)   
                //The template object is being itself stored as a property of the registry object  
                $this-&amp;gt;registry-&amp;gt;template-&amp;gt;module_name = $this-&amp;gt;getModuleName();  
                $this-&amp;gt;registry-&amp;gt;template-&amp;gt;page_data = $this-&amp;gt;getPageData();  
                $this-&amp;gt;registry-&amp;gt;template-&amp;gt;page_name = $this-&amp;gt;getPageName();  
                //note the $this keyword which specifies that we refer to this class only  
                //finally, we call the assign_theme() method from the template object,   
                //that method will take care off preparing the above properties for display,   
                //plus it will include our common template files (header, main and footer)  
                $this-&amp;gt;registry-&amp;gt;template-&amp;gt;assign_theme();  
           }  
           public function getModuleName(){  
                // we instantiate our PageModel class, which is Singleton, so we need to use the :: syntax and call its getInstance() method to do so.   
                $model = PageModel::getInstance();  
                //and we simply call its get_module_name() method, which will simply return the name of the module, as a string.  
                return $model-&amp;gt;get_module_name(); //We return than name  
           }  
           public function getPageName(){  
                // same operation with the get_page_name() method, which will return   
                // the non-formatted localized name of the page from the DB  
                $model = PageModel::getInstance();  
                return $model-&amp;gt;get_page_name();  
           }  
           public function getPageData(){  
                if(CURRENT_PLINK){$this-&amp;gt;page = CURRENT_PLINK;}  
                else{$this-&amp;gt;page = DEFAULT_PLINK;}   
                // we assign whatever plink value we have fetched in the index   
                // otherwise we assign the default plink value  
                $model = PageModel::getInstance(); //we instantiate our PageModel class again   
                return $model-&amp;gt;getData($this-&amp;gt;page,CURRENT_LANG);   
                // and call the getData() method (passing the URL page name and current language as parameters),  
                // which will fetch the page content from the DB for us. We return the result of the call  
           }  
      }  
      ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
We start by declaring our PageController class and declare the few properties we will need along the way. Only the top three properties will be passed to the view.
&lt;br /&gt;
&lt;br /&gt;
Then we call the initialize() method, the top three properties: module_name, page_data and page_name are being passed to the view, as they are being assigned the results of specific method calls to the PageModel object, by being added to the template object, which itself had been added to the registry object as a property and which also contains a __set() method allowing it to store the properties to be then passed to the view.
&lt;br /&gt;
&lt;br /&gt;
Finally we call the assign_theme() method from the template object. That method will prepare the above properties for display and it will include our common template files as well: header.php, main.php and footer.php.
&lt;br /&gt;
&lt;br /&gt;
Then remains the actual calls to some of the PageModel&#39;s methods. Those in this particular case are the calls that feed the values of our above properties, the ones we want to pass to the view. The PageModel class is a Singleton one and so we need to access and instantiate it by using the :: syntax, plus by making a call to its public and static getInstance() method. The calls to our model&#39;s methods are not made directly though. While still inside the initialize() method, we issue a call to a dedicated controller method that will itself invoke the needed model&#39;s method.
&lt;br /&gt;
&lt;br /&gt;
So now has come the time to lay our PageModel class down on the table.
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      /* application/models/PageModel.php */  
      class PageModel{ //declaring our PageModel class  
           static $instance;     // needed to help make the class a Singleton one  
           private $module_name;       
           private $pageData;  
           private $page_name;  
           public static function getInstance(){ //method to create a single static instance of this class,   
                //which will be accessed from anywhere outside the class using the :: operator.   
                //It can be called anything you wish.  
                if(self::$instance == null){ // if no instance was created  
                     self::$instance = new self(); // we create one   
                     //the keyword &#39;self&#39; reffers to the current class  
                }  
                return self::$instance; //returning the newly created instance  
           }  
           private function __construct(){}   
           private function __clone(){}  
           //Those two methods are here to prevent the programmer from creating a second instance of this class by using the new() or clone() methods.   
           //Only one instance of this class will be accessible and it will be accessible using the getInstance() method only.   
           //This is how Singleton classes are built.  
           public function get_module_name(){ //the method that will set and return the module name we need in main.php   
                //If $module_name has been set and passed to main.php, then page.php will be included   
                //in main.php, where we need it to graphically appear.  
                $this-&amp;gt;module_name = &#39;Page&#39;; //setting the value of the module_name property,   
                //which has not yet been turned to a variable by the template object   
                return $this-&amp;gt;module_name; //returning that value  
           }                 
           public function get_page_name(){   
           //method to get the non-formatted, localized name of the page  
                if(CURRENT_PLINK){   
                     // if plink has been set in the URL and is not empty  
                     //we instantiate MenuModel (also a Singleton)  
                     $menu_instance = MenuModel::getInstance();  
                     $page_list = $menu_instance-&amp;gt;getTreeMenuItemTranslations(CURRENT_LANG,&#39;page&#39;);   
                     //and fetch the list of all existing pages found in DB &#39;pages&#39; table.   
                     //This is a method that originally serves other purposes,   
                     //but since part of the data it returns contains all the data we need here,   
                     //we save ourself writing an extra method by using it.  
                     $cv = 0; //we initialize a counter  
                     foreach($page_list as $pl){ foreach known front-end page  
                          if(!in_array(CURRENT_PLINK,$pl)){$cv++;}   
                               //if our page name cannot be found inside the currently   
                               //looped page from the DB, we increment $cv by 1.  
                          else{   
                                    //otherwise, that means our page name is valid  
                                    if($pl[1] == CURRENT_LANG){$this-&amp;gt;page_name = $pl[2];  
                                    //and once the current language is being matched, we assign   
                                    //the corresponfing non-formatted page name to the page_name property  
                               }  
                          }  
                     }   
                     if($cv == sizeof($page_list)){ // if the page name could not be found while looping the array above,//we build a missing link instead and assign it to the to the page_name property  
                          $this-&amp;gt;page_name = CURRENT_PLINK.&#39; (&#39;.$GLOBALS[&#39;t_&#39;.CURRENT_LANG.&#39;) - &#39;.$GLOBALS[&#39;t_page_missing&#39;];  
                     }  
                }  
                else{$this-&amp;gt;page_name = &#39;&#39;; //if no page name was set, we assign it an empty string value}  
                return $this-&amp;gt;page_name; //we return the page name, whatever its value  
           }       
           public function getData($page, $ln){ //we declare the function, taking page name and language as parameters  
                $this-&amp;gt;pageData = array(); //we declare the array that will hold our page data from the DB  
                $page = filter_var($page,FILTER_SANITIZE_MAGIC_QUOTES); //we use the PHP native filter_var function to sanitize our page name.   
                //This is primarily a matter of security as we don&#39;t want issues related to SQL Injection attacks   
                //by means of manually (deliberately?) placing quotes or double quotes in the URL to or in place of the page name.  
                MySQLModel::get_mysql_instance()-&amp;gt;set_char(&#39;latin1&#39;); //we invoke our MySQL library&#39;s set_char() method   
                //to avoid encoding issues with the content we pull here. Latin1? yes, more on that in a future settlement though.   
                $my_query = &quot;SELECT id_page, page_name, page_data   
                              FROM pages WHERE page_name_formatted = &#39;&quot;.$page.&quot;&#39;   
                              AND page_language = &#39;&quot;.$ln.&quot;&#39;  
                            &quot;;   
                //we build a query in which we select all entries of which the formatted named and language correspond respectively to our current page and language names. Only one single row should be found as a result.  
                MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($my_query);  
                // we instantiate our MySQL instance and execute the query  
                $res = MySQLModel::get_mysql_instance()-&amp;gt;getRows($my_query);  
                // and we pull what&#39;s been found, assign it to $res  
                if($res != &#39;&#39;){ //If what&#39;s been found isn&#39;t empty  
                     array_push($this-&amp;gt;pageData, $res[&#39;page_data&#39;], $res[&#39;page_name&#39;]); //we stack it insde the pageData array  
                }            
                else{ //otherwise  
                          if(isset($page) &amp;amp;&amp;amp; $page != &#39;&#39;){ $page = stripslashes($page).&#39; (&#39;.$GLOBALS[&#39;t_&#39;.$ln].&#39;)&#39;;}   
                          else{$page = &#39;&#39;;}   
                          //we stack a missing page message in the pageData array,   
                          //that will include the image file name of the missing page sign we will display in the view.  
                          array_push($this-&amp;gt;pageData, $page.$GLOBALS[&#39;t_page_missing&#39;],&#39;missing_page2.png&#39;);  
                }  
                return $this-&amp;gt;pageData; //and we return whatever we have picked up and stacked in the pageData array.  
           }  
      }  
      ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
The PageModel class is a Singleton one. I have described in the comments the way a Singleton class can be built, by declaring a public static function that will create a single static instance of the class every time it will be called. But, to remain a Singleton, the class needs to be declared empty __construct() and __clone() methods so that a second instantiation cannot be initiated by the programmer, even mistakingly.
&lt;br /&gt;
&lt;br /&gt;
The get_page_name() method checks if page name (CURRENT_PLINK) is set and not empty, then pulls a list of all existing pages and sees if it finds it there. If it does, it assigns the page&#39;s non-formatted and localized name to the page_name property, and if it does not, it assigns a missing page message to that same property. And if page name is not set, it assigns an empty string to page_name. For the sake of this tutorial however, you may skip checking with the existing page list and simply return the value of CURRENT_PLINK, provided it is set and not empty.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
Now, in the getData() method, we set the page_data property as an array, modify the database charset to &#39;Latin1&#39; and sanitize the $page parameter from the method call using PHP&#39;s native filter_var() function. Then we build a query string to pull the page table entry that matches our current page and language names and simply execute that query. In the page_data array, we stack the fields we need from the freshly pulled results. And if results there aren&#39;t, we stack a missing page message in it, along with the name of the missing page image file we will display with the message.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
The page model contains fairly simple methods and its role can mostly be summed up as simply pulling content from the page table, based on page name and current language. It seems therefore fair to warn the reader on that most of the models we will soon be dealing with are not that simple in content and/or architecture. But have no fear, as we have about reviewed the core structure of the architecture we&#39;re going to be evolving in, from now the difficulty level should not be increasing that significantly. However and by nature, some of the models we will be reviewing soon will present algorithms of which the explanation might not appear to be so obvious to all readers at first. In any a case, both code comments and explanations will make up for it, so that you will never really have to worry about the growing complexity of the project.
&lt;br /&gt;
&lt;br /&gt;
Back one level up in our page module hierarchy, now that we have pulled the page data we needed from the DB and returned it to the controller, we&#39;re all set to call the assign_theme() method, which will transform some of our controller&#39;s properties to regular variables/arrays we can actually use inside the theme files.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
Let&#39;s first take a look at a sample from main.php
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      /* themes/your_theme_name/main.php */  
      ...   
      &amp;lt;td&amp;gt;  
           &amp;lt;?php  
                try{     //we do use a try/catch bloc with this kind of include  
                          if(isset($module_name) &amp;amp;&amp;amp; !empty($module_name)){   
                                    //if the controler has passed us the module_name property   
                                    //and provided it&#39;s not empty, we then use it to include the corresponding view file  
                                    $path = PATH_TO_THEMES.&#39;/&#39;.CURRENT_THEME.&#39;/&#39;.strtolower($module_name).&#39;.php&#39;;  
                                    include $path;  
                          }  
                          else {  
                                    //else we throw an exception containing whatever message we need to visually display  
                                    throw new Exception(&#39;&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;div class=&quot;loading_tpl_fails&quot;&amp;gt;&#39;.$GLOBALS[&#39;t_the_theme_named&#39;].&#39; &amp;lt;u&amp;gt;&#39;.$module_name.&#39;&amp;lt;/u&amp;gt; &#39;.$GLOBALS[&#39;t_couldnt_be_found&#39;].&#39;&amp;lt;/div&amp;gt;&#39;);  
                          }  
                }  
                catch(Exception $e){  
                     echo $e-&amp;gt;getMessage();  
                     exit(0);  
                }  
           ?&amp;gt;  
      &amp;lt;/td&amp;gt;  
      ...  
      ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
The main.php file is the place where page.php will actually be included, as again, it will not be included straight from the assign_theme() method of the template object. We do use a try/catch bloc to wrap this include statement.
&lt;br /&gt;
&lt;br /&gt;
And now, let&#39;s see what page.php has to tell us
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      /* themes/your_theme_name/page.php */  
      &amp;lt;?php  
      // See how we find back our controller properties here,   
      // transformed to regular variables or arrays, holding the value(s) we have assigned them  
      // while still in the initialize() method of the controller  
      if(isset($page_data[1]) &amp;amp;&amp;amp; preg_match(&#39;/missing_page/&#39;,$page_data[1])){ //if the required page is missing  
      ?&amp;gt;   
           &amp;lt;img src=&quot;&amp;lt;?php echo CLEAN_PATH.&#39;/&#39;.PATH_TO_THEMES.&#39;/&#39;.CURRENT_THEME.&#39;/&#39;; ?&amp;gt;images/servicing/&amp;lt;?php echo $page_data[1]; ?&amp;gt;&quot; /&amp;gt;  
           //we display a missing page image (remember we stacked the image   
           //file name inside the page_data array while in the model   
           //then returned it to the controller and passed it onto the view here)  
           &amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;  
           &amp;lt;?php  
           }  
           if(isset($page_data[0]) &amp;amp;&amp;amp; is_string($page_data[0]) &amp;amp;&amp;amp; !preg_match(&#39;/missing_page/&#39;,$page_data[0]) ){   
                //if the required page is not missing and its value does not match &#39;missing_string&#39;  
                ?&amp;gt;  
                &amp;lt;div align=&quot;center&quot;&amp;gt;  
                &amp;lt;?php   
                     print $page_data[0]; //we print the data we had stacked in the page_data array  
                ?&amp;gt;  
                &amp;lt;/div&amp;gt;  
           &amp;lt;?php   
      }  
      ?&amp;gt;       
      &amp;lt;br /&amp;gt;  
      ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
We can now clearly see how we find back and use our controller properties while inside the view, eg: page_data, transformed to regular, usable PHP array, holding the value(s) we assigned it while we were still in the initialize() method of the controller.
&lt;br /&gt;
As for the algorithm of the overall page template, it is very simple: if the required page is missing, we display a missing page image with a missing page string underneath, otherwise, we display the content we have pulled from the DB for the required page.
&lt;br /&gt;
&lt;br /&gt;
By now and by having gathered all code from this part as well as from the last one, referring to the first part for directory structure information too, you should already have a basic application that allows you to display a page based on URL input. For now though, we have only been discussing the page module.
&lt;br /&gt;
&lt;br /&gt;
But as an example, loading http://localhost/your_project_name/index.php?q=page&amp;amp;page=page_3&amp;amp;ln=en into your browser, if successful, should serve your screen three template files, namely header.php, main.php and footer.php, with main.php containing page.php, itself containing the DB-pulled content for page_3 (provided page_3 is not missing from the DB). If page_3 is missing from the DB, a missing image will be displayed and you are free to choose an image for yourself to work with. The page.php file can be placed everywhere it would graphically makes sense to you, as long as it stays inside main.php.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
Note that for clarity reasons I have not been including header.php and footer.php in the series yet but for now, you can make up for it by adding some simple header and footer files, header.php file containing the usual html, header tags we all know about. This is where you will link your main theme CSS file, and if your style name is called pinguin, then your main CSS file would have to be called pinguin.css. The favicon file will be stored in /themes/your_style/images/favicon and be called favicon.ico. The main.php file will contain the first body tag and footer.php will contain the closing body and html tags. This way, you can start effectively styling your project while we&#39;re getting advanced but still haven&#39;t reviewed all the theme-handling aspects of our project yet. Those will be the subject of a whole forthcoming settlement.
&lt;br /&gt;
&lt;br /&gt;
Last but not least, in case this wasn&#39;t implicit enough: every time you encounter a global variable of the form : $GLOBAL[&#39;t_variable_name&#39;], this means, by the very standards of this project, that this is a translation variable and that basically, in the language file pertaining to the current language (languages/current_language/current_language.php) should exist a variable called $t_variable_name = &#39;Translation string&#39;;. Otherwise a missing variable error is being thrown by PHP. Again, language-handling will be discussed in more details in a forthcoming settlement.
&lt;br /&gt;
&lt;br /&gt;
Meanwhile, do not hesitate to refer to your copy of &lt;a href=&quot;https://sourceforge.net/projects/gumbo-cms/&quot;&gt;Gumbo&lt;/a&gt; you have probably downloaded already last week. This will hopefully help you solidify your understanding of the project we&#39;re building together.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
So this completely wraps up this settlement on how the page module works, bottom up. Next time, we will get our hands tied on yet another generally essential - not to say vital - feature in Web development, the menu system. &lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: xx-small;&quot;&gt;This &lt;a href=&quot;http://www.rolandc.net/2014/11/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-3.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt; first appeared Tuesday the 5th of November 2014 on &lt;a href=&quot;http://rolandc.net/&quot;&gt;RolandC.net&lt;/a&gt;.&lt;/span&gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</description><link>http://www.rolandc.net/2014/11/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-3.html</link><author>noreply@blogger.com (rolandc.net)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-7510305600067182794</guid><pubDate>Wed, 29 Oct 2014 05:35:00 +0000</pubDate><atom:updated>2014-10-29T08:13:14.160+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cms</category><category domain="http://www.blogger.com/atom/ns#">gumbo-cms</category><category domain="http://www.blogger.com/atom/ns#">mvc</category><category domain="http://www.blogger.com/atom/ns#">opensource</category><category domain="http://www.blogger.com/atom/ns#">php</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">tutorial</category><category domain="http://www.blogger.com/atom/ns#">webdev</category><title>  How to build your own Multilingual PHP MVC CMS from scratch - Part 2 - Starting Point: index.php</title><description>Welcome to this second settlement of our tutorial series on how to build your own Multilingual MVC CMS from scratch and in which we will be discussing the entry point of our application: The index.php file.&lt;br /&gt;
During the &lt;a href=&quot;http://www.rolandc.net/2014/10/how-to-build-your-own-multilingual-mvc-cms-from-scratch-part-1-introduction.html&quot;&gt;first part&lt;/a&gt;, we have, among other things, reviewed the core principles around which we will develop our project and also briefly introduced its directory structure.&lt;br /&gt;
But now, all those would be totally useless if not for one single file, the one and only entry point of our application, the index file, responsible for handling user input, requiring all the needed components along the way and which we are now going to decorticate, step by step.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://gumbo-cms.net/&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;How to build your own Multilingual PHP MVC CMS from scratch - Part 2 on RolandC.net&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPP3d2n85vih37rogpCmzyVjUdxKRQKpdiJ2yUsp84RPdhJBwMnmkE20aeHaciAUuL3BNTUvQQeuZGbaGp4Gu25NmOOuvuBZE_me-O5YHHp6U6pZBgO0rtdlqLgTGIjVFPsebaG7aBf9p_/s1600/gumbo_logo_dark.png&quot; height=&quot;65&quot; title=&quot;How to build your own Multilingual PHP MVC CMS from scratch - Part 2 on RolandC.net&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://gumbo-cms.net/&quot;&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;Gumbo-CMS is the application on which these series are based&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Before we start&lt;/b&gt;: I have based these series entirely on &lt;a href=&quot;http://gumbo-cms.net/&quot;&gt;Gumbo-CMS&lt;/a&gt;, the new Multilingual PHP5 MVC CMS in town, which I have recently released under the GPL V2 License. So feel free to &lt;a href=&quot;https://sourceforge.net/projects/gumbo-cms/files/latest/download?source=directory&quot;&gt;download yourself a copy&lt;/a&gt; before we actually get started, as it will contain every single snippet of code you will be able to find throughout the entire tutorial. This will provide you with some guidance if you ever get lost during one of the forthcoming settlements. Equally, you will find useful insights as to which kind of feature Gumbo-CMS offers by taking a look at its &lt;a href=&quot;https://drive.google.com/file/d/0B0JM9RhD7lhhb0UxaDQxbS1LT00/view?usp=sharing&quot;&gt;Product Overview PDF&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
So, we jump right in by declaring a session on line 2, just below the opening php tag and we also include our config file. 
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      //we initiate a session, useful for user sessions handling,
      // last visited URLs, etc.
      session_start();   
      //and we include our config file
      include &#39;config.php&#39;;   
&lt;/code&gt;&lt;/pre&gt;
Next, we instantiate the UtilsModel class, which contains a few useful &#39;all-purpose&#39; functions.&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; // we instantiate our general-purpose class called UtilsModel
 $utils = new UtilsModel();  
&lt;/code&gt;&lt;/pre&gt;
Then we define our timezone. 
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; date\_default\_timezone_set(DDTS); // we define our timezone  
&lt;/code&gt;&lt;/pre&gt;
DDTS will be the config constant that holds our timezone value, eg: Europe/Stockholm, Europe/Paris. This is required since PHP 5.10 because otherwise, I quote from the php.net site : &#39;every call to a date/time function will generate a E_NOTICE if the timezone isn&#39;t valid, and/or a E_WARNING message if using the system settings or the TZ environment variable&#39;. Alternatively, you can also set the date.timezone parameter in php.ini.
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; /*recording the last visited url*/  
      if(isset($_GET[RLINK]) &amp;amp;&amp;amp; $_GET[RLINK] != &#39;signin&#39; &amp;amp;&amp;amp; $_GET[RLINK] != &#39;fpwd&#39; &amp;amp;&amp;amp; $_GET[RLINK] != &#39;fpwd&#39;){  
           //if a module is set and that is neither the signin model nor the forgotten password one, we store the request URI in a session variable  
           $_SESSION[&#39;last_url&#39;] = $_SERVER[&#39;REQUEST_URI&#39;];  
      }  
&lt;/code&gt;&lt;/pre&gt;
This above is a snippet that pertains to the logic of our CMS, in regard to the recording of the last visited URL. This is also a good occasion to start introducing the URL constant naming scheme we will be using throughout the whole project while developing it.&lt;br /&gt;
&lt;br /&gt;
RLINK, implicitly meaning Router Link, is the constant that holds the $_GET name of a module name, be it current or sought after. Equally important are PLINK and LN, which will respectively hold page and language names. &lt;br /&gt;
&lt;br /&gt;
The value of all URL constants can be modified to taste, as long as the CLEAN_URLS constant is set to false. This because of the rewrite rules in .htaccess that will not allow the dynamic naming of target variables within its rules. But as long as clean URLS aren&#39;t enabled, you are free to give any value you wish to your URL $_GET variables.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s break away momentarily from the index.php file to take a look at this extract from config.php
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; /* config.php */  
      ...  
      define(&quot;CLEAN_URLS&quot;,false); //true or false. true to enable it, false to disable - NOTE : mod_rewrite has to be enabled for it to work. We will set it to true when we reach our settlement about setting up clean URL&#39;s, so for now, let&#39;s keep it disabled  
      /*routing*/  
      define(&quot;RLINK&quot;,&quot;q&quot;); //Router Link- var that holds the current module name in $_GET requests - make sure it&#39;s different from the other url variables found in this file, and different from google cse reserved terms (cx, language, sa, etc...) - use &#39;ctrl + F&#39; to check within this file  
      define(&quot;DEFAULT_RLINK&quot;,&quot;page&quot;);          // Warning: if you choose a default module that&#39;s different from the page module,  
      define(&quot;DEFAULT_PLINK&quot;,&quot;formatted_name_of_the_page_you_wish&quot;);     //Page Link     // then you will have to leave the DEFAULT_PLINK value blank;  
      define(&quot;PLINK&quot;,&quot;p&quot;);  
      ...  
&lt;/code&gt;&lt;/pre&gt;
But back to our little snippet in the index (commented as &#39;recording the last visited url&#39;), what it says is: If a module is set and that module is different from the signin module and different from the forgotten password module too, we then store the REQUEST URI in a session variable we call $_SESSION[&#39;last_url&#39;]. We do this because we simply don&#39;t want to have the user automatically come back to the login screen or the forgotten password procedure once it has successfully logged in.&lt;br /&gt;
&lt;br /&gt;
And now has come the time to call and instantiate some of our core classes, namely the &lt;b&gt;router&lt;/b&gt;, &lt;b&gt;registry&lt;/b&gt; and &lt;b&gt;template&lt;/b&gt; classes.
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; /* requiring the core clases */  
      require_once(&#39;application/Router.php&#39;);   
      require_once(&#39;application/Registry.php&#39;);  
      require_once(&#39;application/Template.php&#39;);  
      /* instantiating them */  
      $router = new Router();  
      $registry = new Registry();  
      $registry-&amp;gt;template = new Template(); // here we add the new template object to the registry object, as one of its properties  
&lt;/code&gt;&lt;/pre&gt;
The router will do the work of routing user input by retrieving the requested GET variable, then including and calling the corresponding controller, based on the value of that variable. We will review what the router actually does in detail when the route() function is called later in the the file. &lt;br /&gt;
&lt;br /&gt;
We call on the registry, which contains magic __set() and __get() methods: this will simply provide a registry to our application in which to store some of our application variables.&lt;br /&gt;
&lt;br /&gt;
We also call and instantiate the template class, adding it to our registry object as one of its properties and which contains our template engine. It will store both the variables assigned by the controller for the view to use and include the view itself.
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; /* DB connection initiating */  
      MySQLModel::get_mysql_instance()-&amp;gt;newConnection(DBHOST,DBUSER,DBPWD,DBNAME,DBPORT);   
      // we instantiate our Singleton MySQLModel class and invoke the newConnection() method, passing our config&#39;s db settings as parameters  
&lt;/code&gt;&lt;/pre&gt;
To connect to and interact with the MySQL database, we will use a custom MySQLModel class, which I have found on the Internet and turned into a Singleton class, so its instantiation gets restricted to one object only and which we will be able to access via the get_mysql_instance() method, every time we need to perform an operation with the database. &lt;br /&gt;
&lt;br /&gt;
Singleton? Yes. Design patterns, in the software development industry, are patterns that have been observed, tried and accepted as being the most likely solutions to a set of commonly encountered problems, so that you do not have to reinvent the wheel and implementing solutions to commonly encountered patterns is somehow the best way you can use to make sure you&#39;re being on the right track, at least on the same track as most others. &lt;br /&gt;
&lt;br /&gt;
The &lt;b&gt;Singleton pattern&lt;/b&gt; is a very popular software design pattern that&#39;s very useful in our case. Our MySQLModel class does not need and shall not be instantiated more than once, all while it will still be able to perform its duties CMS-wide.&lt;br /&gt;
&lt;br /&gt;
Having made our MySQLModel class a Singleton one allows us, once loaded, to access it from anywhere using the :: syntax, thus we do not have to store our database connection inside a global variable, a session variable or whatever else.&lt;br /&gt;
&lt;br /&gt;
Using the MySQLModel::get_mysql_instance()-&amp;gt;newConnection(params) syntax, we simply instantiate our class and add a call to the newConnection() method altogether, passing our database connection constants from the config file as parameters. Since the call is made early enough in our index file, we do not need to perform any additional connection anywhere else. We will only need to use the MySQLModel::get_mysql_instance()-&amp;gt;method_name(params); syntax every time we need to perform an operation with MySQL.&lt;br /&gt;
&lt;br /&gt;
Note that even though we have named our class the MySQL model, we do connect to MYSQL via the mysqli extension. &lt;br /&gt;
&lt;br /&gt;
Next, we engage in pre-routing user input, or what remains of it. Up to Gumbo-CMS v0.96beta (included), pre-routing was operated by means of header redirects, under the logic that we always wanted to have a valid module link (RLINK) and a valid language link (LN) defined at a minimum in the URL, at any time.&lt;br /&gt;
&lt;br /&gt;
Now bare with me, Google does not like header redirects, even though they claim 302 status is fine for them, so you will have all the trouble of the world trying to index your site using such (security?) procedure. While the Bing engine will index your code just fine, Google&#39;s spiders will not because they see these kinds of redirects as hijacking attempts. Which is why v.97 features a new pre-routing approach which does not use PHP headers at all.&lt;br /&gt;
&lt;br /&gt;
In this new approach, we will simply determine a value for PLINK, RLINK and LN and assign those to constants, respectively CURRENT_PLINK, CURRENT_RLINK and CURRENT_LANG &lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; /* pre-routing (or what&#39;s left of it) */  
      
      $plink = &#39;&#39;; 
      // if there is a $_GET[PLINK] set, let&#39;s use it
      if(isset($_GET[PLINK])){$plink = $_GET[PLINK];}
      // otherwise let&#39;s use DEFAULT_PLINK
      else{$plink = DEFAULT_PLINK;}
      // whatever what set, we assign it to CURRENT_PLINK, to use it wherever needed, whenever needed
      define(&quot;CURRENT_PLINK&quot;,$plink);  
      
      $rlink = &#39;&#39;;  //same here for RLINK, with the difference that :
      // we use a module name list stored in config.php to check if that 
      // if we have a module set in URL, it has to be a valid module name
      $exp_menu_type_list = explode(&quot;;&quot;, MENU_TYPE_LIST);  
      if(isset($_GET[RLINK]) &amp;amp;&amp;amp; in_array($_GET[RLINK], $exp_menu_type_list) ){$rlink = $_GET[RLINK];}  
      // otherwise, we use the default RLINK value, namely DEFAULT_RLINK 
      else{$rlink = DEFAULT_RLINK;}
      // same as for PLINK, we store what we have in a constant  
      define(&quot;CURRENT_RLINK&quot;,$rlink);  
      
      $dlang = $utils-&amp;gt;get_default_lang(); //we need to fetch the default language 
      $available_languages = LocModel::getInstance()-&amp;gt;getData_language_bar(); // we also need a list of the currently existing language in the database 
      
      // Since the available language list we&#39;ve just fetched contains more than what we need
      // we create an array in which we will store just the elements we seek 
      $available_language_codes = array();   
      foreach($available_languages as $a_l){array_push($available_language_codes, $a_l[0]);}  
      
      // Now, if a language is set in the URL, AND is a valid language (made of only 2 lowercase letters) 
      // AND can be traced in our available language codes array we&#39;ve just filled in, 
      // then it&#39;s all a valid language that we will store in $langf
      if(isset($_GET[LN]) &amp;amp;&amp;amp; preg_match(&#39;/^[a-z]{2}$/&#39;,$_GET[LN]) &amp;amp;&amp;amp; in_array($_GET[LN], $available_language_codes)){$langf = $_GET[LN];}
      // else if Google&#39;s language URL variable is set (we will have to rely on it while using the search module)
      // we use it  
      elseif(isset($_GET[&#39;language&#39;]) &amp;amp;&amp;amp; preg_match(&#39;/^[a-z]{2}$/&#39;,$_GET[&#39;language&#39;]) &amp;amp;&amp;amp; in_array($_GET[&#39;language&#39;], $available_language_codes) ){$langf = $_GET[&#39;language&#39;];}
      // else if a session language variable is set, we use it
      elseif(isset($_SESSION[&#39;C_LANG&#39;])){$langf = $_SESSION[&#39;C_LANG&#39;];}       
      // else we assign the default language we fetch a little earlier
      else{$langf = trim($dlang);}  
      // and define a CURRENT_LANG constant to assign it whatever value we have just fetched 
      define(&quot;CURRENT_LANG&quot;,$langf);
  
      // we create a language session variable, that will help in cases when a language was selected other than the default one and the user/visitor reloads the home link with no parameter, so the current language is not set back to default.
      $_SESSION[&#39;C_LANG&#39;] = $langf;
  
&lt;/code&gt;&lt;/pre&gt;
So, here we first take care off PLINK - if it wasn&#39;t set, we use the default value found in config.php (DEFAULT_PLINK), assign what we have to CURRENT_PLINK, that we create. &lt;br /&gt;
&lt;br /&gt;
We do just the same for RLINK, except we will confront it to a menu type list found in config.php under the name of MENU_TYPE_LIST and which contains all the valid module names in use on the front-end part.
If a module is set in the URL and can be found in the menu list, we then assign it to $rlink. Otherwise $rlink takes the default rlink constant value, DEFAULT_RLINK. We again create a constant to hold that value, which we name CURRENT_RLINK.&lt;br /&gt;
&lt;br /&gt;
LocModel, also Singleton, is a very useful class that contains a few 
methods that will help us manage languages effectively. We will be using it here 
to fetch all the available languages.&lt;br /&gt;
&lt;br /&gt;
We then fetch the default language and store it into $dlang and we then also fetch a list of our available languages, which we process to a new array called $available_language_codes.&lt;br /&gt;
&lt;br /&gt;
Now, if a language was set in the URL and is found valid, by format and reference to our existing language list, we do use it.&lt;br /&gt;
&lt;br /&gt;
Else, if a Google-owned language URL parameter is set (namely $_GET[&#39;language&#39;]), we will use it instead. This will happen only when using the search module (powered by Goggle CSE), while in two-column view.&lt;br /&gt;
&lt;br /&gt;
Else again, if we have a language session variable set, we make use of it, otherwise, and only otherwise, we try the default language that we have fetched a little earlier&lt;br /&gt;
&lt;br /&gt;
In any case, passed that point, we do have a valid module name, page name (empty or not), and language name, so that, we can route the proper translation files, using a try/catch bloc as follows : 
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; /* routing translations now */  
      try{  
           $ln_file = PATH_TO_LANGUAGES.&#39;/&#39;.CURRENT_LANG.&#39;/&#39;.CURRENT_LANG.&#39;.php&#39;;  
           if (file_exists($ln_file)){include ($ln_file);} // if our language file exists, we include it  
    //otherwise we throw and exception
           else {throw new Exception(&#39;&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;div class=&quot;loading_langfile_fails&quot;&amp;gt;&amp;lt;u&amp;gt;&#39; .$_GET[LN].&#39;.php &amp;lt;/u&amp;gt; cannot be found&amp;lt;/div&amp;gt;&#39;);} 
      }  
      catch(Exception $e){  
           echo $e-&amp;gt;getMessage(); //which we catch and echo here  
           exit(0);  
      }  
&lt;/code&gt;&lt;/pre&gt;
This snippet is pretty much self explanatory. If the language file we are trying to include exists, we do include it, otherwise we throw and exception containing whatever error message we need to display.&lt;br /&gt;
&lt;br /&gt;
And then we pull our current theme from the database
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; //we first define our query  
      $q_tpl = &quot;SELECT id_frontend_settings, frontend_settings_name, frontend_settings_value   
                FROM frontend_settings   
                WHERE frontend_settings_name = &#39;theme&#39;  
           &quot;;  
      MySQLModel::get_mysql_instance()-&amp;gt;executeQuery($q_tpl); //we call on our MySQLModel to execute this query  
      if($myrows = MySQLModel::get_mysql_instance()-&amp;gt;getRows($q_tpl)){ //if a result is found  
           define(&quot;CURRENT_THEME&quot;,$myrows[&#39;frontend_settings_value&#39;]); //we define a constant named CURRENT_THEME and we assign it the value the extract from the returned result  
      }  
&lt;/code&gt;&lt;/pre&gt;
We simply store our freshly-pulled theme name in the constant named CURRENT_THEME, which we will use throughout the CMS wherever necessary.&lt;br /&gt;
&lt;br /&gt;
And now time to take care off the user wanting to log out
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; $auth = AuthModel::getInstance(); we instantiate our Singleton authentification class  
      if(isset($_SESSION[&#39;c_login&#39;]) &amp;amp;&amp;amp; isset($_SESSION[&#39;c_pwd&#39;])){ if the user is logged in and wanting to log out, we invoke our let_out() method from the Authmodel class  
           if(isset($_GET[LETOUT]) &amp;amp;&amp;amp; $_GET[LETOUT] == &#39;yes&#39;){  
                $auth-&amp;gt;let_out($_SESSION[&#39;c_login&#39;],$_SESSION[&#39;c_pwd&#39;]);  
           }  
           if(preg_match(&#39;/\/out/&#39;,$_SERVER[&#39;REQUEST_URI&#39;])){  
                $auth-&amp;gt;let_out($_SESSION[&#39;c_login&#39;],$_SESSION[&#39;c_pwd&#39;]);  
           }  
      }  
&lt;/code&gt;&lt;/pre&gt;
We store an instance of the AuthModel class in the variable named $auth.
Then if the user is logged in and wanting to log out, we call the let_out() method, which will perform the logging out itself.&lt;br /&gt;
&lt;br /&gt;
&lt;hr /&gt;
&lt;br /&gt;
Then comes somehow the most important call of our index file, the call to the router&#39;s route() method.&lt;br /&gt;
&lt;br /&gt;
This is exactly where we go deeper in trying to understand the core mechanisms that will make a module load upon being requested via the URL, be it a module to handle the serving of content, a module to allow the user to sign in or a module to manage some options in the back-end, it all loads from one object only - the router object.&lt;br /&gt;
&lt;br /&gt;
In fact, back to design patterns, the very pattern that we will encounter now is called the &lt;b&gt;factory pattern&lt;/b&gt;. &lt;br /&gt;
&lt;br /&gt;
The factory pattern maybe be defined as a pattern in which the factory object creates other objects. This has a number of obvious benefits, such as the centralizing of the process of creating new objects. Therefore, if you need to change anything to the way you create and instantiate your new objects, then you only have to do so inside the factory. &lt;br /&gt;
&lt;br /&gt;
In our case, the factory is our router. Our router, based on automatic URL parsing, will programmatically instantiate the controller of the module the client/user has asked for, and set it to work it by calling its initialize() method. &lt;br /&gt;
&lt;br /&gt;
Just like with a regular factory. The client company needs a product, sends an order form, the order being our URL, the marketing people take the order form and tear down only the relevant page where the needed product is described, and handle it to the main plant people which in turn create the product and initialize it.&lt;br /&gt;
&lt;br /&gt;
The __construct() method of our router class would be the order handling department, and the route method() would be the product building plant, in which is passed, as a parameter, the registry object we have created earlier, to be passed on, as a parameter again, to the controllers while being instantiated.&lt;br /&gt;
&lt;br /&gt;
The registry works much as a stocking unit or a phone directory if you prefer, in the sense that you can store objects in it, to be later pulled out whenever required. How is that done? by using magic set() and get() methods, that will be triggered internally every time we will try to write/read to/from inaccessible properties, instead of having PHP generate an error. 
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      // application/Registry.php  
      class Registry{   

           private $vars = array(); //we declare the vars array as private  

           public function __set($index, $value){ we declare the magic __set() method, triggered when trying to write inaccessable properties  
                $this-&amp;gt;vars[$index] = $value; // we store in the array the index associated with its value  
           }  

           public function __get($index){ we declare the magic __get() method, triggered when trying to access inaccessable properties  
                return $this-&amp;gt;vars[$index]; //we return the value of a given index contained in the array  
           }  
      }  
      ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
In our __set() method, which is run when writing data to inaccessible properties and which accepts an index/value pair, we simply store the index in the private array named $vars, which is the stocking unit or phone directory really, and we store its value altogether.&lt;br /&gt;
&lt;br /&gt;
In the __get() method, which is run when accessing data to from inaccessible properties, we simply return the value of a given array vars index by passing it as a parameter. &lt;br /&gt;
&lt;br /&gt;
But back to our Router class
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
 class Router{
  
      private $path, $controller, $action;  //declaring the private properties we need

      public function __construct(){  //initializing our router object for when instantiated

           $exp_menu_type_list = explode(&quot;;&quot;, MENU_TYPE_LIST);  //we fetch and explode our menu type list again, we will need it here too

           if(isset($_GET[RLINK])){  //if a module name is set in the URL

                if(     CLEAN_URLS == true   // if clean URLs are on and google search is active and in two-column view mode
                     &amp;amp;&amp;amp; GOOGLE_CSE_PAGING_TYPE == &#39;two_column&#39;        
                     &amp;amp;&amp;amp; preg_match(&#39;/cx=(.*)&amp;amp;gs=(.*)&amp;amp;sa.x=(.*)&amp;amp;sa.y=(.*)/&#39;, $_SERVER[&#39;REQUEST_URI&#39;]))  
                {$request = &#39;search&#39;;}  // we set our current module name as search, wich we store in the $request variable
                else{  //otherwise
   // if the set module name can be traced in our existing module type list
                     if(in_array($_GET[RLINK], $exp_menu_type_list)){$request = $_GET[RLINK];}  // we assign that name to $request
                     else{ 
                          // otherwise we use the DEFAULT_RLINK constant value  
                          $request = DEFAULT_RLINK;  
                     }  
                }       
           }  
           else{$request = DEFAULT_RLINK;} //// otherwise we use the DEFAULT_RLINK constant value 

           $this-&amp;gt;controller = !empty($request) ? ucfirst($request) : &#39;Index&#39;;  // if $request is not empty, we assign its value (with the first letter made uppercase) to the controller property, otherwise, we assign it the value &#39;Index&#39;
           $this-&amp;gt;action = &#39;initialize&#39;;  // we set the action property to &#39;initialize&#39;
      }  

      public function route($registry){ // our factory&#39;s building plant  

                require_once(&#39;application/BaseController.php&#39;); we include our abstract controller class from which every created controller will extend  

                $file = &#39;application/controllers/&#39; . $this-&amp;gt;controller . &#39;Controller.php&#39;; // we build the full controller&#39;s class path using the retrieve URL module variable retrieved in the __construct() method  

                if(is_readable($file)){ // if the controller class is readable,  
                     include $file; //we do include it  
                     $class = $this-&amp;gt;controller . &#39;Controller&#39;; //and set the $class variable  
                }  
                else{  
                     include &#39;application/controllers/Error404Controller.php&#39;; //otherwise we include the 404error class  
                     $class = &#39;Error404Controller&#39;; //and set the $class to that  
                }  

                $controller = new $class($registry); //whatever the included controller, we instantiate it, creating the $controller object, passing our $registry object as parameter, itself containing our template object.  

                if (is_callable(array($controller, $this-&amp;gt;action))){ //if within the newly created $controller object there&#39;s an initialize() method (demanded by the baseController) that&#39;s callable   
                     $action = $this-&amp;gt;action;  
                }  

                $controller-&amp;gt;$action(); we do invoke that initialize() method we have called above 
      }  
 }  
&lt;/code&gt;&lt;/pre&gt;
We first need to pay attention to its __constructor() method, in which we retrieve our URL module variable ($_GET[RLINK]) (the page we tore down from the order form), assign it, after checking it corresponds to a valid module name, to the $controller property, otherwise we assign it the value &#39;Index&#39;. If google CSE is active in two-column view mode and with clean URLs on, we assign &#39;search&#39; to the $controller property.&lt;br /&gt;
&lt;br /&gt;
Now onto the route() method, we bring our last core class, the &lt;b&gt;base&lt;/b&gt; class, by including an abstract baseController class inside the router. The soon-to-be instantiated controller class will extend that BaseController class, so that it will inherit from its protected registry property, however, we as programmers will be free to decide how we want to implement the initialize() method that baseController is imposing on us. Note that as an abstract class, BaseController cannot be instantiated.
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      // application/BaseController.php  
      abstract class BaseController{  
           protected $registry; //holds a protected reference of the registry  
           function __construct($registry){  
                $this-&amp;gt;registry = $registry; //sets the registry  
           }  
           abstract function initialize(); //forces each sub-class to have an initialize() method   
      }  
      ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
But back to the route() method, if the router then finds our controller file to be readable, it includes it, otherwise it includes the Error404Controller class.&lt;br /&gt;
&lt;br /&gt;
Whatever it has included, it instantiates. So now we have a controller object ready.&lt;br /&gt;
&lt;br /&gt;
Further on, if that controller object&#39;s initialize() method is found callable, our router will call it. The initialize() method is where we will define the actions we want each controller to execute for itself and have it write some properties to the registry&#39;s template object to be passed directly to the corresponding view, in order to be used there. Within that method, we will also have each controller call the assign_theme() method of the registry&#39;s template object it holds.&lt;br /&gt;
&lt;br /&gt;
This is how our user input is being routed. But then, we have to find and instantiate the model that corresponds to each created controller too. Because the models are the essential component that will provide our application with the business logic and data it needs. So, this is where the magic __autoload()   function comes handy
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; function __autoload($class_name){   
           //The $class_name parameter is internal to PHP, so we don&#39;t have to set it ourselves.   
           //In fact, this will be set for us when the controller will first call the model it needs but the model will still not be included though.   
           //That will trigger the __autoload() function, in which we specify where to look to find the needed model and include it.  
           try{  
                $filename = $class_name.&#39;.php&#39;;  
                $file = &#39;application/models/&#39; . $filename; //but we make sure the __autoload() function performs its search within our model directory only  
                if (file_exists($file)){include ($file);} // if the class can be found, where we have told the __autoload() function to look for, it includes it  
                else { //otherswise throws and expception  
                          throw new Exception(&#39;&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;div class=&quot;loading_model_fails&quot;&amp;gt;&#39;.$GLOBALS[&#39;t_the_model_named&#39;].&#39; &amp;lt;u&amp;gt;&#39; . $class_name . &#39;&amp;lt;/u&amp;gt; &#39;.$GLOBALS[&#39;t_couldnt_be_found&#39;].&#39;&amp;lt;/div&amp;gt;&#39;);   
                          // note the use of globals for handling the translations, we will come back to that in the next settlements  
                }  
           }  
           catch(Exception $e){  
                echo $e-&amp;gt;getMessage();  
                exit(0);  
           }  
      }  
&lt;/code&gt;&lt;/pre&gt;
Did I say magic? Yes, it is a magic function, just as __set() and __get() are too. It will be called each time a called model class will not be recognized. So, it really is magic because, we do not know in advance which controller is going to be triggered. So, instead of having to include all our model classes, we only need to let PHP do the work of &#39;not recognizing&#39; the model class the then-working controller object is trying to instantiate, call and instantiate it for us. Awesome, isn&#39;t it?&lt;br /&gt;
&lt;br /&gt;
But what of the view then? As we said earlier, the view files, which are in fact basic php files acting as templates, will be included by the template object&#39;s assign_theme() method, itself called in the controller&#39;s initialize() method.
&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
      // application/Template.php  
      class Template{  
           private $vars = array(); //declaring the array that will store the variables to be passed to the view  
           public function __set($index, $value){  
                     //magically assigning index/pairs to that array  
               $this-&amp;gt;vars[$index] = $value;   
            }  
            public function assign_theme(){   
                //the method the controller calls once it has finished   
                //interacting with the model and wants to display the view  
                try{  
                          //We loop thru the vars array containing the variables the __set() method above has stored  
                                    foreach ($this-&amp;gt;vars as $key =&amp;gt; $value){  
                                         $$key = $value;  
                                         //we turn each key name to a variable using the $$ syntax, assigning it the same value it already held before entering the loop.  
                                    }  
                          $common_template_files = array(&#39;header&#39;,&#39;main&#39;,&#39;footer&#39;); // in reality, we do not display only one php view file per/module,   
                          //as for obvious abstraction or visual design reasons, we choose to arbitrarily include the header, main and footer php view file,   
                          //and we will include [module_name].php view file only from within the main view file,   
                          //if the module_name variable has been sent by the controller  
                          foreach($common_template_files as $ctf){ //for each common tpl files  
                               $file = PATH_TO_THEMES.&#39;/&#39;.CURRENT_THEME.&#39;/&#39;.$ctf.&#39;.php&#39;;  
                               //we build its full path  
                               if (!file_exists($file)){ //if the common tpl file does not exist  
                                    throw new Exception(&#39;&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;div class=&quot;loading_tpl_fails&quot;&amp;gt;&amp;lt;u&amp;gt;&#39;.$ctf.&#39;&amp;lt;/u&amp;gt; &#39;.$GLOBALS[&#39;t_couldnt_be_found&#39;].&#39;&amp;lt;/div&amp;gt;&#39;);  
                                    // we throw an exception  
                               }       
                               else{  
                                    include($file); //but if it does exist, we include it  
                               }  
                          }  
                     }  
                     catch(Exception $e){  
                          echo $e-&amp;gt;getMessage();  
                          exit(0);  
                     }  
                }  
           }  
      ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
Note from the code above that we don&#39;t actually display only one php view file per/module, as for obvious abstraction or visual design reasons, we choose to arbitrarily include the header, main and footer view file instead and we will include the [module_name].php view file only from within the main view file called main.php, if the module_name variable has been set and sent by the controller.&lt;br /&gt;
&lt;br /&gt;
So here, in essence, we already have a full-blown MVC application in our hands. A router (factory) that loads the needed controller based on user input, a magically loaded model to provide, through its own set of properties and methods, for all the logic and data our application requires and a view file, fed by the template engine, itself containing a magic set() method and being contained in the registry object that was passed on to the controller as a parameter while being instantiated and that includes, at the controller&#39;s request, all the views needed for display, plus, that also stores, by means of set() methods, and displays all the data the controller wishes to handle to the view.&lt;br /&gt;
&lt;br /&gt;
In the next settlement, we will set the focus on the controller, the model and the view, on how they are built, work together and we will come full circle by creating our first complete module, the page module. Do not miss it!&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;This &lt;a href=&quot;http://www.rolandc.net/2014/10/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-2.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt; first appeared on Tuesday the 28th of October 2014 on &lt;a href=&quot;http://rolandc.net/&quot;&gt;RolandC.net&lt;/a&gt;.&lt;/span&gt;</description><link>http://www.rolandc.net/2014/10/how-to-build-your-own-multilingual-php-mvc-cms-from-scratch-part-2.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPP3d2n85vih37rogpCmzyVjUdxKRQKpdiJ2yUsp84RPdhJBwMnmkE20aeHaciAUuL3BNTUvQQeuZGbaGp4Gu25NmOOuvuBZE_me-O5YHHp6U6pZBgO0rtdlqLgTGIjVFPsebaG7aBf9p_/s72-c/gumbo_logo_dark.png" height="72" width="72"/><thr:total>6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-7154075426239993274</guid><pubDate>Thu, 23 Oct 2014 03:33:00 +0000</pubDate><atom:updated>2014-10-29T08:35:20.717+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cms</category><category domain="http://www.blogger.com/atom/ns#">gumbo-cms</category><category domain="http://www.blogger.com/atom/ns#">mvc</category><category domain="http://www.blogger.com/atom/ns#">opensource</category><category domain="http://www.blogger.com/atom/ns#">php</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">tutorial</category><category domain="http://www.blogger.com/atom/ns#">webdev</category><title>How to build your own Multilingual PHP MVC CMS from scratch - Part 1 - Introduction</title><description>While
 the MVC architecture has been around for quite a long time, it somehow 
took longer for PHP than for other languages like C++ to come to embrace
 it, allowing it to serve as solid ground in a growing number of popular
 CMS&#39;s and Frameworks. But even if nowadays the number of Web developers
 that can truly claim they have never heard of CMS&#39;s like Drupal or 
OpenCart is becoming increasingly seldom, you, as a programmer, may have
 often been wondering, as I once have too, just how does the CMS your 
Project Manager has assigned you on really works, so you could start 
making the most out of it and stop having to loose half your evenings, 
week-ends and personal life just to barely keep up with or learn it, let
 alone the personal stigma that not getting along too well with a custom
 in-house CMS may at times create in a developer&#39;s life.&lt;br /&gt;
Since the best way to understand how a device works is by creating 
one yourself, in this beginning twelve-part series, I will be teaching 
you how to build a complete and working lightweight Multilingual MVC 
PHP5 CMS from scratch, that you will be able to use for your own 
purposes or that will at least leave you with a better insight on how 
those things really work, get you more pro-efficient. And we will only 
be using free software tools throughout the whole course of these 
series. &lt;br /&gt;
But during this very first part, and once I have discussed the 
minimum knowledge required to attend these tutorials, i will be defining
 what the acronyms CMS and MVC stand for, then I will be reviewing the 
reasons as to why building your own CMS should only be an exciting, 
rewarding and beneficial experience. I will also be listing the software
 you will need and finally, I will expose and explain the basic folder 
structure around which we will be developing our new project. &lt;br /&gt;
&lt;h3 id=&quot;target-audience&quot;&gt;
Target audience&lt;/h3&gt;
Although being a senior PHP developer is definitely not mandatory 
here (especially that after all, we all have to learn things before we 
can claim we know them), you should at least be confident with the use 
of classes and of some of the most common  practices/algorithms in 
PHP/Web development, so that, you should not be a total beginner. This 
however does not mean beginners are not welcome, on the contrary they 
are, as beginners, always encouraged to read code, especially code they 
cannot figure out, for their own benefit.&lt;br /&gt;
You should have a fair 
understanding of what Object Oriented Programming means, and ideally of 
software design patterns, but this is not essential for now, as it will 
be discussed later in the series. You should also have a strong interest
 and the time required to build a complete CMS from scratch of course.&lt;br /&gt;
&lt;h3 id=&quot;so-what-does-cms-stand-for-and-what-is-it-used-for-anyway-&quot;&gt;
&lt;b&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;So, what does CMS stand for and what is it used for anyway?&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
For you to properly understand what a CMS is, I will first define 
what a Framework is and what the difference between the two actually is.
 In Web development, a Framework, hear a Web Framework or Web 
Application Framework, is a collection of files and folders (software) 
arranged according a specific structure or architecture and in such a 
way as to allow, to a certain degree of abstraction, for the quick/rapid
 development of Web applications (most commonly Websites) and which aim 
is basically to reduce the overhead traditionally associated with Web 
development. In other words, a Web Framework can mostly be seen as some 
sort of abstract software that would allow someone building a complete 
full-fledged site ready for production and in the least possible 
development time. For the sake of this tutorial, this is the definition 
we are going to stick with for now. &lt;br /&gt;
&lt;h3 id=&quot;how-does-a-cms-compare-to-a-web-framework-then-&quot;&gt;
How does a CMS compare to a Web Framework then?&lt;/h3&gt;
A Content Management System, or Web CMS in this context, simply put, 
is a Web application (most commonly a Website), that could have been 
built on top of a Web Framework, or not, but that is designed and 
optimized to help manage content easily and that ideally provides a 
certain level of customization capability, as, a CMS like for example 
Drupal, should be manageable/customizable by a non programmer, eg: the 
admin of the site can add/edit new articles, change theme etc., without 
having to know about its underlying structure. So a CMS, in essence, 
offers an extra layer to what a Framework usually provides, and can be 
viewed as a ready-made Website/Web application that can be deployed as 
is. Now for some more advanced customization, programming knowledge may 
be required.&lt;br /&gt;
So, while CakePHP is a Framework (by the definition we have used at 
the beginning) that could well serve as the basis for developing your 
own CMS, that is, depending on what your needs as a developer are 
anyway, it does not, by essence, offer out-of-the-box content-management
 oriented functionality like Drupal for example does and can only be 
interacted with by a programmer.&lt;br /&gt;
Now, it tends to get complicated when you know Drupal is called 
either a CMS, a Framework or Web Framework or even Web Application 
Framework (WAF), when you know that CakePHP is a rapid development 
Framework(as explained above) but that the company&#39;s built-in CMS you 
may be struggling with all day and that was created out of mostly 
popular CMS&#39;s parts is called a Framework too by everyone in the company
 you work for. &lt;br /&gt;
Frankly, most people&#39;s definition of a Framework/CMS nowadays is that
 it&#39;s some software/script that&#39;s to some level of abstraction is meant 
to ease/speed up the process of developing/creating a Web site or Web 
project, and it&#39;s still true in its own right. &lt;br /&gt;
Finally, popular CMS provide many functionality you don&#39;t necessarily
 need or use, and that are likely to increase the risk of bugs, 
vulnerabilities and so forth. While with a more abstract Framework like 
CakePHP, you do not have to build feature you don&#39;t need.&lt;br /&gt;
&lt;h3 id=&quot;and-what-about-mvc-in-all-that-&quot;&gt;
And what about MVC in all that?&lt;/h3&gt;
MVC, which has been around for some time with languages other than 
PHP, and which stands for Model View Controller, is a type of software 
architecture that is designed to clearly separate an application(a Web 
application here) into three distinct parts: the control, the model and 
the view. 
The way we will implement it in our project, basically, the controller 
receives an input (via the index file) from the mouse or the keyboard 
for example, addresses the corresponding model in order to get the 
needed data and/or to process it according to the business logic 
contained in the given model, which then sends it back to the 
controller. Then the controller transmits that data to the view, which 
is responsible for managing visual display on the application, thus, how
 the site or app looks like. 
In other words, the controller will act as an interface between the 
model and the view, allowing for a clear logical separation between 
those processes, and most evidently for a cleaner PHP code, not being 
obfuscated by HTML or inline CSS snippets.
Now, as the MVC pattern itself however does not define how the data 
should be passed between entities, it is therefore possible to find Web 
applications that use this architecture in a different way, having for 
example the model updating the view. &lt;br /&gt;
Regardless of how it is being used, such an architecture represents 
an evolved way to design an application and differs from the three-tier 
organization model, somehow very close in essence but where applications
 are structured around three layers: the client(presentation), the 
application(logic), and data access(database). That said, MVC is meant 
to make both the programmers and designers life easier, as, the designer
 can concentrate on working on the visual aspect of the project, 
generally through template files, while the programmer takes care of the
 business logic, eg: PHP code found in models. The controller of a 
PHP-driven CMS contains PHP but does not hold, or should not hold any 
element of your business logic.&lt;br /&gt;
Now that we have explored the main concepts around which we will be 
developing our application, a little word again on what we will exactly 
be building during these twelve-part series. We are going to build a 
simple lightweight Multilingual CMS that follows the MVC pattern and 
that will act as finished Website, ready to be deployed and administered
 by non-programmers. Customizing it for various other small-scale 
projects should only require little programming knowledge, if any at 
all. It should be perfect for a small-scale organization.  &lt;br /&gt;
&lt;h3 id=&quot;fine-but-why-building-your-own-cms-anyway-&quot;&gt;
Fine, but why building your own CMS anyway?&lt;/h3&gt;
Let&#39;s face it, creating your own CMS does require a lot of spare time, BUT &lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;as a hobbyist, it is a great way to learn about the inner workings 
of CMS&#39;s and to learn about the MVC structure, which is growing 
enormously in popularity&lt;/li&gt;
&lt;li&gt;as a PHP professional working inside a company, it will help you 
better cope with the company&#39;s custom-built CMS you already work on 
daily, if you do.&lt;/li&gt;
&lt;li&gt;as a PHP professional working independently and looking to get 
things further, it will help you create the custom CMS you perhaps have 
always dreamed of, tailored to almost instantly meet your most recurrent
 client prerequisites, but best of all, that you will fully know and 
understand, thus allowing you to cut down on development time 
dramatically, leaving you more time to focus on other aspects of your 
activity.&lt;/li&gt;
&lt;li&gt;There are lots of great free popular CMS&#39;s on the market, that some 
developers even specialize in customizing, but for the others, wasting 
hours looking to solve problems that you can&#39;t put your finger on just 
because you haven&#39;t coded it yourself and there is simply too much data 
to deal with - is never that fun, and gives you one more excellent 
reason to give it a go and see for yourself how much you could benefit 
from building your own CMS build from scratch.&lt;/li&gt;
&lt;/ul&gt;
Now, before we start getting our hands all greased up, let&#39;s review the tools you will need to to be working with:  &lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Your favorite programming editor. &lt;/li&gt;
&lt;li&gt;PHP 5 (with GD and jpg support enabled) - version 5.4 to 5.6 should be fine.&lt;/li&gt;
&lt;li&gt;Apache HTTPd server 2.4, version 2.2 should be fine too. &lt;/li&gt;
&lt;li&gt;MySQL Community Servers version 5.5 or 5.6 are good choices for our project.  &lt;/li&gt;
&lt;li&gt;An image editing program: I will be using the Gimp 2.8, but you could still be using Photoshop or alike if you prefer. &lt;/li&gt;
&lt;li&gt;Optional (although strongly recommended) :   &lt;ul&gt;
&lt;li&gt;A debugger like XDebug and a tracer such as Valgrind. &lt;/li&gt;
&lt;li&gt;Composer to help you manage our plugins (PHPMailer/TinyMCE) dynamically. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;basic-overview-of-our-project-s-folder-structure&quot;&gt;
Basic overview of our project&#39;s folder structure&lt;/h3&gt;
So let&#39;s take a brief look at what our overall folder structure is going to look like&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFJYSZuqACzHmzHjc4EzGwHw25pAaBkX-eL4YK2QGFvWnXaPHpr8paJBOnqIPWhvbTEKbhZcJfK02YVnz3KgWaqWX-5fZc9HGIku8VhGaW0Eul7xHUMmJrGpAJ-AjpWRNAsMgOyVdQNTv-/s1600/cms_folder_structure.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;rolandc.net&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFJYSZuqACzHmzHjc4EzGwHw25pAaBkX-eL4YK2QGFvWnXaPHpr8paJBOnqIPWhvbTEKbhZcJfK02YVnz3KgWaqWX-5fZc9HGIku8VhGaW0Eul7xHUMmJrGpAJ-AjpWRNAsMgOyVdQNTv-/s1600/cms_folder_structure.png&quot; height=&quot;154&quot; title=&quot;General folder structure&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;Our project&#39;s general folder structure&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;From the image above, you can see the admin section has its own standalone folder. &lt;/li&gt;
&lt;li&gt;Config.php will be our config file for the whole project, and serve 
for the admin as well. Some settings do rely on the database but most of
 the configuration will be found in config.php though, including 
database access parameters.  &lt;/li&gt;
&lt;li&gt;The /application folder, as it exists in /admin too, contains our 
controller and model files. It is the core part of our application, thus
 the name application. It also contains our base controller, registry, 
router and template engine, which we will review in the next part of our
 tutorial, while studying the index.php file. &lt;/li&gt;
&lt;li&gt;The /languages folder will hold the needed translation files, 
country flags and everything needed to manage encoding as well as 
Javascript translation strings, all on a per-language basis. &lt;/li&gt;
&lt;li&gt;In /vendors we will simply store the plugin files needed for our 
project. In fact, composer will store them for us, as we will only have 
to pull new versions of our plugins from the command line, whenever 
needed. The plugins we will use are TinyMCE 4 (Wysiwyg editor) and 
PHPMailer 5.2.6. &lt;/li&gt;
&lt;li&gt;The /public folder will contain our Javascript files.&lt;/li&gt;
&lt;li&gt;In /themes, we will store all the CSS files, template files and site
 images(site&#39;s visual identity) pertaining to each existing theme.&lt;/li&gt;
&lt;li&gt;At root level, we also find the index.php file, which actually is, 
or should be, the one and only entry point to our application, as all 
user requests should reach the index first, or be redirected to it, no 
matter what. &lt;/li&gt;
&lt;li&gt;about.php, provided you launch it manually, will check your server 
configuration, and more specifically, it will check your 
Apache/PHP/MySQL versions, as well as your GD library version, and if 
you happen to have support for JPEG (used for the captcha image in the 
registration form).&lt;/li&gt;
&lt;li&gt;The /list folder will contain lists like country or language lists and the tools to dynamically manage them.&lt;/li&gt;
&lt;li&gt;The files named composer.json and composer.lock are inherent to 
Composer. They contain data necessary to manage our plugins. Again, we 
will only have to use the command line to pull in the updated plugin 
files whenever available.&lt;/li&gt;
&lt;li&gt;The second multi-level screen capture stops at the beginning of the
 vendor folder because there only remains the content structure of our 
plugins, which will will not take interest in.&lt;/li&gt;
&lt;/ul&gt;
To help you understand the structure better, here is a multi-level folder representation of the same project, without the files.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh39NW5rWLLYuI7mYaWQjqoTIAASYZbBzS2RYNUFLClSLqgjTcyPjY2_Er5WaDC5BCqHPchrKXP3D_j-WJYGBUbVeRXKXgNFXCV28mlb5H67p7Ga9IV8C68o9HxG3MM0mBso2zI2_-mPn5Q/s1600/cms_multilevel_folder_structure_1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;1.2 Multi-level view 1/2 - rolandc.net&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh39NW5rWLLYuI7mYaWQjqoTIAASYZbBzS2RYNUFLClSLqgjTcyPjY2_Er5WaDC5BCqHPchrKXP3D_j-WJYGBUbVeRXKXgNFXCV28mlb5H67p7Ga9IV8C68o9HxG3MM0mBso2zI2_-mPn5Q/s1600/cms_multilevel_folder_structure_1.png&quot; height=&quot;264&quot; title=&quot;1.2 Multi-level view 1/2&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;Multi-level view 1/2&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWQx5W_1x443_t1aXXKnzoKP5RBWy2GqoFyDAdeO5k9REEOED3LSBwajvNTghPCqEPUrHs8F10c87S5G8PQRZKW6Aep3Lzy3JsbeqR71NkY_m4HK58M9shK2t4mWi7RSYnnHPDzvm0TaqJ/s1600/cms_multilevel_folder_structure_2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;Multi-level view 2/2 - rolandc.net&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWQx5W_1x443_t1aXXKnzoKP5RBWy2GqoFyDAdeO5k9REEOED3LSBwajvNTghPCqEPUrHs8F10c87S5G8PQRZKW6Aep3Lzy3JsbeqR71NkY_m4HK58M9shK2t4mWi7RSYnnHPDzvm0TaqJ/s1600/cms_multilevel_folder_structure_2.png&quot; height=&quot;264&quot; title=&quot;Multi-level view 2/2&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;Multi-level view 2/2&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
As you can clearly see, the files and folders that are inside the 
/admin folder repeat almost exactly the same folder structure as we have
 just reviewed. The admin in itself will be like some sort of modified 
front-end within the front end.&lt;br /&gt;
So far, we have discussed who the minimum knowledge requirements for 
this tutorial, defined what the acronyms CMS and MVC mean and explained 
what differentiates a Web CMS from a Web Framework. We have also 
reviewed the reasons as to why choosing to build your own MVC CMS can be
 a very exciting and rewarding decision to make for yourself, before 
enumerating the tools we need to get started, and ultimately engaged in 
reviewing the core folder structure of our project.
In the next chapter, I will be focusing on what I have called earlier 
the one and only entry point to our application, that is the index.php 
file, slowly evolving toward the /application folder, which represents 
the core system of our CMS, containing all the controller/model classes 
that will empower the MVC structure in our project.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;This &lt;a href=&quot;http://www.rolandc.net/2014/10/how-to-build-your-own-multilingual-mvc-cms-from-scratch-part-1-introduction.html&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt; first appeared on Thursday the 23rd of October 2014 on &lt;a href=&quot;http://rolandc.net/&quot;&gt;RolandC.net&lt;/a&gt;. &lt;/span&gt;</description><link>http://www.rolandc.net/2014/10/how-to-build-your-own-multilingual-mvc-cms-from-scratch-part-1-introduction.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFJYSZuqACzHmzHjc4EzGwHw25pAaBkX-eL4YK2QGFvWnXaPHpr8paJBOnqIPWhvbTEKbhZcJfK02YVnz3KgWaqWX-5fZc9HGIku8VhGaW0Eul7xHUMmJrGpAJ-AjpWRNAsMgOyVdQNTv-/s72-c/cms_folder_structure.png" height="72" width="72"/><thr:total>8</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-7904377776957233725</guid><pubDate>Wed, 22 Oct 2014 06:06:00 +0000</pubDate><atom:updated>2014-10-28T13:55:00.152+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">fedora</category><category domain="http://www.blogger.com/atom/ns#">fedora 19</category><category domain="http://www.blogger.com/atom/ns#">gnome</category><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">linux essential</category><category domain="http://www.blogger.com/atom/ns#">linux must-have</category><category domain="http://www.blogger.com/atom/ns#">technology</category><title>Linux must-have essential software 2014</title><description>Whether you&amp;#39;re being new to the world of Linux or not, at one point or another, you&amp;#39;re going to periodically re-question the software that&amp;#39;s already installed on your station.&lt;br&gt;
As a beginner user, you will be haunted by the quest of ever trying to find new alternatives or best, to find copycats of the programs you used to have on... you know. But once you have gained significant experience, you will love digging the net for even more of that great software you already have and use, that is, not just to kill time though.&lt;br&gt;
So here is my 2014 list of all Linux software I believe all of us (or almost) should have, hoping this will help you achieve even more and meet greater challenges through your PC, at least complement your current installation.&lt;br&gt;
&lt;u&gt;&lt;/u&gt;&lt;br&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaWt-BTxFnraVK7I-7p4Rxo9_dMkET0GDjVsOZcgS4h-2GJ5N-yxIf345f0IUb62fjTOnPdYm6QQXL7uSt6Bd3dN6xB5nEo81OCyRlCPISpjmNsvrEordHcfYhm7bVCefdU2MKfDNr5BuV/s1600/linux_must_have_essential_software_2014.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaWt-BTxFnraVK7I-7p4Rxo9_dMkET0GDjVsOZcgS4h-2GJ5N-yxIf345f0IUb62fjTOnPdYm6QQXL7uSt6Bd3dN6xB5nEo81OCyRlCPISpjmNsvrEordHcfYhm7bVCefdU2MKfDNr5BuV/s1600/linux_must_have_essential_software_2014.png&quot;&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;Tux trying to figure out the software he needs on RolandC.net&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;u&gt;&lt;/u&gt;&lt;br&gt;
&lt;u&gt;&lt;/u&gt;&lt;br&gt;
&lt;a href=&quot;http://www.rolandc.net/2014/10/linux-must-have-essential-software-2014.html#more&quot;&gt;More »&lt;/a&gt;</description><link>http://www.rolandc.net/2014/10/linux-must-have-essential-software-2014.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaWt-BTxFnraVK7I-7p4Rxo9_dMkET0GDjVsOZcgS4h-2GJ5N-yxIf345f0IUb62fjTOnPdYm6QQXL7uSt6Bd3dN6xB5nEo81OCyRlCPISpjmNsvrEordHcfYhm7bVCefdU2MKfDNr5BuV/s72-c/linux_must_have_essential_software_2014.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-3025203995947445194</guid><pubDate>Wed, 06 Aug 2014 08:38:00 +0000</pubDate><atom:updated>2014-10-28T17:23:25.848+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">deruny</category><category domain="http://www.blogger.com/atom/ns#">derunys</category><category domain="http://www.blogger.com/atom/ns#">dranyky</category><category domain="http://www.blogger.com/atom/ns#">food</category><category domain="http://www.blogger.com/atom/ns#">international cuisine</category><category domain="http://www.blogger.com/atom/ns#">recipe</category><category domain="http://www.blogger.com/atom/ns#">ukrainian cuisine</category><category domain="http://www.blogger.com/atom/ns#">ukrainian food</category><category domain="http://www.blogger.com/atom/ns#">ukrainian recipe</category><title>How to prepare Ukrainian Derunys</title><description>So you have been watching the news lately, saw every bit of that Ukrainian crisis and started wondering how the Ukrainian Cuisine tasted like? Well here it is, today I&#39;m going to show you how to prepare Ukrainian derunys, genuine Ukrainian Derunys and I am going to show you just how easy (and cheap) it is to make them.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj86HD3cy33-NC0nfwMx_v-YQeOOH9nbDuGNhgt5kCYENMzpviDqbaA5vJHI60hjkZSjnwPRDR3JBqhUjDR-Nz2aiyXcwKyXehFdsdQwyH-5QDvX0jzxC6DR7pA1KkgmN5qXGLlyva1KFSX/s1600/PICT0048.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj86HD3cy33-NC0nfwMx_v-YQeOOH9nbDuGNhgt5kCYENMzpviDqbaA5vJHI60hjkZSjnwPRDR3JBqhUjDR-Nz2aiyXcwKyXehFdsdQwyH-5QDvX0jzxC6DR7pA1KkgmN5qXGLlyva1KFSX/s1600/PICT0048.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;A plate of freshly prepared Ukrainian Derunys&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Deruny(s), in Ukrainian (called Dranyky in Russian), are delicious fried shred-potato doughnuts that are &lt;b&gt;extremely easy to prepare&lt;/b&gt;. They are eaten daily throughout the whole Ukrainian territory, and not only. Somehow, they are like Hash browns, the main difference being that, once the potatoes are shred, you do not need to drain their water. They are extremely similar to the Czech Bramboraky, the word bramborak itself meaning potato in Czech language.&lt;br /&gt;
&lt;br /&gt;
Derunys taste delicious and not only they can be had as part of a breakfast or brunch (just like pancakes with eggs etc.), they can also be served either as a starter or main course meal, with mushroom sauce on it, or with a salad on the side. &lt;br /&gt;
&lt;br /&gt;
Well, no more waiting, let&#39;s jump right into this awesome recipe!&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;The ingredients you will need :&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
- 4 small/medium potatoes&lt;br /&gt;
- 1 egg&lt;br /&gt;
- 1 tbsp all-purpose flour&lt;br /&gt;
- a fair pinch of salt&lt;br /&gt;
- 3 tbsp of oil&lt;br /&gt;
- a tiny pinch of bicarbonate (1/10 teaspoon)&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Optional : a hearty tablespoon of &lt;a href=&quot;http://en.wikipedia.org/wiki/Smetana_%28dairy_product%29&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;smetana&lt;/a&gt; (sour cream or &#39;creme fraiche&#39;)&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;How to prepare derunys:&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
- Start by peeling the potatoes and shred them using the thinest side of your shredder. (You may also shred them using the medium side, this will feel easier for the hand, but the result will be different and it also will take longer to cook)&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgna8cP3LKLTRh9936jLaWjWugEl_4jKet9vD3o_Vxgd2vDVLcZCnUS_vQlHYxR-8qDsG5w-MhbUMK4istU1ovT3AakkuhxzKAWGwXdmsftnQABLhETcA_R4azOp3hVmfGd9_7LkVJrAEut/s1600/PICT0004.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgna8cP3LKLTRh9936jLaWjWugEl_4jKet9vD3o_Vxgd2vDVLcZCnUS_vQlHYxR-8qDsG5w-MhbUMK4istU1ovT3AakkuhxzKAWGwXdmsftnQABLhETcA_R4azOp3hVmfGd9_7LkVJrAEut/s1600/PICT0004.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Freshly shredded potatoes&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
- Once the potatoes are shredded, do not wait, add the salt. This is what will make water from the potato shreds come out. Now, break the egg and add it.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo56JZ1hmJQNYB7UEMfr00QJ54LTasSPnw1XaDMusRP8daT3sr0aknXOJdIHhtHE3iJGMxYB3eLwuvDBL2Z1xcfOXZXQMt8eRmGehvR6yETpLUr8cdEIFPU3nb5Vd4OrVFABYkcCn3Ybzm/s1600/PICT0011.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo56JZ1hmJQNYB7UEMfr00QJ54LTasSPnw1XaDMusRP8daT3sr0aknXOJdIHhtHE3iJGMxYB3eLwuvDBL2Z1xcfOXZXQMt8eRmGehvR6yETpLUr8cdEIFPU3nb5Vd4OrVFABYkcCn3Ybzm/s1600/PICT0011.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Potato shreds with salt and egg waiting to be stirred&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
- Stir for less than a minute using a table spoon, but your dough should look homogenous enough already.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsO2yXR03aJJ7Vz0jpFytgCvjFe3omsRRvAbukgD9zDrr7zAdPJQUMRUDJh8JHHFFAdTQkTu8LQmGH-OQGO4oFpDO2IJIyWBkAfFyhV1J1klk7j9zRtWDjEMDo7db5nRJGr9ichRriAvd1/s1600/PICT0014.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsO2yXR03aJJ7Vz0jpFytgCvjFe3omsRRvAbukgD9zDrr7zAdPJQUMRUDJh8JHHFFAdTQkTu8LQmGH-OQGO4oFpDO2IJIyWBkAfFyhV1J1klk7j9zRtWDjEMDo7db5nRJGr9ichRriAvd1/s1600/PICT0014.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Deruny dough before adding the flour&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
- Add the flour to solidify the mixture and stir for a few seconds again. Now you want to be careful not to add too much flour, not to little either. As you add too much, your derunys will end up tasting like hockey pucks, and if you add to little, they will become too sloppy too. One fair tablespoon for 4 small/medium potatoes is probably a good rule to stick by in this case.&amp;nbsp; In any case, the resulting mixture should look thicker than crepe dough.&lt;br /&gt;
&lt;br /&gt;
- Put a pan/skillet on the stove, medium/high heat, with 3 hearty tablespoon of oil in it. &lt;br /&gt;
&lt;br /&gt;
- Add the bicarbonate to the mixture. Be careful adding only a &#39;tiny&#39; pinch of it. Using the tip of a knife to pick it up from its package is probably the best way to go here. You may pour the bicarbonate into a tablespoon first and add a little water to to dilute it if you wish. This can especially come in handy if you had poured too much flour in the first place. Stir again for a few seconds. At this point, your mixture looks very smooth.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhACEgZR_tkJeo1eNJVwOqadIWMysXeAew6yzlr4-FWjBnVqg4WE6PP0nWUij04Dspu-QateYlonstRMLo4qHUOpnpjaTsgH_y6fp2ccG6STC8wtZw4jBnYMh0g0S3ZRc1r3DJT7UYWXU-P/s1600/PICT0018.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhACEgZR_tkJeo1eNJVwOqadIWMysXeAew6yzlr4-FWjBnVqg4WE6PP0nWUij04Dspu-QateYlonstRMLo4qHUOpnpjaTsgH_y6fp2ccG6STC8wtZw4jBnYMh0g0S3ZRc1r3DJT7UYWXU-P/s1600/PICT0018.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Deruny dough after adding the flour and bicarbonate and stirring&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
- As the oil in your pan has become quite/very hot, pick a whole tablespoon of your mixture and pour it into the pan. Do not worry about having to shape the derunys while they cook because they will do that on their own, naturally. Repeat this step until your pan is full. Make sure also, as the derunys start cooking, that they don&#39;t stand too close one from another. Once they have been cooking for some time, they will be able to touch without sticking, but not at the beginning though.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;- You can leave the heat quite strong while laying down the mixture into the pan, but once that is done, to avoid burning your derunys, reduce the heat by half at least. Wait a minute or two, and once each of the derunys has its bottom brown/golden (though not burned), flip them all using a fork. Start with those who were laid first.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcC_UELd73ZOWvWGMRBFdfgzMks9c9EYcXibCKXSBLAE-wq5T22KcQUXNI5347XQ7zncurDgyfjJIXS4a0gsYcT4ZN5iURWr0SInBRc2UV6oTmKqOtMCVjlg6-fKablz5NzXKV3dsQ24cp/s1600/PICT0023.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcC_UELd73ZOWvWGMRBFdfgzMks9c9EYcXibCKXSBLAE-wq5T22KcQUXNI5347XQ7zncurDgyfjJIXS4a0gsYcT4ZN5iURWr0SInBRc2UV6oTmKqOtMCVjlg6-fKablz5NzXKV3dsQ24cp/s1600/PICT0023.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Derunys starting to fry&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
- Once all the derunys have all been flipped, make sure the heat is kept gentle enough, and slowly cook for 3-4 minutes, again, not trying to burn them, but it also has to cook well on the inside. We don&#39;t want to end up eating raw potato doughnuts, do we?&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjj987ISDtLzbdxSPYi2GYkqxlmV7QdKbAKGOkx4vFNEGJmYGcXKAZyYpLes1nfho8KgR5s-ErGEvrjDxN3iAqc0yt4OoEh2Hx7CS6dAy22ubLe7GSwJOJjHKy987F48VdfBB0jjcAD-ydY/s1600/PICT0024.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjj987ISDtLzbdxSPYi2GYkqxlmV7QdKbAKGOkx4vFNEGJmYGcXKAZyYpLes1nfho8KgR5s-ErGEvrjDxN3iAqc0yt4OoEh2Hx7CS6dAy22ubLe7GSwJOJjHKy987F48VdfBB0jjcAD-ydY/s1600/PICT0024.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Derunys as they just got flipped, ready to cook longer on lower heat&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
- If you end up having more mixture in your hands than the pan can contain, you then have two choices:&amp;nbsp; either you don&#39;t have that much mixture left and you sort of manage to add more onto those frying already (that is if you haven&#39;t flipped sides already), or, you make another round, clearing the pan first and adding some oil again, etc. &lt;br /&gt;
&lt;br /&gt;
- Once ready, lay out the derunys on a clean plate, put a cover on top only if you wish them to become more tender/sloppy/mellow, and ultimately if you wish to retain their heat too.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJlPS4saN20d7fBxcl_ayOKst2pXiCwQtaEW-N0iCBQEB_mwL5aC4C_zh55RgW8Is_v5UGuShw4_eXHFT654Ybp5LCD88JLj9JHlRfNCEMpM4Ps0HuVRPG1a7IXSR1KF8YzqAl8mRlfSvq/s1600/PICT0036.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJlPS4saN20d7fBxcl_ayOKst2pXiCwQtaEW-N0iCBQEB_mwL5aC4C_zh55RgW8Is_v5UGuShw4_eXHFT654Ybp5LCD88JLj9JHlRfNCEMpM4Ps0HuVRPG1a7IXSR1KF8YzqAl8mRlfSvq/s1600/PICT0036.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNa6D2CiTCO7rS2cnYeFvhRXyZCYfYM45Z4PqSaIrhBrs8kOOVeXw0alQHV8kW_4RiRo2bdeq47FwXQoIhYVE8mL5EoUNRQwgYBKOTj4Vr9bllblx3a6N4HIq1k7__u6LXYrYRTAOohX16/s1600/PICT0041.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNa6D2CiTCO7rS2cnYeFvhRXyZCYfYM45Z4PqSaIrhBrs8kOOVeXw0alQHV8kW_4RiRo2bdeq47FwXQoIhYVE8mL5EoUNRQwgYBKOTj4Vr9bllblx3a6N4HIq1k7__u6LXYrYRTAOohX16/s1600/PICT0041.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmrs1orctzpdBxOi1dxPDxxTsWUcoLO607QYDbzfZ7kEW0aDl-AcCd6kWqOq8m1BGuU9orrwNAuNTWgvDmhpt3a5eFJRX2nzuOvFIfFJNosCtm1TdPER__GDgE0pKlIn0UB4ucZopi2arN/s1600/PICT0054.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;How to prepare Ukrainian Derunys&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmrs1orctzpdBxOi1dxPDxxTsWUcoLO607QYDbzfZ7kEW0aDl-AcCd6kWqOq8m1BGuU9orrwNAuNTWgvDmhpt3a5eFJRX2nzuOvFIfFJNosCtm1TdPER__GDgE0pKlIn0UB4ucZopi2arN/s1600/PICT0054.jpg&quot; height=&quot;240&quot; title=&quot;How to prepare Ukrainian Derunys&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Cultural note: In Ukraine, derunys are originally a traditional dish and they tend to be served with some fresh Smetana
 on the side, usually a whole tablespoon of it. They can also be served with 
mushroom sauce on top. As part of a traditional event, they are usually 
part of a larger table containing various traditional dishes.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
This is the perfect example of recipe that&#39;s amazingly easy to master and you will soon be able to prepare derunys in the morning faster than you would prepare scrambled eggs. &amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
Smachnoho! (pronounced SMACH-noho : Ukrainian for bon appétit)&lt;br /&gt;
&lt;br /&gt;
Stay tuned for more on RolandC.net!&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;This &lt;a href=&quot;http://www.rolandc.net/2014/08/how-to-prepare-ukrainian-derunys.html&quot;&gt;article&lt;/a&gt; first appeared on Wednesday the 6th of August 2014 on &lt;a href=&quot;http://rolandc.net/&quot;&gt;RolandC.net&lt;/a&gt;.&lt;/span&gt; </description><link>http://www.rolandc.net/2014/08/how-to-prepare-ukrainian-derunys.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj86HD3cy33-NC0nfwMx_v-YQeOOH9nbDuGNhgt5kCYENMzpviDqbaA5vJHI60hjkZSjnwPRDR3JBqhUjDR-Nz2aiyXcwKyXehFdsdQwyH-5QDvX0jzxC6DR7pA1KkgmN5qXGLlyva1KFSX/s72-c/PICT0048.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-3832193813349749805</guid><pubDate>Tue, 05 Aug 2014 06:43:00 +0000</pubDate><atom:updated>2014-08-06T08:28:20.428+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">android</category><category domain="http://www.blogger.com/atom/ns#">battery</category><category domain="http://www.blogger.com/atom/ns#">battery life</category><category domain="http://www.blogger.com/atom/ns#">idevice</category><category domain="http://www.blogger.com/atom/ns#">iphone</category><category domain="http://www.blogger.com/atom/ns#">smartphone</category><category domain="http://www.blogger.com/atom/ns#">technology</category><title>12 tips to increase your smartphone&#39;s battery life</title><description>When I got my first smartphone, I literally felt shocked at how frequently I had to plug it to the charger, shocked by what I had come to believe was the result of an abnormally poor battery standby capacity. I could not understand why was it that... if my old phone of the same brand could easily last a whole week without having to be recharged, why would it be that this one, a newer one, ten times more sophisticated in every aspect, had to be plugged to the wall at least once a day or more - wrong it felt, and it felt like I had hit a defect - and I could already see myself screaming at some maintenance staff on that I had just bought that phone and that there was something wrong with its battery that needed to be replaced and so on.... STOP.&lt;br&gt;
&lt;br&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAEQRXEPxYjhUDX514KIIIMfqrIAeddVAtWT_N5QKOYvXX5mPcyFEVWxArXFP5a8Rv9PVoIsnZfLjnGG4R9juTUuYbkZXFz8ZeQrjrFzp3PDy9uFUJwqFc3USxpwAjYXdpcvTPbkBp_qC2/s1600/PICT0018.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;10 tips to increase your smartphone&amp;#39;s battery life&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAEQRXEPxYjhUDX514KIIIMfqrIAeddVAtWT_N5QKOYvXX5mPcyFEVWxArXFP5a8Rv9PVoIsnZfLjnGG4R9juTUuYbkZXFz8ZeQrjrFzp3PDy9uFUJwqFc3USxpwAjYXdpcvTPbkBp_qC2/s1600/PICT0018.JPG&quot; height=&quot;240&quot; title=&quot;10 tips to increase your smartphone&amp;#39;s battery life&quot; width=&quot;320&quot;&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;i&gt;A phone&amp;#39;s charger and its battery&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br&gt;
&lt;a href=&quot;http://www.rolandc.net/2014/08/12-tips-to-increase-your-smartphone-battery-life.html#more&quot;&gt;More »&lt;/a&gt;</description><link>http://www.rolandc.net/2014/08/12-tips-to-increase-your-smartphone-battery-life.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAEQRXEPxYjhUDX514KIIIMfqrIAeddVAtWT_N5QKOYvXX5mPcyFEVWxArXFP5a8Rv9PVoIsnZfLjnGG4R9juTUuYbkZXFz8ZeQrjrFzp3PDy9uFUJwqFc3USxpwAjYXdpcvTPbkBp_qC2/s72-c/PICT0018.JPG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-4436058631602782912</guid><pubDate>Mon, 04 Aug 2014 09:50:00 +0000</pubDate><atom:updated>2014-10-29T07:22:41.685+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">breakfast</category><category domain="http://www.blogger.com/atom/ns#">breakfast idea</category><category domain="http://www.blogger.com/atom/ns#">brunch</category><category domain="http://www.blogger.com/atom/ns#">brunchpot</category><category domain="http://www.blogger.com/atom/ns#">dutch cuisine</category><category domain="http://www.blogger.com/atom/ns#">food</category><category domain="http://www.blogger.com/atom/ns#">food idea</category><category domain="http://www.blogger.com/atom/ns#">hutspot</category><title>From the Dutch Hutspot to the Perfect Brunch : Let me introduce you to the BrunchPot!</title><description>Feeling tired of having the same breaky over and over? Needing to change your food habits?&lt;br&gt;
&lt;br&gt;
The Hutspot is a delicious yet full-filling Dutch mashed-potato dish that dates back to the 16th Century and that primarily contains mashed-potato/carrot, onions, and that may contain meat like beef, or pork a sausage.&lt;br&gt;
&lt;br&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkQCSd1RaDVza-oQb7vHihCLnry6et18xUXUnXOIhBWJB7q9y4KSebmptzBYy2YtnWeQr7gzW0XEb4Y9oJoLhR-oMJorK8Ei72Uwf8mbghcQmI9Uk06JAIrd-lOi4_gvO2uJ1uwCCV2hdi/s1600/PICT0020.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;From the Dutch Hutspot to the Perfect Brunch : Let me introduce you to the BrunchPot!&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkQCSd1RaDVza-oQb7vHihCLnry6et18xUXUnXOIhBWJB7q9y4KSebmptzBYy2YtnWeQr7gzW0XEb4Y9oJoLhR-oMJorK8Ei72Uwf8mbghcQmI9Uk06JAIrd-lOi4_gvO2uJ1uwCCV2hdi/s1600/PICT0020.JPG&quot; height=&quot;240&quot; title=&quot;From the Dutch Hutspot to the Perfect Brunch : Let me introduce you to the BrunchPot!&quot; width=&quot;320&quot;&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;A typical BrunchPot served with a sliced cucumber on the side&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br&gt;&lt;/div&gt;
Hutspot is being cooked in the Netherlands down to the Flandres in the North-East of France, and the way it&amp;#39;s being prepared, its name as well as the used ingredients may vary from one place to another.&lt;br&gt;
&lt;a href=&quot;http://www.rolandc.net/2014/08/from-dutch-hutspot-to-brunchpot-the-perfect-brunch.html#more&quot;&gt;More »&lt;/a&gt;</description><link>http://www.rolandc.net/2014/08/from-dutch-hutspot-to-brunchpot-the-perfect-brunch.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkQCSd1RaDVza-oQb7vHihCLnry6et18xUXUnXOIhBWJB7q9y4KSebmptzBYy2YtnWeQr7gzW0XEb4Y9oJoLhR-oMJorK8Ei72Uwf8mbghcQmI9Uk06JAIrd-lOi4_gvO2uJ1uwCCV2hdi/s72-c/PICT0020.JPG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-1516062232758915252</guid><pubDate>Mon, 14 Jul 2014 08:35:00 +0000</pubDate><atom:updated>2014-08-04T14:21:15.206+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cse</category><category domain="http://www.blogger.com/atom/ns#">custom search</category><category domain="http://www.blogger.com/atom/ns#">google cse</category><category domain="http://www.blogger.com/atom/ns#">google custom search</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">webdev</category><title>How to change Google Custom Search Engine title font size in page result</title><description>A quick tip today for those who have been banging their head trying unsuccesfully to modify the font size of the title in their Google CSE result page :

&lt;br /&gt;
&lt;pre style=&quot;background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7j_i2t9Zf6u7yhi4ATsAx6TNVGmbxfJTDxEDjaN1NZtNnsB7cwSO2W390_73RBX8aHovC2R12ybB29U2AfxwKV2VEunWNKK-YNY-swaEWIfHak24fVzSXPj47vfu6NXWHA9iHhIOB7rxh/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; .gs-webResult.gs-result a.gs-title:link, .gs-webResult.gs-result a.gs-title:link b, .gs-imageResult a.gs-title:link, .gs-imageResult a.gs-title:link b {  
      font-size: 12px;  
 }  
&lt;/code&gt;&lt;/pre&gt;
That&#39;s it, enjoy, and stay tuned for more on RolandC.net!</description><link>http://www.rolandc.net/2014/07/how-to-change-google-custom-search.html</link><author>noreply@blogger.com (rolandc.net)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-280883510849895982</guid><pubDate>Fri, 31 Jan 2014 15:56:00 +0000</pubDate><atom:updated>2014-10-22T07:50:07.056+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">android</category><category domain="http://www.blogger.com/atom/ns#">technology</category><title>10 tips/questions you should definitely consider before buying your first smartphone</title><description>If you have never owned a smartphone before, but are looking to buy one and feel unsure as to what product to buy exactly and at which price - look no further - here is a list of 10 tips/questions you should definitely consider in order to ensure you get the smartphone that provides you with all the functionality you need and offer you the best value for your purchase. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Question #1(the obvious):&lt;/b&gt; Do I really need a smartphone and can&#39;t I just do away with my current phone, laptop and/or netbook?&lt;br /&gt;
&lt;br /&gt;
I mean, are you really sure just exactly what kind of features or functionality you need and for which you are going to be spending your hard-earned money ? Are you really sure they will not overlap those your current laptop/netbook already offer and that they will definitely help you improve your activity and/or well-being ? That you are just not buying some expensive tech gadget because others already have it?&lt;b&gt; Weigh first the pros and cons and ask yourself honestly whether you really need one or not&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0lgrm9OiZlrJDe5MWqGeWvwjg4OVWSBEt0sRU747Taaa_mszyOkab0RlKS-tovfa11EI_ox7O1IXQCuYJfw4-FgLeInHm1Yr3VnjBu47u5W6o8BK1vztslSe18sy-1t6aZXgH7gHH-eXI/s1600/9736243206_fff9d6d9b7_n.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;10 tips/questions you should definitely consider before buying your first smartphone&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0lgrm9OiZlrJDe5MWqGeWvwjg4OVWSBEt0sRU747Taaa_mszyOkab0RlKS-tovfa11EI_ox7O1IXQCuYJfw4-FgLeInHm1Yr3VnjBu47u5W6o8BK1vztslSe18sy-1t6aZXgH7gHH-eXI/s1600/9736243206_fff9d6d9b7_n.jpg&quot; title=&quot;10 tips/questions you should definitely consider before buying your first smartphone&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Question #2:&lt;/b&gt; iOS or Android or...?&lt;br /&gt;
&lt;br /&gt;
While both are meant to more or less serve the same purpose, achieve roughly the same results, at least from a beginner user point of view, iOS is a proprietary system designed by Apple Inc to be used in... what was originally called iDevices, therefore iPhones, Ipad, etc., while Android is an open-source Operating System designed by Android Inc (later bought by Google) to work with...many phone brands.&lt;br /&gt;
Some argue that Android is great for tinkering with your smartphone but that if you need something that actually works, then consider getting an iPhone. Personally, I am the owner of an Android phone which works perfectly, gives me the results I have been expecting all along and I can confidently say I am totally happy with it.&lt;br /&gt;
Now, if we have to make a simple but technical comparison, iPhones would be like Macintosh computers and Android phones would be like Linux stations, as Android is linux-based, but the difference being that you do not need Linux knowledge to be able to use an Android phone. In fact, you do not even need to know anything. &lt;b&gt;Windows users do fine on Android&lt;/b&gt;.&amp;nbsp;&amp;nbsp; &lt;br /&gt;
Other systems exist too, like Blackberry, but it&#39;s your first smartphone and we want to make it simple, don&#39;t we?&lt;br /&gt;
But the bottom line being that you should not expect to be able to buy 
the latest iPhone for less than 500 euros, if brand new and not 
counterfeit. This while you can definitely get a descent entry-range&amp;nbsp; smartphone 
with Android 4.1 on it for less than a hundred euros.&lt;br /&gt;
Last but not least - the apps you will be likely to download for your smartphone, most of them will be completely free and available directly from Google Play Store, while you will hardly find free apps in the Apple&#39;s AppStore, which holds apps for devices running iOS. Choice is your alone.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Question #3: &lt;/b&gt;Does size matter? &lt;br /&gt;
&lt;br /&gt;
Looking back at the time before I actually went on buying my first smartphone and although I had researched the internet for clever advice on what criteria to take into consideration, I still wasn&#39;t sure my thin fingers wouldn&#39;t betray me on my future keyboard, would allow me to type a simple sentence in less than half an hour. And as i began to research that topic more consistently, it became clear that in the ever-growing smartphone world, fat-fingering really was an issue. And I started wondering - what if it turns out i can read emails, but can barely type to reply to them, what if I should have gone for a bigger model?&lt;br /&gt;
Well, as it turned out, and even though it&#39;s true my fingers do look thin, from day one I have never had any issue getting along with the typing part. I probably fat-finger once in a while but not more than I usually do while using my standard laptop&#39;s keyboard. So basically, to have a problem with the size of your smartphone, you would have to have really big hands with huge fingers, trying to use them on the smallest smartphones you could buy nowadays. Meaning generally that, &lt;b&gt;if you have average hands, screen size should definitely not matter&lt;/b&gt;.&lt;br /&gt;
Passed the size of your fingers, it seems there&#39;s a trend going on nowadays to buy the largest smartphone just because it looks more expensive or somehow more powerful, but this is a tendency that is balanced by the sense taboo most of us can feel while holding a device the size of a tablet almost, by their ear, talking to it. It is entirely up to you though to decide whether you really need a device of that size or not. No one will blame you if you do.&lt;br /&gt;
&amp;nbsp; &lt;br /&gt;
&lt;b&gt;Question #4:&lt;/b&gt; What kind of protection will I need to buy for my smartphone?&lt;br /&gt;
&lt;br /&gt;
Good question. The answer starts with...what kind of user are you? And also, do you happen to have moist hands?&lt;br /&gt;
Your new smartphone will not be your old black &amp;amp; white telephone that only allowed you making/receiving calls and sending sms/mms. Your new smartphone is something that will - or should - impress you to the point that you will spend countless hours discovering new features on it, starting from when you still haven&#39;t even gotten out of bed yet, up until the podcast/mp3&#39;s you will be wanting to listen to before getting asleep. So there will be a lot of screen touching and device handling involved, probably to the point where you will realize...where you will realize you had never realized how greasy your fingers had always been.&lt;br /&gt;
For some, this is an issue that can be easily addressed by placing a thin vinyl/plastic foil on top of the screen so that your fingers never actually hit it directly. But the problem with that being that your screen then become less sensible and you end up having to hit harder to exercise the same amount of pressure. Another issue with that system being that, after some time, there seems to exist a whole glue-sphere effect going on between the foil and the screen, which is basically something one would store in the unpleasant effect category.&lt;br /&gt;
Now what if your new smartphone falls off the table? &lt;b&gt;You need a case&lt;/b&gt; - that&#39;s right - you need a case, a hard one, that not only will keep the back and sides of your phone off your (potentially) greazy fingers, but that will also make for the strongest protection you could provide your new device with. What&#39;s more, you don&#39;t even have to get it in/out of its case every time you get a call or place one. Simply lift/remove the top part of the case, fold it back once you are done. So that no matter how many hours you will be spending on your smartphone each day, your fingers will essentially be in contact with the case only.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Question #5: &lt;/b&gt;Should I try the smartphone first before buying?&lt;br /&gt;
&lt;br /&gt;
If you have that possibility then yes, go for it, as this could reinforce the legitimacy of your well-weighed choice, help you convince you have made the right choice, otherwise, you will already be on spot to try new models if necessary - but remember, being at the phone shop does not mean you should automatically come out with a brand new smartphone in your hands. This is a device you will probably spend a great deal of time with, so why not taking your time to carefully choose what you need?&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Question #6:&lt;/b&gt; How much should I spend on my first smartphone?&lt;br /&gt;
&lt;br /&gt;
That&#39;s an interesting question, to which I would reply - How much money would you spend for your first car?&lt;b&gt; Would you buy a BMW while you just got your driving license?&lt;/b&gt;&lt;br /&gt;
Since you are (supposedly) only new to the smartphone world, the idea here is that it is probably best for you to buy an entry-range model, which should definitely meet all your expectations, and then, as you grow in knowledge and experience, you might consider buying a smartphone that has more advanced features.&lt;br /&gt;
On the other end, buying exactly the cheapest model might not be the wisest choice to make, as it might seriously lack in features, even or because of the price you will have paid.&lt;br /&gt;
Best in my opinion would be, let&#39;s say, if prices start a 60 euros and stop....say at 250 euros, best would be to perhaps choose a smartphone which price is in the range of between 80 and 120 euros.&lt;br /&gt;
Read reviews about the smartphone you are likely to buy, it should give you an overall idea as to whether other people would pick this brand/model for the same specific reasons you yourself would.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Question #7: &lt;/b&gt;Should I buy a smartphone while I&#39;m on holidays in a foreign country?&lt;br /&gt;
&lt;br /&gt;
Quite frankly, best would be to avoid doing that. Because some apps are actually restricting downloads to certain geographical zones. So, supposing you live in the U.K and you just came back from a holiday in India where you bought yourself a nice smartphone and want to try downloading the BBC&#39;s official iPlayer app right from Google Play Store (you have bought an Android phone) - but surprise - you get a message that actually tells you &lt;b&gt;&quot;this app is not available in your country&quot;&lt;/b&gt;.&lt;br /&gt;
Now, while it&#39;s possible to fake your phone&#39;s geographical location using proxies, this is probably not the way you wish to be using your first smartphone, because this process will automatically force you to install apps that will require rooting your phone and here we are talking about a process that, depending on how it&#39;s done, can seriously damage your phone, &#39;brick&#39; it, which means leaving it as slow/dead as a brick, therefore totally unfit for normal use. It is however true that, a sometimes available workaround is to install the sought app directly using a package (.apk) but this not always being an available option.&lt;br /&gt;
&lt;b&gt; &lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;b&gt;Question #8: &lt;/b&gt;&lt;/b&gt;Should I buy an extended battery?&lt;br /&gt;
&lt;br /&gt;
The one issue that puzzles new smarphone users, and that did puzzle me too at first, is battery longevity - or else - for how long can your smartphone stay operational before having to recharge its battery.&lt;br /&gt;
While most traditional phones can stay lit for a week or more, smartphones, it seems, have a hard time staying on for more than....two/three days in a row without needing a refill. I&#39;d even go as far as saying that most new smartphone users cannot make it through the end of a single day without having to plug the battery cable in. And this is all for good reasons since a smartphone has so many extra energy-consuming features that standard GSM phone just do not have.&lt;br /&gt;
But deciding whether you need extended battery life or not will first depend on how you will use your smartphone, meaning, do you need to listen to mp3&#39;s all day, show your videos to your friends at school? Are you constantly on the road for your job? etc, etc.&lt;br /&gt;
It&#39;s worth mentioning that, although it seems as though using battery-saving apps is the wise thing to do, and there are a lot of them out there, doing so could also dramatically reduce your battery&#39;s life and performance.&amp;nbsp; &lt;br /&gt;
&lt;b&gt;As you gain knowledge and experience, you become better able to recharge less often&lt;/b&gt;, without having to rely on such energy-saving programs&lt;b&gt;.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;b&gt;Question #9: &lt;/b&gt;&lt;/b&gt;Should I buy an extra memory card and if so, what kind should I buy?&lt;br /&gt;
&lt;br /&gt;
Well this also depends on how you are going to be using your smartphone anyway.&lt;br /&gt;
If you buy it just to have a way to check your email while on the road, buying an extra card might not be that urgent or necessary. But if you decide after a while, once you have a good feel of what your purchase can do for you, that you do need an extra card, let&#39;s say to have a few movies ready for watching while in the train, then there are a few things you should consider, namely three. The &lt;b&gt;type&lt;/b&gt; of memory card, its &lt;b&gt;capacity&lt;/b&gt; and &lt;b&gt;class&lt;/b&gt;.&lt;br /&gt;
For the memory type, meaning microSD, etc., you will need to read your manufacturer&#39;s manual or search Google for your phone&#39;s full specifications - this info should be available, that is, what exact kind of extra memory card will work on your smartphone.&lt;br /&gt;
Memory card sizes nowadays can be of 8, 16, 32, 64 gb and having used your smartphone for a few weeks already, you will definitely have a good feel of what capacity it is that you really need, although, your phone&#39;s specs should tell you up to how many gigs you can insert in the device. But in any case do compare prices, because it is likely that you find a 16 gb card that ends up being almost the same price as for a 32 gb, for the same brand, model and class.&lt;br /&gt;
All of which is leading us to discuss the meaning of memory card classes. So what is the class of a memory card? Simply put, it defines the read/write speed of your card. Classes range from 1 to 10 and you&#39;re absolutely better off with a 10 than with a 2. That is why you can find incredible promotions on some cards, because they are of class 2. The thing being that it is definitely worth spending more for class 10 already.&amp;nbsp; &lt;br /&gt;
Last but not least, I find it is not worth spending more than a third of your phone&#39;s price on a memory card. Indeed, it does not really make sense, so for a smartphone bought at the end of 2013, it seems wise to either buy now a 32gb extra card, or wait a few months to buy a 64gb one, once the price has gone down a little and basically once that your card does not cost more than a third of the price you have paid for the main device itself.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;b&gt;Question #10: &lt;/b&gt;&lt;/b&gt;SD, HD or Full HD? What are those and which to choose?&lt;br /&gt;
SD stands Standard Definition, HD means High Definition and Full HD means well... Full High Definition. Those are terms that refer to the image quality/definition of your screen. The more pixels your screen definition has, the better the image, whether photo or video, but the higher the price would be too.&lt;br /&gt;
So even though you might be in a position where you can easily afford to buy a smartphone that offers FullHD resolution, the truth is, I find smartphones that have this feature way too expensive for now, at least in regard to the proportion of devices that come with only SD or HD resolution.&amp;nbsp; All while SD devices offer great human-perceptible image quality anyway. &lt;br /&gt;
But chances are &lt;b&gt;it probably won&#39;t be long until FullHD becomes the new SD and is available at fair prices&lt;/b&gt;, affordable by everyone.&lt;br /&gt;
&lt;br /&gt;
In the meantime, I strongly hope this article will help you choose the smartphone that is right for you and that suits you best, comes with all the functionality you really need, all at an affordable price, offering you the best possible value.&lt;br /&gt;
&lt;br /&gt;</description><link>http://www.rolandc.net/2014/01/10-tips-questions-you-should-definitely-consider-before-buying-your-first-smartphone.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0lgrm9OiZlrJDe5MWqGeWvwjg4OVWSBEt0sRU747Taaa_mszyOkab0RlKS-tovfa11EI_ox7O1IXQCuYJfw4-FgLeInHm1Yr3VnjBu47u5W6o8BK1vztslSe18sy-1t6aZXgH7gHH-eXI/s72-c/9736243206_fff9d6d9b7_n.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-3664563573795930485</guid><pubDate>Wed, 29 Jan 2014 12:57:00 +0000</pubDate><atom:updated>2014-10-22T07:53:22.034+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">miscellaneous</category><title>How to securely and inexpensively send a credit card or other valuable documents abroad</title><description>Sometimes, you need to send someone - or be sent - a valuable document abroad without which you just cannot do, like a credit card for example.&lt;br /&gt;
But you cannot risk having the letter/parcel get lost or stolen, so you need a totally secured, guaranteed way to have that delivery made and you start thinking about costly services like FedEx or DHL, which, indeed are... costly services.&lt;br /&gt;
As an expatriate I have often had, at least several times, to have credit cards and important documents sent to me via postal services, and, don&#39;t get me wrong, I have no personal bias against companies like the ones I have quoted above, and those are great companies providing serious and professional services but they would turn out to be too expensive for me everytime i would need them. So, what did I do then?&lt;br /&gt;
&lt;br /&gt;
Back a few years ago, I decided to google for advice on how to send a credit card the cheap way, meaning still securely, but without having to pay for a minimum of 60/80 euros per document, if not more.&lt;br /&gt;
And I sort of found a post written by someone who explained his trick was to hide the valuable good inside some piece of clothes and send it via ordinary postal service. This got me thinking.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8KyIh0QTEoXrcFD0w4x4lOYLBhHTRSIQCCz5ZmMl1iHIz-IvR3cgc0bKAI6tdSPWimz4WrV5hXd02ErfkO41y662UKeYLBGJMP306TQ6usIupwNPMtNS5vHp5m5nolYy44Bh2nD8curyO/s1600/how+to+send+a+credit+card+abroad+securely.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;How to send a credit card abroad safely&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8KyIh0QTEoXrcFD0w4x4lOYLBhHTRSIQCCz5ZmMl1iHIz-IvR3cgc0bKAI6tdSPWimz4WrV5hXd02ErfkO41y662UKeYLBGJMP306TQ6usIupwNPMtNS5vHp5m5nolYy44Bh2nD8curyO/s1600/how+to+send+a+credit+card+abroad+securely.jpg&quot; height=&quot;286&quot; title=&quot;How to send a credit card abroad safely&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
As a well-travelled person, I have often had to cross foreign borders, standing in line for hours with strangers, having immigration officers peering at me to see if there was anything out of the ordinary that popped up in their mind while they were looking at me, studying my behaviour, as they studied everyone else&#39;s too.&lt;br /&gt;
I had also been temping for a day once, a long time ago at a Royal Mail sorting center and I knew a bit about how foreign parcels were processed, based on what type of county they came from.&lt;br /&gt;
&lt;br /&gt;
So, it all sort of became very logical to me at some point that.... for a credit card to cross oceans and borders without drawing attention and risking getting stolen or anything, &lt;b&gt;everything has to look &#39;normal&#39;&lt;/b&gt;, as after all, your letter or package, is going through the customs like you would yourself while on holidays to a foreign country. And for obvious reasons, it goes through a process of human appreciation, plus through some scanning device, like for someone&#39;s luggage at the airport. Noone wants to take risks with that there and who could blame them?. Frankly I do not.&lt;br /&gt;
&lt;br /&gt;
So, since you need to send or get sent something for legitimate reasons, there is nothing wrong with using a little imagination and let&#39;s say, if we take the example of a credit card, prepare a relatively new cd-rom or dvd-rom from your least preffered artist (you got it as a gift but just did not find the courage to throw it away), attach your card on the inside like the cd-rom manufacturer would attach a valueless promotional card, add a Happy Birthday card beside, and you&#39;re done.&lt;br /&gt;
Such a method should definitely not draw neither human nor machine attention while your letter/parcel awaits at sorting centers, wherever those centers are and whoever is on duty when that happens.&lt;br /&gt;
&lt;br /&gt;
Of course you need to &lt;b&gt;send it as a registered letter with return receipt&lt;/b&gt; and it will cost more, but it will still cost you approximately eight or ten times less than if it had been via specialized/services, and you want to do that because even if somehow it feels that sending it the basic way could draw attention even less, you still want to use some kind of cheap but effective insurance against internal postal theft, which we all know happens, in every country, civilized and less civilized alike.&lt;br /&gt;
Having said that, don&#39;t go, unless you wish to, for any extra-value services that your local post office is likely to propose. You will not get a better service from those, especially if you send your letter/parcel to a country where they can&#39;t figure out your language. What you need is the most basic, cheapest option to register (with receipt) your letter to abroad, the only vital thing here being to register it and it should definitely cost you under 8 or 10 euros that&#39;s for a 20-50g sending.&lt;br /&gt;
The receipt option is something you definitely want to add because even if postal staff of the receiving country cannot decipher it or simply doesn&#39;t know what to do with it, this will secure your sending again internal theft while it still hasn&#39;t reached border, and offer some kind of protection once it has crossed border. Even if as a foreign postal staff who couldn&#39;t read English you can&#39;t read on it, it makes you less likely to try any funny move since you know it looks like a receipt... . &lt;br /&gt;
&lt;br /&gt;
Needless to say too that &lt;b&gt;you do not need to tell your local post office what you are going to send&lt;/b&gt;. Even if you are certain you can trust the staff there and you went to school with them, do not, ever ever tell them you are going to send something of that value, as your letter might not even make it to the next village or town. &lt;br /&gt;
&lt;br /&gt;
Also, you might use some administrative documents that look like they are official and important, but important to you, boring for others, and tape the card on it using some tape.&amp;nbsp; Like it would be a discount or corporate, insurance card. &lt;br /&gt;
But now what about things that are bigger, that take more room? Well the same logic applies, that...everything has to look normal. New clothes could be a good choice, the trick being that, you need to imagine the stuff going through scanning at the airport, to try and picture how it would all look like for a custom officer watching the surveillance monitor, and whose job is to spot everything that falls under the category  &#39;not-ordinary&#39; or &#39;not 1000% ordinary&#39;.&lt;br /&gt;
&lt;br /&gt;
You also need to picture the letter or package being opened manually for extra-checking. I mean, you don&#39;t want to send a pair of thick socks to your son that lives in Thailand while you live in Canada, and hide the card inside the socks. This would draw attention, and this is what you do not want - draw attention. Few people need thick socks in Thailand, but above all, what kind of relatives send socks to someone thousands miles away, and what kind of person is adventurous enough to go to Thailand but coward enough not to buy his/her own socks and needs a relative to send some for them?&lt;br /&gt;
&lt;br /&gt;
That&#39;s what I am about, you need to think just as a border officer would, and you need to &lt;b&gt;use your common sense&lt;/b&gt;, and everything will run smoothly, safely, as securely as with the most popular secure delivery companies, but without having to break the bank.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</description><link>http://www.rolandc.net/2014/01/how-to-very-securely-and-inexpensively-send-a-credit-card-or-other-valuables-abroad.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8KyIh0QTEoXrcFD0w4x4lOYLBhHTRSIQCCz5ZmMl1iHIz-IvR3cgc0bKAI6tdSPWimz4WrV5hXd02ErfkO41y662UKeYLBGJMP306TQ6usIupwNPMtNS5vHp5m5nolYy44Bh2nD8curyO/s72-c/how+to+send+a+credit+card+abroad+securely.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-2151890606165739981</guid><pubDate>Mon, 06 Jan 2014 13:59:00 +0000</pubDate><atom:updated>2014-08-11T12:42:12.015+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">android</category><category domain="http://www.blogger.com/atom/ns#">root gt-s6312</category><category domain="http://www.blogger.com/atom/ns#">samsung gt s6312</category><category domain="http://www.blogger.com/atom/ns#">technology</category><title>Safely root your Samsung GT-S6312</title><description>Today i will show you how to safely root your Samsung GT-S6312 smartphone, without &#39;bricking&#39; it. Note that you can just as well &#39;unroot&#39; your phone the same way.&lt;br /&gt;
Although what I am going to show you now is a very simple procedure that I have succesfully tested on my own Samsung GT-S6312 phone and that should probably also work on any Samsung phone that has Android on it, I still give no guarantee that it is what will happen if you try it on a different model/brand.&lt;br /&gt;
In any case, you try this at your own risk. What i am saying however is that, if you follow my advice and if you own the same phone as I do, everything should go great.&lt;br /&gt;
&lt;br /&gt;
Also, i am assuming you understand what root is, and basically that you know exactly why you wish to root your own phone, that you have weighed the pros and cons and everything, right?.&lt;br /&gt;
&lt;br /&gt;
Having rooted mine, i have not lost any app or personal file/data or whatever, but it&#39;s never a bad idea to backup, so I encourage you to &lt;b&gt;do a backup&lt;/b&gt; in any case. Better be careful than sorry afterwards. &lt;br /&gt;
&lt;br /&gt;
Okay so let&#39;s get started.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk9YCXwPHZY3oTcCvC6TEoA8tLCJdVCUcBxPCpJNrAdEhjHu2py9Ox1aRC0X4lWirw8-llcUWZ3i2h7qMcQT3xDy_G-gKrI-WDomk3VFsd40Vt20pb6Zi6_SWZBZTKC1YEmF8AqfuD_q8u/s1600-h/Safely+root+your+Samsung+GT-S6312.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;Safely root your Samsung GT-S6312&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk9YCXwPHZY3oTcCvC6TEoA8tLCJdVCUcBxPCpJNrAdEhjHu2py9Ox1aRC0X4lWirw8-llcUWZ3i2h7qMcQT3xDy_G-gKrI-WDomk3VFsd40Vt20pb6Zi6_SWZBZTKC1YEmF8AqfuD_q8u/s1600/Safely+root+your+Samsung+GT-S6312.jpg&quot; height=&quot;150&quot; title=&quot;Samsung GT-S6312 Deep Blue&quot; width=&quot;150&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Samsung GT-S6312 Young Duos Deep Blue &lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Step 1 &lt;/b&gt;- You need to download a free app called Framaroot, the version I&#39;ve found on the net was Framaroot-1.8.1.apk, that you can download safely from &lt;b&gt;&lt;a href=&quot;http://forum.xda-developers.com/attachment.php?attachmentid=2482822&amp;amp;d=1388384329&quot;&gt;here&lt;/a&gt;&lt;/b&gt;. Note: Android forums are the closest thing to an android resource site you could find nowadays, probably because Android being very new, so don&#39;t panick.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;b&gt;Step &lt;/b&gt;2&lt;/b&gt; - Once you have donwloaded this app to your comp, your need to transfer it onto your phone and execute it, or execute it directly if you have downloaded it right from your phone. So, complete action using Package installer and click yes when asked if you want to install the app.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;b&gt;Step &lt;/b&gt;3&lt;/b&gt; - Open the app and you will see a dropdown menu where you can select either install SuperSU or Unroot, choose Install SuperSU if you want root your phone, choose Unroot if you want to unroot it, that&#39;s as simple as that.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;b&gt;Step &lt;/b&gt;4 &lt;/b&gt;- Now select the first exploit presented to you bellow, should be called Aragorn, and if like me you get an error message that the exploit failed etc., choose the next one on the list. Actually i have only two exploits on the list, Aragorn and Gandalf, and Gandalf works without any issue, so that&#39;s what probably will work on your phone too, Gandalf. If everything goes great, clicking Gandalf will result in the display of the following : &#39;Sucess :) Super and su binary installed. You have to reboot your device&#39;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;b&gt;Step &lt;/b&gt;5&lt;/b&gt; - So now time to reboot your device and check if everything went smoothly. To do so, just go to Google Play and look for a free app called &lt;b&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.joeykrim.rootcheck&amp;amp;hl=en&quot;&gt;Root Checker&lt;/a&gt;&lt;/b&gt;. Install it, launch it, click on &#39;verify root access&#39; and find out. If everything went well, you&#39;ll get a congratulation message in green saying &#39;Congratulations! This device has root access!&#39;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Note&lt;/b&gt; : many tutorials advise to go first to Settings-&amp;gt;Developper options etc.. to enable debugging, allow mock locations, but in my case this was not a necessary step, rooting worked perfectly witout have to switch on developper options.&lt;br /&gt;
&lt;br /&gt;
That&#39;s it, that&#39;s all there is to it. Stick around for more!</description><link>http://www.rolandc.net/2014/01/how-to-safely-root-samsung-gt-s6312.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk9YCXwPHZY3oTcCvC6TEoA8tLCJdVCUcBxPCpJNrAdEhjHu2py9Ox1aRC0X4lWirw8-llcUWZ3i2h7qMcQT3xDy_G-gKrI-WDomk3VFsd40Vt20pb6Zi6_SWZBZTKC1YEmF8AqfuD_q8u/s72-c/Safely+root+your+Samsung+GT-S6312.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-2569312660539904320</guid><pubDate>Thu, 01 Aug 2013 17:34:00 +0000</pubDate><atom:updated>2014-02-02T12:03:54.790+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">technology</category><title>How not to&#39;s  - Choosing a Gmail address</title><description>The tip i&#39;m about to share with you here might sound like it isn&#39;t a tip but believe me it is, and that very tip could help you avoid having to scratch your hair next time you&#39;ll want to quick-register on let&#39;s say a site like Yahoo.com, using your Gmail address...&lt;br /&gt;
&lt;br /&gt;
Let&#39;s say you&#39;ve created a new Gmail address a week ago and it&#39;s all fine. But now you&#39;re trying to connect toYahoo via your new Gmail addee, such connection procedure being a feature more and more sites tend to offer nowadays, giving users the opportunity to not having to waste 10 mns or more typing registration info by simply allowing them to import their Google or Facebook data.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQgDSfuXhBF7rTqGxPArK4Vwjydecnip0sWA60o8kkJrv7JJ6qU8TD3BwEDGOzw9tvETyVlqivUtyry1_SrGF6Ncsm1vy8aFeRvWwmOP_UukbUmCwpPmVM6b9BV1FysqjDkiK6lhY8J_o2/s1600/Gmail-Logo-150x150.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;How not to&#39;s - Choosing a Gmail address&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQgDSfuXhBF7rTqGxPArK4Vwjydecnip0sWA60o8kkJrv7JJ6qU8TD3BwEDGOzw9tvETyVlqivUtyry1_SrGF6Ncsm1vy8aFeRvWwmOP_UukbUmCwpPmVM6b9BV1FysqjDkiK6lhY8J_o2/s1600/Gmail-Logo-150x150.jpg&quot; title=&quot;How not to&#39;s - Choosing a Gmail address&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Gmail logo&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
And then it happens, you&#39;re denied registration on the ground that, i quote: &#39;&lt;span style=&quot;background-color: #f3f3f3; color: red; font-family: arial, helvetica, clean, sans-serif; font-size: 18px; font-weight: bold; line-height: 22px;&quot;&gt;Your Gmail email address contains text string not allowed in Yahoo!&lt;/span&gt;&#39;. You start copy-pasting the message to your Google search box as soon as it gets thrown at your face but it won&#39;t go, it simply won&#39;t go because there are no workarounds for this one. No workarounds&amp;nbsp;other than&amp;nbsp;using a different Gmail address or use your Facebook credentials (if any) or else going for the full registration spin process, which of course wasn&#39;t your idea at first.&lt;br /&gt;
&lt;br /&gt;
Such situation happens simply because your Gmail address probably contains a dot between your first name and surname, or an underscore maybe, or any character that Google itself allows at registration time. But take my word for it, a dot is enough to make you wish you had known all this in time.&lt;br /&gt;
&lt;br /&gt;
Chances are this should never happen to you now, now that you know ;).&lt;br /&gt;
&lt;br /&gt;
Stay tuned for more on rolandc.net&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</description><link>http://www.rolandc.net/2013/08/how-not-tos-choosing-gmail-address.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQgDSfuXhBF7rTqGxPArK4Vwjydecnip0sWA60o8kkJrv7JJ6qU8TD3BwEDGOzw9tvETyVlqivUtyry1_SrGF6Ncsm1vy8aFeRvWwmOP_UukbUmCwpPmVM6b9BV1FysqjDkiK6lhY8J_o2/s72-c/Gmail-Logo-150x150.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8936921225064570325.post-2519395730931624579</guid><pubDate>Wed, 31 Jul 2013 21:02:00 +0000</pubDate><atom:updated>2014-02-02T12:02:36.978+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">health</category><category domain="http://www.blogger.com/atom/ns#">lifestyle</category><title>Fancy some chocolate? No thanks, i&#39;ve quit!</title><description>It sort of amazes me that i could spend a good 38 years of my life not knowing that chocolate actually contains caffeine. Yes, you&#39;ve heard it, &lt;b&gt;chocolate and all cocoa-related products do contain caffeine&lt;/b&gt;. I can already here you say &#39;hang on, i&#39;ve never seen any chocolate product package stating there was caffeine in there!&#39;, that&#39;s right, there aren&#39;t, and this for a good reason: cocoa beans naturally contain caffeine, so that companies are not legally required to mention its presence, unlike Coca-Cola who has to print the word caffeine on its labels simply because the caffeine that&#39;s contained in a coke has been added, and failing to do so would be against the law.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;Now, as to how much caffeine is contained in chocolate products, one good rule to stick to would be, &lt;b&gt;the darker the chocolate product looks, the more caffeine it contains&lt;/b&gt;, but this isn&#39;t the only rule to be considered. Remember the Tomaco episode from the Simpson&#39;s? well, that&#39;s what industrially-injected-caffeine products reminded me of when i first heard about them. Nowadays you get caffeine-enhanced cereals, so basically even products that originally don&#39;t contain caffeine, can have caffeine added to them. But back to how much caffeine is &#39;contained in chocolate products&#39;, it would be safe to say it averages the levels found in decaffeinated coffee, yes, decaf contains caffeine, even though a lot less, probably 10 mg a cup while a black tea averages 60 mg. But the problem with decaf is the method used to decaf... . If done chemically, then chances are you&#39;re probably swallowing weird chemicals that harm your body even more then caffeine does, when you&#39;re having your cuppa. 
It is difficult to give a reliable comparison since people make tea differently. It&#39;s also true to say that the levels of caffeine contained in the cocoa beans vary according to the way the beans are roasted, the variety itself too. &lt;b&gt;A Hershey bar, dark one, contains 60 mg, just like an average cup of tea does&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxAHbTo5_X4f8WGAkxfCMGjBKyiIqjc7JHdechdS42-d0-6y9ZGPaf8OZGiaDUXA8IzaCndf0MFmFxvR4eIG9fyXai9AfguAXZAWgyjwGKyxKgtLHCNRqWbemijiEytEve4-HIroFOKxtv/s1600/choc.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;Chocolate does contain caffeine&quot; border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxAHbTo5_X4f8WGAkxfCMGjBKyiIqjc7JHdechdS42-d0-6y9ZGPaf8OZGiaDUXA8IzaCndf0MFmFxvR4eIG9fyXai9AfguAXZAWgyjwGKyxKgtLHCNRqWbemijiEytEve4-HIroFOKxtv/s1600/choc.jpg&quot; title=&quot;Chocolate does contain caffeine&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Cocoa products do contain a certain level of caffeine&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&amp;nbsp;Basically, if your doctor sets you on a caffeine-free diet, for whatever the reason, or if you&#39;ve just decided to quit consuming stimulants because you wish to have a life that&#39;s way healthier, you won&#39;t completely be off the hook until you have said no to:&lt;br /&gt;
&lt;br /&gt;
- coffee, decaf, tea, decaf tea and all related products&lt;br /&gt;
- chocolate and all cocoa related products&lt;br /&gt;
- all products to which caffeine is added industrially and belonging or not to the aforementioned categories&lt;br /&gt;
- and...said no for 5 days.&lt;br /&gt;
&lt;br /&gt;
Indeed, &lt;b&gt;caffeine is a drug, not a habit, it&#39;s a drug&lt;/b&gt;, and the physical withdrawal from it, in the most severe cases can take up to 5 days, only. The rest being psychological... . Upon quitting you will most likely experience headache symptoms, which then again, will vanish soon enough.

But if you have no intent to quit consuming caffeine products, then this article would have still helped you get the facts, hopefully.&lt;br /&gt;
&lt;br /&gt;
One vital fact about cocoa product consumption though is that, &lt;b&gt;in 2013, about 75 % of the world cocoa consumption comes from the Congo, where enslaved kids make for forced overhead&lt;/b&gt;. Those kids are more or less deliberately abducted at the Burkinafaso border and sold for approximately 230 euros to local producers or whatever you will call them. Companies like Nestlé deny being engaged in such practices but... documentaries have been made already, containing hidden-cam footage, backed up info, this is the real price for chocolate, not just the price to your health.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;Now you will say, chocolate contains anti-depressants that help me cope with things... . Wrong angle i say, because, &lt;b&gt;consuming anti-depressant foods only contributes to raising the toxicity level in your body&lt;/b&gt;, beside addicting you to the anti-depressants contained in them anyway, and although you first get the impression you&#39;re better of living that way, it only adds up with everything else you usually have, only to affect your metabolism the wrong way, thru time and you end up gradually thinking, being convinced that you couldn&#39;t make it without your chocolate bar once in a while. But it&#39;s just like saying eating more will get you less depressed and than you will feel happy and start eat less... it just cannot work, at least for long enough.&lt;br /&gt;
&lt;br /&gt;
One important debate is, can the levels of caffeine contains in decaf or in cacao make you addicted? Some scientist think that yes, while some don&#39;t and it&#39;s very difficult to come up with consistent data in those areas on the overall. From my experience, the problem with caffeine contained in chocolate is not its levels, especially if you do have coffee already on a daily basis, but rather the toxicity it brings to your body and ultimately to your brain, knowing that all addictions in the body respond to one and the same place in the brain, as much as we know nowadays. So that, in other words, &lt;b&gt;consuming chocolate destabilizes your metabolism&lt;/b&gt; even more, the body not being even able to actually make a difference between the sensation caused by the withdrawal from caffeine (which happens as soon as you have stopped eating your chocolate or having your coffee and NOT when it&#39;s been two days you haven&#39;t had your coffee or chocolate bar) and the normal sensation of hunger for food.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;Writing about the pros and cons of all caffeinated beverages and writing about addiction is beyond the scope of this article, but know that, if you seriously consider quitting caffeine then alternatives there are, such as Rooibos or Hibiscus infusions or simply a hot milk or a glass of water,
depending on the season or where you live,&lt;b&gt; there are always alternatives&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
So, what is your relationship with cocoa-related products and are you one of those who feel they could never do without their afternoon chocolate bar or on the contrary are you already managed to escape the chocolate trap and prefer fruits or anything else as snacks?</description><link>http://www.rolandc.net/2013/07/fancy-some-chocolate-no-thanks-ive-quit.html</link><author>noreply@blogger.com (rolandc.net)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxAHbTo5_X4f8WGAkxfCMGjBKyiIqjc7JHdechdS42-d0-6y9ZGPaf8OZGiaDUXA8IzaCndf0MFmFxvR4eIG9fyXai9AfguAXZAWgyjwGKyxKgtLHCNRqWbemijiEytEve4-HIroFOKxtv/s72-c/choc.jpg" height="72" width="72"/><thr:total>0</thr:total></item></channel></rss>