Quick jump:  What's new

 
Go to:
 
Weblog: docs   
in STRIX
DozvoleACL

Dozvole i ACL

Nasljedjivanje

Dozvole se definiraju nad nekom jedinicom sadržaja - site, kategorija/stranica, vijest, dokument.

Dozvole se daju korisniku ili grupi korisnika.

Dozvole mogu biti READ, WRITE, AUTHOR/SUGGEST, ADMIN/OWN itd. (Skup dozvola nije ograničen i po potrebama se može proširiti.)

Dozvola korisniku je jača od dozvole grupi. Ako je dozvola dana ili oduzeta korisniku zanemaruju se dozvole koje bi taj korisnik imao kao pripadnik grupama kojima je dana dozvola.

Kako postoji hijerarhija jedinica sadržaja tako postoji i nasljeđivanje dozvola.

Ako je jednoj grupi ili korisniku definirana dozvola više puta u više razina, u obzir se uzima dozvola definirana hijerarhijski najbliže jedinici sadržaja, ali opet se uzima pravilo: dozvola korisniku je jača od dozvole grupi. Npr. ako je Peru nad siteom definirano pravo READ, ali mu je nad konkretnim dokumentom PeroNemaPravo.doc zabranjeno READ, on neće moći pristupiti tom dokumentu.

Alternativni opis

Postoje tri pojma:

SUBJEKT = korisnik, grupa korisnika

OBJEKT = dokument/folder

PRAVO = za dokumente READ ili OWN, za foldere READ, SUGGEST, AUTHOR, OWN (svako od prava ima vrijednost DA ili NE)

Jedan zapis u ACL-u mora imati definirane sve tri vrijednosti.

Vrijede pravila:

• PRAVO korisniku je jače od PRAVA grupi. Ako je PRAVO dano ili oduzeto (vrijednost DA ili NE) korisniku, zanemaruju se PRAVA koje bi taj korisnik imao kao pripadnik grupama kojima je dana dozvola.

• ACL se može nasljeđivati s jedne razine na drugu (npr. sa foldera na podfolder ili dokument što je defaultno ponašanje document managementa)

• Ako je jednoj grupi ili korisniku definiran ACL više puta u više razina, u obzir se uzima PRAVO definirano hijerarhijski najbliže jedinici sadržaja (dokumentu ili folderu), ali opet se uzima pravilo: PRAVO korisniku je jače od PRAVA grupi.

• Zabrana (PRAVO=Ne) u kontekstu korisnika je jače od dozvole (PRAVO=Da)

• U kontekstu grupe, dozvola (PRAVO=Da) je jače od zabrane (PRAVO=Ne)

Ostalo

Za sada, možete pogledati AclHacks za hands-on stvari koje možete napraviti direktno u bazi.

Hacking ACL

Ovo je quick guide kako napraviti zanimljive stvari sa ACL-ovima.

Nasljeđivanje ACL-ova za kategorije

Kako pogledati koja ktegorija nasljeđuje od koje?

select
  (select url from kategorija where id = parent_object_id),
  (select url from kategorija where id = object_id)
from acl_inherits
where acl_id = 'kats' and parent_acl_id = 'kats'


Prava pristupa i dozvole za sadržaje

select UserCanDoOnObject(user_id, perm, acl_id, object_id);

perm određuje pravo. Postojeće permisije mogu se pogledati sa:

 strix=# select * from perms order by flag ;

flag mark
1 PERM_READ
2 PERM_WRITE
2 PERM_SUGGEST
4 PERM_AUTHOR
8 PERM_ADMIN
8 PERM_OWN

Neke od njih su aliasi tako da se iste dozvole mogu koristiti u razlicitim kontekstima.

Kratko objašnjenje kako su zamišljene osnovne razine dozvola u STRIXu:

  1. PERM_READ - korisnik s tom dozvolom ima pravo pristupa (u kontekstu kategorije tj. stranice) odnosno pravo čitanja sadržaja (u kontekstu jedinice sadržaja)
  2. PERM_WRITE ili njezin alias PERM_SUGGEST (alias u kontekstu dozvola na foldere) - korisnik ima pravo predlaganja sadržaja, glasanja, komentiranja, postavljanje pitanja. Predloženi sadržaj se ne objavljuje direktno nego se čeka dozvola administratora stranice koji za taj sadržaj može dozvoliti objavu ili odbiti.
  3. PERM_AUTHOR - korisnik ima pravo direktne objave svog contenta bez posredstva administratora. Korisnik s tom dozvolom ne može mijenjati tuđi content, ali može svoj.
  4. PERM_ADMIN - korisnik je administrator stranice.

