Skip to content

Commit

Permalink
Item468: some minor bugfixes, cleaning up and doc additions
Browse files Browse the repository at this point in the history
git-svn-id: http://svn.foswiki.org/trunk/DBConnectorPlugin@1386 0b4bb1d4-4e5a-0410-9cc4-b2b747904278
  • Loading branch information
EugenMayer authored and EugenMayer committed Dec 16, 2008
1 parent 5349d86 commit 3d232f4
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 84 deletions.
71 changes: 43 additions & 28 deletions data/System/DBConnectorPlugin.txt
Expand Up @@ -7,7 +7,11 @@ Enables you to use simple methods to store and read data for topics, you dont wa

---++ Introduction
This plugin enables you to store and read additional topic data in a variable backend very easy. The setup e.g. with SQLite should be straight forward and easy,
if not, leave me a note. In addition, the database is stored on filesystem-level in the working_areas and is backuped by the old great cp / rsync way like the general topic data
if not, leave me a note. In addition, the database is stored on filesystem-level in the working_areas and is backuped by the old great cp / rsync way like the general topic data.

In addition, the Connector watches fields you choose to be updated including references on a topic, when this topic gets renamed in Foswiki. This is excatly the same behavior you get when renaming topics with searching for references, to rename them.
This behavior is enabled by default but can be disabled or only enabled for specific webs


---++ Usage and Methods
I will give a example on using this plugin with !SQLite when you used the optional example installation. There will be a table, named by the Web-Name with
Expand All @@ -16,71 +20,82 @@ if not, leave me a note. In addition, the database is stored on filesystem-level
* samplefield2: and text field
You can also look at [[DBConnectorPluginCreateTableQuery][topic]] to see, which query was used to create the table and also modify it

---+++ getValues( $web, $topic, @fields ) -> ( %result )
*Example application*
The plugin TopicSpecificNavigationPlugin is a good and simple example on how to use DBConnectorPlugin

get values for out of the database
---+++ getValues( $web, $topic, @fields ) -> ( %result )

get values for out of the database
* =$web= - Web name, required, will be used as table
* =$topic= Topic name, required, will be used as identifier/key
* =@fields= - Array of field names, optional. This fields are fetched out of the db
Return: =( %result )= Result, a hash with each fetched field-name as key
* =$fields= - reference on an array of field names, optional. This fields are fetched out of the db
* =$checkAccess= if this is zero, access is not checked
Return: =( %result )= Result, a hash with each fetched field-name as ke

if you want to fetch fields ('samplefield1','samplefield2') from System.WebHome you call it :
<pre>my %result getValues("System",'WebHome',('samplefield1','samplefield2')); accessing results this way print %result-&gt;{'bar'}; </pre>
<pre>my %result getValues("System",'WebHome',('samplefield1','samplefield2'));
accessing results this way
print %result->{'bar'};
</pre>


---+++ updateValues( $web, $topic, $fiedValuePairs ) -&gt; ( )
---+++ updateValues( $web, $topic, $fiedValuePairs ) -> ( )

get values for out of the database
get values for out of the database
* =$web= - Web name, required, will be used as table
* =$topic= Topic name, required, will be used as identifier/key
* =$fiedValuePairs= reference to a hash, which has the field-name as key and the hash-value as field-value
* =$checkAccess= if this is zero, access is not checked
Return: -

if you want to update fields ('samplefield1','samplefield2') from System.WebHome you call it :
<pre>my %pairs; %pairs-&gt;{'samplefield1'} = 20; %pairs-&gt;{'samplefield2'} = "myvalue1"; # Attention, you must use a reference! updateValues("System",'WebHome',\%pairs); </pre>
<pre>
my %pairs;
%pairs->{'samplefield1'} = 20;
%pairs->{'samplefield2'} = "myvalue1";
# Attention, you must use a reference!
updateValues("System",'WebHome',\%pairs);
</pre>

---+++ createdb rest handler
---+++ deleteEntry( $web, $topic ) -> ( )

deletes an entry out of the database $web, identiefied by $topic
* =$web= - Web name, required, will be used as table
* =$topic= Topic name, required, will be used as identifier/key

if you want to create a initial table for a web, where informations can be stored for topics, you got to run this rest handler to initialize/create it. The query defined on topic !DBConnectorPluginCreateTableQuery is used as a template for the query.
---+++ _createDB( $session ) -> ( )

