Merge lp:~tblue/quam-plures/plugins_multiple_instances into lp:quam-plures

Proposed by Tilman Blumenbach
Status: Merged
Merged at revision: 7628
Proposed branch: lp:~tblue/quam-plures/plugins_multiple_instances
Merge into: lp:quam-plures
Diff against target: 201 lines (+61/-16)
4 files modified
qp_inc/plugins/model/_plugins.class.php (+9/-2)
qp_inc/plugins/model/_plugins_admin.class.php (+49/-9)
qp_inc/plugins/plugins.ctrl.php (+0/-2)
qp_inc/plugins/views/_plugin_list_available.view.php (+3/-3)
To merge this branch: bzr merge lp:~tblue/quam-plures/plugins_multiple_instances
Reviewer Review Type Date Requested Status
EdB Approve
Tilman Blumenbach (community) Needs Resubmitting
Review via email: mp+73663@code.launchpad.net

Description of the change

This is an alternate version of lp:~edb/quam-plures/plugins_multiple_instances (see https://code.launchpad.net/~edb/quam-plures/plugins_multiple_instances/+merge/73297 as well). It changes EdB's algorithm slightly:

- We now use the plugin's original code as a base and if it doesn't have one, we use its classname.
- We then look through all other instances of the plugin to find the instance with the highest sequence number. Why do we do this? Imagine you have two instances of a plugin with the code "foo". One instance will have code "foo" while the other one has "foo_2". If you delete instance "foo" and then install a new one, you _could_ end up with the code "foo_2" -- which already exists. My algorithm would assign "foo_3" which is an unique code.
- If we haven't found other instances with sequential codes, we just use $number_of_instances + 1 (two instances already installed => we use 3 => code is "foo_3").

The plugin's name is changed as well (we append " #$seq_number"), just as it being done in EdB's branch.

To post a comment you must log in.
Revision history for this message
EdB (edb) wrote :

Downloading now. I got way too tired yesterday, but some of your feedback on the other version sounded like you hit on things that (a) I was not super-happy with the code and (b) didn't test well. Like deleting out #2 of 3 or the original for example.

Anyway downloading now and it always takes a while. I'll get code review proper happening as I bounce in and out working on the stupid truck.

Revision history for this message
EdB (edb) wrote :

Testing now, so far so good, but reading my comment above made me delete the original. I had already deleted #2 of 4 instances and installed #5 successfully. Now I deleted the original. In other words I have "MyPlugin #3" through #5 - 3 cases of it with those numbers as names.

I clicked on "Install new" and it offers me "MyPlugin #4" which while technically is accurate because it would be the fourth it still seems wrong because a plugin with that name already exists. ACTUALLY INSTALLING IT gives me "MyPlugin #6 which is what we would expect. Is this something you think you can improve on?

That is not a show-stopper by the way - just an observation. I am leaving this as a comment only because I have not actually looked at the code. All I've done is take it out for a test run. So far I'm on the way to marking this approved, but would be even happier if the tiny little issue described can be somehow improved.

Revision history for this message
Tilman Blumenbach (tblue) wrote :

Ah, you mean you get "Install #4" and the name after installing is "MyPlugin #6"? Hmm. What about making it "Install #6"? Not sure if that would be less confusing...? Maybe even more? Tricky.

Revision history for this message
EdB (edb) wrote :

Nice code :)

Approving and perfectly willing to merge in, but would like to hear your thoughts on the minor "it says #4 but #4 exists" issue first.

review: Approve
Revision history for this message
EdB (edb) wrote :

Yes that is what I meant. On the install page it is actually saying "Install #4" which really means "the fourth active installation". The confusion comes in with "but I already have #4 as seen on the previous page".

NOT a serious issue. If you think there is a way through it without great effort, or if you think the way it is will be fine then no problem. It's just that I thought "hey that ain't right" although to be honest I was in a code-review state of mind and therefore looking for issues.

Revision history for this message
Tilman Blumenbach (tblue) wrote :

Implemented, but it could confuse some users.

review: Needs Resubmitting
Revision history for this message
EdB (edb) wrote :

Still awesome, still gonna merge it. I'm figuring Sunday for the 3 ... 4 that aren't from me.

The "this is confusing" can go either way I guess. Hopefully this way is less confusing now that you coded your way to it.

Anyway Sunday looks like a busy day merging for me :)

review: Approve
Revision history for this message
Tilman Blumenbach (tblue) wrote :

