How to prefix your routes with language code in October CMS

How to prefix your routes with language code in October CMS

Preface

Often enough I must localize the web app that I’m building. Usually it’s a quite tedious thing to code, but luckily for me October CMS already has a plugin that supports localization. The only real turn down would be that the language code is not persistent in the routes.

October is a free, open-source, self-hosted CMS platform based on the Laravel 5 PHP Framework.

I wanted my application to have this language code in the route as a prefix at all times. This is how I did it.


How to

It’s easy to implement this method, but to actually figure it out.. well, for me it was a few hours worth of headaches.

We will be building routes with this pattern:

  • http://example.com/en
  • http://example.com/en/page
  • http://example.com/ru/page
  • http://example.com/lv/page

First of all lets create a new plugin with

$ php artisan make:plugin mja.extensions

Once that’s created – go ahead and make a new file in the root directory of the plugin and call it routes.php. This code will be responsible for:

  1. Setting the locale back to the default if we are in the root route;
  2. Prefix all of the page URLs in our code ({{ ‘contact-us’|page }}).
use RainLab\Translate\Classes\Translator;

Route::matched(function($route, $request) {
    $translator = Translator::instance();
    if (!$translator->isConfigured())
        return;

    $locale = Request::segment(1);

    if ($translator->setLocale($locale) === false) {
        $translator->setLocale($translator->getDefaultLocale());
    }

    Route::getRoutes()->getByAction('Cms\Classes\Controller@run')->prefix($translator->getLocale() . '/');
});

Now that page URLs will be prefixed, we must prefix the app URLs aswell ({{ ‘/contact-us’|app }}). This can be accomplished by overwriting the existing twig extension. Add these methods to the Plugin.php file:

/**
 * Lets extend the app filter.
 * @return array
 */
public function registerMarkupTags()
{
    return [
        'filters' => [
            'app' => [$this, 'appFilter'],
        ]
    ];
}

/**
 * Extends the classic app filter
 * @param  string $url
 * @return string
 */
public function appFilter($url)
{
    return URL::to(Translator::instance()->getLocale() . '/' . $url);
}

Great! Now you can launch your app and see the magic!

But there’s more

This is just an extra, if you want to improve the language switcher. This will guarantee that the user stays on the same page after clicking the flag instead of him being redirected to the homepage.

Create a new file partials/localePicker/default.htm in your active theme.

==
function onStart() {
   $this->page['path'] = Request::path();
}
==
{% for code, name in locales %}
   <a href="{{ '/' ~ code ~ (this.page.path|slice(2)) }}">{{ name }}</a>
{% endfor %}

Fin

That’s it! Wasn’t so hard, right? Have a better solution? Be sure to post it below.

Kā var apskatīt, cik komitus katrs ir izdarījis aktīvajā Git repozitorijā (statistika)?

Kā var apskatīt, cik komitus katrs ir izdarījis aktīvajā Git repozitorijā (statistika)?

Darba gamifikācijai vai arī vienkārši, lai apskatītu skaitļus (heck, ja tu lieto Git, tad visdrīzākais, ka esi kādas pakāpes gīks – tātad tev patīk skaitļi), var izmantot šo vienkāršo Git komandu:

git shortlog -sne

Output:

    123  Matiss Janis Aboltins <matiss@mja.lv>
     12  Another User <email@example.com>
Efektīvs mācīšanās veids – validate learning

Efektīvs mācīšanās veids – validate learning

Mācīties nav viegli. Bet, vai šo procesu var padarīt vienkāršāku, ātrāku un patīkamāku? Jā, varam studēt dažādus produktivitātes padomus (grāmatas, rakstus). Bet pavisam ātri nonāksim pie savas dvēseles pārdošanas produktivitātes kultam.

Kā tas ir jāsaprot? Tu kļūsti tik produktīvs, ka katru dienu 9 stundas pavadi, lasot rakstus un grāmatas par produktivitātes uzlabošanu.

Ha! Bet šis raksts nav par produktivitāti, bet gan par mācīšanos, jeb citiem vārdiem sakot, mācīšanās metodi/ideju, ko esmu pamanījis dažādās industrijās. Ja tu esi lasījis Eric Reis “The Lean Startup”, tad tu zināsi par ko es runāju. Varbūt tu esi lasījis Timother Ferriss “The 4-hour chef”? Arī šajā grāmatā parādās šis pats princips. Protams, tās nav vienīgās grāmatas, kurās šis princips tiek atspoguļots. Katrā vietā to nosauc citādāk, bet tā esence paliek nemainīga.