if you want to create a initial table for a web, where informations can be stored for topics, you got to run this rest handler to initialize/create it. The query defined on topic Syste.DBConnectorPluginCreateTableQuery is used as a template for the query.
* %TABLENAME% gets expanded to the corresponding Web, when you create the table;
* %TOPICNAME% gets expanded to the topic. This should be actually always be a existing topic. In normal cases, this value is not needed in the template#
* %DBCONTABLEKEYFIELD% gets expanded to the primary key which is defined in the Configuration-Center $Foswiki::cfg{Plugins}{DBConnectorPlugin}{TableKeyField}
__you can disallow the creation of table by unchecking $Foswiki::cfg{Plugins}{DBConnectorPlugin}{allowCreatedb} in the Configuration-Center__

<em>_you can disallow the creatinof table with unchecking $Foswiki::cfg{Plugins}{DBConnectorPlugin}{allowCreatedb} in the Configuration-Center _</em>
you call the rest handler this way, creating a data for the web "TheWeb"
<pre>%SCRIPTURL{"rest"}%/DBConnectorPlugin/createdb?topic=TheWeb.WebHome </pre>

__Attention: If the table exists already, it will not be touched. No data will be erase or even a other table created__

---+++ sendQry( $query ) -> ( $results)

use this method to simply run querys on the database. You get a result like described by getValues
* $query Complete SQL query;
Return: returning a reference on an hash which has an integer-key for each row fetch, for each of this values a hash is stored, by {fieldname} = value like in getValues


---++ Installation instructions
* just use the installer attached to the topic or even easier, use the configure-system/Extension to easy install it trough an user-interface

---+++ Configuration
* Use the Configuration Center ( bin/configure ) to configure the driver you want to use, the DSN and user / password
* Use the Configuration Center ( bin/configure ) to configure the driver you want to use, the DSN. Decide if you want links to be auto updated, if a topic gets removed and whic webs should be searched for such topic-references

---+++ Optional: Create table
1 check this [[DBConnectorPluginCreateTableQuery][topic]] to see, which query is used to create the table for a web, where information is stored
1 click [[%SCRIPTURL{"rest"}%/DBConnectorPlugin/createdb?topic=System.WebHome][here]] to create a test table for the System-Web
1 no you can query the database with update and get queries like described abo
__Note:__ You do not need to install anything on the browser to use this Plugin. The following instructions are for the administrator who installs the Plugin on the server where Foswiki is running.
1 now you can query the database with update and get queries like described above
__Note:__ You do not need to install anything on the browser to use this plugin. The following instructions are for the administrator who installs the Plugin on the server where Foswiki is running.

---++ !ToDo
* provide easy way to remove values
* provide easy way to set fields for a topic to the default-values
* Build an example-application
* auto-creating of tables for all webs and stuff like that
* Add "onRename" and "onMove" and "onRemove" handler to keep the whole thing sync