Going to change this to WIP, I have found something I could easily optimize. :) Won't change the behaviour but allow for slightly nicer code.

Revision history for this message
Tilman Blumenbach (tblue) wrote :

Changes made, sorry for the interruption. Code "feels" better now, though. ;)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'qp_inc/plugins/model/_plugins.class.php'
--- qp_inc/plugins/model/_plugins.class.php 2010-12-31 12:12:03 +0000
+++ qp_inc/plugins/model/_plugins.class.php 2011-09-03 13:10:25 +0000
@@ -290,6 +290,9 @@
290 *290 *
291 * This handles the indexes, dynamically unregisters a Plugin that does not exist (anymore)291 * This handles the indexes, dynamically unregisters a Plugin that does not exist (anymore)
292 * and instantiates the Plugin's (User)Settings.292 * and instantiates the Plugin's (User)Settings.
293 *
294 * Attention: The code of the plugin is set to NULL if it is found to be not unique!
295 * To get the original code, use the $orig_code parameter.
293 *296 *
294 * @access protected297 * @access protected
295 * @param string name of plugin class to instantiate and register298 * @param string name of plugin class to instantiate and register
@@ -299,9 +302,13 @@
299 * @param string Path of the .php class file of the plugin.302 * @param string Path of the .php class file of the plugin.
300 * @param boolean Must the plugin exist (classfile_path and classname)?303 * @param boolean Must the plugin exist (classfile_path and classname)?
301 * This is used internally to be able to unregister a non-existing plugin.304 * This is used internally to be able to unregister a non-existing plugin.
305 * @param NULL|string If a reference to a variable is given here, it will receive
306 * the code of the plugin we are registering, even if the code
307 * is found to be not unique.
302 * @return Plugin Plugin ref to newly created plugin; string in case of error308 * @return Plugin Plugin ref to newly created plugin; string in case of error
303 */309 */
304 function & register( $classname, $ID = 0, $priority = -1, $apply_rendering = NULL, $classfile_path = NULL, $must_exists = true )310 function & register( $classname, $ID = 0, $priority = -1, $apply_rendering = NULL,
311 $classfile_path = NULL, $must_exists = true, & $orig_code = NULL )
305 {312 {
306 global $Debuglog, $Messages, $Timer;313 global $Debuglog, $Messages, $Timer;
307314
@@ -438,7 +445,7 @@
438 $Plugin->name = $Plugin->classname;445 $Plugin->name = $Plugin->classname;
439 }446 }
440447
441 // Memorizes Plugin in code hash array:448 $orig_code = $Plugin->code;
442 if( ! empty($this->index_code_ID[ $Plugin->code ]) && $this->index_code_ID[ $Plugin->code ] != $Plugin->ID )449 if( ! empty($this->index_code_ID[ $Plugin->code ]) && $this->index_code_ID[ $Plugin->code ] != $Plugin->ID )
443 { // The plugin's default code is already in use!450 { // The plugin's default code is already in use!
444 $Plugin->code = NULL;451 $Plugin->code = NULL;
445452
=== modified file 'qp_inc/plugins/model/_plugins_admin.class.php'
--- qp_inc/plugins/model/_plugins_admin.class.php 2011-02-04 04:06:29 +0000
+++ qp_inc/plugins/model/_plugins_admin.class.php 2011-09-03 13:10:25 +0000
@@ -286,25 +286,43 @@
286286
287287
288 /**288 /**
289 * Count # of registrations of same plugin.289 * Count # of registrations of same plugin. Optionally returns the sequence
290 * number to be used for the next instance of a plugin as well.
290 *291 *
291 * Plugins with negative ID (auto-generated; not installed (yet)) will not get considered.292 * Plugins with negative ID (auto-generated; not installed (yet)) will not get considered.
292 *293 *
293 * @param string class name294 * @param string class name
294 * @return int # of regs295 * @param boolean Return next sequence number as well (will be 1 if there are no other
296 * instances of this plugin).
297 * @return int|array # of regs or array( $regs, $next_seq_number ) if $get_seq_number is true
295 */298 */
296 function count_regs( $classname )299 function count_regs( $classname, $get_seq_number = false )
297 {300 {
298 $count = 0;301 $count = 0;
302 $highest_seq_number = 0;
299303
300 foreach( $this->sorted_IDs as $plugin_ID )304 foreach( $this->sorted_IDs as $plugin_ID )
301 {305 {
302 $Plugin = & $this->get_by_ID( $plugin_ID );306 $Plugin = $this->get_by_ID( $plugin_ID );
303 if( $Plugin && $Plugin->classname == $classname && $Plugin->ID > 0 )307 if( $Plugin && $Plugin->classname == $classname && $Plugin->ID > 0 )
304 {308 {
305 $count++;309 $count++;
310
311 // We assume that nobody has tinkered with the plugin codes in the DB here!
312 if( $get_seq_number && $Plugin->code !== NULL &&
313 preg_match( '/_(\d+)$/', $Plugin->code, $m ) &&
314 $m[1] > $highest_seq_number )
315 {
316 $highest_seq_number = $m[1];
317 }
306 }318 }
307 }319 }
320
321 if( $get_seq_number )
322 {
323 return array( $count, ( $highest_seq_number ? $highest_seq_number : $count ) + 1 );
324 }
325
308 return $count;326 return $count;
309 }327 }
310328
@@ -491,15 +509,18 @@
491 $this->load_plugins_table();509 $this->load_plugins_table();
492510
493 // Register the plugin:511 // Register the plugin:
494 $Plugin = & $this->register( $classname, 0, -1, NULL, $classfile_path ); // Auto-generates negative ID; New ID will be set a few lines below512 // Auto-generates negative ID; New ID will be set a few lines below
513 $Plugin = $this->register( $classname, 0, -1, NULL, $classfile_path,
514 true, $orig_code );
495515
496 if( is_string($Plugin) )516 if( is_string($Plugin) )
497 { // return error message from register()517 { // return error message from register()
498 return $Plugin;518 return $Plugin;
499 }519 }
500520
521 list( $regs, $seq_number ) = $this->count_regs( $Plugin->classname, true );
501 if( isset($Plugin->number_of_installs)522 if( isset($Plugin->number_of_installs)
502 && ( $this->count_regs( $Plugin->classname ) >= $Plugin->number_of_installs ) )523 && ( $regs >= $Plugin->number_of_installs ) )
503 {524 {
504 $this->unregister( $Plugin, true );525 $this->unregister( $Plugin, true );
505 $r = T_('The plugin cannot be installed again.');526 $r = T_('The plugin cannot be installed again.');
@@ -540,14 +561,28 @@
540 $Plugin->code = NULL;561 $Plugin->code = NULL;
541 }562 }
542563
564 // Do we have multiple instances of this plugin installed? If yes, we
565 // want a sequential name and code to make the admin's life a bit easier.
566 if( $seq_number > 1 )
567 {
568 if( $Plugin->code === NULL )
569 { // We need a code.
570 $Plugin->code = ! empty( $orig_code ) ? $orig_code : $classname;
571 }
572
573 $instance_name = $Plugin->name.' #'.$seq_number;
574 $Plugin->name = $instance_name;
575 $Plugin->code .= '_'.$seq_number;
576 }
577
543 $Plugin->status = $plug_status;578 $Plugin->status = $plug_status;
544579
545 // Record into DB580 // Record into DB
546 $DB->begin();581 $DB->begin();
547582
548 $DB->query( '583 $DB->query( '
549 INSERT INTO T_plugins( plug_classname, plug_priority, plug_code, plug_apply_rendering, plug_version, plug_status )584 INSERT INTO T_plugins( plug_classname, plug_priority, plug_code, plug_apply_rendering, plug_version, plug_name, plug_status )
550 VALUES( "'.$classname.'", '.$Plugin->priority.', '.$DB->quote($Plugin->code).', '.$DB->quote($Plugin->apply_rendering).', '.$DB->quote($Plugin->version).', '.$DB->quote($Plugin->status).' ) ' );585 VALUES( "'.$classname.'", '.$Plugin->priority.', '.$DB->quote($Plugin->code).', '.$DB->quote($Plugin->apply_rendering).', '.$DB->quote($Plugin->version).', '.$DB->quote($Plugin->name).', '.$DB->quote($Plugin->status).' ) ' );
551586
552 // Unset auto-generated ID info587 // Unset auto-generated ID info
553 unset( $this->index_ID_Plugins[ $Plugin->ID ] );588 unset( $this->index_ID_Plugins[ $Plugin->ID ] );
@@ -562,8 +597,9 @@
562 'plug_classname' => $Plugin->classname,597 'plug_classname' => $Plugin->classname,
563 'plug_code' => $Plugin->code,598 'plug_code' => $Plugin->code,
564 'plug_apply_rendering' => $Plugin->apply_rendering,599 'plug_apply_rendering' => $Plugin->apply_rendering,
600 'plug_version' => $Plugin->version,
601 'plug_name' => $Plugin->name,
565 'plug_status' => $Plugin->status,602 'plug_status' => $Plugin->status,
566 'plug_version' => $Plugin->version,
567 );603 );
568 $this->sorted_IDs[$key] = $Plugin->ID;604 $this->sorted_IDs[$key] = $Plugin->ID;
569605
@@ -589,6 +625,10 @@
589 $Debuglog->add( 'Unregistered plugin, because PluginInit returned false.', 'plugins' );625 $Debuglog->add( 'Unregistered plugin, because PluginInit returned false.', 'plugins' );
590 $Plugin = '';626 $Plugin = '';
591 }627 }
628 else if( isset( $instance_name ) )
629 { // $Plugin->PluginInit( $tmp_params ) lost our new name :(
630 $Plugin->name = $instance_name;
631 }
592632
593 if( ! defined('QP_IS_INSTALLING') || ! QP_IS_INSTALLING )633 if( ! defined('QP_IS_INSTALLING') || ! QP_IS_INSTALLING )
594 { // do not sort, if we're installing/upgrading.. instantiating Plugins might cause a fatal error!634 { // do not sort, if we're installing/upgrading.. instantiating Plugins might cause a fatal error!
595635
=== modified file 'qp_inc/plugins/plugins.ctrl.php'
--- qp_inc/plugins/plugins.ctrl.php 2010-12-31 12:12:03 +0000
+++ qp_inc/plugins/plugins.ctrl.php 2011-09-03 13:10:25 +0000
@@ -1021,8 +1021,6 @@
10211021
1022 if( $edit_Plugin->ID < 1 )1022 if( $edit_Plugin->ID < 1 )
1023 { // add "Install NOW" submit button (if not already installed)1023 { // add "Install NOW" submit button (if not already installed)
1024 $registrations = $admin_Plugins->count_regs($edit_Plugin->classname);
1025
1026 if( ! isset( $edit_Plugin->number_of_installs )1024 if( ! isset( $edit_Plugin->number_of_installs )
1027 || ( $admin_Plugins->count_regs($edit_Plugin->classname) < $edit_Plugin->number_of_installs ) )1025 || ( $admin_Plugins->count_regs($edit_Plugin->classname) < $edit_Plugin->number_of_installs ) )
1028 { // number of installations are not limited or not reached yet1026 { // number of installations are not limited or not reached yet
10291027
=== modified file 'qp_inc/plugins/views/_plugin_list_available.view.php'
--- qp_inc/plugins/views/_plugin_list_available.view.php 2010-12-31 12:12:03 +0000
+++ qp_inc/plugins/views/_plugin_list_available.view.php 2011-09-03 13:10:25 +0000
@@ -166,7 +166,7 @@
166 $Table->display_col_end();166 $Table->display_col_end();
167167
168 $Table->display_col_start();168 $Table->display_col_start();
169 $registrations = $admin_Plugins->count_regs($loop_Plugin->classname);169 list( $registrations, $seq_number ) = $admin_Plugins->count_regs( $loop_Plugin->classname, true );
170170
171 if( $current_User->check_perm( 'options', 'edit', false )171 if( $current_User->check_perm( 'options', 'edit', false )
172 && ( ! isset( $loop_Plugin->number_of_installs )172 && ( ! isset( $loop_Plugin->number_of_installs )
@@ -175,9 +175,9 @@
175 ?>175 ?>
176 [<a href="<?php echo $dispatcher ?>?ctrl=plugins&amp;action=install&amp;plugin=<?php echo rawurlencode($loop_Plugin->classname) ?>"><?php176 [<a href="<?php echo $dispatcher ?>?ctrl=plugins&amp;action=install&amp;plugin=<?php echo rawurlencode($loop_Plugin->classname) ?>"><?php
177 echo T_('Install');177 echo T_('Install');
178 if( $registrations )178 if( $seq_number > 1 )
179 { // This plugin is already installed179 { // This plugin is already installed
180 echo ' #'.($registrations+1);180 echo ' #', $seq_number;
181 }181 }
182 ?></a>]182 ?></a>]
183 <?php183 <?php

Subscribers

People subscribed via source and target branches