Ja man būtu tam jādod vārds, tad tas būtu – apstiprinoša mācīšanās. Latviski tas izklausās patizli, tādēļ ir vieglāk to atcerēties pēc angļu termina – validate learning.

Ideja ir pavisam vienkārša – ja tu vēlies kaut ko iemācīties vai uzzināt, tad izveido testu pēc kura varēsi noteikt, vai konkrētā lieta ir apgūta. Protams, pirms mēs ķeramies pie testa izveides ir nedaudz jāievāc informācija par konkrēto prasmi. Ir jāizprot tās pamat-pamat principi pirms vari tai veidot testu.

Atgriežamies pie testa izstrādes. Paskaidrošu ar piemēru. Pieņemsim, ka es vēlos iemācīties spēlēt ģitāru. Pirmā lieta, ko es daru: izveidoju pavisam vienkāršu testu:

  • Vai es protu nospēlēt G akordu?
  • Vai es protu nospēlēt C akordu?
  • ———//——— D akordu?
  • ———//——— Em akordu?
  • Vai es protu pārlikt pirkstus no G akorda uz C?
  • Vai es protu pārlikt pirkstus no G akorda uz D?
  • ———//——— G akorda uz Em?

Tātad mācību plāns man ir pilnībā skaidrs un es zinu, kas man ir jāizdara, lai nokļūtu no punkta A (nemāku spēlēt ģitāru) līdz puntkam: Awesome (māku spēlēt ģitāru).

