Openkore.com

OpenKore Forums
It is currently 16 Aug 2018, 21:57

All times are UTC - 5 hours [ DST ]





Post new topic Reply to topic  [ 11 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Selectively loading your plugins
PostPosted: 22 Jun 2008, 23:25 
Offline
Super Moderators
Super Moderators
User avatar

Joined: 06 May 2008, 12:47
Posts: 801
[EDIT]
This feature has been comitted into SVN now !
There is no need to install it yourself and if you want to use this, then read this post.
[/EDIT]

Its usefull to me, i guess it would be usefull for others aswell.
You need plugins.txt in your control folder.
I don't know what you guys think but maybe it won't be bad to put the plugin config in here aswell.
Since there are hooks even before config.txt is loaded.
Improve it if you can.
plugins.txt:
Code:
# 0 : do not automatically load plugins
# 1 : automatically load all plugins
# 2 : selectively load plugins

autoLoad 2
loadPlugins macro, dKore


Diffpatch:
Code:
Index: functions.pl
===================================================================
--- functions.pl   (revision 6428)
+++ functions.pl   (working copy)
@@ -107,7 +107,32 @@
}

sub loadPlugins {
+   no encoding 'utf8';
+   Settings::addControlFile('plugins.txt',      loader => [\&parseConfigFile, \%plugins]);
+   use encoding 'utf8';
   eval {
+      my $progressHandler = sub {
+         my ($filename) = @_;
+         message TF("Loading %s...\n", $filename);
+      };
+      Settings::loadAll($progressHandler);
+   };
+   if (my $e = caught('UTF8MalformedException')) {
+      $interface->errorDialog(TF(
+         "The file %s must be valid UTF-8 encoded, which it is \n" .
+         "currently not. To solve this prolem, please use Notepad\n" .
+         "to save that file as valid UTF-8.",
+         $e->textfile));
+      exit 1;
+   } elsif (my $e = caught('FileNotFoundException')) {
+      $interface->errorDialog(TF(   "Unable to load the file %s. \n" .
+      "It should be located in %s. \n",
+      $e->filename, Settings::getControlFolders));
+      exit 1;
+   } elsif ($@) {
+      die $@;
+   }
+   eval {
      Plugins::loadAll();
   };
   if (my $e = caught('Plugin::LoadException')) {
Index: Globals.pm
===================================================================
--- Globals.pm   (revision 6428)
+++ Globals.pm   (working copy)
@@ -26,7 +26,7 @@
# Do not use any other Kore modules here. It will create circular dependancies.

our %EXPORT_TAGS = (
-   config  => [qw(%arrowcraft_items %avoid @chatResponses %cities_lut %config %consoleColors %directions_lut %equipTypes_lut %equipSlot_rlut %equipSlot_lut %haircolors @headgears_lut %items_control %items_lut %itemSlotCount_lut %itemsDesc_lut %itemTypes_lut %jobs_lut %maps_lut %masterServers %monsters_lut %npcs_lut %packetDescriptions %portals_lut %responses %sex_lut %shop %skillsDesc_lut %skillsLooks %skillsArea %skillsEncore %skillsSP_lut %spells_lut %emotions_lut %timeout $char %mon_control %priority %routeWeights %pickupitems %rpackets %itemSlots_lut %skillsStatus %portals_los %skillsState %skillsAilments %elements_lut)],
+   config  => [qw(%arrowcraft_items %avoid @chatResponses %cities_lut %config %consoleColors %directions_lut %equipTypes_lut %equipSlot_rlut %equipSlot_lut %haircolors @headgears_lut %items_control %items_lut %itemSlotCount_lut %itemsDesc_lut %itemTypes_lut %jobs_lut %maps_lut %masterServers %monsters_lut %npcs_lut %packetDescriptions %plugins %portals_lut %responses %sex_lut %shop %skillsDesc_lut %skillsLooks %skillsArea %skillsEncore %skillsSP_lut %spells_lut %emotions_lut %timeout $char %mon_control %priority %routeWeights %pickupitems %rpackets %itemSlots_lut %skillsStatus %portals_los %skillsState %skillsAilments %elements_lut)],
   ai      => [qw(@ai_seq @ai_seq_args %ai_v $AI $AI_forcedOff %targetTimeout)],
   state   => [qw($accountID $cardMergeIndex @cardMergeItemsID $charID @chars @chars_old %cart @friendsID %friends %incomingFriend %field $field %homunculus $itemsList @itemsID %items $monstersList @monstersID %monsters @npcsID %npcs $npcsList @playersID %players @portalsID @portalsID_old %portals %portals_old $portalsList @storeList $currentChatRoom @currentChatRoomUsers @chatRoomsID %createdChatRoom %chatRooms @skillsID %storage @storageID @arrowCraftID %guild %incomingGuild @spellsID %spells @unknownPlayers @unknownNPCs $statChanged $skillChanged $useArrowCraft %currentDeal %incomingDeal %outgoingDeal @identifyID @partyUsersID %incomingParty @petsID %pets @venderItemList $venderID @venderListsID @articles $articles %venderLists %monsters_old @monstersID_old %npcs_old %items_old %players_old @playersID_old @servers $sessionID $sessionID2 $accountSex $accountSex2 $map_ip $map_port $KoreStartTime $secureLoginKey $initSync $lastConfChangeTime $petsList $playersList $portalsList @playerNameCacheIDs %playerNameCache %pet $pvp @cashList)],
   network => [qw($remote_socket $net $messageSender $charServer $conState $conState_tries $encryptVal $ipc $bus $lastPacketTime $masterServer $lastswitch $packetParser $bytesSent $bytesReceived $incomingMessages $outgoingClientMessages $enc_val1 $enc_val2)],
@@ -103,6 +103,7 @@
our %monsters_lut;
our %npcs_lut;
our %packetDescriptions;
+our %plugins;
our %portals_los;
our %portals_lut;
our %priority;
Index: Plugins.pm
===================================================================
--- Plugins.pm   (revision 6428)
+++ Plugins.pm   (working copy)
@@ -34,7 +34,7 @@
use Modules 'register';
use Globals;
use Utils qw(stringToQuark quarkToString);
-use Utils::DataStructures qw(binAdd);
+use Utils::DataStructures qw(binAdd existsInList);
use Utils::ObjectList;
use Utils::Exceptions;
use Log qw(message);
@@ -71,6 +71,12 @@
##
# void Plugins::loadAll()
#
+# autoLoad
+#  0 : do not automatically load plugins
+#  1 : automatically load all plugins
+#  2 : selectively load plugins
+# loadPlugins pluginname1, pluginname2, ...
+#
# Loads all plugins from the plugins folder, and all plugins that are one subfolder below
# the plugins folder. Plugins must have the .pl extension.
#
@@ -78,41 +84,51 @@
# Throws Plugin::DeniedException if the plugin system refused to load a plugin. This can
# happen, for example, if it detects that a plugin is incompatible.
sub loadAll {
-   my (@plugins, @subdirs);
+   message TF("autoLoad is %s\n", $plugins{'autoLoad'});
+   if ($plugins{'autoLoad'}) {
+      my (@plugins, @subdirs, @files);

-   my @pluginsFolders;
-   @pluginsFolders = Settings::getPluginsFolders() if (defined &Settings::getPluginsFolders);
-   foreach my $dir (@pluginsFolders) {
-      my @items;
+      my @pluginsFolders;
+      @pluginsFolders = Settings::getPluginsFolders() if (defined &Settings::getPluginsFolders);
+      foreach my $dir (@pluginsFolders) {
+         my @items;

-      next if (!opendir(DIR, $dir));
-      @items = readdir DIR;
-      closedir DIR;
+         next if (!opendir(DIR, $dir));
+         @items = readdir DIR;
+         closedir DIR;

-      foreach my $file (@items) {
-         push @plugins, "$dir/$file" if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/);
+         foreach my $file (@items) {
+            push @plugins, "$dir/$file" if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/);
+            push @files, "$file" if ($file =~ /\.(pl|lp)$/);
+         }
+         foreach my $subdir (@items) {
+            push @subdirs, "$dir/$subdir" if (-d "$dir/$subdir" && $subdir !~ /^(\.|CVS$)/i);
+         }
      }
-      foreach my $subdir (@items) {
-         push @subdirs, "$dir/$subdir" if (-d "$dir/$subdir" && $subdir !~ /^(\.|CVS$)/i);
+
+      foreach my $plugin (@plugins) {
+         my $file = substr(shift(@files), 0, - 3);
+         next if (!existsInList($plugins{loadPlugins}, $file) && ($plugins{autoLoad} == 2));
+         load($plugin);
      }
-   }

-   foreach my $plugin (@plugins) {
-      load($plugin);
-   }
+      foreach my $dir (@subdirs) {
+         next unless (opendir(DIR, $dir));
+         @plugins = grep { -f "$dir/$_" && /\.(pl|lp)$/ } readdir(DIR);
+         foreach my $file (@plugins) {
+            push @files, "$file" if ($file =~ /\.(pl|lp)$/);
+         }
+         closedir(DIR);

-   foreach my $dir (@subdirs) {
-      next unless (opendir(DIR, $dir));
-      @plugins = grep { -f "$dir/$_" && /\.(pl|lp)$/ } readdir(DIR);
-      closedir(DIR);
-
-      foreach my $plugin (@plugins) {
-         load("$dir/$plugin");
+         foreach my $plugin (@plugins) {
+         my $file = substr(shift(@files), 0, - 3);
+            next if (!existsInList($plugins{loadPlugins}, $file) && ($plugins{autoLoad} == 2));
+            load("$dir/$plugin");
+         }
      }
   }
}

-
##
# void Plugins::load(String file)
# file: The filename of a plugin.

_________________
One ST0 to rule them all? One PE viewer to find them!
One ST_kRO to bring them all and in the darkness bind them...

Mount Doom awaits us, fellowship of OpenKore!


Last edited by Technology on 27 Jun 2008, 05:06, edited 4 times in total.

Top
 Profile  
 
 Post subject: Re: Selectively loading your plugins
PostPosted: 23 Jun 2008, 07:44 
Offline
Super Moderators
Super Moderators
User avatar

Joined: 16 May 2008, 08:28
Posts: 218
Nice work. I personally think this can be committed readily.

Let me, however, review this for a while more and let other devs see it as well.

Moved.

_________________
Whatever...


Top
 Profile  
 
 Post subject: Re: Selectively loading your plugins
PostPosted: 23 Jun 2008, 08:40 
Offline
Moderators
Moderators
User avatar

Joined: 04 Apr 2008, 09:30
Posts: 235
Location: My House
woot, just been know this idea :D wonder nobody ever mentioned this idea before :mrgreen:

_________________
Image
Follow The template in Asking Question or Be Trashed


Top
 Profile  
 
 Post subject: Re: Selectively loading your plugins
PostPosted: 23 Jun 2008, 11:35 
Offline
Noob
Noob

Joined: 29 May 2008, 12:00
Posts: 11
Hasn't it been in the wiki to do list for a while now?


Top
 Profile  
 
 Post subject: Re: Selectively loading your plugins
PostPosted: 23 Jun 2008, 20:25 
Offline
Super Moderators
Super Moderators
User avatar

Joined: 16 May 2008, 08:28
Posts: 218
I just realized we already have an existing control file that is loaded before any other plugins: "sys.txt"

Maybe we'd rather use that instead of adding a "plugins.txt"

Reference (openkore.pl):
Code:
sub __start {
   use ErrorHandler;
   use XSTools;
   srand();


   ##### BASIC INITIALIZATION #####

   use Translation;
   use Settings qw(%sys);
   use Utils::Exceptions;

   eval "use OpenKoreMod;";
   undef $@;
   parseArguments();
   Settings::loadSysConfig();

_________________
Whatever...


Top
 Profile  
 
 Post subject: Re: Selectively loading your plugins
PostPosted: 26 Jun 2008, 03:19 
Offline
Super Moderators
Super Moderators
User avatar

Joined: 06 May 2008, 12:47
Posts: 801
I guess you could do that.
It doesn't matter much to me, it may be a bit less user friendly tho.
All that counts is its functionality imo.

Since i'm not able to make a diffpatch on this pc, i'll just post a code snippet instead. (given that Plugins.pm hasn't been changed in ages ?)
And since we are using sys.txt here, no edits have to be made in Globals.pm and functions.pl !!!
And don't forget to add autoLoad and loadPlugins to your sys.txt
untested:
Code:
#########################################################################
#  OpenKore - Plugin system
#
#  This software is open source, licensed under the GNU General Public
#  License, version 2.
#  Basically, this means that you're allowed to modify and distribute
#  this software. However, if you distribute modified versions, you MUST
#  also distribute the source code.
#  See http://www.gnu.org/licenses/gpl.html for the full license.
#
#  $Revision: 5989 $
#  $Id: Plugins.pm 5989 2007-09-30 11:36:16Z vcl_kore $
#
#########################################################################
##
# MODULE DESCRIPTION: Plugin system
#
# This module provides an interface for handling plugins.
# See the <a href="http://www.openkore.com/wiki/index.php/How_to_write_plugins_for_OpenKore">Plugin
# Writing Tutorial</a> for more information about plugins.
#
# NOTE: Do not confuse plugins with modules! See Modules.pm for more information.

# TODO: use events instead of printing log information directly.

package Plugins;

use strict;
use warnings;
use Time::HiRes qw(time sleep);
use Exception::Class ('Plugin::LoadException', 'Plugin::DeniedException');
use UNIVERSAL qw(isa);

use Modules 'register';
use Globals;
use Utils qw(stringToQuark quarkToString);
use Utils::DataStructures qw(binAdd existsInList);
use Utils::ObjectList;
use Utils::Exceptions;
use Log qw(message);
use Translation qw(T TF);
use Settings qw(%sys);



#############################
### CATEGORY: Variables
#############################

##
# String $Plugins::current_plugin
#
# When a plugin is being (re)loaded, the filename of the plugin is set in this variable.
our $current_plugin;

##
# String $Plugins::current_plugin_folder
#
# When a plugin is being (re)loaded, the the plugin's folder is set in this variable.
our $current_plugin_folder;

our @plugins;
our %hooks;

use enum qw(HOOKNAME INDEX);
use enum qw(CALLBACK USER_DATA);


#############################
### CATEGORY: Functions
#############################

##
# void Plugins::loadAll()
#
# loadPlugins <0|1|2>
#   this option controls loading of plugins at startup or when the "plugin load all" command is used.
#   0 : do not load plugins
#   1 : load all plugins
#   2 : only load plugins that are listed in loadPlugins_list
# loadPlugins_list <list>
#   if loadPlugins is set to 2, this comma-separated list of plugin filenames specifies
#   which plugin files to load at startup or when the "plugin load all" command is used.
#
# Loads all plugins from the plugins folder, and all plugins that are one subfolder below
# the plugins folder. Plugins must have the .pl extension.#
# Throws Plugin::LoadException if a plugin failed to load.
# Throws Plugin::DeniedException if the plugin system refused to load a plugin. This can
# happen, for example, if it detects that a plugin is incompatible.
sub loadAll {
message TF("loadPlugins is %s\n", $sys{'loadPlugins'});
return unless ($sys{'loadPlugins'});
   my (@plugins, @subdirs, @files);

   my @pluginsFolders;
   @pluginsFolders = Settings::getPluginsFolders() if (defined &Settings::getPluginsFolders);
   foreach my $dir (@pluginsFolders) {
      my @items;

      next if (!opendir(DIR, $dir));
      @items = readdir DIR;
      closedir DIR;

      foreach my $file (@items) {
         push @plugins, "$dir/$file" if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/);
         push @files, "$file" if ($file =~ /\.(pl|lp)$/);
      }
      foreach my $subdir (@items) {
         push @subdirs, "$dir/$subdir" if (-d "$dir/$subdir" && $subdir !~ /^(\.|CVS$)/i);
      }
   }

   foreach my $plugin (@plugins) {
      my $file = substr(shift(@files), 0, - 3);
      next if (!existsInList($sys{loadPlugins_list}, $file) && ($sys{loadPlugins} == 2));
      load($plugin);
   }

   foreach my $dir (@subdirs) {
      next unless (opendir(DIR, $dir));
      @plugins = grep { -f "$dir/$_" && /\.(pl|lp)$/ } readdir(DIR);
foreach my $file (@plugins) {
push @files, "$file" if ($file =~ /\.(pl|lp)$/);
}
      closedir(DIR);

      foreach my $plugin (@plugins) {
         my $file = substr(shift(@files), 0, - 3);
         next if (!existsInList($sys{loadPlugins_list}, $file) && ($sys{loadPlugins} == 2));
         load("$dir/$plugin");
      }
   }
}

Below is a more userfriendly message but i am aware that the scalars in @autoLoad won't be able to be translated, maybe somebody knows a better solution ?
untested:
Code:
my @loadPlugins = ("no", "all", "selectively chosen");
message TF("Loading %s plugins.\n", $loadPlugins[$sys{'loadPlugins'}]);


EDIT: changed names autoLoad to loadPlugins (see Hakore's next post)

_________________
One ST0 to rule them all? One PE viewer to find them!
One ST_kRO to bring them all and in the darkness bind them...

Mount Doom awaits us, fellowship of OpenKore!


Last edited by Technology on 26 Jun 2008, 04:47, edited 2 times in total.

Top
 Profile  
 
 Post subject: Re: Selectively loading your plugins
PostPosted: 26 Jun 2008, 04:28 
Offline
Super Moderators
Super Moderators
User avatar

Joined: 16 May 2008, 08:28
Posts: 218
Well, sys.txt is there for a reason. It controls Openkore's startup behavior, and your proposed "config option" for selectively loading plugins generally falls under this category.

Being a bit less user-friendly can be supplemented by good documentation.

Also, the implementation of loading the control file plugins.txt is not graceful enough since it is loaded twice during the program startup. We should avoid such unnecessary performance overhead.

Finally, since the config options for controling this feature will be placed in sys.txt, we have to rename the proposed config options. "autoLoad" will be confusing when reading sys.txt. "loadPlugins" is a self-explanatory name.

I propose therefore (sys.txt):

Code:
###### Plugin settings ######

# loadPlugins <0|1|2>
#   this option controls loading of plugins at startup or when the "plugin load all" command is used.
#   0 : do not load plugins
#   1 : load all plugins
#   2 : only load plugins that are listed in loadPlugins_list
loadPlugins 1

# loadPlugins_list <list>
#   if loadPlugins is set to 2, this comma-separated list of plugin filenames specifies
#   which plugin files to load at startup or when the "plugin load all" command is used.
loadPlugins_list


I'll commit your proposal later this evening.

_________________
Whatever...


Top
 Profile  
 
 Post subject: Re: Selectively loading your plugins
PostPosted: 26 Jun 2008, 04:39 
Offline
Super Moderators
Super Moderators
User avatar

Joined: 06 May 2008, 12:47
Posts: 801
I agree on every statement u made here.
Also changing the name of the config options is a good idea, i will now adjust these in the code above your post.
Thanks for your involvement in this matter Hakore.

_________________
One ST0 to rule them all? One PE viewer to find them!
One ST_kRO to bring them all and in the darkness bind them...

Mount Doom awaits us, fellowship of OpenKore!


Top
 Profile  
 
 Post subject: Re: Selectively loading your plugins
PostPosted: 26 Jun 2008, 08:32 
Offline
Super Moderators
Super Moderators
User avatar

Joined: 16 May 2008, 08:28
Posts: 218
This will be the working patch:

I just had to clean up and optimize the Plugins::loadAll subroutine a bit (remove redundant loops and avoid redundant if statements) together with the implementation of selective plugin loading. So as you can see, this will be a bit different from your original patch. However, all the logic you proposed comprised this.

I also added a fallback logic for those who do not have loadPlugins set in sys.txt.

Code:
Index: Plugins.pm
===================================================================
--- Plugins.pm   (revision 6435)
+++ Plugins.pm   (working copy)
@@ -34,11 +34,12 @@
use Modules 'register';
use Globals;
use Utils qw(stringToQuark quarkToString);
-use Utils::DataStructures qw(binAdd);
+use Utils::DataStructures qw(binAdd existsInList);
use Utils::ObjectList;
use Utils::Exceptions;
use Log qw(message);
use Translation qw(T TF);
+use Settings qw(%sys);


#############################
@@ -78,10 +79,22 @@
# Throws Plugin::DeniedException if the plugin system refused to load a plugin. This can
# happen, for example, if it detects that a plugin is incompatible.
sub loadAll {
-   my (@plugins, @subdirs);
+   if (!exists $sys{'loadPlugins'}) {
+      message T("Loading all plugins (by default)...\n", 'plugins');
+   } elsif (!$sys{'loadPlugins'}) {
+      message T("Automatic loading of plugins disabled\n", 'plugins');
+      return;
+   } elsif ($sys{'loadPlugins'} eq '1') {
+      message T("Loading all plugins...\n", 'plugins');
+   } elsif ($sys{'loadPlugins'} eq '2') {
+      message T("Selectively loading plugins...\n", 'plugins');
+   }
+   
+   my (@plugins, @subdirs, @names);

   my @pluginsFolders;
   @pluginsFolders = Settings::getPluginsFolders() if (defined &Settings::getPluginsFolders);
+
   foreach my $dir (@pluginsFolders) {
      my @items;

@@ -90,26 +103,38 @@
      closedir DIR;

      foreach my $file (@items) {
-         push @plugins, "$dir/$file" if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/);
+         if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/) {
+            push @plugins, "$dir/$file";
+            push @names, substr($file, 0, -3) if (exists $sys{'loadPlugins'} && $sys{'loadPlugins'} eq '2');
+         } elsif (-d "$dir/$file" && $file !~ /^(\.|CVS$)/i) {
+            push @subdirs, "$dir/$file";
+         }
      }
-      foreach my $subdir (@items) {
-         push @subdirs, "$dir/$subdir" if (-d "$dir/$subdir" && $subdir !~ /^(\.|CVS$)/i);
-      }
   }

-   foreach my $plugin (@plugins) {
-      load($plugin);
-   }
-
   foreach my $dir (@subdirs) {
-      next unless (opendir(DIR, $dir));
-      @plugins = grep { -f "$dir/$_" && /\.(pl|lp)$/ } readdir(DIR);
-      closedir(DIR);
+      my @items;

-      foreach my $plugin (@plugins) {
-         load("$dir/$plugin");
+      next if (!opendir(DIR, $dir));
+      @items = readdir DIR;
+      closedir DIR;
+
+      foreach my $file (@items) {
+         if (-f "$dir/$file" && $file =~ /\.(pl|lp)$/) {
+            push @plugins, "$dir/$file";
+            push @names, substr($file, 0, -3) if (exists $sys{'loadPlugins'} && $sys{'loadPlugins'} eq '2');
+         }
      }
   }
+
+   while (@plugins) {
+      my $plugin = shift(@plugins);
+      if (exists $sys{'loadPlugins'} && $sys{'loadPlugins'} eq '2') {
+         my $file = shift(@names);
+         next if (exists $sys{'loadPlugins_list'} && !existsInList($sys{'loadPlugins_list'}, $file));
+      }
+      load($plugin);
+   }
}




And the default sys.txt for the confpack.
Code:
Index: sys.txt
===================================================================
--- sys.txt   (revision 6435)
+++ sys.txt   (working copy)
@@ -35,3 +35,17 @@

###### Miscellaneous ######
sendAnonymousStatisticReport 1
+
+
+###### Plugin settings ######
+# loadPlugins <0|1|2>
+#   this option controls loading of plugins at startup or when the "plugin load all" command is used.
+#   0 : do not load plugins
+#   1 : load all plugins
+#   2 : only load plugins that are listed in loadPlugins_list
+loadPlugins 1
+
+# loadPlugins_list <list>
+#   if loadPlugins is set to 2, this comma-separated list of plugin names (filename without the extension)
+#   specifies which plugin files to load at startup or when the "plugin load all" command is used.
+loadPlugins_list macro

_________________
Whatever...


Top
 Profile  
 
 Post subject: Re: Selectively loading your plugins
PostPosted: 26 Jun 2008, 15:48 
Offline
Super Moderators
Super Moderators
User avatar

Joined: 16 May 2008, 08:28
Posts: 218
Finally, committed.

Thanks Technology.

_________________
Whatever...


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours [ DST ]


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group