Docstoc

A Bookmark Plugin for Flux CMS

Document Sample
A Bookmark Plugin for Flux CMS Powered By Docstoc
					A Bookmark Plugin for Flux CMS




  Author:           Alain Petignat - petigala@zhwin.ch
  Supervisor:       Visvanath Ratnaweera, B.Sc.Eng., Dipl.El.Ing.ETH
  Creation Date:    February 24, 2006
  Last Modification: February 25, 2006
  Version:          1.0
  Status:           Final
Summary
When introducing a new content management system (CMS), the ability to adapt it to a given situation
and to integrate into an existent infrastructure is an important consideration. On one hand should ex-
istent datasources and content be included and on the other hand it is important, that content inserted
into the CMS can be converted in a variety of formats. XML is the state of art today.

    In this project study, three example-plugins of different complexity for the XML-based ”Flux CMS”
were implemented. They illustrate, how plugins are basically integrated, what kind of plugins can be
distinguished and what classes and files are needed for those.


Zusammenfassung
Bei der Wahl eines Content Management Systems (CMS) spielt nicht zuletzt die Anpassbarkeit an die
Gegebenheiten einer bestehenden Infrastruktur eine grosse Rolle. Auf der einen Seite sollen bereits
                                                                        o
bestehende Datenbanken oder Inhalte in ein CMS eingebunden werden k¨nnen und auf der anderen
Seite ist es ebenso unabdingbar, die in einem CMS eingegebenen Inhalte auch andernorts und in an-
                        u                          u                      a
deren Formaten zur Verf¨gung zu stellen. XML ist f¨r diese Interoperabilit¨t zwischen Applikationen
die erste Wahl.

                                                              a                                     u
    In der vorliegenden Arbeit wurden drei, in ihrer Komplexit¨t unterschiedliche Beispiel-Plugins f¨r
                                                                                               a
das auf XML basierende ”Flux CMS” geschrieben. Dabei wurde aufgezeigt, wie Plugins grunds¨tzlich
                          u                                           o
zu integrieren sind, was f¨r Arten von Plugins unterschieden werden k¨nnen und welche Klassen und
       u
Files f¨r diese Arten gebraucht werden.
    a                         a
Erkl¨rung betreffend das selbst¨ndige Verfassen der Projektarbeit im Departement T

                                                                                                a
   Mit der Abgabe dieser Projektarbeit versichert der Studierende, dass er/sie die Arbeit selbst¨ndig
und ohne fremde Hilfe verfasst hat

                                           a
    Der unterzeichnende Studierende erkl¨rt, dass alle zitierten Quellen (auch Internetseiten) im Text
                                                                                         a
oder Anhang korrekt nachgewiesen sind, d.h. dass die Projektarbeit keine Plagiate enth¨lt, also keine
                                a
Teile, die teilweise oder vollst¨ndig aus einem fremden Text oder einer fremden Arbeit unter Vorgabe
                                                      ¨
der eigenen Urheberschaft bzw. ohne Quellenangabe ubernommen worden sind.

    Bei Verfehlungen aller Art treten die Paragraphen 41 und 42 (Unredlichkeit und Verfahren bei
                               u    u
Unredlichkeit) des Reglements f¨r Pr¨fungen am TWI sowie die Bestimmungen des Disziplinarverfahrens
der Hochschulordnung in Kraft.

   Ort, Datum:

   Unterschrift:




     This work is hereby released into the Public Domain. To view a copy of the public domain dedication,
visit http://creativecommons.org/licenses/publicdomain/ or send a letter to Creative Commons, 543
Howard Street, 5th Floor, San Francisco, California, 94105, USA.
Contents
1 Introduction                                                                                                                                                 1
  1.1 Project description (Aufgabenstellung)      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    1
  1.2 Goals . . . . . . . . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    1
  1.3 Motivation . . . . . . . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    1
  1.4 The Flux CMS . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    2
  1.5 Techniques . . . . . . . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    2
       1.5.1 PHP5 . . . . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    2
       1.5.2 Cocoon/Popoon . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    2

2 Plugin Basics                                                                                                                                                4
  2.1 General information . . . . . . . . . . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    4
  2.2 Developing plugins in a different repository .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    6
  2.3 ”Hello World”-Plugin . . . . . . . . . . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    6
       2.3.1 Requirements . . . . . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    6
       2.3.2 Needed Files . . . . . . . . . . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    6
       2.3.3 Implementation . . . . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    6
  2.4 ”Random Quotes”-Plugin . . . . . . . . . .              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
       2.4.1 Requirements . . . . . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
       2.4.2 Files needed . . . . . . . . . . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
       2.4.3 Database . . . . . . . . . . . . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
       2.4.4 Building an editor with dbforms2 . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
       2.4.5 Generating the data . . . . . . . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   11
       2.4.6 Activating the plugin . . . . . . . .            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   12
       2.4.7 Conclusion . . . . . . . . . . . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   12

3 ”Linklog”-Plugin                                                                                                                                            14
  3.1 Requirements . . . . . . . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   14
  3.2 Implementation . . . . . . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   15
       3.2.1 Database Structure . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   15
       3.2.2 The internal DOM-structures . .          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   17
       3.2.3 Classes and files for the admin .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   18
       3.2.4 Classes and files for the frontend        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   22

4 Conclusion                                                                                                                                                  25

References                                                                                                                                                    26

5 Appendix                                                                                                                                                    27
  5.1 Sourcecode ”Hello World”-plugin . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   27
      5.1.1 /inc/bx/plugins/helloworld.php . . .              .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   27
      5.1.2 /themes/2-cols/helloworld.xsl . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   29
  5.2 Sourcecode ”Random Quotes”-plugin . . .                 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   30
      5.2.1 /inc/bx/plugins/randomquotes.php .                .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   30
      5.2.2 /themes/2-cols/randomquotes.xsl .                 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   34
      5.2.3 /dbforms2/randomquotes.xml . . .                  .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   35
  5.3 Sourcecode ”Linklog”-plugin . . . . . . . .             .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   36
      5.3.1 /admin/sitemap/linklog.xml . . . .                .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   36
      5.3.2 /inc/bx/editors/linklog.php . . . . . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   38
      5.3.3 /inc/bx/editors/linklog/linklog.xsl . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   52
      5.3.4 /webinc/plugins/linklog/admin.css . . . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   56
      5.3.5 /inc/bx/plugins/linklog.php . . . . . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   57
      5.3.6 /inc/bx/plugins/linklog/tags.php . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   67
      5.3.7 /themes/2-cols/linklog.xsl . . . . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   70
      5.3.8 /themes/standard/admin/plugins/linklog/linklog2rss.xsl          .   .   .   .   .   .   .   .   .   .   .   .   .   74
      5.3.9 /admin/webinc/install/linklog/index.php . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   76
5.4   Used Software . . . . . . . . . . . . . . . . . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   79
5.5   Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   79
List of Figures
  1    Official description of project study . . . . . . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    1
  2    Generator-Transformer-Serializer-Model from Cocoon . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    3
  3    Basic sequence of processing a request in Cocoon . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    3
  4    Basic mechanism of Cocoon handling a request . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    4
  5    Popoon and ”Flux CMS” handling a request . . . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    5
  6    Output of bx helpers debug::webdump . . . . . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    6
  7    Option in the admin to add .configxml . . . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    7
  8    ”Hello World”-plugin without a parameter . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    9
  9    ”Hello World”-plugin with the parameter ”foo” . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    9
  10   ”Random Quotes”-plugin: simple editor build with dbforms2          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   11
  11   ”Random Quotes”-plugin: Appearance in ”Quicklinks”-menu            .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   13
  12   ”Random Quotes”-plugin: output of one quote . . . . . . .          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   13
  13   ”Random Quotes”-plugin: output of all quotes . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   14
  14   ”Random Quotes”-plugin: XML-output of all quotes . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   15
  15   ”Linklog”-plugin: overview over all files needed . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   16
  16   ”Linklog”-plugin: three-table-structure used for database . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   17
  17   ”Linklog”-plugin: internal XML-stream of editor . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   18
  18   ”Linklog”-plugin: internal XML-stream in frontend . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   19
  19   ”Linklog”-plugin: layout of the editor . . . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   21
1     Introduction
1.1   Project description (Aufgabenstellung)




                             Figure 1: Official description of project study


1.2   Goals
The main goal of this project-study is to get an understanding of the plugin-architecture of the open-
source content management system (CMS) ”Flux CMS” and its underlying technology ”Popoon”.
    To achive this, two basic plugins will be implemented first. One is a really simple ”Hello World”-
plugin, the second, one that outputs quotes (”Random Quotes”), uses a database in the backend.
Finally, a more complex plugin to store bookmarks will be implemented, this is called ”Linklog”-plugin.
In addition, this study should simplify the future development of plugins for this CMS.
    Note: The plugins were written for the current trunk. This is version 1.4-dev (r6526).

1.3   Motivation
Keeping bookmarks in one place is a desire of many internetusers. Successful launches of plattforms
such as del.icio.us [1] proofed the acceptance and the needs to store favorites on a central place without
being dependent on the moods of a local computer. Problems such as having different bookmarks stored
on various computers or installations are solved by saving all links on one place on a server, which is


                                                                                            Page 1 of 79
reachable throughout the internet.

    Since ”Flux CMS” is the choice of the personal homepage of mine for about a year now, i was
very interested in being able to gain some deeper knowledge of the popoon framework and the plugin-
architecture of ”Flux CMS” to extend it according to my ideas.

1.4     The Flux CMS
The ”Flux CMS” [2] is a serverside-software to publish content on the internet. It is developed by the
”Bitflux GmbH” [3], a company based in Zurich. The first releases were back in 2002. Initially it was
based on PHP4. With a total rewrite in 2004, the sources were ported to PHP5. ”Flux CMS” uses
XSLT as its templating engine and XML for its internal dataflow and configuration-files.
    The ”Flux CMS” already offers quite a broad set of plugins. For example an advanced blog-plugin
with mobile blogging support and XML-RPC-interface, a plugin to publish pictures and also a simple
permission-module.

   Prerequisites for a ”Flux CMS”-installation basically is Apache [4] with mod rewirte-support [5] and
PHP5 with some extensions such as XSL. A detailed instruction on how to install ”Flux CMS” can be
found in the documentation [6].
   A list of sites running on ”Flux CMS” is available in the wiki [7].

1.5     Techniques
1.5.1    PHP5
Flux CMS entirely builds on PHP [8] Version 5. Compared to the very widely spread PHP4, this Version
brought many improvements especially in the support for ”real” objectorientation. For example, the
visibility of class methods and variables is now fully supported. They can be set to ”public”, ”protected”
and ”private” just like in e.g. Java.

    A broad overview on this new objectmodel in PHP5 provides the PHP Manual (Chapter 19 [9]).
PHP5 also brought many improvements regarding performance and handling of XML-files. For a set of
presentations about the topic, have a look at the publications-section of the Bitflux-page [10].

1.5.2    Cocoon/Popoon
Cocoon [11] is a advanced web-development framework written in Java and relies on XML for datapro-
cessing. The clue of cocoon are the so called ”pipeline”. These are processing instructions on how to
handle a request. The main three steps are called ”Generation”, ”Transformation” and ”Serialisation”
(see Figure 2). This makes it possible to store data only once and depending on the clients request,
process the data in different ways to output content in any possible way defined in a so called sitemap.
Sitemaps are XML-files, where all these rules are defined. This architecture has been proven very solid
on many projects.
    A request can be modelled as described in Figure 3. The a visitor comes to a website, Cocoon parses
the URL requested and passes it to the sitemap which will then call the appropriate pipeline. A pipeline
defines the whole setup, means it defines the generator, transforms through the right XSL and handles
out the content trough the serializer, for example as HTML.
    A pipeline can also use more than one transformers, this is visualized in Figure 4.



                                                                                            Page 2 of 79
                   Figure 2: Generator-Transformer-Serializer-Model from Cocoon




                    Figure 3: Basic sequence of processing a request in Cocoon


   More basic information about Cocoon can be found in the ”Understanding Apache Cocoon”-
Section [12] of the manual or in the overview-section of the documentation [13].

    Popoon[14] uses the same concepts of Cocoon and it is written in PHP5. Compared to Cocoon,
Popoon is not as feature-rich and complex. The syntax for the sitemaps of Cocoon are mostly the same
in Popoon.




                                                                                       Page 3 of 79
                        Figure 4: Basic mechanism of Cocoon handling a request


2     Plugin Basics
2.1     General information
Figure 5 visualized the basic procedure of handling a request and how popoon and ”Flux CMS” interact
together.
    For writing a simple plugin without an own editor, usually there is no need to worry about the
sitemaps and the actions of popoon. ”Flux CMS” handles this. The main task is to keep the naming
conventions and put the classes in the right places. This is described in more detail below.

    The most imported thing one must realize when starting with ”Flux CMS”-plugins is, compared to
maybe other known PHP-software, that everything passed around are DOM-objects (and not arrays or
strings as such). So a bit of basics about DOM in PHP5 are quite helpful. Further information about
the DOM-functions in PHP are found in the manual [15]. Bitflux had held quite a few presentations on
the subject as well [10].

    Second thing is, that the method actually returning the output is usually called getContentById.
According to this, the such called ”$id” is the most important parameter. An easy way to output and
debug the parameters while developing is to use the method ”webdump” found in ”bx helpers debug”.
getContentById receives two parameters, $path and $id. To see, what these two values currently are,
the following two lines can be put into getContentById:

 1              bx_helpers_debug :: webdump ( $path );
 2              bx_helpers_debug :: webdump ( $id );

    The output will be similar to Figure 6. These parameters are available from within the plugin itself.
    There are also a bunch of globally available constants defined. To print them all out, the following
line can temporarly be added to getContentById-method:
 1    bx_helpers_debug :: webdump ( g e t _ de fine d_co nsta nts ());


                                                                                           Page 4 of 79
                        Figure 5: Popoon and ”Flux CMS” handling a request

 2   // also interesting , containing e . g . the db - handler :
 3   bx_helpers_debug :: webdump ( $GLOBALS [ ’ POOL ’]);

    Generally spoken, a plugin consist of 2 parts: the frontend and the backend. The frontend is the
part, which is displayed to the enduser. The backend is where all the administrator task are done, such
as inserting, editing and deleting content. ”Flux CMS” offers for the backend the class ”dbforms2”,
which will allow you to define your forms through a XML-file. If more flexibility is needed, one can still
write its very own editor.

    The ”Hello World”-plugin uses only the ”frontend” and is kept very simple. It shows the very basic
setup of a plugin without any needs of administration. Such a plugin could be used to include external
datasources.

    The ”Random Quotes”-plugin uses the frontend and sets up the backend with dbforms2. The con-
tent get saved in a database with a single table. A similar plugin could be about anything that requires
basic datamanipulation such as a guestbook, a basic newsscript or similar stuff.

    The third plugin created is the ”Linklog”-plugin. This is the ”Bookmark”-Plugin, but here it is called
”Linklog”, since it outputs the links (=bookmarks) like a logfile (chronologically). Different sorting-
mechanisms could be implemented easily though. Compared to the previous plugins, the ”Linklog”-

                                                                                            Page 5 of 79
                              Figure 6: Output of bx helpers debug::webdump


plugin will additionally contain an own editor for the administration-tasks. The content is generated out
of three tables in the database. It also extends the navigation-tree.

