So, how do you get fully accepted into Adsense these days? (August 2016)

Thursday, August 4, 2016

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't the best thing on earth afterall and so on and so on... STOP



I thought I'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're in a state of almost wanting to bang your head against the wall because you can't figure out what it takes to get Adsense and that it makes you feel psychotic every time you start thinking about it.

Fine, but RolandC.net, what do I need to do to get fully accepted into the Adsense program?

The answer is both simple and short: BE TRULY USEFUL to someone (not just to yourself).

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't even listen to those who recommend having at least 20-25 quality articles.

And frankly, quality is not even the point here. Google does not need quality, it needs usefulness, hear problem solving, creating solutions.

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 

Of course, your content should reflect the fact that you've benefited from some sort of, quote on quote, education somewhere along the way, and your website/blog's visual identity should convey your own personal spirit or your product/services's spirit or whatever, but that's not to the point, the point being that your content should solve problems, bring solutions (and not just to you but to people that are actually real and that actually need your content, so that the reader went 'Oh, gee! thanks so much for your article, it really saved my day!' or 'Gosh, I didn't know that! I'm so happy I found your blog!' or even 'Wow, this is so interesting, I gotta try that on my own too!'.


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's your Adsense ID coming up right now! (well actually they first give you a link to download the Adsense app :))

Anyway, I'm not going to mention anything about the 'you have to be passionate' thing because it'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'll only have to sit on it afterwards waiting for Paypal® to come thru while sipping your beverage of choice on some sand beach somewhere, then this article wasn't for you to read, sorry.

Think about it this way, as an advertiser, would you invest money in people who don't have nothing helpful to tell (hear sell)?  Would you invest your hard-earned money in people who only keep sharing things they haven't even created themselves? Would you invest in people who'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.

Anyway I hope you've enjoyed reading this article, that it'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 DMOZ directory project to get ideas if you don't have any - link opens in a new window and they have a brand new mascot there, green mascot actually).

Thank you for reading and don't forget to like&share&subscribe to rss and you know...

Stay tuned for more on RolandC.net!


This article first appeared Thursday the 4th of August 2016 on RolandC.net.

Can't access your Hotmail pop email anymore? (24th June 2016)

Friday, June 24, 2016

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


Hotmail pop sync problems on rolandc.net
 
Hotmail pop sync problems on rolandc.net


I had also noticed that Hotmail's Webmail interface got a new paint job that day, and somehow I knew this had to be connected.

Right, it was !

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's pop email retrieved was now disabled by default!

So, here's what I found while visiting Hotmail's Webmail option page:


Hotmail pop sync problems on rolandc.net


Nice paint job huh?!

Just click on Yes and you will get your Hotmail email again as usual. No complicated two-step verification process involved or anything. That's as simple as that.

By the way, in case you can't figure out where the dialog box above came from, read on:

- Visit hotmail.com or I guess outlook.live.com is a shorter way here
- Click on the Settings menu (as found below)


Hotmail pop sync problems on rolandc.net


- Click on Options


Hotmail pop sync problems on rolandc.net

Then click on Mail -> Accounts -> POP and IMAP


Hotmail pop sync problems on rolandc.net


 Once there, don't forget to hit Save (a little above) and you're done.

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


This article first appeared Thursday the 16th of February 2016 on RolandC.net.

How to build your own Multilingual PHP MVC CMS from scratch - Part 6 - Language/Theme Handling

Tuesday, February 16, 2016

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.

During the last settlement, 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  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.

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.

Language handling


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




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.
 <?php  
   
      /* languages/encoding.php */  
   
      $encod = array(  
           'en' => 'utf-8',  
           'fr' => 'utf-8',  
           'uk' => 'utf-8',  
           'ru' => 'utf-8',  
           'de' => 'utf-8',  
           'nl' => 'utf-8',  
           'se' => 'utf-8',  
           'es' => 'utf-8'  
      );       
 ?>  

The file named translate.js contains a script that's needed to help localize all Javascript strings. Since I have already presented it in a previous article (http://www.sitepoint.com/localizing-javascript-strings-php-mvc-framework/), I will not need to do so here. 

Obvious is the fact that each language folder contains its own flag, in .png format.

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. 




Let's now take the example of registration_en.php
 <?php   
   
 /* languages/templates/en/registration_en.php */  
   
 $content = "  
 <html>  
 <head>  
 <title>Your registration</title>  
 </head>  
 <body>  
 <p>Thank you for your registration on \"".SITE_TITLE."\"!</p>  
 Your credentials are  
 <table>  
 <tr>  
 <td>Login : </td>  
 <th>".$login."</th>  
 </tr>  
 <tr>  
 <td>Pass : </td>  
 <th>".$password."</th>  
 </tr>  
 </table>  
 <p>  
 Regards,<br />   
 the site's team  
 </body>  
 </html>  
 ";  
 ?>  
Knowing HTML, you can all imagine what the output is going to look like in your favorite email client program. 

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.
 define("CONTACT_FORM_RECEIVER_LANGUAGE","en"); // the code of the language in which you wish to receive the user contact messages  
Now for the core language files that hold most of the PHP localized strings, let's take the example of en.php
 <?php  
   
 /* from languages/en/en.php */  
   
 $t_welcome = 'Welcome';  
 $t_contact = 'Contact';  
 $t_registration = 'Register';  
 $t_reg = 'Register';  
 $t_copyright = 'Copyright';  
 $t_submit = 'Submit';       
 $t_save = 'Save';  
 ...  
 ?>  
As an unspoken rule, localized PHP strings follow the format $t_my_word_or_sentence = 'My word or sentence'; 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.

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['t_welcome'], which turns out to be greatly convenient, for a number of reasons.

Although... I can hear some of you screaming 'globals? DO NOT USE GLOBALS!', 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't feel truly answers the specific needs I have been having for this project so far. 

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's easily and quickly implementable, even in large-scale environments, that'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: 'Don't repair it if it ain't broken!'.

But the truth is, within our project, there is much more to language handling than mere string localization. 

Let's start from the localization table and then go on with the language selector bar 
 <?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('no','yes') NOT NULL,  
  `localization_default` enum('no','yes') 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, 'se', 'se.png', 'no', 'no', '2014-10-24 19:17:26'),  
 (8, 'es', 'es.png', 'no', 'no', '2014-10-24 19:17:26'),  
 (5, 'ru', 'ru.png', 'no', 'no', '2014-10-24 19:17:26'),  
 (6, 'nl', 'nl.png', 'no', 'no', '2014-10-24 19:17:26'),  
 (3, 'fr', 'fr.png', 'yes', 'yes', '2014-10-24 19:17:26'),  
 (4, 'uk', 'uk.png', 'no', 'no', '2014-10-24 19:17:26'),  
 (1, 'en', 'en.png', 'yes', 'no', '2014-10-24 19:17:26'),  
 (2, 'de', 'de.png', 'no', 'no', '2014-10-24 19:17:26'),  
 (10, 'dk', 'dk.png', 'no', 'no', '2014-10-24 19:17:26');  
   
 --  
 -- 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;  
 ?>  