acl_id su tipovi sadržaja. Mogu se pogledati sa:

 strix=# select * from acl_register ;

id table_name id_col_name detail_col_name description
grup grupe id naziv Group
site site id naziv Site
kats kategorija id naziv Category
file document id title File
news news id title Vijest
faq faq_qanda id question FAQ
foru f_topics id title Forum
cale calendar id title Calendar

object_id je identifikator objekta (obično primarni ključ) objekta za koji se provjerava permisija.

Primjer korištenja:

select UserCanDoOnObject(1, 'PERM_ADMIN', 'news', 42 );

permalink
Uvod

Uvod u STRIX engine

Svaka web stranica koju engine generira i koja ima svoj URL se strogo veže uz jednu kategoriju (kategorija). Za svaku stranicu, tj. kategoriju engine učitava iz baze njen izgled i sadržaj. Izgled stranice je određen pre-definiranim glavnim templateom (template) i rasporedom modula unutar nje. Moduli (modules) su funkcionalno neovisni dijelovi web stranice koji generiraju konačan sadržaj. Za svaku kategoriju, moduli se po želji odabiru i raspoređuju unutar nje (layout).

Više uređenih kategorija čini jedan site (site). Site služi za zgodno grupiranje npr. prava pristupa ili dizajna.

Engine podržava do 3 uređene (poredane) liste modula (npr. za lijevi, srednji i desni stupac).

Podržana je i jedna ne-uređena (slobodna) lista modula, čija pozicija prikaza je definirana unutar glavnog templatea (npr. glavna navigacija, baneri, footer, breadcrumbs, datum i vrijeme i slične). Ovi moduli će se prikazivati na svim stranicama koje imaju isti template.

 Module je moguće definirati i za cijeli site, u kojem slučaju se oni prikazuju iznad ili ispod modula definiranih za pojedinu stranicu (pre_layout).

 Prilikom generiranja stranice, engine dohvati sve instance modula za trenutnu kategoriju, izvrši ih, a njihove generirane sadržaje posloži u glavni template.

Opisi glavnih relacija

kategorija

id unique id svake kategorije
url uniqur url svake kategorija
naziv puni naziv (ime) kategorije
knaziv kratki naziv koji se ispisuje u navigaciji
customize - ne koristi se
flags - 0.bit - stranica je višejezična , ostali ništa
template_id id templatea koji se koristi prilkom generiranja stranice
site_id id sitea kojem stranica pripada
description opis (za search)
keywords ključne riječi (za search)
lang defaultni jezik stranice.
lastchange - ne koristi se
redirecturl ukoliko postoji, engine redirecta uneseni url
special kategorije za posebne namjene (nema ih u navigaciji)
force_ssl cijela stranica se isporuèuje preko SSL

 Foreign-key constraints:
    "$3" FOREIGN KEY (site_id) REFERENCES site(id) ON UPDATE CASCADE ON DELETE     
 CASCADE
    "$2" FOREIGN KEY (lang) REFERENCES lang(id) ON UPDATE CASCADE ON DELETE CASCADE
    "$1" FOREIGN KEY (template_id) REFERENCES "template"(id) ON UPDATE CASCADE ON   
 DELETE CASCADE

site

id unique id svakog sitea
naziv naziv sitea
admin_mail defaultni e-mail
forcedesign - defaultni design, ne korsti se
default_template - defualtni template, ne koristi se
root glavni (root) url site
content_providers id grupe content providera (admina)
ordstr za prkaz siteova u hijerarhiji

template

id uniqe id
tfilename ime filea glavnog templatea ('main.tpl').
tflags - ne koristi se
design_id id designa koji se koristi za template
tname puno ime templatea

 Foreign-key constraints:
    "$1" FOREIGN KEY (design_id) REFERENCES design(id)

Glavni template (tfilename) je html/smarty file, koji se svaki put koristi prilikom generiranje konačne stranice. U fileu su definirane pozicije za prikaz uređenih i slobodih modula, kao i includeovi za CSS i JS.