2.2      Developing plugins in a different repository
While developing a plugin, it may be handy to keep it in an own repository. For this case, just create
a folder called ”localinc/bx/plugins ” in your ”Flux CMS”-folder. This then gets autoloaded as well
without any further adjustments. This folder can then be submitted to a separate repository.

2.3      ”Hello World”-Plugin
2.3.1     Requirements
This plugin should display ”Hello World” when being directly calling the matching collection.
    It should as well display ”Hello $String” where $String can be passed in the URL, so e.g. www.example.com/hello/fo
will display ”Hello foo”. The task here is to be able to place the necessary files into the right folders
and to see how a plugin is included into the ”Flux CMS”.

2.3.2     Needed Files
./inc/bx/plugins/helloworld.php: This contains the actual plugin which gets called by the autoload()-
function 1 and is defined in the .configxml of the current collection.

./themes/$your-theme/helloworld.xsl: This is the XSL-file, which transforms the XML-output of
the plugin into HTML. This can also be set within the .configxml.

2.3.3     Implementation


The .configxml:
  1
      Autoloading Objects: http://www.php.net/autoload


                                                                                           Page 6 of 79
    After a new collection named e.g. ”hello” is created, the plugin must be hooked to the collection
via the .configxml-file. Figure 7 shows where to add a .configxml.
    Here is the .configxml for the ”Hello World”-plugin.
 1   < bxcms xmlns =" http :// bitflux . org / config " >
 2      < plugins >
 3         < parameter name =" xslt " type =" pipeline " value =" helloworld . xsl "/ >
 4         < extension type =" html "/ >
 5         < plugin type =" helloworld " >
 6         </ plugin >
 7         < plugin type =" navitree " > </ plugin >
 8      </ plugins >
 9   </ bxcms >

    Line 3 gives the instruction to process the output through ”helloworld.xsl” which is placed within the
chosen theme-folder (e.g. ”/themes/2-cols/helloworld.xsl”). Line 5 will then call the bx plugin helloworld-
class, which will act as the generator of the XML. The extension-part tells where to match. ”html”
matches for ”index.html”. ”foo.html” etc. Instead of ”html”, one can set e.g. ”xml”, then the plugin
would be executed, when e.g. ”index.xml” would be in the URL.
    Line 7 will call the plugin ”bx plugins navitree” as well - it is responsible to display the navigation-
tree.




                            Figure 7: Option in the admin to add .configxml


The class file:

   The plugin-class must extend the bxIplugin which is found in /inc/bx/interfaces/plugin.php and
contains three methods:
 1   interface bxIplugin      {
 2       public function      getResourceById ( $path , $id );
 3       public function      getIdByRequest ( $path , $name = NULL , $ext           = NULL );
 4       public function      g etCon tentUriById ( $path , $id );
 5   }

     A minimal plugin must though at least implement the following methods.
 1   <? php
 2   class b x _ p l u g i n s _ h e l l o w o r l d extends bx_plugin implements bxIplugin {
 3        static public $instance = array ();
 4        public static function getInstance ( $mode ) {}
 5        protected function __construct ( $mode ) {}


                                                                                                Page 7 of 79
 6         public function getContentById ( $path , $id ) {}
 7         public function isRealResource ( $path , $id ) {}
 8   }
 9   ?>

   The most important method is the getContentById, it returns a DOM-document which then must
be processed by the given XSL-file.

     The XML that has to be returned by this plugin may look like this:
 1   < hello > World </ hello >

  The textnode will contain the string which will be outputted by within the frontend. Really ”Hello
World”-like ;)

     The fully working then class looks like this:
 1   <? php
 2   class b x _ p l u g i n s _ h e l l o w o r l d extends bx_plugin implements bxIplugin {
 3
 4         static public $instance = array ();
 5
 6        public static function getInstance ( $mode ) {
 7            if (! isset ( self :: $instance [ $mode ])) {
 8                self :: $instance [ $mode ] = new bx_ plug ins_ hell owor ld ( $mode );
 9            }
10            return self :: $instance [ $mode ];
11        }
12
13        protected function __construct ( $mode ) {
14            $this - > mode = $mode ;
15        }
16
17         public function getContentById ( $path , $id ) {
18             $dom = new DomDocument ();
19             $root = $dom - > createElement (" hello ");
20             $root = $dom - > appendChild ( $root );
21
22              $dirname = dirname ( $id );
23
24              if ( $dirname !== "."){
25                    $root - > appendChild ( $dom - > createTextNode ( $dirname ));
26              } else {
27                    $root - > appendChild ( $dom - > createTextNode (" World "));
28              }
29              return $dom ;
30        }
31
32        public function isRealResource ( $path , $id ) {
33            return true ;
34        }
35   }
36   ?>

   getInstance($mode) (Line 6) is called from the ”outside”. It makes sure, that only one instance per
request is created.

   The getContentById distinguishes the $id to build up a different DOM depending on the call. This is,
what propably most plugins will have to do. Take the URI apart and return a different DOM depending

                                                                                                Page 8 of 79
on the request.
    This already does the job. The XML-output can be viewed by calling www.example.com/hello/?XML=1
(make sure you’re logged in!).

The XSL-file

     The XSL is really simple, it just has to match the textnode within the hello-tags.
 1   < xsl : template name =" content " >
 2      <h1 > Hello < xsl : value - of select ="/ bx / plugin / hello /" / > </ h1 >
 3   </ xsl : template >

     For the whole sourcecode have a look at the Appendix 5.1.1.




                           Figure 8: ”Hello World”-plugin without a parameter




                        Figure 9: ”Hello World”-plugin with the parameter ”foo”




                                                                                          Page 9 of 79
2.4     ”Random Quotes”-Plugin
2.4.1     Requirements
The purpose of this second example-plugin is to output a page with a single random quote which
gets fetched from the database. A second page should output all the quotes. In the backend, the
administrator should be able to insert new quotes as well as editing and deleting existent ones.

2.4.2     Files needed
Here a list of files that will be needed by this plugin.

     • ./dbforms2/randomquotes.xml

     • ./inc/bx/plugins/randomquotes.php

     • ./themes/2-cols/randomquotes.xsl

      All the sources can be found in Appendix 5.2.

2.4.3     Database
The database is quite simple. All it has to store is a quote plus an author, so it consist of one single
table with the following fields: ”id”, ”author” and ”quote”.
    Here is a dump for creating the table:
 1    CREATE TABLE ‘ fluxcms_randomquotes ‘ (
 2      ‘ quote ‘ varchar (255) NOT NULL default ’’,
 3      ‘ author ‘ varchar (30) default ’ unknown ’ ,
 4      ‘id ‘ int (10) unsigned NOT NULL auto_increment ,
 5      PRIMARY KEY ( ‘ id ‘)
 6    ) ENGINE = MyISAM DEFAULT CHARSET = utf8 ;

      Make sure to add your prefix of the database when creating this.

2.4.4     Building an editor with dbforms2
dbforms2 is a library developed by the Bitflux GmbH. It allows to create an editor to manipulate a
database simply by configuring an XML-File. This XML-File can be placed in /dbforms2/youreditor.xml
- and call it at www.example.com/admin/dbforms2/youreditor/.

 1    < dbform : form xmlns : dbform =" http :// bitflux . org / dbforms2 /1.0" >
 2       < dbform : fields table =" randomquotes " onsavejs ="" >
 3          < dbform : field name =" quote " type =" text_area " descr =" Quote " > </ dbform : field >
 4          < dbform : field name =" author " type =" text " descr =" Author " > </ dbform : field >
 5       </ dbform : fields >
 6       < dbform : chooser namefield =" quote " wherefields =" author , quote " limit ="35"/ >
 7    </ dbform : form >

    This is already it. The form created by this plugin is shown in Figure 10. If the database needs to
be extended, simply adding a line to the XML-file above will adjust the form.
    For more information about dbforms2, refer to the pages in the Wiki [16], there are many possible
fieldtypes, even wysiwig-fields.



                                                                                           Page 10 of 79
                    Figure 10: ”Random Quotes”-plugin: simple editor build with dbforms2

   Now the database can already be filled with smart quotes - also see ”fortunes-bofh-excuses”      2   for
some inspiration ;)

    To allow access to the editor through the Quicklinks at the very top right, extend the array in
/structure/quicklinks.php like this:
 1       $quicklinks = array (
 2           ’ admin / siteoptions / ’ = > $i18n - > translate ( ’ Site - Options ’) ,
 3           ’ users ’ = > ’ Users ’ ,
 4           ’ admin / dbforms2 / randomquotes / ’ = > ’ Random Quotes ’ ,
 5       );

         The result is shown in Figure 11.

2.4.5        Generating the data
In order to output the data, the ”front”-plugin itself is created next. It has to implement the same
interface and is structured the same way as the ”Hello World”-plugin. Only some remarkable snipplets
are described here.

   The database-connection gets included directly in the constructor. It is available through the
$ GLOBALS-object:
 1            protected function __construct ( $mode ) {
 2                $this - > tablePrefix = $GLOBALS [ ’ POOL ’] - > config - > getTablePrefix ();
 3                $this - > db = $GLOBALS [ ’ POOL ’] - > db ;
 4                $this - > mode = $mode ;
 5            }

    The getContentById then mainly uses this database-connection to fetch the links. According to the
id, different queries are executed.
 1       $db2xml = new XML db2xml ( $this - > db ," randomquotes ");
 2       if ( $set === " all ") {
 3            $query = " SELECT * FROM ".
 4            $this - > tablePrefix . $this - > randomquotesTable
 5            " AS quotes ORDER BY rand ()";
 6       } else {
 7            $query = " SELECT * FROM ".
 8            $this - > tablePrefix . $this - > randomquotesTable
     2
         http://packages.debian.org/stable/games/fortunes-bofh-excuses


                                                                                           Page 11 of 79
 9      ." AS quotes ORDER BY rand () LIMIT 0 ,1";
10   }
11   $res = $this - > db - > query ( $query );
12   $dom = $db2xml - > getXMLObject ( $res );
13   return $dom ;

     The output for the above query is for example the following:
 1   < result >
 2      <row >
 3         < quote > Operator , please trace this call and tell me where I am . </ quote >
 4         < author > unknown </ author >
 5         <id >1 </ id >
 6         </ row >
 7      </ result >
 8   </ randomquotes >


The XSL-file:

   To display the quotes in the content-area, the template is overwritten and the quotes are displayed.
The important section looks like this (See 5.2.2):
 1        < xsl : template name =" content " >
 2              <h1 > Randomquotes </ h1 >
 3                 < xsl : for - each
 4                   select ="/ bx / plugin [ @name = ’ randomquotes ’]/ randomquotes / result /*" >
 5                          <ul >
 6                               <li > < xsl : value - of select =" quote "/ > </ li >
 7                               <li > < xsl : value - of select =" author "/ > </ li >
 8                          </ ul >
 9                          < hr / >
10                   </ xsl : for - each >
11        </ xsl : template >


2.4.6    Activating the plugin
The .configxml of this plugin is quite the same as the one in the ”Hello World”-plugin, except for the
name of the plugin and the xsl being passed back:
 1   < bxcms xmlns =" http :// bitflux . org / config " >
 2             < plugins >
 3                   < parameter name =" xslt " type =" pipeline " value =" randomquotes . xsl "/ >
 4                   < extension type =" html "/ >
 5                   < plugin type =" randomquotes " >
 6                   </ plugin >
 7                   < plugin type =" navitree " > </ plugin >
 8             </ plugins >
 9        </ bxcms >


2.4.7    Conclusion
With dbforms2 it is very simple to setup an editor to manipulate a database. The documentation has
been improved a lot lately. If a simple editor is needed, dbforms2 surely should be the way to go. Setting
up a whole form within a single XML-file is very convenient and the class offers very nice formfields
such as the date-chooser or the file-uploader.


                                                                                           Page 12 of 79
               Figure 11: ”Random Quotes”-plugin: Appearance in ”Quicklinks”-menu




                      Figure 12: ”Random Quotes”-plugin: output of one quote


    The class sql2xml used in this plugin is very handy. It not only is able to handle queries on a single
table, but also allows joining tables. There is also another class to be used in the combination of DB
and XML, it is called ”structure2xml” [17] and according to the developers preferable.

    For some weeks now, there is also a ”Plugin Generator” included in the sourcetree of ”Flux CMS”.
It generates all files needed, including dbforms2-editor, a basic XSL plus the plugin-class itself. Only a
database-table has to be created. More infos and a howto about this is found on the ”devblog” [18].




                                                                                           Page 13 of 79
                            Figure 13: ”Random Quotes”-plugin: output of all quotes


3        ”Linklog”-Plugin
3.1        Requirements
The general requirements include:

    • Adding of new links

    • Editing existing links

    • Deleting exisiting links

    • Grouping of links

    • Show newest links first

    • RSS-Output3 for all links

    • RSS-Output only for groups

        Additional features such as:

    • Browsable archive

    • Different views

    • Bookmarklet-Support

    • displaying links from del.icio.us

    • Displaying screenshots to each link

        The requirements for the code:
    3
        RSS is a Web content syndication format, see http://blogs.law.harvard.edu/tech/rss for more information



                                                                                                   Page 14 of 79
                      Figure 14: ”Random Quotes”-plugin: XML-output of all quotes


   • Extensible

   • As readable as possible

   • Using of existent code

   • Possibility of caching

      Additional to these points, the handling should be simple and as intuitive as possible.

3.2     Implementation
Figure 15 shows the filesystem with all necessary files for the ”Linklog”-plugin. It is a screenshot
from ”Eclipse” [19] with the ”Subclipse-Plugin” [20], which is a handy plugin while developing with a
subversion-repository.
    The full sourcecode can be found in Appendix 5.3.

3.2.1     Database Structure
The cleanest solution to implement a taggingstructure is solved by a three-table-construction.
    Different setups for this kind of data have been tested and nicely documented by Philipp Keller in
his blog [21]. The used database is pretty similar to the one in Figure 16.
    Below a dump of the used database with the prefix ”fluxcms ”:

                                                                                            Page 15 of 79
                         Figure 15: ”Linklog”-plugin: overview over all files needed


 1   CREATE TABLE f l u x c m s _ l i n k l o g _ l ink s (
 2     id int (11) NOT NULL auto_increment ,
 3     title varchar (40) NOT NULL default ’’,
 4     description varchar (255) NOT NULL default ’’,
 5     url varchar (255) NOT NULL default ’’,
 6     ‘ status ‘ smallint (2) NOT NULL default ’0 ’ ,
 7     timeadded varchar (12) default NULL ,
 8     ‘ time ‘ datetime NOT NULL default ’0000 -00 -00 00:00:00 ’ ,
 9     PRIMARY KEY ( id ) ,
10     KEY id ( id )
11   ) ENGINE = MyISAM DEFAULT CHARSET = utf8 ;
12
13   CREATE TABLE f l u x c m s _ l i n k l o g _ l i n k s 2 t a g s (
14     id int (10) unsigned NOT NULL auto_increment ,
15     linkid int (10) unsigned NOT NULL default ’0 ’ ,
16     tagid int (10) unsigned NOT NULL default ’0 ’ ,
17     PRIMARY KEY ( id ) ,
18     KEY lid ( linkid , tagid )
19   ) ENGINE = MyISAM DEFAULT CHARSET = utf8 ;
20
21   CREATE TABLE f l u x c m s _ l i n k l o g _ t ags (
22     id int (11) NOT NULL auto_increment ,
23     name varchar (128) character set latin1 NOT NULL default ’’,
24     fulluri varchar (255) character set latin1 default NULL ,


                                                                                      Page 16 of 79
                   Figure 16: ”Linklog”-plugin: three-table-structure used for database


25     PRIMARY KEY ( id ) ,
26     KEY SRLR ( id ) ,
27     KEY node_id ( id )
28   ) ENGINE = MyISAM DEFAULT CHARSET = utf8 ;