Nothing too unexplainable about this one, although it'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't changed.

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.
 <?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()->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  
           ?>   
                <a href="<?php echo LocModel::getInstance()->get_ln_straight($ln_item[0]); ?>"><img src="<?php echo CLEAN_PATH.'/'; ?>languages/<?php echo $ln_item[0]; ?>/<?php echo $ln_item[1]; ?>" width="<?php echo FLAG_WIDTH; ?>" height="<?php echo FLAG_HEIGHT; ?>" alt="<?php echo isset($GLOBALS['t_'.$ln_item[0].'_lng'])?$GLOBALS['t_'.$ln_item[0].'_lng']:"$ln_item[0]"; ?>" /></a> <?php   
           }  
           else{   
           ?>  
                <a href="<?php echo LocModel::getInstance()->get_ln_straight($ln_item[0]); ?>"><u><?php echo $ln_item[0]; ?></u></a> <?php   
           }  
      }  
 }  
 ?>  
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'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's consider the useful getData_language_bar() method
 <?php  
   
      /* from application/models/LocModel.php */  
        
      ...  
      public function getData_language_bar(){  
   
           $this->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's glob() function for that  
           $language_directories = glob(PATH_TO_LANGUAGES . '/*' , GLOB_ONLYDIR);  
           $language_admin_directories = glob('admin/'.PATH_TO_LANGUAGES. '/*' , GLOB_ONLYDIR);  
   
           $admin_language_list = array();  
   
           foreach($language_admin_directories as $admin_dir){  
                $exp_admin_dir = explode("/",$admin_dir);  
                array_push($admin_language_list, end($exp_admin_dir));  
           }  
   
           $language_list = array();  
   
           foreach($language_directories as $dir){  
   
                $exp_dir = explode("/",$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 = "SELECT id_localization, localization_name, localization_flag, localization_enabled   
                           FROM localization   
                           WHERE localization_enabled = 'yes'  
                           ORDER BY id_localization ASC                      
                          ";  
   
           MySQLModel::get_mysql_instance()->executeQuery($q_ln_bar);  
   
           while($mylnbar = MySQLModel::get_mysql_instance()->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['localization_name'], $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['localization_name'],$mylnbar['localization_flag']);  
                     array_push($this->locData,$line);$line = array();  
                }  
           }  
           return $this->locData;   
      }       
      ...  
   
 ?>  
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
 <?php  
      /* from application/models/LocModel.php */  
        
      ...  
      public function get_ln_straight($lang){  
        
           //using $_SERVER['QUERY_STRING'] will make our life a lot easier when it comes to dealing with clean URLs  
        
           if(empty($_SERVER['QUERY_STRING'])){ // if $_SERVER['QUERY_STRING'] turns out empty we use default values  
                if(DEFAULT_PLINK){$plink = '&'.PLINK.'='.CURRENT_PLINK;} else{$plink = '';}  
                $_SERVER['QUERY_STRING'] = RLINK.'='.CURRENT_RLINK.$plink.'&'.LN.'='.CURRENT_LANG;  
           }   
   
           $this->query_string = $_SERVER['QUERY_STRING'];  
           $pat = '/'.LN.'=[a-z]{2}/';  
           $pat2 = '/language=[a-z]{2}/'; //that'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->query_string, $matches, PREG_OFFSET_CAPTURE, 3)){  
                $rpl = LN.'='.$lang;  
                $this->query_string = preg_replace($pat, $rpl, $this->query_string);  
           }  
   
           if(preg_match($pat2, $this->query_string, $matches, PREG_OFFSET_CAPTURE, 3)){  
                $rpl2 = 'language='.$lang;  
                $this->query_string = preg_replace($pat2, $rpl2, $this->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't just preg_replace our query string, we explode it.  
                $exp_query_string = explode('&', $this->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  
                     'pn' => PGN,  
                     //note that 'pn' is what will be written in .htaccess  
                     //and PGN is the constant name that normally holds the page number url variable 'pgn'.   
                     // again, with clean urls being off, you could assign any other value than pgn to the constant PGN  
                     'sn' => 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("=",$atom); // we explode it (the atom)  
   
                     $needle = $exp_atom[0];     //and take its left part, called the needle in this case  
   
                     if(!preg_match('/success/',$atom)){// we avoid the word success in our algorithm for collision reasons       
                            
                          //we then search for that needle inside the haystack (marker's array)  
                            
                          if(in_array($needle, $markers) ){   
                               //if found, we will create a new clean atom with the marker in it.   
                               //because that'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.'/'.$exp_atom[1]; //and actually create the new 'clean' 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 '/' to implode all parts  
        
                $new_string = implode("/", $fake_string);  
                  
                //we quickly append our clean path constant and we're good to go  
                $this->query_string = CLEAN_PATH.'/'.$new_string;  
   
                //not just yet though... in case of Google CSE  
                if(preg_match('/cx=(.*)&gs=(.*)&sa.x=(.*)&sa.y=(.*)/', $_SERVER['REQUEST_URI'])){   
                     // so, if google search is on  
                     //we explode not the query string but the request URI this time  
                     $exp_q_s = explode('?',$_SERVER['REQUEST_URI']);  
                     $this->query_string .= '?'.$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've just built and now we're good to go.  
                }  
           }   
           else{  
                //else, if clean urls weren't set, we only need to append $_SERVER['PHP_SELF']   
                //instead of the clean path constant before the new string we have built   
                $this->query_string = $_SERVER['PHP_SELF'].'?'.$this->query_string;  
           }  
   
           return $this->query_string; //we return our resulting string  
      }  
 ?>  
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.  
 <?php  
      /* from application/models/LocModel.php */  
   
      ...  
      public function get_encoding_straight($lang){  
   
           require PATH_TO_LANGUAGES.'/encoding.php'; // 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->encoding = $encod[$lang]; we then use its corresponding value  
           }  
           else{$this->encoding = 'utf-8';} //else we default to utf-8  
   
           return $this->encoding; //we return what we have then  
      }  
 ?>  
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's being displayed in.

Theme handling


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's of course more to theme handling than this powerful routine. I justify:

Let's start by analyzing the overall structure of the theme folder




Then the same structure in more detail

 

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.

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.

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.

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).

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.

Entering the /themes/your_style/css folder again, we find there'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.
 
Back at the root level of the themes folder reside two of our common template files. The first one is header.php
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <?php  
 // here we sort out language code names to be compatible with most standards  
 if(CURRENT_LANG){  
      if(CURRENT_LANG == 'uk'){$tinymce_lang = 'uk_UA';}  
      elseif(CURRENT_LANG == 'fr'){$tinymce_lang = 'fr_FR';}  
      elseif(CURRENT_LANG == 'se'){$tinymce_lang = 'sv_SE';}  
      else{$tinymce_lang = CURRENT_LANG;}  
 }  
 else{$tinymce_lang = 'en';} // if nothing was found, we default to English  
 ?>  
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $tinymce_lang; ?>" lang="<?php echo $tinymce_lang; ?>">  
 <head>  
 <?php   
 $header_utils = new UtilsModel();  
 $met = $header_utils->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] == 'title'){ // if tag is title  
           echo '<title> ';   
           if(isset($page_name)){echo $page_name;}   
           else{  
                if(isset($module_name)){  
                     echo isset($GLOBALS['t_'.strtolower($module_name)]) ? $GLOBALS['t_'.strtolower($module_name)] : $module_name;  
                }  
           }  
           echo '</title>';   
           }  
      else{ //if not title tag, we use the rest of what we have pulled from the meta table.  
           if($m[0] == 'http-equiv'){   
                if($m[1] == "Content_Type"){$m[1] = "Content-Type";}   
                $char = " charset = ".LocModel::getInstance()->get_encoding_straight(CURRENT_LANG)."";  
                $m[2] .= $char;  
           }  
           echo '<meta '.$m[0].'="'.$m[1].'" content="'.$m[2].'" />';  
      }  
 }  
 ?>  
 <!-- now we link all the CSS/JS we need on our project -->  
 <link rel="shortcut icon" href="<?php echo CLEAN_PATH.'/'.PATH_TO_THEMES; ?>/<?php echo CURRENT_THEME; ?>/images/favicon/favicon.ico" type="image/x-icon" />  
 <link rel="stylesheet" href="<?php echo CLEAN_PATH.'/'.PATH_TO_THEMES; ?>/<?php echo CURRENT_THEME; ?>/css/<?php echo CURRENT_THEME; ?>.css.php" type="text/css" />  
 <link rel="stylesheet" type="text/css" href="<?php echo CLEAN_PATH.'/'.PATH_TO_THEMES; ?>/<?php echo CURRENT_THEME; ?>/css/dtree.css" />  
 <link rel="stylesheet" type="text/css" href="<?php echo CLEAN_PATH.'/'.PATH_TO_THEMES; ?>/<?php echo CURRENT_THEME; ?>/css/hmenu.css" />  
 <link rel="stylesheet" type="text/css" href="<?php echo CLEAN_PATH.'/'.PATH_TO_THEMES; ?>/<?php echo CURRENT_THEME; ?>/css/dialog_box.css" />  
 <link rel="stylesheet" type="text/css" href="<?php echo CLEAN_PATH.'/'.PATH_TO_THEMES; ?>/<?php echo CURRENT_THEME; ?>/css/dtp.css" />  
 <script src="<?php echo CLEAN_PATH.'/'; ?>languages/<?php echo CURRENT_LANG; ?>/JS/<?php echo CURRENT_LANG; ?>.js" type="text/javascript"></script>  
 <script src="<?php echo CLEAN_PATH.'/'; ?>languages/translate.js" type="text/javascript"></script>  
 <?php  
 include 'public/js/main.php';  
 include 'public/js/dtree.php';  
 ?>  
 <script src="<?php echo CLEAN_PATH.'/'; ?>/public/js/hmenu.js" type="text/javascript"></script>  
 <?php include 'public/js/dialog_box.php'; ?>  
 <script src="<?php echo CLEAN_PATH.'/'; ?>public/js/dtp.js"type="text/javascript"></script>  
 <!-- and in case Javascript is disabled, we use a noscript tag -->  
 <noscript><div class="disabled_feature"><?php echo $GLOBALS['t_javascript_has_been_disabled']; ?> <?php echo $GLOBALS['t_please_reenable_it']; ?> - <a href="http://www.enable-javascript.com" target="_blank"><u><?php echo $GLOBALS['t_instructions_here']; ?></u></a></div>  
 </noscript>  
 </head>  
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
 --  
 -- 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, 'title', 'title', 'Gumbo-CMS'),  
 (2, 'name', 'description', 'Gumbo (or Gumbo-CMS) is a Mutilingual MVC CMS written \'from scratch\' in PHP 5 and that requires Apache 2.2+ and MySQL 5.6+'),  
 (3, 'name', 'keywords', 'cms, mvc, php, multilingual cms'),  
 (4, 'name', 'robots', 'index, follow'),  
 (5, 'name', 'google_bot', 'index, follow'),  
 (6, 'name', 'google', 'notranslation'),  
 (7, 'http-equiv', 'Content_Type', 'text/html;');  
   
 --  
 -- 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;  
and we start looping the results we have successfully fetched.

A note about meta tags though, please visit https://support.google.com/webmasters/answer/79812?hl=en for a brief overview of the tags Google Inc. understands.

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'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. 

Meanwhile, in footer.php, the starting tag is a tr tag and the closing tag is the closing html tag
 <?php  
      /* themes/your_style/footer.php */  
 ?>  
      <tr class="footer_tr">  
           <td colspan="2" class="footer">  
                <?php echo $GLOBALS['t_copyright'].' '.COPYRIGHT; ?>  
           </td>  
      </tr>  
 </table>  
 </div>  
 </body>  
 </html>  
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.

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.

This article first appeared Thursday the 16th of February 2016 on RolandC.net.