1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
|
DEBIAN GFORGE PLUGINS HOWTO
--------------------------------
Here is a short HOWTO explaining how plugins work in Debian
GForge, and how to make a new one.
It was written by Roland Mas <lolando@debian.org>.
WHAT PLUGINS ARE, AND WHY THEY ARE USEFUL
-----------------------------------------
Plugins are extensions to the "core" of GForge, providing extra
functionality without being tightly integrated within Sourceforge
proper. They are useful because they allow for independent
development of third-party functionality, and they add flexibility to
Sourceforge as to what features are available on a particular
installation.
As an example, it's been suggested to integrate a shared calendar
application in Sourceforge. It's a good idea and an interesting
feature, but not one that everybody wants. Thus, including it in the
GForge code would piss off someone. Additionnally, there might
be several competing implementations for such a calnedar application.
Choosing one among them would also piss off people. So it is made
possible to have a system so that different implementations can exist
and be installed separately.
HOW PLUGINS WORK
----------------
It is expected that a plugin is just some new feature added to
GForge, and not a change in the behaviour of existing features.
A plug-in should therefore only add files, not change existing ones.
Whether these files be web pages, offline scripts, static
documentation or else is not relevant.
Of course, *some* changes will have to be made to the "core" files,
if only to add links to new web pages, for instance. These changes
are acceptable, and will be discussed below. Here come the details
about how the plugin system is implemented.
- A plugin will be identified primarily by a string handle, which will
be static across all installations of this plugin. It should be
composed of lowercase letters only, because it's going to be used in
table names and we don't want namespace conflicts. For instance, if
the ACME company writes a time tracking tool plugin, the handle for
that plugin could be "acmetimetracker". When installed, the plugin
will be assigned an integer identifier. This id might vary from site
to site, and should not be depended upon.
We [the GForge-proper maintainers team] will maintain some sort
of list of allocated plugin names so that different plugins get
different allocated identifiers, see below.
- Tables in the database schema: special tables have been added to the
database schema to keep track of installed plugins. They are
described below (simplified descriptions):
,----
| CREATE TABLE plugins (plugin_id integer,
| plugin_name character(32),
| plugin_desc text,
| CONSTRAINT plugins_pkey PRIMARY KEY (plugin_id)
| )
| CREATE TABLE group_plugin (group_plugin_id integer,
| group_id integer,
| plugin_id integer,
| CONSTRAINT PRIMARY KEY (plugin_id),
| CONSTRAINT FOREIGN KEY (group_id) REFERENCES groups(group_id)
| )
| CREATE TABLE user_plugin (user_plugin_id integer,
| user_id integer,
| plugin_id integer,
| CONSTRAINT PRIMARY KEY (plugin_id),
| CONSTRAINT FOREIGN KEY (user_id) REFERENCES users(user_id)
| )
`----
"plugins" lists the installed plugins, with the numeric id, the
string handle (say, "acmetimetracker") and a description.
"group_plugin" is a way to store the fact that a group "uses" a
plugin without needing to add a "uses_acmetimetracker" to the groups
table for each known plugin.
"user_plugin" is the same, for users.
- A plugin may create its own tables in the same database. These
tables must be named plugin_foo_* if the plugin's string identifier is
"foo". One suggested table is plugin_foo_meta_data, which should be
used to store the plugin meta-data, such as the installed version.
The plugin can then use some code like db-upgrade.pl if its database
schema changes over time.
[TODO: Standardise the command/script/something below]
These tables may have foreign key referential integrity constraints
going from them to standard tables, but not the other way round. If
they have, then a command/script/something must be provided so that
the main db-upgrade.pl can disable the constraints and re-enable them
afterwards in case some database schema changes are needed.
Similarly, a plugin may create sequences, indexes, views, etc,
provided that their names are prefixed with plugin_foo_ too.
A plugin should not modify the data in tables that do not belong to
it. If it really needs to, then please discuss it with us first,
there might be cases where it's needed/useful. Reading those data is
okay, but it must be careful not to leak any info to places/users
which normally wouldn't have had access to it.
- Functions in Group.class and User.class: the Group and User classes
now have a usesPlugin() method. It takes a single parameter, the
"acmetimetracker" identifier for the module, and returns a boolean if
the particular user/group has turned on the use of that module. Also
provided are setPluginUsage() methods, taking a plugin name and a
boolean value as arguments and returning true on success and false on
failure.
- A plugin should not change the existing files. Of course, it will
need a way to adds links to its own web pages. This is done by a
"hook" system. Each plugin can hook itself up to a number of hook
points in the main code, and execute arbitrary code when that point is
reached. Basically, the plugin registers itself to a global object
(of the PluginManager class, if you want to know). You have to call
the register_plugin() function, providing it an object of a subclass
of the Plugin class that is provided by the main code. That object
must provide a GetHooks() method, which returns a list of hook names.
Whenever one of these hooks is encountered, the object's CallHook()
method is called with the hook name and extra parameters that depend
on the hook. Adding a link to your page in some place is just a
matter of "subscribing" yourself to the hook that is in the
appropriate place, and printing the appropriate link whenever your
CallHook() method is called from that place.
Registering your plugin is done by providing a
/usr/lib/gforge/plugins/<pluginname>/include/<pluginname>-init.php.
It will be parsed by the PluginManager object. That file should
contain a call to register_plugin(), passing it an object of the
appropriate class. See the helloworld plugin for an example.
The hooks are managed centrally by the GForge code maintainers.
If you need one, please ask, we'll add it. The current list of
hooks is provided at the end of this document.
I rely on you plugins developers to provide more ideas :-)
- Plugin-specific web pages should reside either in the /plugin/*/
URL-space (that is, plugin "foo" will probably put its files in
/usr/share/gforge/www/plugins/foo/) or (if the web interface is not
written in PHP) in /plugin/*/cgi-bin/ URL-space (files in
/usr/lib/sourceforge/cgi-bin/plugins/foo/).
If possible, and as much as possible, a plugin should use the layout
functions defined by GForge (Layout.class, HTML.class, or
whatever they're called), to ensure a consistent look and themability.
Of course, the previous point only applies to plugins written in
PHP. Plugins written in other languages are not excluded by this
proposal, and there is no need to restrict them. Should they appear,
they might need to recode some of GForge's functions in Perl or
another language. I see no need to restrict that either. Only thing:
it would be better if the porting were as straightforward as possible.
Do not reimplement, please. Just translate from PHP to Perl or
whatever. If you do, please submit your translation to us, so that it
can be provided by GForge proper and maintained in common.
[TODO: Think about that, design, implement]
Speaking of languages... There should be some way to have
plugin-specific language files, so that the plugins can use the
standard methods used elsewhere in Sourceforge. I haven't thought
about that very deeply yet, but I think it will evolve into a
recommendation that the "handles" in the language files are
plugin_foo_page / item (as compared to the current page / item model
used for "core" GForge i18n strings).
- A plugin should register itself into the database using the provided
register-plugin script on its installation, and unregister itself
using unregister-plugin on removal. When unregistering, be careful
to delete all the rows in tables that contain a reference to your
plugin_id, so that the unregistration process (which deletes your row
in the plugins table) does not fail due to referential integrity errors.
HOW DO I MAKE A PLUGIN?
-----------------------
Your best bet would be to start with the sample "helloworld" plugin
and change parts of it. It shows an example of most of the things you
should need to make your plugin: PHP pages, configuration files, bits
of Apache configuration, cron jobs, etc. If you need something else,
please ask, we'll discuss it, (hopefully) reach an agreement on how to
Do It Right, and implement a sample in helloworld.
HOW TO NAME MY PLUGIN
---------------------
If you plan on distributing your plugin to the public, please contact
the GForge maintainer with your proposed name, and we'll add it
to the list below. This ensures that no other plugin will use the
same name, so as to reduce the risk of name conflicts.
If you only intend to keep your plugin for yourself, you might still
contact us with your plugin name. If you're really nice, we might
consider adding it here too, so that other people who want to
distribute their plugin do not reuse the same name.
For reference, this is the list of currently used plugin names:
- helloworld, the plugin provided as an example.
- extldapauth, a plugin allowing on-the-fly account creation from an
existing LDAP directory;
CURRENT LIST OF PLUGIN HOOKS
----------------------------
The following is a list of hooks available in GForge for plugins to utilise.
Each hook is listed with its name, locations in the source code where the
hook is called, and parameters that are passed into the hook and a brief
description. There may be other hooks available, added after this section
had been written.
Hook Name : after_session_set
Locations : www/include/pre.php
Description: Called once the GForge session has been initialised.
You can use this hook to handle session setup specific
to your plugin.
Hook Name : artifact_extra_detail
Parameters : artifact_id - The ID of a tracker item
Locations : www/tracker/detail.php
www/tracker/mod-limited.php
www/tracker/mod.php
Description: Use this hook to provide additional information about a
tracker item based upon its ID.
Hook Name : before_logout_redirect
Locations : www/account/logout.php
Description: Called once the GForge user has had their session logged
out, and before the user is redirected to the homepage of
the site.
Hook Name : cssfile
Locations : www/include/Layout.class
Description: Used to include a CSS link element to include in the page
layout. The hook should return a complete <link> element.
Hook Name : cssstyle
Locations : www/include/Layout.class
Description: Used to include inline CSS into the page layout. The
hook should return pure CSS, without surrounding
<style> elements.
Hook Name : group_approved
Parameters : group_id - The numeric ID of the group
Locations : www/admin/approve-pending.php
Description: When a group is approved by a site admin, this hook is called.
Hook Name : groupisactivecheckboxpost
Parameters : group_id - The numeric ID of the group
Locations : www/project/admin/editgroupinfo.php
Description: Called when a plugin is activated for a specific group from
the group's Edit Public Info page. Use this to perform
actions to initialise the plugin for a specific group.
Hook Name : groupisactivecheckbox
Parameters : group_id - The numeric ID of the group
Locations : www/project/admin/editgroupinfo.php
Description: Used to display a portion of a form on a group's Edit
Public Info page. It should return a HTML <tr> line containing
two cells.
Hook Name : groupmenu
Parameters : DIRS - A reference to the array of tab URLs
TITLES - A reference to the array of tab titles
toptab - A reference to a string containing the name of
the GForge tab menu in use (eg. admin, tracker)
selected - A reference to an array index of the tabs.
group - The numeric ID of the current group
Locations : www/include/Layout.class
Description: Used to provide a plugin specific tab in when viewing
group pages.
[TODO: The use of the 'group' name as a parameter is inconsistent
with most other group plugin hooks - which use group_id.]
Hook Name : groupmenu_scm
Parameters : DIRS - A reference to the array of tab URLs
TITLES - A reference to the array of tab titles
toptab - A reference to a string containing the name of
the GForge tab menu in use (eg. admin, tracker)
selected - A reference to an array index of the tabs.
group_id - The numeric ID of the current group
Locations : www/include/Layout.class
Description: Provides a tab for the SCM system in the group pages.
Hook Name : javascript
Locations : www/include/Layout.class
www/include/LayoutSF.class
Description: Provides a place to add inline Javascript into the page.
The output of the hook should be pure Javascript, as it will
be placed within an existing <script> block.
[TODO: The output of the hook appears after the closing SGML comment marker
and before the closing </script> element. Is this what is really indended?]
Hook Name : project_admin_plugins
Parameters : group_id - The numeric ID of the group
Locations : www/project/admin/index.php
Description: Provides a place for plugin authors to add a link on the
group summary page to the admin page for a plugin.
Hook Name : project_after_description
Locations : www/include/project_home.php
Description: Provides some space for plugin specific text on a group's
summary page.
[TODO: This isn't passed in the group_id... is it of any use??]
Hook Name : scm_admin_update
Parameters : group_id - The numeric ID of the group
Parameters : scmradio
A number of scm specific values generated from the
form fields created by the scm_admin_page hook.
Locations : www/scm/admin/index.php
Description: When the SCM admin page is submitted, this hook is called.
[TODO: Is scmradio actually used anywhere or is it legacy? A grep through
the source code seems to indicate its never used!]
Hook Name : scm_admin_page
Parameters : group_id - The numeric ID of the group
Locations : www/scm/admin/index.php
Description: Used to generate an administrative form for the SCM
plugin. All the form fields generated by this hook should
be named [pluginname]_[fieldname] where [pluginname] is the
SCM plugin name and [fieldname] is a field name for the
form element. Using this naming scheme ensures the fields
are properly passed as parameters to the scm_admin_update hook.
Hook Name : scm_page
Parameters : group_id - The numeric ID of the group
Locations : www/scm/index.php
Description: Show a page for the SCM in use by a group.
Hook Name : scm_plugin
Parameters : scm_plugins - A reference to an array of plugins providing
SCM features. Each element is a plugin string name.
Locations : common/scm/SCMFactory.class
Description: This is used by GForge to identify SCM plugins. Any plugin that
provides SCM features should add itself to the scm_plugins array
when this hook is called.
Hook Name : scm_stats
Parameters : group_id - The numeric ID of the group
Locations : www/include/project_home.php
Description: Shows SCM specific statistics on the group's summary page.
Hook Name : search_engines
Locations : www/search/include/SearchManager.class
Hook Name : session_before_login
Parameters : loginname - The login as passed in from the user
passwd - The password as passed in from the user
Locations : common/include/session.php
Description: Authentication plugins can use this hook to authenticate
a user before GForge passes the authentication details on
to its own database. The hook should return true if the
authentication succeeds.
Hook Name : site_admin_option_hook
Locations : www/admin/index.php
Description: Use this to provide a link to the site wide administrative
pages for your plugin. The hook should return HTML within
a <li> block and will appear on the Site Admin page in the
Site Utilities list.
Hook Name : task_extra_detail
Parameters : task_id - The numeric ID for a task
Locations : www/pm/detail_task.php
www/pm/mod_task.php
Description: Provides a place to include extra information about a
task. The hook should return a <tr> row containing 2 cells
(or colspan'ed to 2).
Hook Name : usermenu
Locations : www/include/html.php
Description: Prints out a tab to show when displaying user pages.
Unlike the groupmenu hook, this hook should use the PrintSubMenu
method to display the tab itself.
-- Roland Mas <lolando@debian.org>
|