3.2.2    The internal DOM-structures
This is a good point to define some internal XML-Files.

XML used in the Editor

 1   < linklog >
 2      < link >
 3         < id / >
 4         < url / >
 5         < title / >
 6         < description / >
 7         < time / >
 8         < tags / >
 9      < link >
10      < tags >
11         ...
12      </ tags >
13   < linklog >

    the ”/linklog/link” contains the data about one single link. If there is no link selected, those values
are empty. ”/linklog/tags” contains all tags, which have been used until now. This makes it possible
to show all tags in the editor, so links can be easily tagged identically.
    Be aware, that the generated XML will be embedded into a predefined XML-trees by the framework.
The actual DOM-output will look like this:
 1   <bx >
 2     < plugin name =" admin_edit " >
 3         < linklog / >
 4     </ plugin >
 5   <bx >

     So the XPATH-expressions in the xsl-files must be adjusted to match.
     The internal output of the editor will later look as shown in Figure 17.

XML used by the frontend

 1   < links >
 2      < meta >
 3         ...



                                                                                            Page 17 of 79
                          Figure 17: ”Linklog”-plugin: internal XML-stream of editor


 4      < meta >
 5      < link >
 6         ...
 7         < tags >
 8            < tag / >
 9            ...
10         </ tags >
11      < link >
12      < link / >
13      ...
14   < linklog >

    The ”/links/meta” will contain some metadata about the page itself (e.g. title). It could as well be
extended with data such as a link to the current RSS-feed. ”/links/link” containes all the data about
a link. For as ”/links/link/tags/tag” contains infos about the tags. The finally outputted XML in the
frontend will also contain the navigationtree-data, which gets generated through bx plugins linklog tags.
    Same as above, ”Flux CMS” will wrap the tree into it’s own tags:
 1   <bx >
 2     < plugin name =" linklog " >
 3         < links / >
 4     </ plugin >
 5   <bx >


3.2.3    Classes and files for the admin
To be able to insert new links, the setup of the editor is descibed first. First, all files needed for the
editor are listed and casually described. To get an overview over all files needed, see Figure 15.



                                                                                          Page 18 of 79
                     Figure 18: ”Linklog”-plugin: internal XML-stream in frontend



/admin/sitemap/linklog.xml

     This is the popoon-sitemap for the editor. It defines a few parameters for the pipeline used.
 1   ...
 2   < map : select type =" phpglobalvars " var =" GET " key =" XML " >
 3         < map : when test ="1" >
 4            < map : serialize type =" xml " >
 5               < map : parameter name =" t rickMozillaDisplay " value =" true "/ >
 6            </ map : serialize >
 7         </ map : when >
 8      < map : otherwise >
 9         < map : transform type =" xslt "
10            src =" constant ( BX_LIBS_DIR ) editors / linklog / linklog . xsl " >
11            ...
12         </ map : transform >
13         < map : serialize type =" html "/ >
14      </ map : otherwise >
15   </ map : select >
16   ...

    This select-instruction checks, if there is a ”XML” passed via URL, and if its value is 1, the content-
type ”text/xml” is returned. This makes debugging a lot easier, since it allows you, to add the already
known ?XML=1 in the admin to see the XML-output. Otherwise (if there is no XML=1 in the URL),
the generated data gets processed by the ”/inc/bx/editors/linklog/linklog.xsl”-stylesheet.

/inc/bx/editors/linklog.php


                                                                                            Page 19 of 79
    This is the main file for the editor. It includes all methods to insert, edit and delete links. The most
important methods are ”getEditContentById” and ”handlePOST”. The first is about similar to the
getContendById-method and returns a DOM-Object, which gets, in this case, handled by ”/inc/bx/ed-
itors/linklog/linklog.xsl”. The handlePost-Method is called, as soon as the form is submitted. Both
methods will be described below in more detail.
    An editor must extend the class bx editors and implement the interface bxIeditors. Allthough those
two classes do not include much, we adapt this from the other editors.
    Here is the basic setup of this editor:
 1    class b x_ ed it or s_ li nk lo g extends bx_editor implements bxIeditor {
 2
 3         public function __construct (){}
 4
 5         public function getPipelineName () {
 6             return ’ linklog ’;
 7         }
 8
 9         public function getDisplayName () {
10             return ’ Linklog Editor ’;
11         }
12
13         public function handlePOST ( $path , $id , $data ) {}
14         public function ge tE di tC on tentById ( $id ) {}
15    }

    getPipelineName defines the pipeline internally used by this editor (see ”/admin/sitemap/linklog.xsl”).
    First, we will setup the getEditContentById()-Method. This is supposed to return a DOM-Object
as described in the XML-File in 3.2.2.
    Since a database connection is used, the handler will be called, when the editor is initialized:
 1    public function __construct (){
 2      $this - > tablePrefix = $GLOBALS [ ’ POOL ’] - > config - > getTablePrefix ();
 3      $this - > db = $GLOBALS [ ’ POOL ’] - > db ;
 4    }

     Definition of the $ids used in the admin-interface are:

     • /admin/$collection/

     • /admin/$collection/edit/$linkid/

     • /admin/$collection/delete/$linkid/

    The first is the default handler, it will only return an empty form to insert a new link. The second
is to edit an existing link. It will return the known form from the first case, but filled with the values
of the chosen link. The third pattern will just delete a link with the given id. Knowing this, the
getEditContentById can basically be setup. First, we take the $id apart to get the values matching our
patterns.
 1    public function ge tE di tC on te nt ById ( $id ) {
 2        $parts = bx_collections :: g e t C o l l e c t i o n U r i A n d F i l e P a r t s ( $id );
 3        $colluri = $parts [ colluri ]; // is something like "/ linklog /"
 4        $myPath = str_replace (" $colluri " ,"" , $id );
 5
 6         if ( $myPath === ""){
 7               return $this - > getEmptyLink ();
 8         } elseif ( substr ( $myPath ,0 ,4) == " edit "){


                                                                                                        Page 20 of 79
 9             return $this - > getSingleLink ( $linkid );
10        } elseif ( substr ( $myPath ,0 ,6) == " delete "){
11             if ( $this - > deleteLink ( $linkid ) === true ){
12                   header ( ’ Location : ../? deleted =1 ’);
13             }
14        } else {
15             return $this - > getEmptyLink ();
16        }
17   }

    As you can see, this was really simple. Now, three different methods have to be implemented.
getEmptyLink() and getSingleLink() will both return the whole DOM-tree for the linklog.xsl-File, while
deleteLink() will return a boolean.
    The only thing which is missing yet, is the value of $linkid. It can be received easily:
 1             $tmp = explode ("/" , $myPath );
 2             $linkid = end ( $tmp ) + 0;

    The single methods are not described in detail here. Please refer to the Appendix 5.3.5 to review
the whole class.


/inc/bx/editors/linklog/linklog.xsl

    The form to edit and insert links is constructed here. It is a simple XSL-File which transform the
DOM-Object returned by getEditContentById() into an editor. Important in the form is, that the fields
are all named in this manner: ”bx[plugins][admin edit][your field name]”. Like this, the data is passed
directly to the handlePost-method in bx editors linklog and available as the $data-array.

     Figure 19 displays the generated editor.




                            Figure 19: ”Linklog”-plugin: layout of the editor




                                                                                        Page 21 of 79
3.2.4    Classes and files for the frontend


/inc/bx/plugins/linklog.php

    This again is the actual ”pulling the content”-file. It’s most important method is getContentById().
The basic behaviour is the same as in the ”Hello World”- and the ”Random Quotes”-plugin. Again, the
first thing to be decided is how different requests should be mapped, so specific output can be delivered.
Similar to the backend, the following patterns will be used:

     • /$collection/

     • /$collection/rss.xml

     • /$collection/$tag/

     • /$collection/$tag/rss.xml

     • /$collection/all/

    The first will generate a page with the latest 30 links. The second will return the same in RSS-Format.
Third will only return links marked with $tag - as well in reverse chronological order. /$tag/rss.xml will
return links marked with the given tag in RSS-Format. The latest will return all links.
    Knowing this, setting up the getContentById()-method is quite simple:
 1    public function getContentById ( $path , $id ) {
 2        $dirname = dirname ( $id );
 3        switch ( $dirname ) {
 4            case " all ":
 5                 return $this - > getAll ();
 6            case ".":
 7                 return $this - > getSplash ();
 8            default :
 9                 return $this - > getLinksByTag ( $id );
10        }
11    }

   All we have to do yet, is to implement these methods that they return the to our definition corre-
sponding DOM-tree shown in figure 18.

/inc/bx/plugins/linklog/tags.php

    This is somehow it’s own plugin within the ”Linklog”-plugin, also called a ”portlet”. It is called
from within the XSL with the following syntax (plus some stuff around, see section 5.3.7):
 1    portlet :// ’ , $collectionUri , ’ plugin = tags ( ’ , $filename , ’). xml ’)

    All this does is requesting the URL http://www.example.com/collectionUri/plugin=tags().xml in an
internal way. The output can be debugged by just adding ”?XML=1” to this URL. The XSL receives
internally a document like this:
 1    <bx >
 2      < plugin name =" linklog " >
 3          < collection selected =" all " >
 4             < items >
 5                < collection selected =" all " >


                                                                                           Page 22 of 79
 6                  < title / >
 7                  < uri / >
 8                  < display - order / >
 9               </ collection >
10               < collection selected =" selected " >
11                  ...
12               </ collection >
13            </ items >
14         </ collection >
15      </ plugin >
16   </ bx >

    This is the navigation-tree which gets handled by the predifined master.xsl-template.
    Since all URL-Patterns have been defined before and /plugin= has not been caught, this has to be
done. Otherwise bx plugins linklog tags would never be called. So we add the following lines previous
to the other handlers:
 1        if ( strpos ( $id ," plugin =") === 0) {
 2                 return $this - > ca ll InternalPlugin ( $id , $path );
 3        }

     The final construct will then look like this:
 1   public function getContentById ( $path , $id ) {
 2       $dirname = dirname ( $id );
 3       if ( strpos ( $id ," plugin =") === 0) {
 4            return $this - > ca ll In te rnalPlugin ( $id , $path );
 5       }
 6       switch ( $dirname ) {
 7            case " all ":
 8                return $this - > getAll ();
 9            case ".":
10                return $this - > getSplash ();
11            default :
12                return $this - > getLinksByTag ( $id );
13       }
14   }

    The method callInternalPlugin will then return a DOM generated by the tags-class. This snippled
has been copied from the blog-plugin. It is not really necessary when only one ”sub”-plugin is available,
but it makes it very flexible to add new ones.

/themes/$yourtheme/linklog.xsl

    This is the XSL-file which transforms the DOM returned by the plugin. Again, the content-template
from master.xsl is overwritten. Additinally, since an own navigation-tree is provided, the left-template
gets handled here, too. Other than that, a meta-template is implemented as well. But this just creates
the headers.

/themes/standard/admin/plugins/linklog/linklog2rss.xsl

    This is for all the rss-files, which, according to 3.2.4, will be called, when the URI matches ”rss.xml”.
This basically just calles a different XSL for the same XML. So all linklog2rss.xml has to do, is to
transform the given XML to an RSS-valid output. The currently implemented RSS-Output is very basic
and could certainly improved a lot.



                                                                                             Page 23 of 79
.configxml

 1   < bxcms xmlns =" http :// bitflux . org / config " >
 2        < plugins inGetChildren =" false " >
 3             < extension type =" xml "/ >
 4             < file preg ="# plugin =#"/ >
 5             < plugin type =" linklog " >
 6             </ plugin >
 7        </ plugins >
 8
 9        < plugins >
10             < parameter name =" xslt " type =" pipeline " value =" linklog . xsl "/ >
11               < extension type =" html "/ >
12               < plugin type =" linklog " >
13               </ plugin >
14               < plugin type =" navitree " > </ plugin >
15        </ plugins >
16
17        < plugins inGetChildren =" false " >
18              < extension type =" xml "/ >
19              < file preg ="# rss$ #"/ >
20              < parameter name =" output - mimetype " type =" pipeline " value =" text / xml "/ >
21              < parameter type =" pipeline " name =" xslt "
22                 value ="../ standard / plugins / linklog / linklog2rss . xsl "/ >
23              < plugin type =" linklog " >
24                   < parameter name =" mode " value =" rss "/ >
25              </ plugin >
26        </ plugins >
27   </ bxcms >

    The first match is responsible to call the internal plugins. In this case, there is only one plugin
currently available (bx plugins linklog tags).
    The second plugins-section matches the regular request and delivers the HTML. Additionally, the
navitree is displayed (this is the first-level navigation).
    The third plugins-section matches requests ending with ”rss.xml”. It then just transforms the regular
XML-output through the ”linkog2rss.xsl”.

    This covers the basic requirements listed for the plugin. Displaying links through del.icio.us should
be implemented quite easy.




                                                                                           Page 24 of 79
4    Conclusion
Implemeting a basic plugin is actually quite simple, once the basics are known. The harder task is to
write an own editor, but in most cases this is propably not necessary due the help of dbforms2.

    I was only able to implement the very basic features for the ”Linklog”-plugin, since it took me lots
of hours to figure out how the classes, especially in the backend play together. Quite a challenge were
the different encodings. A strict switch to UTF-8 resolved these troubles (but requires MySQL 4.1 or
newer). Once all these troubles were solved, i was able to focuse on PHP-development again. I will try
to extend the ”Linklog”-plugin in the next few weeks, so it’s actually usable for everyone.Since it has
been accepted in the current trunk, everyone can use and improve it.

   It is fun to be able to develop within the ”Flux CMS”-environment, since it offers a broad variety of
possibilities to resolve requirements. PHP5 and it’s oo-features finally allow real advanced webdevelop-
ment with PHP.




                                                                                         Page 25 of 79
References
 [1] del.icio.us, URL: http://del.icio.us/ - [20.02.2006]

 [2] Flux CMS Wiki: ”Main Page”, URL: http://wiki.flux-cms.org/ - [06.01.2006]

 [3] Bitflux GmbH, URL: http://www.bitflux.ch - [20.02.2006]

 [4] Apache HTTP Server, URL: http://www.apache.org - [20.02.2006]

 [5] Apache HTTP Server: ”Apache Module mod rewrite”, URL: http://httpd.apache.org/docs/
     2.0/mod/mod rewrite.html - [18.12.2005]

 [6] Flux CMS Wiki: ”Installation of Flux CMS”, URL: http://wiki.bitflux.org/Installation
     of Flux CMS - [13.01.2006]

 [7] Flux CMS Wiki: ”Live sites running Flux CMS”, URL: http://wiki.flux-cms.org/LiveSites
     - [20.02.2006]

 [8] PHP Hypertext Preprocessor, URL: http://www.php.net - [18.12.2005]

 [9] PHP Manual: ”Classes and Objects (PHP 5)”, URL: http://www.php.net/manual/en/
     language.oop5.php - [18.12.2005]

                        a
