SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 46
  1. #1
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Cool another sax like template engine

    Hi there!
    I know i'm reinventing the wheel, but i am coding a little template engine inspired on http://www.freemarker.org/, a great template engine developed in Java.
    So i'm using a SAX parser to asign tasks to some tags, everything goes good (right now i dont care about performance, i just want something that works hehe) but the SAX parser ignores HTML comments and DOCTYPEs declarations, so the (x)HTML output is not well formed, how can i handle this kind of tags? can SAX handle them?
    Source code is available at http://www.sourceforge.net/projects/phpmarker, you can see how to use it at the post #34.

    thanks for reading me.
    PS: sorry if my english sounds a little strange
    Last edited by viTxo; Jan 16, 2004 at 07:47. Reason: fixed project URL and added "howto" hehe

  2. #2
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hasn't PEAR dealt with this I ask ? Not too sure though might be worth your time to look at PEAR and hack it ?

  3. #3
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'll give a look at PEAR and tell you, thanks.

  4. #4
    SitePoint Zealot prefab's Avatar
    Join Date
    Jan 2003
    Location
    Belgium
    Posts
    133
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    As Dr Livingston might remember, I have been working on an SAX template engine. It works with several PEAR classes, to provide ColdFusion like functionality.

    It still needs some more refinement, but I'm pretty satisfied with results sofar.

    - prefab

    btw sorry for the wide code fragment post, dunno what's causing it...

    HTML Code:
    <ff:template sid="main" xmlns:ff="http://www.atelierfabien.net/fusion" type="text/html">                                
            <html>
                <head>
                    <title>#header.title#</title>
                </head>
                <body>
                    <ff:cache id="test.#server.request_uri#" expire="60">
                    
                    <ff:database shortcut="ezpublish"/>
                    
                    <ff:sql sid="datasrc">
                        SELECT ezcontentobject.*, ezcontentclass.name AS classname FROM ezcontentobject
                        LEFT JOIN ezcontentclass ON ezcontentobject.contentclass_id = ezcontentclass.id     
                        WHERE ezcontentobject.contentclass_id = #pathinfo.1#
                        ORDER BY classname, name
                    </ff:sql>
                    
                    <table border="0" cellspacing="1" cellpadding="4" width="400" style="background-color: #FF813B">
                    <ff:output sid="datasrc" groupby="contentclass_id" alternate="#FFD18C|#CCB77E" alter="item" offset="#pathinfo.2#" limit="6">                                                        
                        <tr><td>#_groupcounter_#</td><td colspan="2">#item.classname#</td></tr>
                        <ff:item>
                            <tr style="background-color: #_alternate_#"><td></td><td>#_counter_#</td><td>#item.id#</td><td>#item.name#</td></tr>
                        </ff:item>                                                      
                    </ff:output>
                    <ff:outputelse sid="datasrc">
                        <tr><td>#_msg_#</td></tr>
                    </ff:outputelse>    
                    </table>
                    
                    <ff:pager sid="datasrc" active="red" inactive="black">
                        <div style="margin: 20px">
                        <a href="#server.script_name#/#pathinfo.1#" style="color: #_highlight_#; padding: 10px">first</a>
                        <a href="#server.script_name#/#pathinfo.1#/#prev#" style="color: #_highlight_#; padding: 10px">prev</a>
                        <ff:item><a href="#server.script_name#/#pathinfo.1#/#_idx_#" style="color: #_highlight_#; padding: 10px">#_counter_#</a></ff:item>
                        <a href="#server.script_name#/#pathinfo.1#/#next#" style="color: #_highlight_#; padding: 10px">next</a>
                        <a href="#server.script_name#/#pathinfo.1#/#last#" style="color: #_highlight_#; padding: 10px">last</a>
                        (from #from# to #to# of total: #total#)
                        </div>
                    </ff:pager>
            
                    </ff:cache>
                    </body>
            </html>
    </ff:template>

  5. #5
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I forgot to tell you that I have been reading you all (prefab, dr. livingstone, harryF)
    My goal is to keep every template very simple and to separate presentation from bussines logic, I want to write some docs in english (not only in spanish) so I'll post again around here when I got them ready.

  6. #6
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Nice work there Prefab; been busy myself so to speak ?

    Whereas your using SAX in my case it's the DOMXML. After much thought though this is what I have at the moment...

    PHP Code:
    .
    .
    include(
    'widgets.class.php');
        
        
    $dom domxml_new_doc('1.0');
        
        
    $page = & new pageWidget(& $dom);
        
    $page -> create();
        
        
    $title = & new titleWidget(& $dom);
        
    $title -> create('Sample Title Text Folks...');
        
        
    $page -> addHead($title -> fetch());
        
        echo(
    $page -> show());
    .
    .
    .
    class 
    Widgets {
            
            function 
    Widgets() {
            }
            
            function 
    appendChild($dom$ele) {
                
    $node $dom -> append_child($ele);
                
                return 
    $node;
            }
            
            function 
    createElement($dom$ele) {
                
    $node $dom -> create_element($ele);
                
                return 
    $node;
            }
            
            function 
    createTextNode($dom$txt) {
                
    $text $dom -> create_text_node($txt);
                
                return 
    $text;
            }
        }
        
        class 
    pageWidget extends Widgets {
            var 
    $dom;    
            var 
    $html;    
            var 
    $head;    
            var 
    $body;
            
            function 
    pageWidget(& $dom) {
                
    Widgets::Widgets(& $dom);
                
    //
                
    $this -> dom = & $dom;
            }
            
            function 
    create() {
                
    // begin
                
    $this -> html Widgets::createElement($this -> dom'html');
                
    $this -> head Widgets::createElement($this -> dom'head');
                
    $this -> body Widgets::createElement($this -> dom'body');
                
    // create actual page
                
    Widgets::appendChild($this -> dom$this -> html);
            } 
            
            function 
    addHead($ele) {
                
    Widgets::appendChild($this -> head$ele);
            }
            
            function 
    addBody($ele) {
                
    Widgets::appendChild($this -> body$ele);
            }
            
            function 
    finalise() {
                
    Widgets::appendChild($this -> dom$this -> head);
                
    Widgets::appendChild($this -> dom$this -> body);
            }
            
            function 
    show() {
                
    $this -> finalise();
                return 
    $this -> dom -> dump_mem(true);
            }
        }
        
        class 
    titleWidget extends Widgets {
            var 
    $dom;
            var 
    $node;
            
            function 
    titleWidget(& $dom) {
                
    $this -> dom = & $dom;
                
    //
                
    $this -> node Widgets::createElement($this -> dom'title');
            }
            
            function 
    create($msg) {
                
    $text Widgets::createTextNode($this -> dom$msg);
                
    Widgets::appendChild($this -> node$text);
            }
            
            function 
    fetch() {
                return 
    $this -> node;
            }
        } 
    Again still needs some more work on it ? Is that namespaces you have in your tags ? HarryF done some work on using the DOMXML and namespaces if I recall from a thread ?

    It would be interesting to see how an XSL stylesheet would work with a DOM tree huh ?

    What do you think folks ? Proberly really need to wait for PHP5 though.

  7. #7
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Is your goal to create an HTML document from scratch using OO PHP? If so, why? It would be more useful to implement widgets only for "special" template tags? like mine:
    Code:
    <list name="myList" as="hash_item">
      ${hash_item.key1}<br/> ${hash_item.key2}
    </list>

  8. #8
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I forgot the PHP code corresponding to the template code above:
    Code:
    $view =& new Template( 'fileXYZ.html', 'NameXYZ');
    $hashList[0] = array( 'key1' => 'value1', 'key2' => 'value2' );
    $hashList[1] = array( 'key1' => 'value3', 'key2' => 'value4' );
     
    $view -> expose( 'myList', $hashList );
    $view -> process();
    It actually works, listing scalar lists work too and accessing hashMaps (associative arrays) with ${hashMap.keyName} works either.
    Last edited by viTxo; Aug 29, 2004 at 04:45.

  9. #9
    SitePoint Zealot prefab's Avatar
    Join Date
    Jan 2003
    Location
    Belgium
    Posts
    133
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    Nice work there Prefab; been busy myself so to speak ?

    Whereas your using SAX in my case it's the DOMXML. After much thought though this is what I have at the moment...

    Again still needs some more work on it ? Is that namespaces you have in your tags ? HarryF done some work on using the DOMXML and namespaces if I recall from a thread ?
    Thanks!

    I see where your approach is going. I've been working on a similar thing in the past. You would simply extend a widget to override certain characteristics. Although my approach doesn't advocate logic seperation, it's meant for quick and easy use.
    Logic is kept minimal, and you can achieve quite a lot with those sets of tags. One nicety I implemented is filters you assign to datasources, and result columns. If I find some time to sort alle the example code etc. out I might post it here...

    btw those are namespaces yes, and they're used accordingly (with a namespace enabled SAX parser, to prevent tag mixups).

    - prefab

  10. #10
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Is your goal to create an HTML document from scratch using OO PHP? If so, why?
    Umm... Personally I am very, very keen to avoid this style of templating in any regard for various reasons;

    1) Smarty for example is an excellent templating engine but for most projects it's way too complex and thus has functionality which I'd never really use so this introduces overheads.

    2) If I have an XML tree [DOM] I can then use currently with PHP4.x [abeit with some hacking] XSL-T to transform to another document format which is today very important.

    2.1) Later PHP5 will accommodate my needs very, very easilly as far as XML and XSL-T goes with minimum amount of problems.

    3) I like the DOM and all for web standards; very important to me personally and IMO to all developers and DOM should be a basic required skill as with OOP and Reg Expression in any language/technology.

    4) Using DOMXML I can not only create Widgets as per HTML tag, but as HarryF has demonstrated I can then create Widgets that will basically create from nothing dynamic building blocks or page structures with next to no effort at all;

    4.1) For example, a navigational menu ? One, maybe Two classes should accomplish this I'd say ? These two classes would use other smaller Widgets and any modifications would take effect immediatlely to the menu - ie CSS changes for example ?

    4.2) I could in theory have an entire set of Widgets basically to create a pages CSS and nothing more...

    Hope that helps you understand why I use the DOM ? To me it is an important technology and is very scallable and offers far more advantages over a variable name based template engine;

    Not to say that's a bad thing, but to me it's not what I'm looking for.

  11. #11
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    One nicety I implemented is filters you assign to datasources, and result columns.
    Sounds interesting

  12. #12
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes Dr., now I understand why you use DOM.

  13. #13
    SitePoint Zealot prefab's Avatar
    Join Date
    Jan 2003
    Location
    Belgium
    Posts
    133
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    For those who are interested, here are some examples of filters.
    Note that you can set filters seperately, or group them in filtersets.
    A set can reference a dataset and property (column), or only the dataset, so all contained filters only need to specify the property.

    You can 'register' multiple filter 'sets' (classes) to the parser.
    That way you can finetune which filters are available to the template. A similar approach used for datasources, which are currently PEAR DB and PEAR DB_DataObject.

    HTML Code:
    // for convenience I create some variables with cData sections
    <ff:variable sid="cond" scope="test"> //if I switch to HTMLSax, cData sections won't be necessary, but I think this is more clean and better
    	<![CDATA[$price > 40]]>
    </ff:variable> 
    <ff:variable sid="then" scope="test">
    	<![CDATA[<i style="color: red">%s</i>]]>
    </ff:variable>
    <ff:variable sid="else" scope="test">
    	<![CDATA[<b>%s</b>]]>
    </ff:variable>
    
    <ff:outputfilter sid="dataSource" prop="price" _assign="filtered" filter="condformat" condition="#test.cond#" then="#test.then#" else="#test.else#" set="advanced"/>
    //a new column 'filtered' is added and assigned the conditionally formatted column 'price' (condformat filter is part of the 'advanced' filterset)
    
    <ff:filterset sid="dataSource" prop="descr"> //dataSource property (or column) 'descr' will be filtered
    	<ff:outputfilter _assign="substrtest" filter="substr" offset="2" length="5"/> //here 'descr' is substr'ed and assigned to a new column 'substrtest'
    	<ff:outputfilter filter="format"> //descr will be bold
    		<![CDATA[<b>%s</b>]]>
    	</ff:outputfilter>
    	<ff:outputfilter filter="upper"/> //descr will be uppercase too
    </ff:filterset>
    - prefab

  14. #14
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi again guys,
    I'm thinking these days in delegating the code corresponding to each XML tag (the list tag for example) in a class, like a TagHandler class, because I don't want to loose flexibility by having all the code inside my TemplateParser class (the sax parser).
    Which aproach would you use? I'm thinking in implementing some kind of JSP Custom Tags system, but I looks a little complex :O Every tag handler would inherit from this class:
    PHP Code:
    if ( defined('ABSTRACT_TAG') ) { return; }
    define('ABSTRACT_TAG'true );
    define('EVAL_BODY'0);
    define('SKIP_BODY'1);
    define('EVAL_PAGE'2);

    class 
    AbstractTagHandler {
        var 
    $body;
        
        function 
    doStartTag($attributes, &$model){}
        function 
    setBodyContent($body){
            
    $this->body body;
        }
        function 
    doInitBody(&$model){}
        function 
    doAfterBody(&$model){}
        function 
    doEndTag(&$model){}

    $model instance is where I keep variables and items (lists and hashmaps) exposed to the template through Template::expose($key, $value) method.

    What do you think about it? Is there an easier way? Could a design pattern help us?

    How do you deal with your tags prefab? (uhmmm.. is well written this question? )

    read you soon

  15. #15
    SitePoint Zealot prefab's Avatar
    Join Date
    Jan 2003
    Location
    Belgium
    Posts
    133
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by viTxo
    How do you deal with your tags prefab? (uhmmm.. is well written this question? )

    read you soon
    To be honest, ATM I have most tag handling functions inside a 'processing' class. I do have 'plugin' classes for filters and datasources. The actual parser is seperate, and I have 'state' class that manages the current output and buffer (cData etc). Having a more indepth view on PEAR XMLTransformer -almost- convinced me to change to that instead. Shouldn't take took long if I -once again- find the time...

    Good luck,

    Fabien

  16. #16
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Umm.... Interesting ideas folks. I was thinking myself though that you could actually construct commonly used, regular page structures that are specified within a page by using one simply tag ?

    Thinking this very thing myself actually since it would for one thing simpliy the page template and you use a series of classes basically then to build the required structure ?

    In my case I'd use DOM Widgets and they themselves for example would use Daos (via the MODEL Layer) for database read/writes ?

    Would be very very easy then to make modifications to templates yes ?


  17. #17
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Umm.... Interesting ideas folks. I was thinking myself though that you could actually construct commonly used, regular page structures that are specified within a page by using one simply tag ?
    Could you post a little sample? I'm not understanding what you want to mean
    I have been thinking on aplying some kind of MVC pattern to the problem... but I'm not very sure about it: every special tag could 'execute' an action in the controller (the sax parser in my case) and every action would have an AbstractAction's instance:
    PHP Code:
    class AbstractAction {
      
    //returns a String with the result
      
    function process( &$model );
      function 
    addProperty$key$value );

    The developer would user a TemplateManager singleton, that loads all the instances of Actions one time (when the constructor is called):
    PHP Code:
    $manager =& TemplateManager::getInstance();
    $manager -> setDirectory('presentation/');

    $view =& $manager -> getTemplate('news/portlet.html''newsPortlet');
    $view -> expose('var''value');
    ...
    $view -> process(); 
    And let's say we got a helloworld template tag:
    Code:
    <helloworld user="viTxo"/>
    And its Action class:
    PHP Code:
    class HelloWorldAction extends AbstractAction {
      var 
    $user;
       
    //@param model: template model contaning template data
       // (lists, variables, html, current tag body?)
      
    function process( &$model ){
        
    $model -> append('íHola Mundo! Ey '.$this->user.' whats up!');     
      }
      
    //this method would be called
      //by the sax parser
      
    function addProperty$ket$value ){
        if ( 
    $key == 'user' ){
         
    $this->user $value;
       }
      }

    what do you think folks?
    Maybe it is wrong to use so many classes to generate the view?
    I'm waiting your posts guys

  18. #18
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Umm.... At the moment it's only an idea as I have yet to get to the stage of having a working example ?

    But your sample script you've posted, the idea behind it anyway seams to be sound enough ? My view only though

    Using a specific tag to build an element of a page is a smart idea for various reasons, for example someone with an WYSIWYG editor would not then be editing the actual HTML for example, but merely structured elements ?

    For example, to add a FORM you'd only need to put a simple

    PHP Code:
    <form attr='subscribe' /> 
    Into the template no ? Rather than for example having to format a FORM and all it's elements into the template as HTML ?

    I used this idea with XML and PHPs Sablotron extension and it worked very well

    From my perspective though from using DOMXML it'd be slightly easier - as I see it - to script for these tags ? But SAX might just as well as be up for the job as well though I'm never really been all that keen in using SAX as it's an event based parser...

    I like the idea of the DOM better As to the VIEWs though maybe you should think about the idea of having a ''controller'' for your VIEWs and then implement a VIEW Helper Layer ? When I refer to a ''controller'' btw I do not mean the actual CONTROLLER Layer it's self but just something to control seperate, smaller VIEWs if you understand ?

  19. #19
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What I got in mind is not to code simple tags, the hello world one was an example. For this tag:
    Code:
    <list name="friends" as="friend">
    <p> ${friend.name}</br>${friend.email} </p>
    </list>
    And it's action inside the engine:
    PHP Code:
    class ListItemsAction extends AbstractAction {  
      var 
    $listName$itemName;
      var 
    $currentList;

      function 
    process( &$model ){
        
    /*1.get the current list from TemplateModel using it's name
          2.iterate over it{      
             $model -> expose( $this->itemName, $current_hashmap
             $body_processed = $model -> doReplace(..);
             $model -> append( $body_processed ); (to the output var)
          }
        */
      
    }

      function 
    addProperty$key$value ){
        if ( 
    'name' == $key ){
          
    $this->listName $name;
        }else if ( 
    'as' == $key ){
           
    $this->itemName $value;
        }

    I hope having something working tomorrow...

    I know DOMXML is much better and easier, I've been using it in Java. the only thing I dont like of phpdom is that it is an experimental module.

    I dont understand very well what you mean with smaller views...

  20. #20
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Umm.... I agree that at the moment [PHP4.x] DOMXML is a bit questionable although the methodology is still sound for use with PHP5 ?

    About the point of VIEWs have a look at Sun Microsystems site on Java's MVC ? There is a bit in there about VIEW Helpers if I remember ?

    http://java.sun.com/blueprints/patterns/ViewHelper.html

  21. #21
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If DOMXML becomes 'official' with PHP5, I'll port my template engine, or even extend it to use DOMXML or a SAX Parser (the TemplateManager class would act as a Factory producing TemplateProcessors...?)
    Right now, the "MVC-like" approach posted above works, so I can continue coding the classes for if/else conditions and text transformations: apply a pattern to the tags body, highlight text in the body and cut the body to the specified length, which I consider as basic actions. (and I think they are the only actions you need on a template?)

    I'm waiting to the public key activation on SourceForge, to upload the new code to the cvs, I will post again when a I got ready the project summary

  22. #22
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Sounds good to me... Your TemplateProcessor would know ahead which to use; ie DOM or SAX based on the actual object reference passed ?

    Polymorphism I think it is called ?

  23. #23
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes, I would have an abstract template processor class and a child class for every 'process technology': sax, dom, ...

    my f***ing cvs client doesn't work and I dont know why I cannot use apt-get on my Debian to install the cvs command line grf! So the code is still on my HD hehe

  24. #24
    SitePoint Member viTxo's Avatar
    Join Date
    May 2003
    Location
    Valencia, Spain
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Well I realised that I got a little problem, HOW TO DEAL WITH NESTED TAGS???? I know that PHPDOM would make it a lot easier Let's imagine to examples:
    Code:
    <if expression="isError">
      <p>Some errores here found while procesing your request
       <ul>
         <list name="errorList" as="error">
           <li>${error}</li>
         </list>
       </ul>
    </p>
    </if>
    Another stupid example with nested loops:
    Code:
    <list name="odd_numbers" as="odd">
      <list name="even_numbers" as="even">
        ${odd}${even}
      </list>
    </list>
    I'm thinking that every TagHandler would modify its own TemplateModel, and finally the process result would be added to the Main TemplateModel? uhmmm hell, i though it was going to be easier

  25. #25
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Umm... Tricky isn't it ?

    What I'm thinking is that you need to implement first some kind of check to see if there is a nested tag; and if so branch off to another part of functionality with the tag as a reference point.

    With this you begin a recursive loop which will return an array holding all the tags found ?

    From my point of view I would proberly call the recursive functionality with the ROOT tag which would then return a TREE of tags if you see what I mean ?

    Thinking this same thing myself so I can use DOMXML to extract values for a configuration format. At the moment I can only have the one level of tags...


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •