The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

#!/usr/bin/perl
=head1 NAME
Konstrukt::Plugin - Base class for the Konstrukt plugins.
=head1 SYNOPSIS
=head2 Writing own plugins
package Konstrukt::Plugin::my_plugin;
use base 'Konstrukt::Plugin';
#overwrite (some of) the methods:
sub execute {
my ($self, $tag) = @_;
#...
$self->reset_nodes();
#...
$self->add_node('foo');
#...
return $self->get_nodes();
}
#...
=head2 Using existing plugins
use Konstrukt::Plugin; #import use_plugin
#...
sub init {
#...
#if you use a plugin in several places you may load it once at initialization
$self->{some_plugin} = use_plugin 'some_plugin' or return undef;
#...
}
#...
sub my_method {
#if you saved the plugin object at init, you may use it like this
$self->{some_plugin}->some_method();
#alternatively. but more unsafe (use_plugin may return undef on error)
my $plugin = use_plugin 'some_plugin';
$plugin->some_method();
#or even
use_plugin('some_plugin')->some_method();
}
=head1 DESCRIPTION
Base class for Konstrukt plugins. To write your own plugins you have to overload
some of the methods of this class.
Take a look at the documentation of the methods and at the L</SYNOPSIS> to get an
overview of what has to be done to create an own plugin.
You might also want to take a look at L<Konstrukt::Doc::PluginDevelopment>, at
L<Konstrukt::SimplePlugin> and at the plugins that already exist.
=cut
use strict;
=head1 EXPORTS
=head2 use_plugin
Exported by default. Allows you to load a plugin easily and get its object:
use Konstrukt::Plugin; #import use_plugin
my $plugin = use_plugin 'someplugin';
=cut
use Exporter ();
@Konstrukt::Plugin::ISA = qw(Exporter);
@Konstrukt::Plugin::EXPORT = qw/use_plugin/;
sub use_plugin {
return $Konstrukt::TagHandler::Plugin->load_plugin($_[0]);
}
=head1 METHODS
=head2 new
Constructor of this class
=cut
sub new {
my ($class) = @_;
return bless {}, $class;
}
#= /new
=head2 init
Method that will be called right before the first usage within a request.
Here you should do per request initialization work like definition of
L<default settings|Konstrukt::Settings/default>.
Should be overridden by the inheriting class.
=cut
sub init {
return 1;
}
# /init
=head2 install
Should be overloaded by your plugin and its backends, if they need
installation before the first run.
The installation should be performed according to the current settings like
the selected backend and database settings as well as the template path.
Usually in the backend module this method will create database tables for your
plugin. The "main" plugin should load the backend plugin on init,
that is defined in the settings, and then the install method will be
called automatically on the backend module when it gets loaded.
The "main" plugin may create templates that are needed for the output.
Will be called automatically after the plugin has been loaded and C<init()>ialized,
if the setting C<autoinstall> is true.
You may want to use L<Konstrukt::Lib/plugin_dbi_install_helper> for the
installation of your DBI backend plugins and
L<Konstrukt::Lib/plugin_file_install_helper> for the installation of default
templates for your plugin, which can be embedded at the end of your plugin module.
Don't confuse this method with L</init>, which will be called once on B<every>
request.
B<Parameters:>
none
=cut
sub install {
return 1;
}
# /install
=head2 prepare_again
If this method returns a true value the result generated by this plugin will be
piped through the prepare run before it will be inserted into the result tree.
Should return true, when this plugin may generate plaintext, that will parse to
dynamic content (e.g. return plaintext nodes that contain Konstrukt tags (<& .. &>)),
or tag nodes that should be prepare()'d.
In detail all returned plaintext nodes will be parsed for special tags and
existing tag nodes will be prepare()'d recursively.
As this is a time consuming job (especially when your tag has many/deeply nested
children), you should only return true, if your plugin really generates new tags
(as plaintext or as tag nodes).
=cut
sub prepare_again {
return 0;
}
#= /prepare_again
=head2 execute_again
If this method returns a true value the result generated by this plugin will be
piped through the execute run before it will be inserted into the result tree.
Should return true, when this plugin may generate (dynamic) tag nodes that shall
be executed (e.g. return a template or perl node).
=cut
sub execute_again {
return 0;
}
#= /execute_again
=head2 execution_stage
Returns the execution stage of the tag. Defaults to 1.
Usually all tags are L</execute>d in the order of appearence in the processed
document.
But sometimes you might want a tag to be executed last/later, although it's
located at the top of the document.
The C<execution_stage>s allow you to specifiy an execution order that's different
from the appearance order.
<& perl execution_stage="2" &>print `date +%H:%M:%S`<& / &>
<& perl &>print `date +%H:%M:%S`; sleep 2<& / &>
Will actually be rendered to something like:
10:50:54
10:50:52
=cut
sub execution_stage {
return 1;
}
#= /execution_stage
=head2 prepare
The prepare method of this plugin.
This sub should be overridden by the inheriting class.
Every content that will be the same with each page request ("static" content)
should be processed by the plugin in this stage.
If you want to replace the tag with other nodes, you must return a (dummy) tag
L<node|Konstrukt::Parser::Node> whose children will replace the original tag.
You may also just return a scalar(ref).
You may want to use L</reset_nodes>, L</add_node> and L</get_nodes> for an
easier node handling.
Note that you should return C<undef> if you modify the tree yourself instead of
returning replacement nodes.
(Some of) the nodes passed to your plugin may be dynamic content (i.e. tags that
have to be executed on every request as they may produce different output on
every request). You can check it like this:
if ($tag->{dynamic}) { ... }
If B<your> plugin has to do some work in the execute run, you should set the
dynamic flag of your tag to a true value to let the parser know, that this
tag cannot be finally processed in the prepare run:
$tag->{dynamic} = 1;
If your plugin returns plaintext that will parse into new tag nodes or if it
returns new tag nodes that should be prepare()'d your L</prepare_again> method
should return a true value.
B<Parameters>:
=over
=item * $tag - Reference to the tag node (and its children) that shall be handled.
Contains the plugin tag in the parse tree and all related information - espeacially
the tag attributes (C<$tag->{tag}->{attributes}>) and the content inside the tag:
my $node = $tag->{first_child};
while (defined $node) {
#do stuff on $node->{content}...
$node = $node->{next};
}
=back
=cut
sub prepare {
my ($self, $tag) = @_;
$Konstrukt::Debug->error_message("Not overloaded!") if Konstrukt::Debug::ERROR;
return 'Your plugin should do its prepare-work here!';
}
#= /prepare
=head2 execute
The execute method of this plugin.
This sub should be overridden by the inheriting class.
Every content that may be different on each page request ("dynamic" content)
should be processed by the plugin in this stage.
If you want to replace the tag with other nodes, you must return a (dummy) tag
L<node|Konstrukt::Parser::Node> whose children will replace the original tag.
You may also just return a scalar(ref).
You may want to use L</reset_nodes>, L</add_node> and L</get_nodes> for an
easier node handling.
Note that you should return C<undef> if you modify the tree yourself instead of
returning replacement nodes.
B<Parameters>:
=over
=item * $tag - Reference to the tag node (and its children) that shall be handled.
Contains the plugin tag in the parse tree and all related information - espeacially
the tag attributes (C<$tag->{tag}->{attributes}>) and the content inside the tag:
my $node = $tag->{first_child};
while (defined $node) {
#do stuff on $node->{content}...
$node = $node->{next};
}
=back
=cut
sub execute {
my ($self, $tag) = @_;
$Konstrukt::Debug->error_message("Not overloaded!") if Konstrukt::Debug::ERROR;
return 'Your plugin should do its execute work here!';
}
#= /execute
=head2 reset_nodes
Creates a new node collection. See L</push_node>.
=cut
sub reset_nodes {
my ($self) = @_;
$self->{collector_node} = Konstrukt::Parser::Node->new();
}
#= /reset_nodes
=head2 add_node
This method allows you to add a node to the list of temporary nodes. Usually
you would use it like this, if your plugin will return some nodes:
$self->reset_nodes();
$self->add_node('some plaintext node');
$self->add_node(Konstrukt::Parser::Node->new({ type => 'comment', content => '<!-- comment node -->'}));
#you may also add any other tag
return $self->get_nodes();
If the argument is no C<Konstrukt::Parser::Node> object, a plaintext node with the
argument as content will be created.
B<Parameters>:
=over
=item * $node - C<Konstrukt::Parser::Node> object.
=back
=cut
sub add_node {
my ($self, $node) = @_;
#the node must be a hash reference
if (ref($node) ne 'Konstrukt::Parser::Node') {
$Konstrukt::Debug->debug_message("Passed node is no Konstrukt::Parser::Node! Assuming plaintext node.") if Konstrukt::Debug::DEBUG;
$node = Konstrukt::Parser::Node->new({ type => 'plaintext', content => $node });
}
#add node
$self->{collector_node}->add_child($node);
}
#= /add_node
=head2 get_nodes
Retruns the node, whose children are the collected nodes.
=cut
sub get_nodes {
my ($self) = @_;
return $self->{collector_node};
}
#= /get_nodes
1;
=head1 AUTHOR
Copyright 2006 Thomas Wittek (mail at gedankenkonstrukt dot de). All rights reserved.
This document is free software.
It is distributed under the same terms as Perl itself.
=head1 SEE ALSO
L<Konstrukt::Plugin::date>, L<Konstrukt::Plugin::upcase>, L<Konstrukt::Parser::Node>, L<Konstrukt>
=cut