[10] Bitflux GmbH: ”Vortr¨ge”, URL: http://www.bitflux.ch/publikationen/vortraege.html -
     [20.02.2006]

[11] Apache Cocoon Project, URL: http://cocoon.apache.org/ - [20.02.2006]

[12] Apache Cocoon Project: ”Understanding Apache Cocoon”, URL: http://cocoon.apache.org/
     2.1/userdocs/concepts/index.html - [20.02.2006]

[13] Apache Cocoon Project: ”Overview of Apache Cocoon”, URL: http://cocoon.apache.org/2.
     1/overview.html - [20.02.2006]

[14] Popoon Homepage, URL: http://popoon.org/ - [06.01.2006]

[15] PHP Manual:    ”DOM Functions”, URL: http://www.php.net/manual/en/ref.dom.php -
     [20.02.2006]

[16] Flux CMS Wiki: ”dbforms2”, URL: http://wiki.flux-cms.org/DBForms2 - [04.02.2006]

[17] Flux CMS Wiki:     ”Structure2xml”, URL: http://wiki.flux-cms.org/Structure2xml -
     [04.02.2006]

[18] Flux CMS DevBlog: ”Build your own Flux CMS plugin in an instant”, URL: http://devblog.
     flux-cms.org/build-your-own-flux-cms-plugin-in-an-instant.html - [06.01.2006]

[19] Eclipe IDE, URL: http://www.eclispe.org/ - [20.02.2006]

[20] Subclipse: ”Subversion Integration in Eclispe”, URL: http://subclipse.tigris.org/ -
     [20.02.2006]

[21] Keller, Philipp: ”Tags: Database schemas”, URL: http://www.pui.ch/phred/archives/2005/
     04/tags-database-schemas.html - [23.02.2006]


                                                                              Page 26 of 79
5         Appendix
5.1       Sourcecode ”Hello World”-plugin
5.1.1      /inc/bx/plugins/helloworld.php
      0 <?php
      1 /*
      4 * BX INC DIR.’bx/plugins/helloworld.php’
      5 */
      6
      7 /**
      8 * This plugin does not really do anything.      It’s just a simple example
      9 * of how a plugin is workting.
    10 *
    11 * calling it on e.g.        /hello will output "Hello World"
    12 * calling it on e.g.        /hello/foo will output "Hello foo"
    13 *
    14 * To use this plugin in a collection, put the following into .configxml
    15 ***
    16        <bxcms xmlns="http://bitflux.org/config">
    17            <plugins>
    18                <parameter name="xslt"type="pipeline"value="helloworld.xsl"/>
    19                <extension type="html"/>
    20                <plugin type="helloworld">
    21                </plugin>
    22                <plugin type="navitree"></plugin>
    23            </plugins>
    24        </bxcms>
    25 *
    26 * See also the helloworld.xsl for the actual output
    27 */
    28
    29 class bx plugins helloworld extends bx plugin implements bxIplugin {
    30
    31        static public $instance = array();
    32        protected $res = array();
    33
    34        public static function getInstance($mode) {
    35            if (!isset(self::$instance[$mode])) {
    36                self::$instance[$mode] = new bx plugins helloworld($mode);
    37            }
    38            return self::$instance[$mode];
    39        }
    40
    41        protected function   construct($mode) {
    42            $this->mode = $mode;
    43        }
    44


                                                                                      Page 27 of 79
45      public function getContentById($path, $id) {
46      $dom = new DomDocument();
47      $root = $dom->createElement("hello");
48      $root = $dom->appendChild($root);
49      $dirname = dirname($id);
50
51          if($dirname !== "."):
52              $root->appendChild($dom->createTextNode($dirname));
53          else:
54              $root->appendChild($dom->createTextNode("World"));
55          endif;
56          return $dom;
57      }
58
59      public function isRealResource($path , $id) {
60          return true;
61      }
62 }
63 ?>




                                                                      Page 28 of 79
5.1.2   /themes/2-cols/helloworld.xsl
   0 <?xml version="1.0"?>
   1 <xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.
   2                            org/1999/XSL/Transform"xmlns:xhtml="http://www.w3.
   3                            org/1999/xhtml"xmlns="http://www.w3.
   4                            org/1999/xhtml"exclude-result-prefixes="xhtml">
   5        <xsl:import href="master.xsl"/>
   6        <xsl:import href="../standard/common.xsl"/>
   7        <xsl:output encoding="utf-8"method="xml"doctype-system="http://www.w3.
   8                             org/TR/xhtml1/DTD/xhtml1-transitional.dtd"doctype-
   9                             public="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
  10
  11        <xsl:template name="content">
  12 <!--
  13                 have a look at www.example.com/yourCollection/?XML=1
  14                 for the actual XML being transformed
  15           -->
  16           <h1>Hello <xsl:value-of select="/bx/plugin/hello/"/></h1>
  17        </xsl:template>
  18
  19 </xsl:stylesheet>
  20




                                                                                      Page 29 of 79
5.2     Sourcecode ”Random Quotes”-plugin
5.2.1    /inc/bx/plugins/randomquotes.php
      0 <?php
      8 /**
      9 * Simple testplugin
  10 *
  11 * To use this plugin in a collection, put the following into .configxml
  12 ***
  13          <bxcms xmlns="http://bitflux.org/config">
  14                <plugins>
  15                   <parameter name="xslt"type="pipeline"value="randomquotes.
  16                                    xsl"/>
  17                   <extension type="html"/>
  18                   <plugin type="randomquotes">
  19                   </plugin>
  20                   <plugin type="navitree"></plugin>
  21                </plugins>
  22          </bxcms>
  23 ***
  24 * This plugin spits out randomquotes out of a simple db table.
  25 *
  26 * DB Dump:
  27 *<pre>
  28          CREATE TABLE ‘randomquotes‘ (
  29           ‘quote‘ varchar(255) NOT NULL default ’’,
  30           ‘author‘ varchar(30) default NULL,
  31           ‘status‘ tinyint(1) NOT NULL default ’0’,
  32           ‘id‘ int(10) unsigned NOT NULL auto increment,
  33           PRIMARY KEY   (‘id‘)
  34          ) ENGINE=MyISAM;
  35 *</pre>
  36 *
  37 * It can be called two ways:
  38 * www.example.com/$collection/
  39 *        and
  40 * www.example.com/$collection/all/
  41 *
  42 * The second one spits all the quotes in the db
  43 *
  44 * It gets editored through the dbforms2.         Just call
  45 * www.example.com/admin/dbforms2/randomquotes/
  46 *        and to debug the query:
  47 * www.example.com/admin/dbforms2/randomquotes/chooser?q=
  48 *
  49 * For the dbforms2 editor have a look at
  50 * BX ROOT DIR/dbforms2/randomquotes.xml
  51 *


                                                                                   Page 30 of 79
52 * Futher Documentation:
53 * https://svn.sequenz.ch/pa-ws-05/wiki/Randomquotes
54 *
55 * @author Alain Petignat <alain@flux-cms.org>
56 * @todo alles
57 */
58 class bx plugins randomquotes extends bx plugin implements bxIplugin {
59
60      static public $instance = array();
61      protected $res = array();
62
63      /**
64      The table names
65      */
66      public $randomquotesTable = "randomquotes";
67      protected $db = null;
68      protected $tablePrefix = null;
69
70      public static function getInstance($mode) {
71             if (!isset(self::$instance[$mode])) {
72                 self::$instance[$mode] = new bx plugins randomquotes($mode);
73             }
74             return self::$instance[$mode];
75      }
76
77      protected function    construct($mode) {
78             $this->tablePrefix = $GLOBALS[’POOL’]->config->getTablePrefix();
79             $this->db = $GLOBALS[’POOL’]->db;
80             $this->mode = $mode;
81      }
82
83      public function getContentById($path, $id) {
84             $dirname = dirname($id);
85             switch ($dirname) {
86                 case "all":
87                     return $this->getQuote(’all’);
88                 default:
89                     return $this->getQuote();
90             }
91      }
92
93      public function isRealResource($path , $id) {
94             return true;
95      }
96
97      /**
98          * @param path string
99          * @param id string


                                                                                  Page 31 of 79
100       * @return false
101       * */
102   public function getResourceById($path, $id){
103                  return false;
104   }
105
106   public function getIdByRequest($path, $name = NULL, $ext         = NULL){
107             return $name.’.test’;
108   }
109
110   public function getContentUriById($path, $id){
111                  return false;
112   }
113
114   public function adminResourceExists($path, $id, $ext=null, $sample =
115                                               false) {
116             return true;
117   }
118
119   /**
120   * we need to "register"what editors are beeing able to handle this
121   plugin
122   */
123   public function getEditorsById($path, $id) {
124            return false;
125   }
126
127   /**
128       * getQuote
129       *
130       * Retuns a random Quote in the DB-Table.      Either a single, or,
131       * when used with the attribute "all"returns all, quotes as a
132       * dom-object
133       *
134       * @input string "all"for all quotes
135       * @retuns object Dom Object with DB-Results
136       * @todo set and check status,to be sure, all quotes appear equally
137       */
138   private function getQuote($set = false){
139
140            /**
141             * This is a nice class to get xml out of a db-table
142             */
143             $db2xml = new XML db2xml($this->db,"randomquotes");
144
145             if($set === ’all’){
146                  $query = "SELECT * FROM ".
147                            $this->tablePrefix.$this->randomquotesTable


                                                                                  Page 32 of 79
148                         ." AS quotes ORDER BY rand()";
149          }else{
150              $query = "SELECT * FROM ".
151                         $this->tablePrefix.$this->randomquotesTable
152                         ." AS quotes ORDER BY rand() LIMIT 0,1";
153          }
154
155          $res = $this->db->query($query);
156          $dom = $db2xml->getXMLObject($res);
157
158          return $dom;
159
160      }
161 }
162 ?>




                                                                          Page 33 of 79