design

id unique id
name ime teme i foldera u /img/themes
data dio CSS-a koji se koristi
special true za administracijski site
htmlareabodystyle - ne korsti se (stlye za htmlarea)
htmlareapagestyle - ne korsti se (stlye za htmlarea)

layout

id uniqe id svake instance PAGE modula
user_id - ne koristi se (id usera koji ga je postavio)
kategorija_id kategoria u kojoj se modul nalazi
pozicija 0,1,2 => uređene liste (stupci lijevo,sredina,desno); 3 => slobodni
module_id id modula koju se prikazuje
redoslijed integer po kojem se instance sortiraju unutar uređene liste DESC
module_args argumenti koji se šalju toj instanci modula (npr. title=nesto)
state stanje prikaza: 0 => prikazano, 1 => zarolano
notitle true ukoliko se ne želi prikazati naslov (titlebar) modula

 Foreign-key constraints:
    "$3" FOREIGN KEY (module_id) REFERENCES modules(id) ON UPDATE CASCADE ON DELETE CASCADE
    "$2" FOREIGN KEY (kategorija_id) REFERENCES kategorija(id) ON UPDATE CASCADE ON DELETE CASCADE
    "$1" FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE

pre_layout

layout_id uniqe id svake instance SITE modula
template_id id templatea koji je vezan za site u kojem se pokazuje modul
pozicija isto kao i kod layout
redoslijed isto kao i kod layout, ako je <0 prikazuju se nakon layout modula
module_id isto kao i kod layout
module_args isto kao i kod layout
notitle isto kao i kod layout

 Foreign-key constraints:
    "$2" FOREIGN KEY (module_id) REFERENCES modules(id) ON UPDATE CASCADE ON DELETE CASCADE
    "$1" FOREIGN KEY (template_id) REFERENCES "template"(id) ON UPDATE CASCADE ON DELETE CASCADE

modules

id unique id svakog modula
name sistemsko ime modula (ime filea bez '.php', npr. 'mod_docman')
fname opisno puno ime (ispisuje se kod odabira)
hidden true ako su skriveni (admin) moduli
nocache true ako se nikada ne želi koristit caching za taj modul
pos veličina: 0 => Large or small, 1 => Large, 2 => Small
fdescript opisni tekst (ispisuje se kod odabira)
permalink
Klase

Klase

Ovdje se nalazi opisi korisnih klasa koje postoje unutar Strix engina.

Dokumentacija klasa koje su napisane tako da se dotična može iz njih automatski izgenerirati pomoću phpDocumentora nalazi se dostupna na http://plivaweb.pliva.hr/phpdoc/

phpDocumentator se pokreće sa:

$ phpdoc -o HTML:frames:phpedit -f class.foo.php -t /data/phpdoc

Opcije su:

 -o : definira izlaz u obliku output:converter:templatedir

 -f : datoteka koja sadrži klasu za koju generiramo dokumentaciju

 -t : path do direktorija gdje će se zapisati generirana dokumentacija

Više o opcijama koje nudi phpDocumentator može se saznati sa:

$ phpdoc -h

Dobar Quickstart manual nalazi se ovdje.

permalink
Profile timer API

Dvije funkcije koje se mogu koristi bilo gdje u kodu:

  • _time_start('name_of_timer')
  • _time_stop('name_of_timer')

Stranice pozvane sa profiling url-om oblika

http://www.example.com/?_do_profile=1

koristiti će ove timer-e.

Timeri se mogu u ugnježđivati. To se koristi u STRIX-u za mjerenje
ukupnog vremena potrošenog na Smarty, layout i cijelu stranicu.

Ako je profile uključen, postaviti će se i cookie _do_profile, tako da možete dalje surfati po site-u i gledati profile informacije svake stranice.

Modifikacije u datotekama:

index.php
sysinc/dbinit.php
sysinc/time_profile.php
sysinc/MemCachedClient.inc.php
mod_admin_qlinks.php
templates/admin_qlinks.tpl
app/profile/*
permalink
StrixLogging

Logiranje rada programa i grešaka

Logging for fun and profit!

Kako logirati pogreške?

jednostavno:

<?php
_debug("...");
_info("...");
_error("boink, error: "._var($foo));
?>

Level logiranja definira se u konfiguracijskoj datoteci ili u enviromentu servera. Defaultne vrijednosti su:

<?php
$_LOG_ERROR = 1;
$_LOG_INFO = 0;
$_LOG_DEBUG = 0;
?>

Oprez! Info level se po defaultu ne zapisuje u log.

Log datoteke

Lokacija log file-a definirana je u php.ini datoteci (obično u /etc/php4/apache/php.ini) kao:

 ; Log errors to specified file.
 error_log = /var/log/php.log

Vlasnik log datoteke mora biti korisnik pod kojim se izvršava web server (na Debianu www-data) da bi se išta zapisivalo u nju osim PHP Warninga.

Što se trenutno logira?

U server details admin quick links postoji ispis koji je trenutan log level, npr:

  Logging: errors, debug, info
permalink
StrixDocumentManagementForProgrammers

Strix Document Management s naglaskom na import dokumenata

O dokumentu

Nove verzije ovog dokumenta bi se mogle naci na strix wikiju.

Verzija 1: Matko Anđelinić, Srpanj 2005., Inicijalna verzija

Opis tablica

Tablica "document"

Glavna tablica. Zapisi i za dokumente i foldere.

id integer jedinstveni id dokumenta
title character varying(256) naslov dokumenta (ne nuzno isto sto i filename)
description text opis - text/html polje s opisom dokumenta
activation_time timestamp without time zone vrijeme aktivacije dokumenta
expiration_time timestamp without time zone vrijema isteka dokumenta
creation_time timestamp without time zone vrijeme dokumenta (glavno datumsko polje na dokumentu)
lang_id integer jezik u tablici lang
allow_discussion boolean t/f - dopusteno otvaranje diskusije o dokumentu na forumu
author_id integer zastarjelo polje za autora. pogledaj tablicu document_author
parent_folder_id integer id parent foldera (zapis u istoj tablici)
folder boolean t/f - radi li se o folderu ili o dokumentu
locked boolean nije u upotrebi
approved boolean dokument je odobren - ako je false dokument se pojavljuje u "waiting for approval" popisu
keywords text kljucne rijeci
edit_time timestamp without time zone zadnje vrijeme promjene zapisa
edit_user integer korisnik koji je napravio zadnju promjenu (tablica users)
source text free text polje - atribut "izvor dokumenta"

PRIMARY KEY (id)

Tablica "document_revision"

Tablica koja veze verziju dokumenta s filenameom na filesystemu servera. Filename
je samo filename i ne ukljucuje path (ni relativni ni apsolutni).

document_id integer id dokumenta
version_num smallint redni broj verzija (nije vezan ni za jednu sekvencu)
filename character varying(256) naziv dokumenta na filesystemu (bez patha, samo filename)
filesize integer veličina fajla u bajtovima
edit_time timestamp without time zone vrijeme promjene
editor_id integer user koji je napravio promjenu
last_author integer polje last author (mijenja se preko sucelja)

PRIMARY KEY (document_id, version_num)

Napomena 1: Verzioniranje ide redom 1, 2, ....

Napomena 2: Iako folderi ne mogu imati verzije, za njih je svejedno potrebno
imati zapis u ovoj tablici s version_num=0, te proizvoljnim vrijednostima u
ostalim poljima.

Napomena 3: Lokacija dokumenta na disku se racuna ovako:

$docman_dir/$document_id/$version_num/$filename

gdje je

  • $docman_dir - konfiguracijska varijabla iz etc/.conf.php
  • $document_id - id dokumenta (public.document.id ili public.document_revision.id)
  • $version_num - redni broj verzije dokumenta (public.document_revision.version_num)
  • $filename - vrijednost u polju public.document_revision.filename

Tablica "document_author"

Vise zapisa po dokumentu. Informacija o autorima dokumenta koja se moze unositi
i mijenjati preko sucelja. Po defaultu bi trebao biti barem jedan zapis s npr.
osobom koja je uploadala dokument.

document_id integer id dokumenta
version_num smallint verzija
author_id integer autor iz tablice users

PRIMARY KEY (document_id, version_num, author_id)
FOREIGN KEY (document_id, version_num) referencira "document_revision"

Ostale tablice

atributi
komentari

Dozvole

Koristi se STRIX-ov sustav dozvola - ACL. Oznaka za dokumente u ACL-u je 'file'.

Tablice acl_user i acl_group

Opis ACL-a nije u scopeu ovog dokumenta pa se nece opisivati. Vazno je da postoje
te tablice, te zgodna plpgsql funkcija:

acl_userObjectAddPerm(user_id, 'file', document_id, perm)

Za dokumente perm moze biti:

  • 'PERM_READ' ili integer vrijednost 1
  • 'PERM_SUGGEST' ili integer vrijednost 2
  • 'PERM_AUTHOR' ili integer vrijednost 4
  • 'PERM_OWN' ili integer vrijednost -1 ili 8

Automatska, defaultna dodjela dozvola

Uploaderu se, na razini baze (triggerom) automatski dodjeljuje OWN pravo. Bez
obzira na sučelje kojim je zapis zavrsio u bazi. Uploader je osoba upisana
u polju author_id prilikom inserta recorda u tablicu document.

Za one koji zele znati vise, za to je zaduzen sljedeci zapis u tablici
acl_users_default:

 acl_id |                              default_sql
--------+-----------------------------------------------------------------------
 file   | SELECT 'file', NEW.id, author_id, -1 FROM document WHERE id = NEW.id

Dodatno, preporuka je da se OWN pravo dodijeli i svim osobama navedenim kao autori
dokumenta u tablici document_author.

SELECT acl_userObjectAddPerm($user_id, 'file', $document_id, 'PERM_OWN');

Mali primjer importa dokumenta koristenjem PHP libraryja

Zamislimo slucaj da imamo na filesystemu dokument /data/tmp/dokument.doc kojeg
zelimo importati u novi folder "Novi folder" koji bi se nalazio ispod postojeceg
foldera XXXX sa id-jem 9.

Stanje sadrzaja foldera XXXX prije pokretanja skripte za import:

	plivaweb=# select id, title, folder, depth, ord from getDocumentTree(9);
	 id |   title   | folder | depth | ord
	----+-----------+--------+-------+-----
	 12 | Bayer.zip | f      |     0 |   1
	 13 | Lilly.zip | f      |     0 |   2
	(2 rows)

Skripta za import: docman-import.php

	<?php
	
		include_once("sysinc/stdlib.php");
		include_once "docman.php";
		
		// globalna varijabla koja (ako je true) libraryju govori da 
		// pobrise OWN dozvole koje su automatski dodijeljene uploaderu (author_id)
		// preko triggera u bazi
		$PD_no_automatic_perms = false; 
		
		$parent_folder = 9; 
		$document_path = "/data/tmp/dokument.doc";
		
		$in = array (
		     "title" => "Novi folder"
		   , "description" => ""
		   , "creation_time" => strftime("%Y-%m-%d")
		   , "lang_id" => "1"
		   , "allow_discussion" => "t"
		   , "author_id" => "4"
		   , "approved" => "t"
		   , "keywords" => "testni folder, document import"
		   , "edit_time" => ""
		   , "edit_user" => ""
		   , "source" => ""
		   , "folder" => "t"
		);
		$folder = PlivawebDocuments::createFolder($parent_folder, $in);
		
		if (PlivawebDocumentsError::isError($folder)) die ($folder->text);
		
		//ako smo ovdje u varijabli $folder imamo id novokreiranog foldera
		
		$in = array (
		     "title" => "Moj dokument"
		   , "description" => "Dokument koji sam uploadao"
		   , "creation_time" => strftime("%Y-%m-%d")
		   , "lang_id" => 1 
		   , "allow_discussion" => "t"
		   , "author_id" => 3 
		   , "approved" => "t"
		   , "keywords" => ""
		   , "edit_time" => strftime("%Y-%m-%d")
		   , "edit_user" => 3
		   , "source" => ""
		
		   , "attributes" => array()
		
		   , "local_file_name" => $document_path
		
		);
		
		$did = PlivawebDocuments::addDocument($folder, $in);
		if (PlivawebDocumentsError::isError($did)) die ($did->text);
		
		echo "Document created";

	?>

Pokrenuta skripta:

$ php4 docmanimport-example.php plivaweb

Stanje sadrzaja foldera XXXX poslije pokretanja skripte za import:

	plivaweb=# select id, title, folder, depth, ord from getDocumentTree(9);
	  id  |    title     | folder | depth | ord
	------+--------------+--------+-------+-----
	 4949 | Novi folder  | t      |     0 |   3
	 4950 | Moj dokument | f      |     1 |   4
	   12 | Bayer.zip    | f      |     0 |   6
	   13 | Lilly.zip    | f      |     0 |   7
	(4 rows)

Napomena 1: PHP library u inc/docman.php nije ni blizu kompletan i 100% fail safe,
ali je dobar start za nekoga tko zeli importati dokumente kroz skriptu.

Napomena 2: Nema razloga da se ne napise skripta u nekom drugom jeziku koja
bi direktno pisala po document* tablicama pridrzavajuci se gore opisanih pravila.

Dodatak: Funkcije za dohvat podataka

Postoji niz funkcija za rukovanje (dohvacanje podataka). One su mahom rekurzivne.
Popis tih plpgsql funkcija, zajedno s implementacijom moze se naci u
tables2/docman.sql.

Najcesce koristena takva funkcija je getDocumentTree(folder_id integer) koja
vraca popis zapisa iz document tablicom skupa s dvije dodatne kolone depth (dubina,
razina) te ord (redoslijed po kriteriju folder, title)

Npr.

plivaweb=# select id, title, folder, depth, ord from getDocumentTree(26);
 id  |               title                | folder | depth | ord
-----+------------------------------------+--------+-------+-----
  27 | forms                              | t      |     0 |   1
  28 | AC form                            | f      |     1 |   2
 426 | ChemClient Izjava                  | f      |     1 |   3
 427 | Formular za publikacije            | f      |     1 |   4
 417 | HTS_form.pdf                       | f      |     1 |   5
 418 | New_PL_form.pdf                    | f      |     1 |   6
 419 | New_PL_form.xls                    | f      |     1 |   7
 422 | PK&M form                          | f      |     1 |   8
  29 | Publications Form                  | f      |     1 |   9
 428 | Rjesenje GO                        | f      |     1 |  10
 429 | Zahtjev za nabavu                  | f      |     1 |  11
  33 | instructions                       | t      |     0 |  12
  35 | LabNotebooks law.pdf               | f      |     1 |  13
 421 | Pravilnik za pisanje lab. dnevnika | f      |     1 |  14
 420 | Upute o kretanju NII.pdf           | f      |     1 |  15
  30 | instruments                        | t      |     0 |  16
  31 | instruments_development.xls        | f      |     1 |  17
  32 | instruments_research.xls           | f      |     1 |  18
  37 | presentations                      | t      |     0 |  19
(19 rows)

Preporuka je, ne koristiti ovu funkciju bas za svaku moguci dohvat dokumenata,
jer dodavanje WHERE uvjeta nad ovakav upit nece nimalo ubrzati dohvat podataka.
Ako bi se dodao Where uvjet, svejedno bi se za svaki upit izgenerirao kompletan
result set nakon cega se tek radi filtriranje. Bolje rjesenje je da se za svaku
specificnu upotrebu pri kojoj je bitna efikasnost pise svoja rekurzivna funkcija.

Napomena: Iako to nije slucaj sa funkcijama u tables2/docman.sql, zgodno je
dodati STABLE u definiciji takve funkcije sto bi moglo bitno ubrzati te funkcije
u nekim slucajevima. (Npr. u sucajevima kada se u jednom upitu radi visestruko
pozivanje iste funkcije s istim parametrima).

permalink
Plugini

U STRIXU postoje dvije vrste plugina:

  • Smarty plugini (sysinc/plugins/*.php) - koriste se u templateima
  • interni plugini za različite infrastrukture (inc/plugins/*) - koriste se u PHP datotekama

Smarty plugini

Detaljna dokumentacija o njihovoj arhitekturi nalazi se na http://smarty.php.net/manual/en/plugins.php

Osim standradnih plugina koji dolaze sa Smartyjem, u STRIXU postoje i custom-made Smarty plugini:

 


  
    
  

      

        
          

            

              
                Search for plugin smarty
              
            

          

          

            
              

            
            
          

        
      

    

interni plugini

Interni plugini postoje za sljedeće infrastrukuture (svaka ima svoj direktorij u inc/plugins):



za ACL vidi UserCanDoOnObject

permalink
Weblog Navigation
Loading...
Weblog Archives
  • Loading...