Clone a Module

Topics: Writing modules
Jan 5, 2012 at 7:10 AM
Edited Jan 6, 2012 at 7:26 AM

I installed a module, and then made modifications to a driver-part file in that module.  I tested my in-place changes and found they worked. Now I want to deploy my customization, without conflicting with the original module's code.  Thus I'd like to know what is the minimum I must do to create a clone of the original module, so that there is no run-time conflict with the original module.

I took a guess that I must at least:

  1. Copy all module files to a new module folder, and restore the code of the original module.
  2. Edit the cloned module.txt file
  3. Edit the cloned .csproj file to change the assembly name

Do I also need to change all the namespaces?  Do I need to change the names of all tables in the migration file?

When I followed those first three steps, the Orchard dashboard did "see" the new module and allowed me to enable it.  Unfortunately, the subsequent behavior appeared to be unstable.

Jan 5, 2012 at 6:15 PM

It's much simpler. Create a new, empty module, and add just your modified driver class. Finally decorate it with [OrchardSuppressDependency("Namespace.And.Class.To.Override")]. This minimizes the amount of cloned code when you want to modify other peoples modules.

Jan 6, 2012 at 7:21 AM
Edited Jan 6, 2012 at 7:25 AM

I followed your description, but it didn't work.  I did this:

1) create a new installation of Orchard (just to be very safe)
2) From the gallery, I installed the code generation module and the "CoolModule" I want to modify/extend.
3) I used the orchard command line to "codegen module TableOfContents"
4) I modified my new module.txt to say:

Name: TableOfContents
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardproject.net
Version: 1.0
OrchardVersion: 1.0
Description: Description for the module
FeaturesDescription: Yahoo!
Category: Navigation

5) I created a "Drivers" subfolder and copied CoolModuleDriver.cs from the cool module; I did not change the file name.
6) I made my modifications to CoolModuleDriver.cs, and I added the class decoration to say:

//...in namespace CoolModuleWidgets.Drivers...
[OrchardSuppressDependency("CoolModuleWidgets.Drivers.CoolModuleWidgetDriver")]
public class CoolModuleWidgetDriver : ContentPartDriver<CoolModuleWidgetPart> {//....}

 //As you can see, I did not modify the namespace or the class name.  I merely added the class attribute decoration and changed the implementation.

7) I added an altogether new view-model class in the 'Models' subfolder.
8) I copied and modified the view called CoolModuleWidget.cshtml.

When I refreshed in my dashboard, the new extended module was present and I enabled it.  However, when I went to add my TableOfContents widget to a zone, the CoolModuleWidget was one of the widget options, but my TableOfContents widget was not one of the options.  It seems that Orchard is not fully or correctly "seeing" it.

Besides asking the usual "what am I doing wrong," this leads to a number of other questions:

Do I need to list the original module in the "Dependencies" of my new module.txt file? 

Does the original module need to have been 'enabled'? 

Does it matter if I installed the original module, and had never 'enabled' it?  Will my extension module cause the migration file to be executed even though the original module did not have that chance?

Shouldn't I rename the namespace and/or class that I modified?  Perhaps this is why it didn't work?

Once I get this working properly, will I still be able to access the original module part/widget within the same site?

 

Coordinator
Jan 6, 2012 at 7:47 AM

Yes, at least the namespace needs to be different. And yes if you are replacing stuff from that module, it needs to be present and enabled.

All in all, depending on what that mysterious module is, you may be making your job harder than it needs to be. It may be simpler to just copy that original module and modify it in place. In fact, modules are dynamically compiled precisely to allow for that scenario.

Jan 6, 2012 at 12:48 PM

Yes the problem is in 6) by leaving the namespace the same you are suppressing both the original driver and your own modified one!

Also yes I would recommend adding the dependency to your Module.txt file, it ensure that you have to have the original module enabled, and it will force the correct inheritance order if, e.g., you also tried to override any .cshtml files. Semantically the original module is a dependency because it's required for your module to do anything.

Jan 7, 2012 at 12:20 AM

I think this is getting closer to working.  I now get an exception, but at least it indicates that *something* in my module is being acknowledged when it says it cannot find my 'TableOfContents' namespace referenced in the view I modified in my new module:

c:\build\Orchard.Source.1.3.10\src\Orchard.Web\Modules\AxisCode.TableOfContents\Views\Parts\TagMenuWidget.cshtml(2): error CS0246: The type or namespace name 'TableOfContents' could not be found (are you missing a using directive or an assembly reference?)

I get this exception whether or not I have the OrchardSuppressDependency attribute on my driver-override class.  This tells me that some other convention is causing this result.  I suspect it is merely because my module.txt indicates a dependency on the original module, and then naming conventions took over.  If so, it would rather explain why I'm getting the exception - because my override driver is still somehow not being picked up or noticed (even though I do indeed have the OrchardSuppressDependency decorating it).

To solve this issue, in case it helps I will clarify:  I had my new module added to the overall VS solution, and compiled it from VS (along with the rest of Orchard).  I did this to ensure there were no compilation bugs.  This also meant I had to appropriately update the .csproj and add assembly references, etc.  I made sure to add the dependency on the original module via module.txt (which also showed in the feature list).  I changed the namespace that my driver lives in, and of course I had applied the 'OrchardSuppressDependency' attribute to my driver.

My module.txt looks like this:

Name: TableOfContents
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardproject.net
Version: 1.0
OrchardVersion: 1.0
Description: Description for the module
FeaturesDescription: Yahoo!
Dependencies: TagMenuWidget, Orchard.Tags
Category: Navigation

Coordinator
Jan 7, 2012 at 1:27 AM

Your project needs to reference the other one. Module.txt dependency is not doing that.

Jan 8, 2012 at 6:11 PM

My project does indeed reference the other one, it was one of the steps I took in the solution to make sure everything was in order.

When the exception says it cannot find namespace 'TableOfContents', that is not from the original module!  That missing namespace is from my very own module.  It is a little strange why it can't see it.  All I can think is that the IOC Container (or other factory methods in Orchard) do not recognize the need to instantiate instances of my classes.  As it is, I have no proof that my module's assembly is even being loaded at all; The exception suggests it is not being loaded.

Changing direction, perhaps there is an example of a module that is already doing this substitution technique I can study?  Perhaps I would then spot what the difference is.

Coordinator
Jan 17, 2012 at 8:36 PM

There is a "modules" window under the debug/windows menu in VS that shows you what modules have been loaded effectively during an debugging session. I've found that window to be extremely useful throughout years of debugging tricky stuff.