5.2.2   /themes/2-cols/randomquotes.xsl
   0 <?xml version="1.0"?>
   1 <xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.
   2                          org/1999/XSL/Transform"xmlns:xhtml="http://www.w3.
   3                          org/1999/xhtml"xmlns="http://www.w3.
   4                          org/1999/xhtml"exclude-result-prefixes="xhtml">
   5     <xsl:import href="master.xsl"/>
   6     <xsl:import href="../standard/common.xsl"/>
   7     <xsl:output encoding="utf-8"method="xml"doctype-system="http://www.w3.
   8                          org/TR/xhtml1/DTD/xhtml1-transitional.dtd"doctype-
   9                          public="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
  10
  11     <xsl:template name="content">
  12         <h1>Randomquotes</h1>
  13           <xsl:for-each select="/bx/plugin[@name = ’randomquotes’]
  14                                 /randomquotes/result/*">
  15                 <ul>
  16                     <li><xsl:value-of select="quote"/></li>
  17                     <li><xsl:value-of select="author"/></li>
  18                 </ul>
  19                 <hr />
  20            </xsl:for-each>
  21     </xsl:template>
  22
  23 </xsl:stylesheet>




                                                                                   Page 34 of 79
5.2.3   /dbforms2/randomquotes.xml
   0 <?xml version="1.0"?>
   1 <!--
   2 this is a very simple way to insert and update db-data.
   3 place in /BXDIR/dbforms2/randomquotes.xml and call via
   4 $url/admin/dbforms2/randomquotes/
   5 -->
   6 <dbform:form xmlns:dbform="http://bitflux.org/dbforms2/1.0">
   7    <dbform:fields table="randomquotes"onsavejs="">
   8        <dbform:field name="quote"type="text area"descr="Quote"></dbform:
   9                          field>
  10        <dbform:field name="author"type="text"descr="Author"></dbform:field>
  11    </dbform:fields>
  12    <dbform:chooser namefield="quote"wherefields="author,quote"   limit="35"/>
  13 </dbform:form>




                                                                                     Page 35 of 79
5.3       Sourcecode ”Linklog”-plugin
5.3.1      /admin/sitemap/linklog.xml
      0 <?xml version="1.0"?>
      1
      2 <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
      3
      4      <map:pipelines>
      5
      6          <map:pipeline>
      7               <map:match type="uri"pattern="*">
      8                    <map:include label="standardPipeline"/>
      9               </map:match>
  10             </map:pipeline>
  11
  12             <map:pipeline>
  13                  <map:include label="standardPipeline"/>
  14             </map:pipeline>
  15
  16         </map:pipelines>
  17
  18         <map:include-definitions>
  19
  20
  21                 <map:include-definition label="standardPipeline">
  22                 <!-- this is a supercheap solution... it actually also fetches
  23                 the navitree
  24                            the proper way would be to make a stream for links and
  25                            to fetch that..
  26                 -->
  27                 <map:generate type="bxcmsadmin"src="{1}">
  28                       <map:parameter name="type"value="content"/>
  29                       <map:parameter name="collection"value="{../collection}"/>
  30                       <map:parameter name="id"value="{../id}"/>
  31                  </map:generate>
  32
  33        <!-- this turn on the ?XML=1 Ability in the admin to debug -->
  34                  <map:select type="phpglobalvars"var="GET"key="XML">
  35                       <map:when test="1">
  36                            <map:serialize type="xml">
  37                                 <map:parameter name="trickMozillaDisplay"
  38                                                           value="true"/>
  39                            </map:serialize>
  40
  41                       </map:when>
  42                       <map:otherwise>
  43
  44


                                                                                         Page 36 of 79
45          <map:transform type="xslt"src="constant(BX LIBS DIR)
46                               editors/linklog/linklog.xsl">
47                  <map:parameter name="webroot"value="’constant(BX WEBROOT)
48                                     ’"/>
49                  <map:parameter name="url"value="{1}"/>
50                  <map:parameter name="dataUri"value="{../dataUri}"/>
51                  <map:parameter name="updateTree"value="phpglobalsclean:
52                                      // GET[updateTree]"/>
53                  <map:parameter type="options"name="registerPhpFunctions"
54                                      value="true"/>
55             </map:transform>
56
57
58             <map:serialize type="html"/>
59
60                  </map:otherwise>
61            </map:select>
62         </map:include-definition>
63
64
65    </map:include-definitions>
66
67 </map:sitemap>




                                                                                Page 37 of 79
5.3.2    /inc/bx/editors/linklog.php
   0 <?php
   1 /**
   2 * class bx editors linklog
   3    *
   4 * Class for editing links.
   5    *
   6 * interface bxIeditor {
   7 *      public function getDisplayName();
   8 * }
   9 *
  10 * @todo validate data correctly
  11    * @todo insert the bookmarklet
  12    * @todo replace dbtables with variables
  13    * @todo check if it is possible to use different xsl as editors (light
  14                                                                         editor)
  15 *
  16 * */
  17 class bx editors linklog extends bx editor implements bxIeditor {
  18
  19        /**
  20        The table names
  21        */
  22        public $linksTable          = ’linklog links’;
  23        public $tagsTable       = ’linklog tags’;
  24        public $links2tags          = ’linklog links2tags’;
  25
  26        // data for a link:
  27        protected $id       = ’’;
  28        protected $url       = ’’;
  29        protected $description       = ’’;
  30        protected $title       = ’’;
  31        protected $tags       = array();
  32
  33            /*
  34            * database
  35            */
  36 protected $tablePrefix;
  37 protected $db;
  38        protected $tagCacheFile;
  39
  40        public function     construct(){
  41                 $this->tablePrefix = $GLOBALS[’POOL’]->config->getTablePrefix();
  42                 $this->db = $GLOBALS[’POOL’]->db;
  43        }
  44
  45        // besagt, das eigene xml geholt wird



                                                                                        Page 38 of 79
46        public function getPipelineName() {
47                    return ’linklog’;
48        }
49
50        public function getDisplayName() {
51                    return ’Linklog Editor’;
52        }
53
54        /**
55            * handlePost
56            *
57            * @param String path
58            * @param String id
59            * @param Array data
60            *
61            * @todo setPostData should be renamed to checkAndSetPostData and do
62            validation
63            *       */
64        public function handlePOST($path, $id, $data) {
65   $this->setPostData($data);
66                    $cachefile = BX TEMP DIR."/linklogtags.arr";
67                    if(file exists($cachefile)){
68                              unlink($cachefile);
69                    }
70   /*
71    * the id is only present, when an empty form is being submitted.
72    * otherwise its passed via a hidden field.
73    */
74                    if($data[’id’] == ’’){
75                          $linkid = $this->insertLink();
76                          header(’Location:    ./edit/’.$linkid);
77                    }else{
78                          $this->updateLink($data[’id’]);
79                    }
80            }
81
82   /**
83                * getEditContentById
84                *
85                * The "actual being called"-method when returning content
86                *
87                * @param String id $ GET-Value of URL, e.g /$colluri/edit/1
88                * @return datatype DomDocument to be processed by xsl
89                *
90                * */
91            public function getEditContentById($id) {
92                    /* for debugging:
93                        $this->debug($ GET, "get");


                                                                                    Page 39 of 79
94              $this->debug($id, "id");
95             */
96
97
98             $tablePrefix = $GLOBALS[’POOL’]->config->getTablePrefix();
99             $db = $GLOBALS[’POOL’]->db;
100
101   // $this->debug($this->tablePrefix, "table prefix");
102
103   $path = $ GET[’path’];
104
105   $parts = bx collections::getCollectionUriAndFileParts($id);
106
107   $colluri = $parts[colluri];
108
109   $myPath = str replace("admin/edit$colluri","",$path);
110
111            /* default behaviour is to return an empty document */
112            if($myPath === ""){
113                  return $this->getEmptyLink();
114
115            /*
116             * editing an existing link:
117             * id:../edit/$id
118             */
119   }elseif(substr($myPath,0,4) == "edit"){
120                  $tmp = explode("/",$myPath);
121                  $linkid = end($tmp) + 0;
122
123                  return $this->getSingleLink($linkid);
124
125   }elseif(substr($myPath,0,6) == "delete"){
126                  /**
127                      * deleting a link and redirecting to editors-root
128                      * id:../delete/$id
129                      */
130                  $tmp = explode("/",$myPath);
131                  $linkid = end($tmp) + 0;
132
133                  if($this->deleteLink($linkid) === true){
134                           header(’Location:   ../?deleted=1’);
135                  }
136
137   }else{
138       $myPath = ’’;
139   }
140        }
141


                                                                             Page 40 of 79
142         /**
143             * dataIsValid
144             *
145             * this function is never used yet
146             *
147             * @todo:   implement this function and add return proper error to user
148             *
149             */
150         private function dataIsValid(){
151   if($this->title == ’’){
152       return false;
153   }
154   if($this->url == ’’){
155       return false;
156   }
157   return true;
158         }
159
160   /**
161   * setPostData
162             *
163             * does basic correction of data and sets them to locally declared
164             * parameters
165             *
166             * @param Array     $ POST-data
167             * @return void
168             *
169             * passing the postdata to local variables and basic check and set some
170             values - pass tags to array
171   * */
172         private function setPostData($data){
173       $this->title           = trim($data[’title’]);
174       $this->url           = str replace("&","&amp;",trim($data[’url’]));
175
176   // check description
177       $this->description          = trim($data[’description’]);
178   if($this->description == ’’){
179       $this->description = ’no description’;
180   }
181
182   // check tags
183       $this->tags = explode(’ ’,trim($data[’tags’]));
184   if($this->tags[0] == ’’){
185       $this->tags = array(’default’);
186   }
187         }
188
189   /**


                                                                                         Page 41 of 79
190     * getEmptyLink
191     *
192     * return empty element, may use values passed by $ GET (when using
193                                                                          bookmarklet)
194     *
195     * @return Object an empty link as a domdocument
196     * */
197         private function getEmptyLink () {
198               /* to fill in the http into the empty link */
199               if($ GET[’url’]){
200                     $url = $ GET[’url’];
201               }else{
202                     $url = ’http://’;
203               }
204
205               $xml = ’<linklog>’ .
206                             ’<link>’ .
207                             ’<id />’ .
208                             ’<title>’.$ GET[’name’].’</title>’ .
209                             ’<description>’ .      $ GET[’description’] .’</description>’.
210                             ’<time />’ .
211                             ’<url>’.$url.’</url>’ .
212                             ’<tags />’ .
213                             ’</link>’ ;
214                   $xml .= $this->getTags();
215                   $xml.= ’</linklog>’;
216
217
218                             $dom = new DomDocument();
219                             if (function exists(’iconv’)) {
220                                  $xml =    @iconv(’UTF-8’,’UTF-8//IGNORE’,$xml);
221                             }
222                             $dom->loadXML($xml);
223                        //       print htmlentities($dom->saveXML());
224                             return $dom;
225 }
226
227         /**
228          * get single link out
229          *
230          * @param linkid int id of link to get
231          *
232          */
233         private function getSingleLink ($linkid) {
234
235                     $row = $this->getSingleLinkFromDb($linkid);
236                     if($row === false){
237                        return       $this->getEmptyLink();


                                                                                                 Page 42 of 79
238                  }
239
240                  $tagstring = $this->getTagsForLink($linkid);
241
242                  /*
243                      * put together the dom for returning
244                      * @todo:     xml-schema
245                      * */
246                  $xml       = ’<linklog>’;
247                  $xml .= ’<link>’;
248                  $xml .= ’<id>’.$row[’id’].’</id>’;
249                  $xml .= ’<title>’.$row[’title’].’</title>’;
250                  $xml .= ’<description>’.$row[’description’] .’</description>’;
251                  $xml .= ’<time>’.$row[’time’].’</time>’;
252                  $xml .= ’<url>’.str replace(’&’,’&amp;’,$row[’url’]).’</url>’;
253                  $xml .= ’<tags>’.$tagstring.’</tags>’;
254                  $xml .= ’</link>’;
255                  $xml .= $this->getTags();
256                  $xml .= ’</linklog>’;
257
258
259
260
261                  $dom = new DomDocument();
262                  if (function exists(’iconv’)) {
263                         $xml =    @iconv(’UTF-8’,’UTF-8//IGNORE’,$xml);
264                  }
265
266                  $dom->loadXML($xml);
267                  return $dom;
268 }
269
270     /**
271      * insertLink
272      *
273      * inserts a link to the db with the entry to the merge-table
274      * @return id of inserted link
275      *
276      * */
277     private function insertLink () {
278
279           /*
280             * add       link to db, get back id
281             * @todo make own private method
282             */
283           $query = ’insert into ’.$this->tablePrefix.$this->linksTable.’           (
284                             title, description, url, status, time)’ .
285                             ’VALUES ("’.$this->title.’", "’.$this->description.’", "’.


                                                                                             Page 43 of 79
286                                    $this->url.’", 1, now())’;
287
288           $res = $this->db->query($query);
289           if (MDB2::isError($res)) {
290                    throw new PopoonDBException($res);
291           }
292
293           // get back id:
294           $query = ’select id from ’.$this->tablePrefix.$this->linksTable.’
295                          order by id DESC LIMIT 0,1’;
296           $res = $this->db->query($query);
297           if (MDB2::isError($res)) {
298                         throw new PopoonDBException($res);
299           }
300
301           $lid = $res->fetchRow(MDB2 FETCHMODE ASSOC);
302           $linkid = $lid[’id’];
303
304           /*
305               * do the tags:
306               */
307           $tag array = $this->checkAndGetTagIds($this->tags);
308           foreach($tag array as $tagid){
309                    $this->insertLinks2Tags($tagid, $linkid);
310           }
311           return $linkid;
312 }
313
314     /**
315      * fetches the tags for a link
316      *
317      * @param int linkid id of the link to fetch tag for
318      * @param boolean asArray true if tags should be returned as an array
319      * @return String Tags as a simple String
320      * @return Array Tags as an array
321      *
322      */
323     private function getTagsForLink($linkid, $asArray = false){
324                    /*
325                     * fetch tags
326                     */
327                    $query = ’SELECT ’.$this->tablePrefix.’linklog tags.name, ’
328                             .$this->tablePrefix.’linklog tags.id ’ .
329                             ’FROM ’.$this->tablePrefix.’linklog tags ’ .
330                             ’LEFT JOIN ’.$this->tablePrefix.$this->links2tags.’   ’   .
331                             ’ON ’.$this->tablePrefix.’linklog tags.id = ’.
332                             $this->tablePrefix.$this->links2tags.’.tagid ’ .
333                             ’WHERE ’.$this->tablePrefix.$this->links2tags.


                                                                                              Page 44 of 79
334                             ’.linkid=’.$linkid;
335
336                    $res = $this->db->query($query);
337                    if (MDB2::isError($res)) {
338                        throw new PopoonDBException($res);
339                    }
340
341                    while($tag = $res->fetchRow(MDB2 FETCHMODE ASSOC)){
342                        $tagstring .= "".str replace(’&’,’&amp;’,$tag[’name’])." ";
343
344                        $tagarray[$tag[’name’]]    = $tag[’id’];
345
346
347                    }
348                    if($asArray == false){
349                        return trim($tagstring);
350
351                    }else{
352                        return $tagarray;
353                    }
354     }
355
356     /**
357         * Simple debug function:
358         * */
359     private function debug($data, $name = "var:        "){
360                if(is array($data)){
361                        print ’<pre>’;
362                        print "<b>$name:</b>\n";
363                        print r($data);
364                        print "</pre>";
365                }elseif(is string($data)){
366                        print "<pre><b>$name:</b> $data\n</pre>";
367                }
368     }
369
370     /**
371 * updateLink
372     *
373     * @param String linkid
374     * @return boolean true on success
375     *
376     */
377     private function updateLink($linkid){
378
379            $linkid = $linkid + 0;
380
381            /*


                                                                                         Page 45 of 79
382                * update linktable
383                */
384            $query = ’UPDATE ’.$this->tablePrefix.$this->linksTable.’        SET ’ .
385                           ’title="’.$this->title.’", ’ .
386                           ’description="’.$this->description.’", ’ .
387                           ’url="’.$this->url.’"’ .
388                            ’WHERE id=’.$linkid;
389            $res = $this->db->query($query);
390            if (MDB2::isError($res)) {
391                           throw new PopoonDBException($res);
392            }
393
394            /*
395                * tags are a bit more complicated
396                *
397                * easiest way is to kill existent and renew them
398                * maybe first check if they have changed at all...
399                */
400            $oldTags               = $this->getTagsForLink($linkid, true);
401            $newTagsExisting = $this->getExistingTags($this->tags);
402            $query = ’delete from ’.$this->tablePrefix.$this->links2tags.’
403                           where linkid=’.$linkid;
404            $res = $this->db->query($query);
405            if (MDB2::isError($res)) {
406                           throw new PopoonDBException($res);
407            }
408            $tag array = $this->checkAndGetTagIds($this->tags);
409            foreach($tag array as $tagid){
410                     $this->insertLinks2Tags($tagid, $linkid);
411            }
412
413            return true;
414   }
415
416   /**
417       * getSingleLinkFromDb
418       *
419       * @param int linkid
420       * @return array row out of db
421       */
422   private function getSingleLinkFromDb($linkid){
423                     /*
424                      * fetch data from link
425                      * @todo make private method
426                      */
427                     $query = ’SELECT * from ’.$this->tablePrefix.$this->linksTable.
428                               ’ where id=’.$linkid;
429                     $res = $this->db->query($query);


                                                                                          Page 46 of 79
430                   if (MDB2::isError($res)) {
431                            throw new PopoonDBException($res);
432                   }
433                   $row = $res->fetchRow(MDB2 FETCHMODE ASSOC);
434
435                   // someone produced shit when this happens:
436
437                   if($res->numRows() === 0){
438                            return false;
439                   }else{
440                            return $row;
441                   }
442   }
443
444   /**
445       * function checkAndGetTagIds
446       *
447       * does the main checking of the tags.              if a tag already exsists, it
448       fetches
449       * its id, otherwise, it inserts the tag to the db and also returns the
450       id
451       * of the newly inserted tag
452       *
453       * @param array tags in an array
454       * @return array with ids of corresponding tags for links from db
455       *
456       * */
457   private function checkAndGetTagIds($tags){
458
459            /* get array of already existing tags of the passed tag-array */
460            $tagid = $this->getExistingTags($tags);
461
462            /*
463              * create the tags in the db if they do not exist yet
464              * @todo private method
465              */
466            if((count($tagid) < count($tags)) ||( $tagid == false )){
467                   /*
468                       * loop through tags given by post
469                       */
470                   foreach($tags as $tag){
471                            /*
472                             * compare with previous created array of existing tags
473                             */
474                            // use the fulluri as index
475                            $fulluri = bx helpers string::makeUri($tag);
476                            if(!$tagid[$fulluri]){
477                                  $tagid[$fulluri] = $this->insertSingleTag($tag);


                                                                                            Page 47 of 79
478                        } // end if must insert new tag to db
479                    }        // end foreach tags
480           }                 // end if count of db check < count of tags
481           return $tagid;
482   }
483
484   /**
485       * function getExistingTags
486       *
487       * @return Array
488       *
489       * */
490   private function getExistingTags($tags){
491           /*
492               * check which tags are already in the db
493               */
494               $where = ’(’;
495           foreach($tags as $fulluri){
496                    $where .= ’"’.bx helpers string::makeUri($fulluri) .’", ’;
497           }
498           $where = substr($where, 0, -2);
499           $where .= ’)’;
500           $query = ’select id, fulluri from ’.$this->tablePrefix.
501                            ’linklog tags ’ .
502                            ’WHERE fulluri in ’.$where;
503           $res = $this->db->query($query);
504           if (MDB2::isError($res)) {
505                        throw new PopoonDBException($res);
506           }
507
508           /*
509               * put them in an array $tagid (
510               * @todo:       make private function returning array
511               */
512           if( $res->numRows() > 0 ){
513                        while($row = $res->fetchRow()){
514                               $tagid[$row[1]] = $row[0];
515                        }
516                        return $tagid;
517           }else{
518                    return false;
519           }
520   }
521
522   /**
523       * insertSingleTag
524       *
525       * inserts a tag to the db


                                                                                    Page 48 of 79
526            *
527            * @param String Tag
528            * @return int Id of just inserted tag
529            *
530   */
531        private function insertSingleTag($tag){
532                // make a clean uri
533                $fulluri         = bx helpers string::makeUri($tag);
534
535                $query           =     ’INSERT INTO ’.$this->tablePrefix.’linklog tags’   .
536                                       ’ (name, fulluri)’    .
537                                       ’ VALUES ("’.$tag.’", "’.$fulluri.’")’;
538
539                $res = $this->db->query($query);
540                if (MDB2::isError($res)) {
541                    throw new PopoonDBException($res);
542                    exit;
543                }
544
545                $query           = ’SELECT id, name FROM ’.$this->tablePrefix.
546                                     ’linklog tags ’ .
547                                     ’ORDER BY id DESC LIMIT 0,1’;
548                $res = $this->db->query($query);
549                if (MDB2::isError($res)) {
550                         throw new PopoonDBException($res);
551                         exit;
552                }
553                $row = $res->fetchRow(MDB2 FETCHMODE ASSOC);
554
555                return $row[’id’];
556        }
557
558        /**
559            * insertLinks2Tags
560            *
561            * inserts the values to the merge-table
562            *
563            * @param int tagid - id of the tag in db
564            * @param int linkid - id of link
565            * @return boolean true on success
566            *
567            * */
568        private function insertLinks2Tags($tagid, $linkid){
569                    $query = ’INSERT INTO ’.$this->tablePrefix.$this->links2tags.’            (
570                                  linkid, tagid) ’       .
571                                  ’VALUES (’.$linkid.’, ’.$tagid.’)’;
572
573                    $res = $this->db->query($query);


                                                                                                     Page 49 of 79
574                  if (MDB2::isError($res)) {
575                      throw new PopoonDBException($res);
576                      exit;
577                  }
578                  return true;
579   }
580
581   /**
582       * deleteLink
583       *
584       * deletes a single link in the db plus the corresponding entries in
585       the
586       * merge table
587       *
588       * @param int linkid id of the link to delete
589       * @todo garbagecollection on tags-table
590       */
591   private function deleteLink($linkid){
592             // print $linkid;
593             $queries[] = ’DELETE FROM ’.$this->tablePrefix.$this->links2tags.’
594                              ’ .
595                              ’WHERE linkid=’.$linkid;
596             $queries[] = ’DELETE FROM ’.$this->tablePrefix.$this->linksTable.’
597                              ’ .
598                              ’WHERE id=’.$linkid;
599
600             foreach($queries as $query){
601                  $res = $this->db->query($query);
602                  if (MDB2::isError($res)) {
603                      throw new PopoonDBException($res);
604                      exit;
605                  }
606             }
607             return true;
608   }
609
610   /**
611       * getTags
612       *
613       * builds up the three, that one can see which tags are already used
614       *
615       * @return String XML of tags
616       * <pre>
617       *     <tags>
618       *         <tag>
619       *          <name />
620       *          <id />
621       *          <numberentries />


                                                                                     Page 50 of 79
622          *      </tag>
623          *      ...
624          *    </tags>
625          * </pre>
626          * */
627      private function getTags(){
628              $query = ’SELECT ’.$this->tablePrefix.’linklog tags.name, ’
629                         .$this->tablePrefix.’linklog tags.id, count( DISTINCT ’
630                         .$this->tablePrefix.$this->linksTable.’.id ) AS c
631                       FROM ’.$this->tablePrefix.’linklog tags
632                         LEFT JOIN ’.$this->tablePrefix.$this->links2tags.’   ’    .
633                         ’ON ’.$this->tablePrefix.’linklog tags.id = ’ .
634                        $this->tablePrefix.$this->links2tags.’.tagid
635                         LEFT JOIN ’.$this->tablePrefix.$this->linksTable.’   ’    .
636                         ’ON ’.$this->tablePrefix.$this->links2tags.’.linkid = ’.
637                         $this->tablePrefix.$this->linksTable.’.id
638                        GROUP BY id
639                        ORDER BY c DESC’;
640                   $res = $this->db->query($query);
641                   if (MDB2::isError($res)) {
642                        throw new PopoonDBException($res);
643                        exit;
644                   }
645
646                   $xml = ’<tags>’;
647                   while($tag = $res->fetchRow(MDB2 FETCHMODE ASSOC)){
648                        $xml .= "<tag>".
649                                  "<name>$tag[name]</name>".
650                                  "<id>$tag[name]</id>".
651                                  "<numberentries>$tag[c]</numberentries>".
652                                  "</tag>";
653                   }
654                   $xml .= ’</tags>’;
655
656                   return $xml;
657      }
658 }
659
660 ?>




                                                                                          Page 51 of 79
5.3.3    /inc/bx/editors/linklog/linklog.xsl
   0 <?xml version="1.0"?>
   1 <xsl:stylesheet version="1.0"
   2     xmlns:php="http://php.net/xsl"
   3     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   4     xmlns:xhtml="http://www.w3.org/1999/xhtml"
   5     xmlns="http://www.w3.org/1999/xhtml">
   6 <xsl:output
   7     encoding="utf-8"
   8     method="html"
   9     doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
  10     doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
  11
  12 <xsl:param name="webroot"/>
  13 <!--
  14 stylesheet for the linklog-editor
  15     -->
  16           <xsl:param name="url"select="’/’"/>
  17           <xsl:param name="id"select="’/’"/>
  18           <xsl:param name="dataUri"select="’/’"/>
  19           <xsl:param name="webroot"select="’/’"/>
  20           <xsl:template match="/">
  21
  22 <!-- of any use? -->
  23           <xsl:variable name="selectedID"
  24                              select="/bx/plugin/linklog/linklog/id/text()"/>
  25
  26 <!-- a bit javascript to add tags to formfield: -->
  27 <script type="text/javascript">
  28 function addTag (tag) {
  29 document.forms.Master.elements["bx[plugins][admin edit][tags]"].value
  30     = trim(document.forms.Master.elements["bx[plugins][admin edit][tags]"].
  31       value
  32     + " "+ tag);
  33 }
  34
  35 function trim(sString){
  36 while (sString.substring(0,1) == ’ ’){
  37      sString = sString.substring(1, sString.length);
  38 }
  39 while (sString.substring(sString.length-1, sString.length) == ’ ’){
  40      sString = sString.substring(0,sString.length-1);
  41 }
  42 return sString;
  43 }
  44
  45     </script>



                                                                                    Page 52 of 79
46   <html>
47 <head>
48    <title>linklog Editor</title>
49        <link rel="stylesheet"href="{$webroot}
50                   themes/standard/admin/css/formedit.css"type="text/css"/>
51         <link rel="stylesheet"href="{$webroot}webinc/plugins/linklog/admin.
52                     css"type="text/css"/>
53     </head>
54     <body>
55
56     <h1>linklog editor</h1>
57
58 <!-- we can only use one document because we return an emtpy xml-tree when
59 entering a new link -->
60 <xsl:for-each select="/bx/plugin/linklog/link">
61        <form name="Master"action=""method="post"enctype="multipart/form-
62                     data"id="Master">
63
64        <!-- buidling up the form, naming of fields is important here -->
65        <div class="linklog editor data">
66          <p>URL:<br />
67            <input id="url"class="blackH5"name="bx[plugins][admin edit][url]
68                        "size="40"value="{url}"type="text"/>
69          </p>
70          <p>Title:     <br />
71            <input id="title"class="blackH5"name="bx[plugins][admin edit][
72                        title]"size="40"value="{title}"type="text"/>
73          </p>
74          <p>Description:      <br />
75          <textarea id="description"class="blackH5"wrap="virtual"name="bx[
76                         plugins][admin edit][description]"rows="2"cols="38">
77            <xsl:value-of select="description"/>
78          </textarea>
79          </p>
80          <!-- hier muss foreach rein -->
81          <p>Tags:     <br />
82          <input id="tags"class="blackH5"name="bx[plugins][admin edit][tags]
83                      "size="40"value="{tags}"type="text"/>
84            <xsl:call-template name="buttons"/>
85          </p>
86        </div>
87
88        <!-- right box with tags -->
89        <div class="linklog editor tags">
90         <xsl:call-template name="tags"/>
91        </div>
92
93          <br class="clear"/>


                                                                                  Page 53 of 79
 94   <!-- this is filled when an already commited link is edited, else it’s
 95   empty -->
 96   <input type="hidden"id="id"name="bx[plugins][admin edit][id]"value="{
 97                  id}"/>
 98         </form>
 99 </xsl:for-each>
100
101                </body>
102           </html>
103        </xsl:template>
104
105
106        <xsl:template name="buttons">
107        <p>
108 &#160;<input accesskey="s"type="submit"value="Save Entry"/>&#160;
109 <input type="button"name=" notindb"value="New Entry"onclick="javascript:
110                 window.location.href=’./?new=1’;"/>&#160;
111
112 <!--    only display delete-button when a link contains data -->
113   <xsl:choose>
114         <xsl:when test=’/bx/plugin/linklog/link/id[.!=""]’>
115        <input type="button"name=" notindb"value="Delete Entry"
116                      onclick="javascript:document.forms.Master.action += ’..
117                      /delete/{/bx/plugin/linklog/link/id/.}’; document.forms.
118                      Master.submit();"/>
119         </xsl:when>
120    </xsl:choose>
121 </p>
122
123
124 <h3>Additional Juice:</h3>
125 <p>
126 <!--    <a href=”javascript:h=location.href;t=document.title;e=%22%22 +(
127                  window.getSelection ?        window.getSelection() :      document.
128                  getSelection ?document.getSelection() :            document.selection.
129                  createRange().text); if(!e) e = prompt(%22You didn’tselect
130                  any text.     Enter a description(or not):%22, %22%22);
131                  location=%22{$webroot}/admin/edit/linklog/?&url=%22 + escape(
132                  h) + %22&name=%22 + escape(t) + %22&description=%22+ escape(e)
133                  .replace(/ /g, %22+%22); void 0">linklog it</a> - pull this
134                  to your bookmark toolbarfolder          -->
135 <a href="javascript:%20var%20baseUrl%20=%20’{$webroot}admin/edit/linklog/?’;
136           %20var%20url=baseUrl;var%20title=document.title;%20url=url%20+
137           %20’name=’%20+%20encodeURIComponent(title);
138           %20var%20currentUrl=document.location.href;%20url=url%20+%20’&amp;
139           url=’%20+%20encodeURIComponent(currentUrl);%20var%20selectedText;
140           %20selectedText=getSelection();%20if%20(selectedText%20!=%20’’)
141           %20url=url%20+%20’&amp;description=’%20+%20encodeURIComponent(


                                                                                              Page 54 of 79
142         selectedText);var win = window.open(null, ’’, ’width=700,height=500,
143         scrollbars,resizable,location,toolbar’);win.location.href=url;win.
144         focus();">Bookmarklet</a>
145 - not yet done, you must adjust it a bit to your needs.
146 </p>
147    </xsl:template>
148
149    <xsl:template name="tags">
150    <h3>Used Tags:</h3>
151    <ul>
152        <xsl:for-each select="/bx/plugin/linklog/tags/tag">
153         <li><a href="#">
154            <xsl:attribute name="onClick">addTag(’<xsl:value-of
155                                select="name"/>’)</xsl:attribute>
156            <xsl:value-of select="name"/>
157         </a> (<xsl:value-of select="numberentries"/>)</li>
158        </xsl:for-each>
159    </ul>
160    </xsl:template>
161
162 </xsl:stylesheet>




                                                                                   Page 55 of 79
5.3.4    /webinc/plugins/linklog/admin.css
   0 .linklog editor data{
   1      border:       1px solid black;
   2      width:300px;
   3      float:left;
   4      padding:      0 20px 0 20px;
   5 }
   6
   7
   8 .linklog editor tags{
   9 margin-left:       390px;
  10 border:    1px solid black;
  11 width:180px;
  12 }
  13 .clear {
  14 clear:     both;
  15 }
  16




                                             Page 56 of 79
5.3.5    /inc/bx/plugins/linklog.php
   0 <?php
   1 /**
   2 * bx plugins linklog
   3    *
   4 * Enables to keep links in place comparable to del.icio.us
   5    *
   6 * @author Alain Petignat
   7    *
   8 * */
   9 /*
  10 * BX INC DIR.’bx/plugins/linklog.php’
  11 */
  12 /**
  13 * To use this plugin in a collection, put the following into .configxml
  14 * and call /admin/webinc/install/linklog/ to create the databases.
  15 ***
  16 <bxcms xmlns="http://bitflux.org/config">
  17        <plugins inGetChildren="false">
  18           <extension type="xml"/>
  19           <file preg="#plugin=#"/>
  20           <plugin type="linklog">
  21           </plugin>
  22        </plugins>
  23
  24        <plugins>
  25           <parameter name="xslt"type="pipeline"value="linklog.xsl"/>
  26            <extension type="html"/>
  27            <plugin type="linklog">
  28            </plugin>
  29            <plugin type="navitree"></plugin>
  30        </plugins>
  31
  32        <plugins inGetChildren="false">
  33           <extension type="xml"/>
  34           <file preg="#rss$#"/>
  35           <parameter name="output-mimetype"type="pipeline"value="text/xml"/>
  36           <parameter type="pipeline"name="xslt"value="..
  37                            /standard/plugins/linklog/linklog2rss.xsl"/>
  38           <plugin type="linklog">
  39               <parameter name="mode"value="rss"/>
  40           </plugin>
  41        </plugins>
  42 </bxcms>
  43 *
  44 * See also the linklog.xsl for the output
  45 */



                                                                                    Page 57 of 79