| Plugin Author: | Foswiki:Main.EugenMayer |
| Copyright: | &copy; Impressive.media |
| License: | GPL ([[http://www.gnu.org/copyleft/gpl.html][GNU General Public License]]) |
| Plugin Version: | 15 Dez 2008 (V0.1) |
| Plugin Version: | 16 Dez 2008 (V0.2) |
| Change History: | <!-- versions below in reverse order --> |
| 16 Dez 2008: | extended functionality to update references(links) on topics, when this topcis are renamed. Tested, bugixed. |
| 15 Dez 2008: | initial release |
| Foswiki Dependency: | |
| CPAN Dependencies: | DBI |
Expand Down
133 changes: 78 additions & 55 deletions lib/Foswiki/Plugins/DBConnectorPlugin.pm
Expand Up @@ -28,6 +28,7 @@ use DBI;
use Error qw(:try);



# $VERSION is referred to by Foswiki, and is the only global variable that
# *must* exist in this package.
use vars qw( $VERSION $RELEASE $SHORTDESCRIPTION $debug $pluginName $NO_PREFS_IN_TOPIC );
Expand All @@ -39,7 +40,7 @@ $VERSION = '$Rev: 12445$';
# This is a free-form string you can use to "name" your own plugin version.
# It is *not* used by the build automation tools, but is reported as part
# of the version number in PLUGINDESCRIPTIONS.
$RELEASE = '0.1';
$RELEASE = '0.2';

# Short description of this plugin
# One line description, is shown in the %FoswikiWEB%.TextFormattingRules topic:
Expand All @@ -55,6 +56,7 @@ our $curWeb;
our $curTopic;
our $curUser;


sub initPlugin {
my( $topic, $web, $user, $installWeb ) = @_;

Expand All @@ -63,6 +65,9 @@ sub initPlugin {
$TableKeyField = $Foswiki::cfg{Plugins}{DBConnectorPlugin}{TableKeyField};
# Plugin correctly initialized
_connect();
# setup a error handler
$DBC_con->{HandleError} = sub { _warn(shift) };

$curWeb = $web;
$curTopic = $topic;
$curUser = $user;
Expand Down Expand Up @@ -144,36 +149,39 @@ my %pairs;
updateValues("System",'WebHome',\%pairs);
</pre>
=cut

sub updateValues {
my $web = shift;
my $topic = shift;
my $fiedValuePairs = shift;
my $checkAccess = shift || 1;
_debug("Updating values inserted",keys %{$fiedValuePairs});
#checking acces if i have to. Also checking if the table exists
if($checkAccess && !_hasAccess("CHANGE")) {
# no access;
return undef;
return ();
}
_createEntryForTopicIfNotExitent($web,$topic);
# craete a field list with placeholder(?), while each field is surrounded by `
my $values ="`". join("`=?,`", keys %{$fiedValuePairs}) . "`=?";
my $qry = qq(UPDATE $web SET $values WHERE `$TableKeyField`='$topic' );

_debug("Query: $qry");
my $qryobj = $DBC_con->prepare($qry);
my $qryobj = eval { $DBC_con->prepare($qry) };
unless ($qryobj) {
_warn("could not prepare qry ($qry), table missing? \nerror:".$DBC_con->errstr);
_warn("could not prepare qry (".$DBC_con->Statement()."), table missing? \nerror:".$DBC_con->errstr);
return;
}
# now insert the values for the placeholders into the query
$qryobj->execute(values %{$fiedValuePairs}) or _warn("could not insert values for $web.$topic \nerror:".$qryobj->err);
eval { $qryobj->execute(values %{$fiedValuePairs}) }or _warn("could not insert values for $web.$topic \nerror:".$DBC_con->errstr,values %{$fiedValuePairs});
$qryobj->finish();
_debug("Values upated");
}

sub _createEntryForTopicIfNotExitent {
my ( $web, $topic ) = @_;
_debug("Creating topic entry if not existent");

my $created = 0;
if(!getValues($web,$topic,["topic_id"],0)) {
my $qry = "INSERT into $web (`$TableKeyField`) VALUES ('$topic')";
Expand All @@ -185,6 +193,59 @@ sub _createEntryForTopicIfNotExitent {
}


=begin TML
---+++ sendQry( $query ) -> ( $results)
use this method to simply run querys on the database. You get a result like described by getValues
* $query Complete SQL query;
Return: returning a hash which has an the topic-identiefer as key for each row fetch, for each of this values a hash is stored, by {fieldname} = value like in getValues
=cut

sub sendQry {
my $qry = shift;
my $table = shift || "";

_debug("Sending direct query '$qry'");
# TODO: add access control? how?

my $qryobj = $DBC_con->prepare($qry);
my $results;
if(! (defined $qryobj)){
_warn("could not prepare qry ($qry), table missing? \nerror:".$DBC_con->errstr);
return ();

}


# now insert the values for the placeholders into the query
$qryobj->execute() or _warn("could not run direct query".$qry);
$results = $qryobj->fetchall_hashref($TableKeyField);
# returning the values as {fieldname} = value pairs. If no row could be fetched, this result is undef
_debug("Returned values:", values(%{ $results }));
$qryobj->finish;
return %{$results};
}

=begin TML
---+++ deleteEntry( $web, $topic ) -> ( )
deletes an entry out of the database $web, identiefied by $topic
* =$web= - Web name, required, will be used as table
* =$topic= Topic name, required, will be used as identifier/key
=cut

sub deleteEntry{
my $web = shift;
my $topic = shift;
my $checkAccess = shift || 1;

if($checkAccess && !_hasAccess("CHANGE")) {
# no access;
return;
}

$DBC_con->do("DELETE from `$web` where `$TableKeyField`='$topic'");
}

=begin TML
Expand All @@ -198,7 +259,6 @@ if you want to create a initial table for a web, where informations can be store
you call the rest handler this way, creating a data for the web "TheWeb"
<pre>%SCRIPTURL{"rest"}%/DBConnectorPlugin/createdb?topic=TheWeb.WebHome </pre>
__ Attention: If the table exists allready, it will not be touched. No data will be erase or even a other table created __
=cut
sub _createDB {
# TODO: test if there is allready a database, if yes, do not create anything and cancel
Expand Down Expand Up @@ -254,45 +314,6 @@ sub _createDB {
}


=begin TML
---+++ sendQry( $query ) -> ( $results)
use this method to simply run querys on the database. You get a result like described by getValues
* $query Complete SQL query;
Return: returning a hash which has an the topic-identiefer as key for each row fetch, for each of this values a hash is stored, by {fieldname} = value like in getValues
=cut

sub sendQry {
my $qry = shift;
# TODO: add access control? how?
my $qryobj = $DBC_con->prepare($qry);
my $results;
unless ($qryobj) {
_warn("could not prepare qry ($qry), table missing? \nerror:".$DBC_con->errstr);
return $results;
}

_debug("Runnging direct query '$qry'");
# now insert the values for the placeholders into the query
$qryobj->execute() or _warn("could not run direct query".$qry);
$results = $qryobj->fetchall_hashref($TableKeyField);
# returning the values as {fieldname} = value pairs. If no row could be fetched, this result is undef
_debug("Returned values:", values(%{ $results }));
$qryobj->finish;
return %{$results};
}

sub deleteEntry{
my $web = shift;
my $topic = shift;
my $checkAccess = shift || 1;
if($checkAccess && !_hasAccess("CHANGE")) {
# no access;
return;
}

$DBC_con->do("DELETE from `$web` where `$TableKeyField`='$topic'");
}


sub afterRenameHandler {
Expand Down Expand Up @@ -331,7 +352,8 @@ sub afterRenameHandler {
}

_updateLinksInWebs($oldWeb, $oldTopic, $newWeb, $newTopic,\@webs);
}
}
return 1;
}

sub _updateLinksInWebs {
Expand All @@ -340,11 +362,12 @@ sub _updateLinksInWebs {
my @fieldlist = split(";",$Foswiki::cfg{Plugins}{DBConnectorPlugin}{UpdateOnInvolveFiedlsList});
my $pattern = '%'.$oldTopic.'%';
for(my $i = 0; $i < @fieldlist;$i++) {
@fieldlist[$i] = "`".@fieldlist[$i]."` LIKE '$pattern'";
$fieldlist[$i] = "`".$fieldlist[$i]."` LIKE '$pattern'";
}
foreach my $curWeb (@webs) {
#get all entries needing an update
my %topicsNeedUpdates = sendQry("SELECT * FROM $curWeb WHERE ".join(" OR ", @fieldlist));
_debug("getting topics needs to be fixed");
my %topicsNeedUpdates = sendQry("SELECT * FROM $curWeb WHERE ".join(" OR ", @fieldlist), $curWeb);

# go trough all topics needs an update
if(%topicsNeedUpdates) {
Expand All @@ -356,7 +379,7 @@ sub _updateLinksInWebs {
_debug("fixed string is:\n".%topicsNeedUpdates->{$topicid}{$field});
}
# update the entry in the DB. DB name is the current web, the identifier
updateValues($curWeb,$topicid, \%topicsNeedUpdates->{$topicid},0);
updateValues($curWeb,$topicid, $topicsNeedUpdates{$topicid},0);
}
}
}
Expand All @@ -367,7 +390,7 @@ sub _updateLinksInString {
# TODO: this one should be checked for really working properly -> unit test
$string =~ s/$oldWeb.$oldTopic/$newWeb.$newTopic/g;
$string =~ s/$oldWeb\/$oldTopic/$newWeb\/$newTopic/g;
$string =~ s/$oldTopic/$newTopic/g;
$string =~ s/\b$oldTopic\b/\b$newTopic\b/g;
return $string;
}

Expand All @@ -387,8 +410,8 @@ sub _debug

sub _warn
{
my $message = shift;
_debug($message);
my ($message,@param) = @_;;
_debug($message,@param);
return TWiki::Func::writeWarning( $message );
}

Expand Down Expand Up @@ -419,7 +442,7 @@ sub _connect {
$DBC_con = DBI->connect(
$dsn, "","",
{
#RaiseError => 1,
RaiseError => 1,
#PrintError => 1,
FetchHashKeyName => NAME_lc =>
@_
Expand All @@ -430,8 +453,7 @@ sub _connect {
_debug($error);
throw Error::Simple($error);
}
_debug("connection successfully");

_debug("connection successfully");
}

sub _disconnect
Expand All @@ -441,5 +463,6 @@ sub _disconnect
$DBC_con->disconnect;
}


1;
# vim: ft=perl foldmethod=marker

0 comments on commit 3d232f4

Please sign in to comment.