Šķiet ļoti vienkārši, vai ne? Jā, tās nav nekādas 10`000 stundas, kā arī tie nav 20% no šīm 10k stundām (stulbs pieņēmums, ka vajag 2000 stundas kaut ko darīt, lai to iemācītos). Ir cilvēki, kas argumentē, ka pilnībā pietiek ar 20 stundām un es viņiem pilnībā piekrītu!

Lean Startup ideja ir pavisam līdzīga.

  • Izdomājam hipotēzi;
  • Izdomājam kā pārbaudīt šo hipotēzi;
  • Ieviešam produktā izmaiņas;
  • Ievācam datus hipotēzes pārbaudei;
  • Atgriežamies 1. solī.

Nekaunies uzdot stulbus jautājumus un radīt stulbas hipotēzes. Tu būsi pārsteigts, ka nereti šīs idejas ir patiesas. Piemēram: vai tu zināji, ka izveidot 100 apsveikuma kartītes ir ātrāk, ja tās veido pa vienai nevis visas kopā (visas saloka, tad visas apraksta, tad visas aizlīmē utt.)? Pieņemu, ka netici. Nekas – pamēģini!

Tātad vēlreiz: bezmērķīgi mācīties ir grūti, bet mācīties nav grūti, ja tev ir konkrēts tests/plāns/mērķis (sauc kā vēlies), lai noteiktu esošo stāvokli. Šādi tu vari ne tikai apgūt kādu prasmi, bet arī vari labāk izprast un iemācīties daudz jaunas lietas par sevi un citiem.

Vēlies uzzināt vairāk? Izlasi The Lean Startup – laba grāmata arī tiem, kas neplāno veidot biznesu. Tos pašus principus var pavisam viegli izmantot arī ikdienas dzīvē.

PyroCMS arhitektūra – noderīgs e-pasts

PyroCMS arhitektūra – noderīgs e-pasts

Varbūt Tu varētu uzrakstīt vispārīgu aprakstu par PyroCMS arhitektūru, un/vai kas šā projekta ietvaros būtu jāņem vērā?
Būtībā saprotu, ka visa programmēšana notiek pa addons/shared_addons mapi, un kāda tur ir vispārīgā hierarhija? Tur kaut kāds sava veida HMVC tiek realizēts?

PyroCMS ir būvēts ar CodeIgniter PHP framework. Konkrēti CI faili atrodas /system/codeigniter/ mapē, bet PyroCMS lietas atrodas /system/cms/ mapē. system/ folderī nevajadzētu likt neko jaunu, vai mainīt (ja nu vienīgi /system/cms/config/).

Pārsvarā strādājam veidojot dažādus Pyro moduļus /addons/shared_addons/modules.

Dizaina darbības faili atrodas /addons/shared_addons/themes/{theme_name}/. Tur ir gan view faili, gan CSS, JS un bilžu faili. Apskatot šo mapju saturu, domāju, ka struktūru būs skaidra.


Ā, un varbūt kaut ko vari pastāstīt arī par template sistēmu un view’u realizēšanu?

View faili atrodas /addons/shared_addons/themes/onfrex/views/  mapē. Tur ir vēl 3 mapes:

  • modules – moduļu view faili;
  • layouts – jeb moduļu templeiti (vecāka viewi);
  • partials – visādi sīki nieciņi, ko vajag kaut kur iekļaut.

Arī /shared_addons/modules/{module_name}/views/ ir view faili. Šie tiek izmantoti tad, ja theme mapē nav šis view fails pārrakstīts – ja tur tāds neeksistē. Tātad, ja vajag labot kādu no /modules/{module_name}/views/ failiem, tad to pārkopējam uz /themes/{theme_name}/views/modules/{module_name}/ un tur arī labojam.

6 PyroCMS speed improvement tips

6 PyroCMS speed improvement tips

PyroCMS is a content management system that’s built on Codeigniter PHP framework. For the last few years I’ve been developing on it. It has a really clean and modular codebase, but, if you use this beast improperly, it can become extremely slow. So here are a few tips on how to create fast sites with PyroCMS.

1. Enable production environment.

This simple, but yet so often forgotten task is a must do for sites that are in production mode (live for client to see).

To enable production environment you have to add this line to your .htaccess file.

SetEnv PYRO_ENV production

Or alternatively you can use this line to enable it only on a certain domain.

RewriteCond %{HTTP_HOST} ^sub.domain.com$
RewriteRule (.*) $1 [E=PYRO_ENV:production]

What does this do?

  • Enables the asset (JS, CSS) minimization;
  • Enables the asset (JS, CSS) compression;
  • Enables HTML output minimization;
  • Disabled error output (but logging will still be working);
  • Enables the usage of different config files for different environments (but this is not necessarily a speed improvement).

2. Get to know the asset config file

Located in: system/cms/config/asset.php.

Once you’ll open this file, you will a lot of comments. This file is documented brilliantly, so I don’t see much that I could explain here.

Probably the only thing that they forgot to document is the asset_filepath_callback config variable. It’s a callback that get’s called once a filename has been built. I use it to append timestamp to the asset files and I’d suggest you to do the same.

This is my code:

$config['asset_filepath_callback'] = function ($filepath, $type, $remote) {
    return $filepath . '?ts=' . filemtime($filepath);
};

3. Understand the difference between asset and theme plugins.

Do you know what’s the difference between these two lines?

{{ theme:css file="my-file.css" }}
{{ asset:css file="theme::my-file.css" group="global" }}

At a first glance they may seem quite identical, but in fact they are not so.

Lets look at the theme plugin..

<!-- The plugin.. -->
{{ theme:css file="my-file.css" }}

<!-- Results in this output -->
<link href="/addons/shared_addons/themes/theme/css/my-file.css" type="text/css" rel="stylesheet" />

Nothing strange about this, right? Everything looks as it if should, as it was expected to work.

Bet now lets look at the asset plugin (please take in consideration that we have production environment enabled).

<!-- The plugin.. but lets use many css files -->
{{ asset:css file="theme::my-file.css" group="global" }}
{{ asset:css file="theme::page.css" group="global" }}
{{ asset:css file="theme::typography.css" group="global" }}

<!-- Output the css files -->
{{ asset::render_css group="global" }}

<!-- The output -->
<link href="/assets/cache/0123gs2r2zf1.css" type="text/css" rel="stylesheet" />

Wait, why did it output only one CSS file? And why does it have such a weird filename?

That’s because by using the asset plugin in production environment the CSS (or JS) files get minimized and are combined together in a single file.

This is a really powerful feature of PyroCMS, but – with great power comes great responsibility. Use this feature wisely and don’t over-use it.

4. Enable DB caching

Another simple, but powerful speed improvement is to cache your database query results. This can be done by tweaking a bit the database.php config file.

// Production
$db[PYRO_PRODUCTION] = array(
    'hostname'      =>  'localhost',
    'username'      =>  'root',
    'password'      =>  '',
    'database'      =>  'pyrocms',
    'dbdriver'      =>  'mysqli',
    'pconnect'      =>  FALSE,
    'db_debug'      =>  FALSE,
    'cache_on'      =>  TRUE,
    'cachedir'      =>  'system/cms/cache/db-cache/',
    'char_set'      =>  'utf8',
    'dbcollat'      =>  'utf8_unicode_ci',
    'port'          =>  '3306',
);

Please notice the cache_on and cachedir lines. They are responsible for the magic.

Note: Don’t forget to create the new db-cache folder.

5. Disable the installation check hook.

This is dead simple, but often developers forget about it. After we’ve installed PyroCMS – we should disable the installation check hook. It will not give you a huge boost in speed, but every small bit counts.

The installation hook can be found in system/cms/config/hooks.php. It should look somewhat like this:

$hook['pre_controller'][] = array(
    'function' => 'check_installed',
    'filename' => 'check_installed.php',
    'filepath' => 'hooks'
);

So you just have to comment it out, or remove it completely.

6. Enable GZip compression

Read about it here.

My .htaccess file addition:

# ==== GZIP =====
# compress text, html, javascript, css, xml:
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript

# Or, compress certain file types by extension:
<files *.html>
SetOutputFilter DEFLATE
</files>
# ==== GZIP END =====

Anything else?

Did I forget about something? Perhaps you know another way how we could improve the speed of our site drastically? Feel free to leave a comment to educate me and others.

Sociālas eksperiments – bezmaksas Coffee Inn kafija

Sociālas eksperiments – bezmaksas Coffee Inn kafija

TL;DR: Kolektīva Coffee Inn bonusu kartiņa, kuru var izmantot, lai iegūtu bezmaksas kafiju.

Vai tu esi kādreiz dzirdējis par Jonathan’s Card? Tā ir Starbucks klienta karte, kurā var ieskaitīt naudu un turpmāk izmantot to, lai iegādātos kafiju. Būtībā tāda, kā papildināma dāvanu karte.

Šī karte pavisam ātri ASV kļuva populāra. To izmantoja vairāki simti cilvēku! Diez, vai Latvijā kaut kas tāds nostrādātu?

Varam mēģināt! Šajā lapā ir informācija par to, kā tu vari piedalīties šādā sociālajā eksperimentā. Atšķirībā no Jonathan’s Card, šajā kafijas kartiņā krājas bonusa punkti par katru pirkumu. Piemēram, ja tu nopērc Late par 2 EUR, tad kartei pieskaitās 0.20 EUR.

Būtībā katra 10 kafija sanāk bezmaksas, bet – neviens nezin, cik šajā kartē ir naudiņa, tātad arī neviens nevar pateikt, kurš būs šis laimīgais bezmaksas kafijas uzvarētājs.

Kādēļ nepamēģināt? Kādēļ nepievienoties? Tas jau nevienam neko nemaksā, un pie tam varbūt tieši tu būsi tas, kuram paveiksies nopirkt bezmaksas kafiju!

Pievienoties eksperimentam Atceries padalīties ar šo lapu, ja arī tu piedalies šajā eksperimentā. Jo vairāk cilvēki piedalīsies, jo labāk mums visiem!

How I hacked Airbaltic to get cheap flights

How I hacked Airbaltic to get cheap flights

TL;DR: This is a story of how I hacked Airbaltic’s cheap flight calendar to find even cheaper offers.

Screenshot from 2014-03-06 20:20:21

Airbaltic site has an interesting feature – a low fare calendar. One can choose the boarding and landing airport and see if there are any cheap flights in the selected month. All of these prices change daily. Perhaps even hourly – I’m not entirely sure.

But how can we make this even better? How can we use the calendar to find out about bargains to our desired location? This is where my hacking came in to play.

At first I had to find out all of the airport codes. This was not a hard task. To be honest – quite frankly it was as simple as taking a candy from a kid. All I had to do was open up the developers console and view the network tab. I was able to see the data that must be sent to a AJAX url and the URL itself.

Screenshot from 2014-03-06 20:19:02

Now that I had the airport codes, I had to get the prices. This task was basically the same as getting the codes, but… yes, the prices aren’t sent to the client in a JSON array. The JSON response contains HTML code. Hmm? Why would they send so much useless data? Why not simply send the prices+dates and then build the HTML on the client side?

Ah, who knows.. anyways, to get the prices I had to parse the HTML. That was not a difficult task (Ha, even for me.. a person who’s terrible at RegExp).

Screenshot from 2014-03-06 20:23:59

So now that I had aggregated all of this data, what could I do with it? Sort it by the price and see when are the cheapest flights and to where. I can store the data somewhere and pull it daily to get a sense of how the prices change, and perhaps see a pattern. Who knows.

Anyways, with this – bare price/date data – I can find quite a few bargains. For example – 30 EUR for a flight to Germany? Why not?


If you’re interested in the code go to this github repo.

Kā extendot PyroCMS base-functionality vai moduļus

Kā extendot PyroCMS base-functionality vai moduļus

Nemelošu PyroCMS nav ieplānota native moduļu extendošana. Tas ir milzīgs pain-in-the-ass, jo bieži vien esošā funkcionalitāte neapmierina.

Jebkurā gadījumā – ir iespējams extendot šos moduļus, bet tas nav pavisam vienkārši. Šajā rakstā centīšos paskaidrot, kā to pēc iespējas nesāpīgāk izdarīt. Iespējams, ka vairākas lietas var izdarīt labāk/ātrāk/vienkāršāk. Ja tev ir kādi ieteikumi – labprāt tos uzklausīšu.


Pirms uzsākam kaut ko darīt – pamainīsim failu ielādes kārtību. Par cik mēs vēlamies, lai mūsu overwrite klases tiktu ielādētas pirms īstajām PyroCMS klasēm, ir jāpamaina ielādes kārtība.

To var izdarīt pavisam vienkārši (bet nekur tas nav nodokumentēts…). Faila cms/config/config.php pašā apakšā tiek uzsetots mainīgais modules_locations. Šis kalpo arī kā failu ielādes kārtība. Tātad, mums vajadzīgā kārtība būtu šāda:

#!/bin/php
$config['modules_locations'] = array(
    ADDON_FOLDER.'__SITE_REF__/modules/' => '../../../addons/__SITE_REF__/modules/',
    SHARED_ADDONPATH.'modules/' => '../../../addons/shared_addons/modules/',
    APPPATH.'modules/' => '../modules/',
);

Jeb citiem vārdiem sakot – meklējam addons/default/ mapē failus, tad addons/shared_addons/ un tikai tad system/cms/.

modules/{name}/config/

Neesmu iedziļinājies (nav bijusi vajadzība), kā šo sadaļu var extendot. Ja tev ir idejas – droši raksti.

modules/{name}/controllers/

Šī ir viena no svarīgākajām daļām, ko extendosim. Ideja ir pavisam vienkārša:

  1. Ielādējam jauno MY_{controller_name}.php failu
  2. Extendojam veco {controller_name}.php failu
  3. Live hapily ever after

Kā to izdarīt?

Izveidojam jaunu routes ceļu uz šo kontroleri

Par cik mēs vēlamies extendot default kontroleri nevis to pilnībā pārrakstīt (overwrite), tad nevaram arī izmantot defaultā kontrolera vārdu (jo @PHP nevar būt divas klases ar to pašu vārdu).

Tātad, lai izmantotu kontroleri ar citu vārdu, mums ir jāizveido route, kas norādīs uz šo, jauno kontroleri.

system/cms/config/routes.php

#!/bin/php
// We still want to use this
$route['admin/navigation/groups/(:any)']  = 'navigation/admin_groups/$1';

// Overwrite the default core modules with a custom one (if one exists)
$route['admin/navigation/(:any)']         = 'navigation/my_admin/$1';
$route['admin/navigation']                = 'navigation/my_admin/index';

Šis ir piemērs navigation moduļa admin kontrolerim. Par cik eksistē arī admin_groups kontroleris un to mēs ne-extendojam, tad ir arī jānorāda, ka šajā gadījumā izmantosim veco kontroleri.

Izveidojam jauno kontroleri

addons/shared_addons/modules/{name}/MY_{controller_name}.php

#!/bin/php

// Get the parent
require(set_realpath(APPPATH . 'modules/navigation/controllers/admin.php'));

/**
 * Extends the PyroCMS navigation admin controller
 */
Class MY_Admin extends Admin {

    private $validation_rules = array(
        /**
         * There are actually more rules here. Most
         * of them are located in navigation/admin.php
         * constroller.
         *
         * You can set custom ones right here if your
         * heart desires to do so.
         */
    );

    /**
     * Constructor
     */
    function __construct()
    {
        parent::__construct();

        // Set more rules
        $this->form_validation->set_rules($this->validation_rules);

        // And of curse.. an overwrite for the model
        $this->navigation_m = $this->load->model('MY_navigation_m');
    }

    public function edit($id = 0)
    {
        $this->template->set('variable', 'Hello World');

        // Call the parent with the proper params
        call_user_func_array(array($this, 'parent::edit'), func_get_args());
    }
}

Domāju, ka nav jāpaskaidro šis, sample kods. Būtībā visas šīs sample metodes var aizvākt un aizstāt ar citām, vajadzīgajām, bet, navigation_m mainīgā setošanu gan nevajadzētu aizvākt (paskaidrošu sekcijā par moduļu ekstendošanu).

Kā redzam, My_Admin klase extendo Admin klasi, kas tiek ielādēta ar require() funkcijas palīdzību nedaudz augstāk.

Funkcija call_user_func_array() tiek izmantota, jo var sanākt, ka nākotnē PyroCMS edit() metodei pieliek vēl kādu argumentu. Šī funkcija nodrošina to, ka edit() metode strādās neatkarīgi no argumentu daudzuma.

Protams, var darīt arī šādi, bet tad jāuzmanās no nākotnes apgreidiem, lai tie neko nesaplēš.

#!/bin/php
public function edit($id = 0)
{
    parent::edit($id);
}

modules/{name}/language/

Neesmu iedziļinājies (nav bijusi vajadzība), kā šo sadaļu var extendot. Ja tev ir idejas – droši raksti.

modules/{name}/libraries/

Neesmu iedziļinājies (nav bijusi vajadzība), kā šo sadaļu var extendot. Ja tev ir idejas – droši raksti.

Iespējams, ka MY_{library_name}.php triks strādās arī te (līdzīgi kā ar kontroleriem). T.i. – Class MY_{library_name} extends {library_name} {.

modules/{name}/models/

Modeļus extendot var līdzīgi kontroleriem (bet, protams, bez routinga). Tātad..

addons/shared_addons/modules/{name}/models/MY_{model_name}.php

#!/bin/php

if (class_exists('navigation_m') === FALSE)
    get_instance()->load->model('navigation/navigation_m');

/**
 * Extend/overwrite the default functionality of
 * navigation_m model
 */
Class MY_Navigation_m extends Navigation_m
{
    function __construct()
    {
        parent::__construct();
        get_instance()->navigation_m = $this;
        
        public function delete_link($id = 0)
        {
            parent::delete_link($id);

            // My custom functionality goes here

            return TRUE;
        }
}

modules/{name}/views/

View faili nenodrošina extendošanu. Es neredzu situāciju, kad tā būtu vajadzīga.

View faili nodrošina overraidošanu. Tātad, ja vēlamies izveidot jaunus formas laukus vai citādāk pamainīt defaulto HTML (piemēram, admin panelī), tad droši izveidojam identisku failu jau moduļu struktūrai un strādājam tajā.

T.i. addons/shared_addons/modules/navigation/views/index.php = system/cms/modules/navigation/views/index.php. Strādājam shared_addons index failā.

modules/{name}/plugin.php

Pluginus extendot ir, manuprāt, vissarežģītāk. Daudzas metodes šajos failos ir private vai protected, tātad tās nevēlas, lai kāds jauktos kaut kur pa vidu.

Vienīgais veids, kā man sanāca extendot šo lapas sadaļu bija duplicējot kodu. Citiem vārdiem sakot – nokopēju visu veco funkcionalitāti un iekopēju jaunā failā.

  • Faila atrašanās vieta: addons/shared_addons/modules/{name}/plugin.php
  • Klases header: Class MY_{name}_Plugin extends Plugins {

Diemžēl šī funkcionalitāte nestrādās out-of-the-box. Ir jāveic modifikācijas Plugins.php bāzes bibliotēkai, lai:

  1. Tiktu izmantots MY_{name}_Plugin (preferably) nevis {name}_Plugin
  2. Tiktu ielādēts šis jaunais plugin fails (by default ielāde nenotiek pareizajā secībā – šī secība ir hardkodēta)

Tātad jaunais fails:

addons/shared_addons/libraries/Plugin.php

#!/bin/php

Class MY_Plugins extends Plugins {

    private $loaded = array();

    public function __construct()
    {
        parent::__construct();
        $this->_ci = & get_instance();
    }

    public function locate($plugin, $attributes, $content)
    {
        if (strpos($plugin, ':') === FALSE)
        {
            return FALSE;
        }
        // Setup our paths from the data array
        list($class, $method) = explode(':', $plugin);
        $locations = $this->get_locations();

        foreach ($locations as $directory)
        {
            if (file_exists($path = $directory.'plugins/'.$class.'.php'))
            {
                return $this->_process($path, $class, $method, $attributes, $content);
            }

            else {
                if (defined('ADMIN_THEME') and file_exists($path = APPPATH.'themes/'.ADMIN_THEME.'/plugins/'.$class.'.php'))
                {
                    return $this->_process($path, $class, $method, $attributes, $content);
                }
            }

            // Maybe it's a module
            if (module_exists($class))
            {
                if (file_exists($path = $directory.'modules/'.$class.'/plugin.php'))
                {
                    $dirname = dirname($path).'/';

                    // Set the module as a package so I can load stuff
                    $this->_ci->load->add_package_path($dirname);

                    $response = $this->_process($path, $class, $method, $attributes, $content);

                    $this->_ci->load->remove_package_path($dirname);

                    return $response;
                }
            }
        }

        log_message('debug', 'Unable to load: '.$class);

        return false;
    }

    private function _process($path, $class, $method, $attributes, $content)
    {
        $class = strtolower($class);
        $class_name = 'Plugin_'.ucfirst($class);

        if ( ! isset($this->loaded[$class]))
        {
            include $path;
            $this->loaded[$class] = true;
        }

        if ( ! class_exists($class_name) && ! class_exists('MY_' . $class_name))
        {
            //throw new Exception('Plugin "' . $class_name . '" does not exist.');
            //return FALSE;

            log_message('error', 'Plugin class "'.$class_name.'" does not exist.');

            return false;
        }

        if (class_exists('MY_' . $class_name))
        {
            $class_name = 'MY_' . $class_name;
        }

        $class_init = new $class_name;
        $class_init->set_data($content, $attributes);

        if ( ! is_callable(array($class_init, $method)))
        {
            // But does a property exist by that name?
            if (property_exists($class_init, $method))
            {
                return true;
            }

            //throw new Exception('Method "' . $method . '" does not exist in plugin "' . $class_name . '".');
            //return FALSE;

            log_message('error', 'Plugin method "'.$method.'" does not exist on class "'.$class_name.'".');

            return false;
        }

        return call_user_func(array($class_init, $method));
    }

    /**
     * Set the proper loading order
     * @return array
     */
    public function get_locations()
    {
        $return = array();
        $locations = $this->_ci->config->item('modules_locations');

        foreach ($locations AS $loc => $val)
        {
            if (strpos($loc, SHARED_ADDONPATH) !== FALSE)
            {
                $return[] = SHARED_ADDONPATH;
            }
            else if (strpos($loc, ADDON_FOLDER) !== FALSE)
            {
                $return[] = ADDONPATH;
            }
            else if (strpos($loc, APPPATH) !== FALSE)
            {
                $return[] = APPPATH;
            }
        }

        return $return;
    }
}

Kā redzam – tiek dublēta jau esošā PyroCMS Plugins.php funkcionalitāte. Diemžēl to apiet nevaram. Lai nu kā, šī faila jauninājumi: get_locations() && MY_ klašu meklēšana.

cms/core/

Iespējams, ka šo var extendot, izveidojot addons/shared_addons/core/ mapi un tur ieliekot MY_{classname}.php failu (varbūt pat MY_MY_{classname}, lai veiktu trešā līmeņa extendošanu?). Neesmu šo iztestējis. ja zini kā to izdarīt – droši raksti.

cms/libraries/

Skatīt sadaļu nedaudz augstāk par plugins.php extendošanu. Ideja pavisam vienkārša: šajā mapē varam likt MY_{classname}.php klases. Tās tad arī extendos esošās, default klases.

cms/helpers/

Iespējams, ka MY_{helper_name}.php strādās. Jāiztestē.

Laravel 3.x authorization system

Laravel 3.x authorization system

Recently I started using Laravel PHP Framework and I must say – it is one of the most modular, orthogonal and CLi (command line) friendly frameworks out in the wild. CodeIgniter is nothing compared to this amazing piece of art.

But… as much as I love Laravel, it has a few downsides compared to other frameworks. One of the largest turndowns is the lack of community and stupid questions. What do I mean by that? Well, there are plenty of “How do I do X?” CodeIgniter questions out there and it’s easy to just google for a solution, but Laravel doesn’t have such. Therefore it is a wee-bit harder for newbies to adapt to this framework.

Never the less, this is a tutorial.. a guide of how to build a simple authorization system with Laravel. Let’s jump right in, shall we?

1. Create the User table

Obviously this is the first thing that we are supposed to do. I assume that you already have set up your database.

Lets create a new migration to create this awesome user table. Run this command in CLi.

<code>php artisan migrate:make create_user_table
</code>

Now open up the new migration at application/migrations/…_create_user_table.php and add the table creation and deletion queries.

It should look something like this:

<code>public function up() {
    Schema::create('users', function($t) {
        $t-&gt;increments('id');
        $t-&gt;string('username', 30);
        $t-&gt;string('password', 30);
        $t-&gt;timestamps();
    });
}

public function down() {
    Schema::drop('users');
}
</code>

Once that’s done: just run the migration in CLi.

<code>php artisan migrate
</code>

2. Create User model

I assume that you’re using Eloquent ORM. Don’t know what’s that? Well, then you’re using it, let’s not worry about the details.

Create the model application/model/users.php.

<code>&lt;?php
class User extends Eloquent { }
</code>

It doesn’t have to have anything in it just now. You may (and should!) use it in the future, but for now we simply need it to be able to run the Laravel Auth class.

3. Configuration

The first and obvious thing to point is the application/config/auth.php configuration file. Go ahead and have a look at what it offers.

Did you open it up? Did you look thought it? Good.. let’s continue.

Now we should create and add out auth filter to routes. Actually, this is really simple. Let’s open up the application/routes.php file.

At the bottom of the file you should see something like this:

<code>Route::filter('auth', function() {
    if (Auth::guest()) return Redirect::to('login');
});
</code>

If it’s not in the file – add it as we will need it. Basically it’s a filter that we can call whenever we want. It checks if the user is a guest (not logged in) and redirects the user to the login page.

To activate this filter in our routes we simply have to add a _before_ param with the filters name to our route. For example:

<code>Route::get('/', array('before' =&gt; 'auth', 'uses' =&gt; 'home@index'));
</code>

So, whenever a user enters the / (root) page – the authorization filter will be called. If it passes – we show the index method of the home controller.

But of course, we don’t want to use this filter for all routes.

<code>Route::get('login', array('uses' =&gt; 'login@index'));
</code>

Or even shorter

<code>Route::controller('login');
</code>

4. Manage the logins and registrations

Further reading: Auth class

Geolokācijas spēles ideja

Geolokācijas spēles ideja

TL;DR: Iepazīsti un izpēti savas mājvietas apkārti ar šo geolokācijas spēli.

Spēles ideja ir pavisam vienkārša – es (lietotājs) kartē atzīmēju savu mājvietu. Turpmāk katru dienu tiks izveidoti un uz kartes izvietoti 10 punkti max 4km attālumā no manām mājām. Šie punkti tiek izvēlēti pēc pilnīga nejaušības principa, tātad tie var arī būt ezerā, meža vidū, uz šosejas vai kaimiņa pagalmā. Citiem vārdiem sakot – dažādās dīvainās, neierastās vietās.

Interesanti ir tas, ka šie punkti visiem izvietojas vienādi. Formula:

Mājvietas koordinātes + X,
kur X = visiem vienāds random skaitlis

Tātad tas, cik labā vai sliktā vietā šie punkti iekrīt, ir pilnībā atkarīgs no spēlētāja mājvietas atrašanās vietas.

Mērķis: dienas laikā apmeklēt pēc iespējas vairāk šos punktus. Katrā punktā uzņemam bildi un ieposto onlainā. Katru nedēļu tiek paziņoti visaktīvākie dalībnieki.

Šī ir ideāla spēle piedzīvojumu meklētājiem un tiem, kuri vēlas nedaudz vairāk izpētīt savas mājvietas apkārtni. Apsolu, ka jūs nokļūsiet vietās, kur nekad neesat bijuši, jo nav bijis iemesls tur būt (Bet tagad gan ir!).

Tehniskās lietas

Arī pati izstrāde ir ļoti vienkārša. Priekš MVP jāsaglabā ir tikai:

  • lietotāja mājas lokācija (@Sessijas vai @DB)
  • šodienas koordinātes (@DB)
  • lietotāju iesūtītie attēli

Nepieciešamā funkcionalitāte:

  • Google maps ar šodienas punktiem
  • Lietotājam iespēja atzīmēt mājas atrašanās vietu
  • CRONJob 1x dienā, kas ģenerē jaunas koordinātes
  • Image upload

Fin

Secinājums: Izveidojam MVP un sākot ar 1. jūniju dodamies mazajos piedzīvojumos!

Ps. – pašlaik ir jau pietiecies bariņš ar draugiem, kuri labprāt izmēģinātu. Varbūt arī tu vēlies?