46 class bx plugins linklog extends bx plugin implements bxIplugin {
47     /*
48     * The table names
49     */
50
51     public $name                    = "linklog";
52
53     private $linksTable        = "linklog links";
54     private $tagsTable       = "linklog tags";
55     private $links2tagsTable        = "linklog links2tags";
56
57     /*
58         * database
59         */
60     private $db                    = null;
61     private $tablePrefix           = NULL;
62     private $cache4tags;
63     private $isLoggedIn            = false;
64
65     // Variable for the CMS:
66     static public $instance = array();
67
68     /**
69         * getInstance
70         *
71         * returns an instance of the plugin
72         *
73         * @param mode not used until now.
74         *
75         */
76     public static function getInstance($mode) {
77               if (!isset(self::$instance[$mode])) {
78                   self::$instance[$mode] = new bx plugins linklog($mode);
79               }
80               return self::$instance[$mode];
81     }
82
83     /*
84         *
85         */
86     public function getIdByRequest($path, $name = NULL, $ext = NULL) {
87              return $name.’.’.$this->name;
88     }
89
90
91     // this gets called on every instance of the class
92     protected function      construct($mode) {
93               $this->tablePrefix    = $GLOBALS[’POOL’]->config->getTablePrefix();


                                                                                       Page 58 of 79
94             $this->db             = $GLOBALS[’POOL’]->db;
95             $this->mode           = $mode;
96
97             $this->cache4tags = BX TEMP DIR."/linklogtags.arr";
98             /*
99             * check if logged in:
100            */
101            $perm = bx permm::getInstance();
102            if($perm->isLoggedIn()){
103                    $this->isLoggedIn = true;
104            }
105   }
106
107   /**
108       * getContentById
109       *
110       * @param string $path
111       * @param string $id
112       */
113   public function getContentById($path, $id) {
114
115            $dirname = dirname($id);
116            $this->path=$path;
117
118            // when a plugin is called:
119            if (strpos($id,"plugin=") === 0) {
120                    return $this->callInternalPlugin($id, $path);
121            }
122
123            /**
124                * reserved values:
125                *
126                * .               (newest links, configurable output)
127                *
128                * all           (all links, mainly for testing)
129                *
130                *   */
131            switch ($dirname) {
132                    case "all":
133                         return $this->getAll();
134                    /*
135                    case "archive":
136                         return $this->getArchive($id);
137                    case "detail":
138                         return $this->getDetail($id);
139                    */
140
141                    case ".":


                                                                           Page 59 of 79
142                     return $this->getSplash();
143                default:
144                     return $this->getLinksByTag($id);
145            }
146
147   }
148
149   /**
150       * getSplash
151       *
152       * By default returning newest links
153       * */
154   private function getSplash(){
155
156            $query = ’SELECT
157                       ’.$this->tablePrefix.$this->linksTable.’.id,
158                       ’.$this->tablePrefix.$this->linksTable.’.title,
159                       ’.$this->tablePrefix.$this->linksTable.’.url,
160                       ’.$this->tablePrefix.$this->linksTable.’.description,
161                       ’.$this->tablePrefix.$this->linksTable.’.time, ’ .
162                       ’DATE FORMAT(’.$this->tablePrefix.$this->linksTable.’.
163                                    time, ’ .
164                       ’"%Y-%m-%dT%H:%i:%SZ") as isotime ’.
165                       ’FROM ’.$this->tablePrefix.$this->linksTable.’    ’   .
166                       ’ORDER BY time desc limit 0,30’;
167
168            $res = $this->db->query($query);
169            return $this->processLinks($res);
170   }
171
172   /**
173       * getAll
174       *
175       * fetching all links, ordered chronologically
176       */
177   private function getAll(){
178            $db2xml = new XML db2xml($this->db,"links");
179            $query = ’SELECT
180                       ’.$this->tablePrefix.$this->linksTable.’.id,
181                       ’.$this->tablePrefix.$this->linksTable.’.title,
182                       ’.$this->tablePrefix.$this->linksTable.’.url,
183                       ’.$this->tablePrefix.$this->linksTable.’.description,
184                       ’.$this->tablePrefix.$this->linksTable.’.time, ’ .
185                       ’DATE FORMAT(’.$this->tablePrefix.$this->linksTable.’.
186                                    time, ’ .
187                       ’"%Y-%m-%dT%H:%i:%SZ") as isotime ’.
188                     "FROM ".$this->tablePrefix.$this->linksTable." ".
189                      "ORDER BY time DESC";


                                                                                    Page 60 of 79
190            $res = $this->db->query($query);
191            return $this->processLinks($res);
192   }
193
194   /**
195       * getLinksByTag
196       *
197       * @param $id
198       *
199       */
200   private function getLinksByTag($id){
201
202            if (($pos = strrpos($id,"/")) > 0) {
203                $cat = substr($id,0,$pos);
204                $id = substr($id, $pos + 1);
205            } else {
206                $cat = "";
207            }
208
209            if (isset($cat) && $cat && $cat != ’ all’) {
210
211                       /**
212                           * contains current category
213                           */
214                       $q = "select * from ".$this->tablePrefix.$this->tagsTable."
215                                ".
216                                "where ".$this->tablePrefix.$this->tagsTable.".fulluri
217                                = ’$cat’ ";
218
219                       $res = $GLOBALS[’POOL’]->db->query($q);
220
221                       if (MDB2::isError($res)) {
222                                throw new PopoonDBException($res);
223                       }
224
225                       $c = $res->fetchRow(MDB2 FETCHMODE ASSOC);
226
227                       $meta = "<meta><title>".$c[’name’]."</title></meta>";
228
229                if (isset($c)) {
230                $q = ’SELECT
231                           ’.$this->tablePrefix.$this->linksTable.’.id,
232                           ’.$this->tablePrefix.$this->linksTable.’.title,
233                           ’.$this->tablePrefix.$this->linksTable.’.url,
234                           ’.$this->tablePrefix.$this->linksTable.’.description,
235                           ’.$this->tablePrefix.$this->linksTable.’.time, ’ .
236                           ’DATE FORMAT(’.$this->tablePrefix.$this->linksTable.’.
237                                          time, ’ .


                                                                                            Page 61 of 79
238                               ’"%Y-%m-%dT%H:%i:%SZ") as isotime ’.
239                               ’FROM ’.$this->tablePrefix.$this->linksTable.’   ’.
240                               ’RIGHT JOIN ’.$this->tablePrefix.$this->links2tagsTable.’
241                               ’ .
242                               ’ON ’.$this->tablePrefix.$this->linksTable.’.id=’.
243                               $this->tablePrefix.$this->links2tagsTable.’.linkid
244                                LEFT JOIN ’.$this->tablePrefix.$this->tagsTable.’    ON ’ .
245                               ’’.$this->tablePrefix.$this->links2tagsTable.
246                               ’.tagid=’.$this->tablePrefix.$this->tagsTable.’.id
247                                WHERE ’.$this->tablePrefix.$this->links2tagsTable.’.
248                                tagid=’.$c[’id’].’   ’   .
249                               ’ORDER BY ’.$this->tablePrefix.$this->linksTable.’.time
250                               DESC’;
251
252                          $links = $this->db->query($q);
253
254                              if (MDB2::isError($links)) {
255                                 throw new PopoonDBException($links);
256                          }
257                          return $this->processLinks($links, $meta);
258                   }
259               }
260   }
261
262   /**
263       * processLinks
264       *
265       * @param $links db-object containing link-data
266       * @param $meta xml-string with metadata being displayed in title
267       *
268       * */
269   private function processLinks($links, $meta = false){
270           $map2tags = $this->mapTags2Links();
271
272           if(is string($meta)){
273                   $xml        = "<links>";
274                   $xml       .= $meta;
275           }else{
276                   $xml = "<links>";
277           }
278
279           while($row = $links->fetchRow(MDB2 FETCHMODE ASSOC)){
280                   $xml .= "<link>";
281                   $xml .= "<id>".$row[’id’]."</id>";
282                   $xml .= "<title>".$row[’title’]."</title>";
283                   $xml .= "<description>".$row[’description’] ."</description>";
284                   $xml .= "<time>".$row[’time’]."</time>";
285


                                                                                                 Page 62 of 79
286       $xml .= "<isotime>".$row[’isotime’]."</isotime>";
287
288       $xml .= "<url>".str replace(’&’,’&amp;’,$row[’url’])."</url>";
289
290       if($this->isLoggedIn){
291                  $xml .= "<edituri>".BX WEBROOT W.
292                           "/admin/edit/linklog/edit/".$row[’id’]."<
293                           /edituri>";
294                  $xml .= "<deleteuri>".BX WEBROOT W.
295                           "/admin/edit/linklog/delete/".$row[’id’]."<
296                           /deleteuri>";
297       }
298
299       $xml .= "<tags>";
300
301       // have fun with categories:
302       $tags = $map2tags[$row[’id’]];
303
304       if(is array($tags)){
305           foreach($tags as $t){
306                  $xml .= "<tag>";
307                  $xml .= "<id>".$t[’id’]."</id>";
308                  $xml .= "<fulluri>".BX WEBROOT W.$this->path.$t[
309                           ’fulluri’]."</fulluri>";
310                  $xml .= "<name>".str replace(’&’,’&amp;’,$t[’name’])."<
311                           /name>";
312                  $xml .= "</tag>";
313           }
314       }
315       $xml .= "</tags>";
316       $xml .= "</link>";
317   }
318
319   $xml .= "</links>";
320
321   $dom = new DomDocument();
322
323
324       if (is string($xml)) {
325           $dom = new DomDocument();
326           if (function exists(’iconv’)) {
327                  $xml =   @iconv("UTF-8","UTF-8//IGNORE",$xml);
328           }
329           $dom->loadXML($xml);
330           return $dom;
331       } else {
332           return $xml;
333       }


                                                                               Page 63 of 79
334   }
335
336   /**
337       * calls static plugin from extending class in
338       /inc/bx/plugins/linklog/*
339       *
340       * currently, only tags exists
341       */
342   private function callInternalPlugin($id, $path){
343             // print $id;
344              $plugin = substr($id,7);
345
346              if ($pos = strpos($plugin,"(")) {
347                   $pos2 = strpos($plugin,")");
348                   $params = substr($plugin,$pos+1, $pos2 - $pos - 1);
349                   $plugin = substr($plugin,0,$pos);
350                   $params = explode(",",$params);
351              }   else {
352                   $params = array();
353              }
354
355              $plugin = "bx plugins linklog ".$plugin;
356
357              $xml =       call user func(array($plugin,"getContentById"), $path,
358                           $id, $params,$this->tablePrefix);
359
360              if (is string($xml)) {
361                   $dom = new DomDocument();
362                   if (function exists(’iconv’)) {
363                           $xml =   @iconv("UTF-8","UTF-8//IGNORE",$xml);
364                   }
365                   $dom->loadXML($xml);
366                   return $dom;
367              } else {
368                   return $xml;
369              }
370   }
371
372
373   /*
374       * this is a really timeconsuming method, since it maps all links to
375       its specific tags
376       *
377       * @return array $map
378       *
379       * array(
380       *     $linkid => array(
381       *                            $catid1 => ...


                                                                                       Page 64 of 79
382    *                                  $catid2 => ...
383    *                                  ...
384    *                              )
385    *     ...
386    * )
387    */
388   private function mapTags2Links(){
389                if(file exists($this->cache4tags)){
390                          return unserialize(file get contents($this->cache4tags));
391
392                }else{
393                $query = "SELECT * FROM ".$this->tablePrefix.$this->tagsTable;
394                $res = $this->db->query($query);
395                if (MDB2::isError($res)) {
396                          throw new PopoonDBException($res);
397                }
398
399                /*
400                    * loop through all tags to create an array with its id as
401                    index
402                    */
403                while($row = $res->fetchRow(MDB2 FETCHMODE ASSOC)){
404                          $tags[$row[’id’]] = $row;
405                }
406
407                $query = "SELECT * FROM ".$this->tablePrefix.$this->
408                               links2tagsTable."";
409                $res = $this->db->query($query);
410                if (MDB2::isError($res)) {
411                          throw new PopoonDBException($res);
412                }
413
414                /*
415                    * loop through all merges, to be able to fetch a category of
416                    the
417                    * link in one catch
418                    */
419                $map = array();
420                while($row = $res->fetchRow(MDB2 FETCHMODE ASSOC)){
421                          if(!array key exists($row[’linkid’], $map)){
422                              $map[$row[’linkid’]] = array($tags[$row[’tagid’]]);
423                          }else{
424                              array push($map[$row[’linkid’]], $tags[$row[’tagid’]]);
425                          }
426                }
427
428                // caching it, must be deleted when new link is added...
429                file put contents($this->cache4tags, serialize($map));


                                                                                           Page 65 of 79
430
431                  return $map;
432                  }
433      }
434
435      /*
436          *
437          * */
438      public function isRealResource($path , $id) {
439              return true;
440      }
441
442      /*
443      * to actually being able to edit links in the admin, we have to return
444      * true here, if the admin actions asks us for that.   We don’t care about
445      * path,id, etc here
446      */
447      public function adminResourceExists($path, $id, $ext=null, $sample =
448                                           false) {
449              return true;
450      }
451
452      /**
453      * we need to "register"what editors are beeing able to handle this
454      plugin
455      */
456      public function getEditorsById($path, $id) {
457              return array("linklog");
458      }
459 }
460 ?>




                                                                                     Page 66 of 79
5.3.6   /inc/bx/plugins/linklog/tags.php
   0 <?php
   1 /**
   2 * bx plugins linklog tags
   3    *
   4 * Supposed to handle everything to display the
   5    * navigationtree of the linklogplugin
   6    *
   7 * Inspired from              bx plugins blog categories by Christian Stocker.
   8    *
   9 * @author Alain Petignat
  10    *
  11 * */
  12 class bx plugins linklog tags {
  13        /**
  14        * static function getContentById
  15        *
  16        * @param string $path
  17        * @param string $id
  18        * @param array $params
  19        * @param int $parent
  20        * @param string $tablePrefix
  21        * @return object DOM
  22        *
  23        * @todo everything, first of all being able to get called correctly
  24        *
  25        * */
  26        static function getContentById($path,$id,$params,$tablePrefix = "") {
  27
  28               if (isset($params[0])) {
  29                         $lastslash = strrpos($params[0],"/");
  30                         $tag = substr($params[0],0,$lastslash);
  31               } else {
  32                         $tag = "";
  33               }
  34
  35               /*
  36                   * This query gets tags as well as number of links marked with that
  37                   tag
  38                   * */
  39        $query = ’SELECT ’.$tablePrefix.’linklog tags.name, ’
  40         .$tablePrefix.’linklog tags.id, ’
  41         .$tablePrefix.’linklog tags.fulluri, ’ .
  42        ’count( DISTINCT ’.$tablePrefix.’linklog links.id ) AS c
  43         FROM ’.$tablePrefix.’linklog tags
  44        LEFT JOIN ’.$tablePrefix.’linklog links2tags ON ’
  45        .$tablePrefix.’linklog tags.id = ’.$tablePrefix.’linklog links2tags.



                                                                                            Page 67 of 79
46                                        tagid
47   LEFT JOIN ’.$tablePrefix.’linklog links ON ’
48   .$tablePrefix.’linklog links2tags.linkid = ’.$tablePrefix.
49                                                     ’linklog links.id
50   GROUP BY id
51   ORDER BY c DESC’;
52
53       $res = $GLOBALS[’POOL’]->db->query($query);
54       if (MDB2::isError($res)) {
55               // throw error
56               throw new PopoonDBException($res);
57               // echo "error";
58               exit;
59           }
60
61       // this could be done by DOM-functions as well.         Important is just
62       the output ;)
63       $i = 1; // for the order...
64
65       $xml = "<items>";
66       while($row = $res->fetchRow(MDB2 FETCHMODE ASSOC)) {
67               if($row[’c’] > 0){    // dont display tags without a link
68                   if ($tag === $row[’fulluri’]) {
69                       $xml .= ’<collection selected="selected">’;
70                       $title = $row[’title’];
71                       $uri = BX WEBROOT W.$path.$row[’fulluri’]."/";
72                   } else {
73                       $xml .= ’<collection selected="all">’;
74                   }
75
76                   $xml .= ’<title>’.$row[’name’].’      (’.$row[’c’].’)   </title>’;
77                   $xml .= ’<uri>’.BX WEBROOT W.$path.$row[’fulluri’].’</uri>’;
78                   $xml .= ’<display-order>’.$i.’</display-order>’;
79                   $xml .= ’</collection>’;
80
81                   $i++;
82                   }
83
84       }
85
86       $xml .= "</items>";
87
88
89       $xml2       = ’<collection selected="all">’;
90       $xml2      .= ’<title>’.$title.’</title>’;
91       $xml2      .= ’<uri>’.$uri.’</uri>’;
92       $xml2      .= $xml;
93       $xml2      .= ’</collection>’;


                                                                                          Page 68 of 79
 94
 95           // print $xml2;
 96
 97           $dom = new DomDocument();
 98
 99           if (function exists(’iconv’)) {
100               $xml2 =    @iconv("UTF-8","UTF-8//IGNORE",$xml2);
101           }
102
103           $dom->loadXML($xml2);
104
105          // this could be used to "easily"debug ;)
106          // file put contents("/Library/WebServer/Documents/info/coll.xml",
107                                $xml2);
108
109           return $dom;
110
111      }
112
113
114
115 }
116 ?>




                                                                                  Page 69 of 79
5.3.7   /themes/2-cols/linklog.xsl
   0 <?xml version="1.0"?>
   1 <xsl:stylesheet version="1.0"
   2    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   3    xmlns:xhtml="http://www.w3.org/1999/xhtml"
   4    xmlns:php="http://php.net/xsl"
   5    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   6    xmlns:rss="http://purl.org/rss/1.0/"
   7    xmlns:dc="http://purl.org/dc/elements/1.1/"
   8        xmlns:blog="http://bitflux.org/doctypes/blog"
   9    xmlns:bxf="http://bitflux.org/functions"
  10    xmlns="http://www.w3.org/1999/xhtml"exclude-result-prefixes="xhtml rdf
  11           rss dc php blog">
  12 <xsl:import href="master.xsl"/>
  13 <xsl:import href="../standard/common.xsl"/>
  14 <xsl:output encoding="utf-8"method="xml"/>
  15
  16 <!--
  17 if you want some "personal data"here:
  18 add this to namespace:
  19 xmlns:php="http://php.net/xsl"
  20
  21    get e.g.   the username
  22        <xsl:variable name="username">
  23          <xsl:value-of select="php:functionString(’bx helpers perm::
  24                                    getUsername’)"/>
  25         </xsl:variable>
  26
  27        call as
  28         <xsl:value-of select="$username"/>
  29 -->
  30
  31
  32 <!--    this might be used somewhere,
  33     also shows howto set a variable within xsl
  34 -->
  35 <xsl:variable name="currenttag">
  36       <xsl:value-of select="/bx/plugin[@name=’linklog’]/links/meta/title"/>
  37 </xsl:variable>
  38
  39    <!-- <title>-tag in head: -->
  40 <xsl:template name="html head title">
  41     <xsl:apply-templates
  42       select="/bx/plugin[@name=’linklog’]/links/meta/title"/>
  43     |sequenz/linklog
  44    </xsl:template>
  45



                                                                                   Page 70 of 79
46 <!--
47 overwriting default template for content:
48 -->
49 <xsl:template name="content">
50    <!-- Titel: -->
51    <xsl:apply-templates
52       select="/bx/plugin[@name=’linklog’]/links/meta"mode="meta"/>
53
54    <!-- Links: -->
55    <xsl:apply-templates
56       select="/bx/plugin[@name=’linklog’]/links/link"mode="links"/>
57 </xsl:template>
58
59
60   <!-- Construct the Heading of the title: -->
61 <xsl:template match="meta"mode="meta">
62    <h1>
63       <xsl:value-of select="title"/>
64    </h1>
65    <!-- this is not yet used, having a description for each tag
66    <p>
67       <xsl:value-of select="description"/>
68    </p>
69       -->
70 </xsl:template>
71
72   <!-- here is the template for a single link -->
73
74   <xsl:template match="link"mode="links">
75    <div class="linkmanager single link">
76       <h3>
77        <a href="{url}">
78         <xsl:value-of select="title"/>
79        </a>
80       <!-- here are the admin buttons, not that nice yet. -->
81       <xsl:choose>
82             <xsl:when test="edituri != ’’">
83             &#160;(&#160;<a href="{edituri}"><img
84                      src="/admin/webinc/img/icons/edit.gif"border="0"alt="edit"
85                      height="13px"width="13px"title="edit {title}"/></a>
86             </xsl:when>
87       </xsl:choose>
88
89       <xsl:choose>
90             <xsl:when test="deleteuri != ’’">
91              <!-- here i shoud add a javascript warning -->
92             &#160; <a href="{deleteuri}"><img
93                               src="/admin/webinc/img/icons/delete.gif"border="0"


                                                                                      Page 71 of 79
 94                                alt="delete"height="13px"width="13px"
 95                                title="delete {title}"/></a> )
 96           </xsl:when>
 97     </xsl:choose>
 98
 99     </h3>
100     <p>
101        <xsl:value-of select="description"/>
102        <br />
103        Tags:
104        <xsl:for-each select="tags/tag">
105         &#160;
106         <a href="{fulluri}">
107          <xsl:value-of select="name"/>
108         </a>
109        </xsl:for-each>
110        <br />
111        <xsl:value-of select="time"/>
112     </p>
113    </div>
114 </xsl:template>
115
116   <!-- This is the navigation, calls it’s own plugin -->
117 <xsl:template name="leftnavi">
118    <div id="left">
119
120          <!--    to see output of this, just call
121          http://localhost/linklog/plugin=tags(yourtag/index.html).xml?XML=1
122          or just
123          http://localhost/linklog/plugin=tags().xml?XML=1
124          but be sure you are logged in ;)
125           -->
126    <xsl:apply-templates
127     select="document(concat(’portlet://’,$collectionUri,’plugin=tags(’,
128                 $filename,’).xml’))/bx/plugin/collection"
129    />
130
131    <!-- this is not done yet - want an image only for current page, too -->
132    <p>
133     <a href="{$collectionUri}rss.xml"title="linklog as rss">
134        <img src="http://www.sequenz.ch/files/img/xml.gif"></img>
135     </a>
136    </p>
137
138 <!--               <xsl:call-template name=”delicious” />    -->
139
140    </div>
141 </xsl:template>


                                                                                  Page 72 of 79
142
143
144 <!--
145 this does not quite work out yet, but the idea is
146 to fetch links from del.icio.us tagged identical:
147
148        <xsl:template name="delicious">
149           <h3 class="blog">
150               <a href="http://del.icio.us/tag/freeflux">
151                  del.icio.us/tag/<xsl:value-of select="$currenttag"/>
152               </a>
153           </h3>
154           <ul>
155               <xsl:for-each select="document(concat(’portlet:
156                                     //blog/plugin=deliciousrdf(tag/bitflux).
157                                     xml’))/bx/plugin/rdf:RDF/rss:item[position(
158                                     ) &lt; 11]">
159                    <li>
160                        <a title="{rss:description} - Categories:   {dc:subject}"
161                                 class="blogLinkPad"href="{rss:link}">
162                            <xsl:value-of select="rss:title"/>
163                        </a>
164                    </li>
165               </xsl:for-each>
166           </ul>
167        </xsl:template>
168   -->
169 </xsl:stylesheet>




                                                                                       Page 73 of 79
5.3.8   /themes/standard/admin/plugins/linklog/linklog2rss.xsl
   0 <?xml version="1.0"encoding="UTF-8"?>
   1
   2 <xsl:stylesheet version="1.0"
   3 xmlns:creativeCommons="http://backend.userland.
   4                               com/creativeCommonsRssModule"
   5    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   6    xmlns:xhtml="http://www.w3.org/1999/xhtml"
   7    xmlns:content="http://purl.org/rss/1.0/modules/content/"
   8    xmlns:dc="http://purl.org/dc/elements/1.1/"
   9    xmlns:php="http://php.net/xsl"
  10
  11    >
  12
  13 <!--
  14 this is a really basic stylesheet which transforms the XML-output
  15    of the linklog plugin into a simple RSS-Format.
  16    -->
  17
  18          <xsl:output method="xml"indent="yes"omit-xml-declaration="no"/>
  19          <xsl:param name="webroot"/>
  20          <xsl:param name="webrootLang"/>
  21          <xsl:param name="collectionUri"/>
  22          <xsl:variable name="linklogroot"select="concat(substring($webroot,1,
  23                                string-length($webroot)-1),$collectionUri)"/>
  24          <xsl:variable name="sitename"select="php:functionString(
  25                                ’bx helpers config::getOption’,’sitename’)"/>
  26
  27          <xsl:template match="/">
  28              <rss version="2.0"xmlns:blogChannel="http://backend.userland.
  29                             com/blogChannelModule">
  30                   <channel>
  31                        <title><xsl:value-of select="$sitename"/> - Linklog</title>
  32                        <link><xsl:value-of select="$linklogroot"/></link>
  33                        <description>collected links</description>
  34                        <generator>Flux CMS - http://www.flux-cms.org</generator>
  35          <!-- apply the items -->
  36          <xsl:apply-templates select="/bx/plugin[@name=’linklog’]/links/link"
  37                                            mode="links"/>
  38                   </channel>
  39              </rss>
  40          </xsl:template>
  41           <xsl:template match="text()"mode="xhtml">
  42            <xsl:value-of select="php:functionString(’htmlspecialchars’,.,0,’UTF-
  43                                     8’)"   />
  44          </xsl:template>
  45



                                                                                          Page 74 of 79
46 <!-- here is the single item: -->
47 <xsl:template match="link"mode="links">
48        <item>
49             <title>
50                  <xsl:value-of select="title"/>
51                 </title>
52                 <link>
53                  <xsl:value-of select="url"/>
54                 </link>
55
56     <!-- howto insert some html here? -->
57                 <content:encoded xmlns="http://www.w3.org/1999/xhtml">
58                  <xsl:apply-templates select="description/text()"
59                                                     mode="xhtml"/>
60                 </content:encoded>
61
62                 <dc:date><xsl:value-of select="isotime"/></dc:date>
63
64                 <!-- Tags -->
65                 <xsl:for-each select="tags/tag">
66                  <dc:subject><xsl:value-of select="name"/></dc:subject>
67                 </xsl:for-each>
68
69            </item>
70 </xsl:template>
71
72
73      <!-- calling html-specialchars to all text()-nodes -->
74      <xsl:template match="text()"mode="xhtml">
75        <xsl:value-of select="php:functionString(’htmlspecialchars’,.,0,’UTF-
76                                     8’)"   />
77      </xsl:template>
78
79
80 </xsl:stylesheet>




                                                                                  Page 75 of 79
5.3.9    /admin/webinc/install/linklog/index.php
   0 <?php
   1 /*
   2 * Basic installer script for the linklog-plugin
   3     * of flux cms.
   4     *
   5 * Creates three tables:
   6 * linklog links
   7     * linklog tags
   8     * linklog links2tags
   9     * (all with actual prefix of installation)
  10 *
  11 * */
  12
  13 include once("../../../../inc/bx/init.php");
  14 bx init::start(’conf/config.xml’, "../../../..");
  15
  16 $conf = bx config::getInstance();
  17
  18 $confvars = $conf->getConfProperty(’permm’);
  19 $permObj = bx permm::getInstance($confvars);
  20
  21 if (!$permObj->isAllowed(’/’,array(’admin’))) {
  22         print "Access denied";
  23         die();
  24 }
  25 $tablePrefix = $conf->getTablePrefix();
  26
  27 echo "<h2>Install Linklog Plugin</h2>";
  28
  29 print "<pre/>";
  30 $db = $GLOBALS[’POOL’]->dbwrite;
  31
  32 // links
  33 $queries[] = "CREATE TABLE ‘".$tablePrefix."linklog links‘ (
  34      ‘id‘ int(11) NOT NULL auto increment,
  35      ‘title‘ varchar(40) NOT NULL default ’’,
  36      ‘description‘ varchar(255) NOT NULL default ’’,
  37      ‘url‘ varchar(255) NOT NULL default ’’,
  38      ‘status‘ smallint(2) NOT NULL default ’0’,
  39      ‘timeadded‘ varchar(12) default NULL,
  40      ‘time‘ datetime NOT NULL default ’0000-00-00 00:00:00’,
  41      PRIMARY KEY     (‘id‘),
  42      KEY ‘id‘ (‘id‘)
  43 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
  44
  45 // tags



                                                                    Page 76 of 79
46 $queries[] = "CREATE TABLE ‘".$tablePrefix."linklog tags‘ (
47     ‘id‘ int(11) NOT NULL auto increment,
48     ‘name‘ varchar(128) character set latin1 NOT NULL default ’’,
49     ‘fulluri‘ varchar(255) character set latin1 default NULL,
50     PRIMARY KEY   (‘id‘),
51     KEY ‘SRLR‘ (‘id‘),
52     KEY ‘node id‘ (‘id‘)
53 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
54
55 // links2tags
56 $queries[] = "CREATE TABLE ‘".$tablePrefix."linklog links2tags‘ (
57     ‘id‘ int(10) unsigned NOT NULL auto increment,
58     ‘linkid‘ int(10) unsigned NOT NULL default ’0’,
59     ‘tagid‘ int(10) unsigned NOT NULL default ’0’,
60     PRIMARY KEY   (‘id‘),
61     KEY ‘lid‘ (‘linkid‘,‘tagid‘)
62 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
63
64
65 foreach($queries as $query){
66        $res = $db->query($query);
67        if ($db->isError($res)) {
68            "installation failed, please report to alain@flux-cms.org";
69             printError($res);
70        }
71 }
72
73 echo "<h1>Success ;)</h1>";
74 echo "<p>Linklog-Plugin-Tables successfully created.      Now you can create a
75 collection with the following .configxml:</p>";
76
77 printConfigXML();
78
79 echo "<p>Make sure, you have the correspondig linklog.xsl in your themes-
80 folder.     Default can be found in 2-cols and 3-cols.</p>";
81
82
83 /**
84 * just prints the configxml used for linkplugin.
85     * */
86 function printConfigXML() {
87 $configxml = ’<bxcms xmlns="http://bitflux.org/config">
88        <plugins inGetChildren="false">
89            <extension type="xml"/>
90            <file preg="#plugin=#"/>
91            <plugin type="linklog">
92            </plugin>
93        </plugins>


                                                                                    Page 77 of 79
 94
 95       <plugins>
 96           <parameter name="xslt"type="pipeline"value="linklog.xsl"/>
 97            <extension type="html"/>
 98            <plugin type="linklog">
 99            </plugin>
100            <plugin type="navitree"></plugin>
101       </plugins>
102
103       <plugins inGetChildren="false">
104           <extension type="xml"/>
105           <file preg="#rss$#"/>
106           <parameter name="output-mimetype"type="pipeline"value="text/xml"/>
107           <parameter type="pipeline"name="xslt"value="..
108                           /standard/plugins/linklog/linklog2rss.xsl"/>
109           <plugin type="linklog">
110               <parameter name="mode"value="rss"/>
111           </plugin>
112       </plugins>
113 </bxcms>’;
114
115     print ’<pre>’.htmlentities($configxml).’</pre>’;
116
117 }
118
119 // additional functions:
120 function printError($res) {
121       if ($GLOBALS[’POOL’]->db->isError($res)) {
122           print $res->message ."\n";
123           print $res->userinfo ."\n";
124           die();
125       }
126 }




                                                                                   Page 78 of 79
5.4   Used Software
  • This paper was written in LaTeX using TeX-Shop (http://www.uoregon.edu/∼koch/texshop/
    texshop.html).

  • highlight to convert sourcecode to LaTeX (http://www.andre-simon.de/).

  • Eclipse (http://www.eclispe.org) with the following plugins
        PHP-Eclipse (http://www.phpeclipse.de)
        Subclipse (http://subclipse.tigris.org/)

  • Trac (http://www.edgewall.com/trac/) for keeping an overview

5.5   Acknowledgements
  • Bitflux GmbH for ”Flux CMS”, popoon and all the support

  • Jean-Louis Fuchs for LaTeX-support

  • Beat G¨tzi for feedback testing
          a

  • Mediagonal initializing a workshop about ”Flux CMS”

  • Hans Doran for publishing the ZHW-LaTeX-templates

  • Kaspar Bopp for writing them

  • Rene Moser for chasing typos

  • Visvanath Ratnaweera for making this project possible




                                                                             Page 79 of 79

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:49
posted:5/7/2011
language:English
pages:85