Why adding business controller class namespace in DNN makes module disappear? - search

I am trying to make a module searchable i DNN 9 so I created a class which implements ISearchable and overrides GetSearchItems.
following this documentation:
https://www.dnnsoftware.com/wiki/isearchable
public SearchItemInfoCollection GetSearchItems(ModuleInfo ModInfo)
{
SearchItemInfoCollection SearchItemCollection = new SearchItemInfoCollection();
ArrayList Documents = GetDocuments(ModInfo.ModuleID, ModInfo.PortalID, true);
foreach (var objDocument in Documents)
{
SearchItemInfo SearchItem;
{
var withBlock = (DocumentInfo)objDocument;
int UserId = Null.NullInteger;
// If IsNumeric(.CreatedByUser) Then
// UserId = Integer.Parse(.CreatedByUser)
// End If
UserId = withBlock.CreatedByUserID;
SearchItem = new SearchItemInfo(ModInfo.ModuleTitle + " - " + withBlock.Title, withBlock.Title, UserId, withBlock.CreatedDate, ModInfo.ModuleID, withBlock.ItemId.ToString(), withBlock.Title + " " + withBlock.Category, "ItemId=" + withBlock.ItemId.ToString());
SearchItemCollection.Add(SearchItem);
}
}
return SearchItemCollection;
}
But when I add the namespace of the class in the module manifest, the module disappears from the page (and indexing doesn't work).
The namespace of the class is ProjectName.ListaNews.ListaController
I have also tried ProjectName.ListaNews and changing it to DotNetNuke.Modules.ProjectName.ListaNews.ListaController
I have tried changing the business class controller both from module settings and from .dnn file.
Why adding business class namespace breaks my module and makes it disappear from the page?
-------------- EDIT ----------------
The business controller class name seems to be correct.
I checked the log file and found this error:
DotNetNuke.Framework.Reflection - ProjectName.ListaNews.ListaController, ProjectName.ListaNews
System.IO.FileNotFoundException: Could not load file or assembly 'ProjectName.ListaNews' or one of its dependencies.
Cannot find specified file.
File name: 'ProjectName.ListaNews'
in bin\ folder there is ListaNews.dll. I tried renaming it into ProjectName.ListaNews.dll but I had the same error (+ the site crash).
So I changed the business controller class to ListaNews.ListaController, ListaNews without ProjectName and it gave a new error:
DotNetNuke.Framework.Reflection - ListaNews.ListaController, ListaNews
System.TypeLoadException: Could not load type 'ListaNews.ListaController' from assembly 'ListaNews'.
Looks like it didn't find ListaController class.
I noticed ListaController.cs was not defined in .dnn file when I installed it.
I added this to the .dnn file
<component type="File">
other files definitions....
<files>
<file>
<name>ListaController.cs</name>
</file>
</files>
</component>
So I deleted and re-installed my module. Nothing changed though.
Does that give any hint?
Thanks
-------------- EDIT 2 ----------------
Since it was a namespace error I moved the controller class from ListaController.cs to View.ascx.cs and the previous error disappeared, so the class is probably visible now. But the module is still not visible (crashes) so i checked the log and it gave 2 very similiar errors:
1)
Message: Value cannot be null. Parameter name: type
StackTrace: in System.Activator.CreateInstance(Type type, Boolean nonPublic)
in System.Activator.CreateInstance(Type type)
in DotNetNuke.UI.Skins.Pane.IsVesionableModule(ModuleInfo moduleInfo)
in DotNetNuke.UI.Skins.Pane.InjectModule(ModuleInfo module)
in DotNetNuke.UI.Skins.Skin.InjectModule(Pane pane, ModuleInfo module)
InnerMessage: Value cannot be null. Parameter name: type
InnerStackTrace: at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at DotNetNuke.Services.Search.ModuleIndexer.GetModuleList(Int32 portalId)
I read System.Activator.CreateInstance documentation here:
https://learn.microsoft.com/it-it/dotnet/api/system.activator.createinstance?view=netcore-3.1#System_Activator_CreateInstance_System_Type_System_Boolean_
and actually CreateInstance has a constructor with type parameter,
But I think it is called in DNN compiled libraries and I can't see what is going on
I added this line in constructor
object instance = Activator.CreateInstance(typeof(ListaController));
and it logged this error:
Error Creating BusinessControllerClass
'ProjectName.ListaNews.ListaController, ProjectName.ListaNews' of
module(ProjectName.ListaNews) id=(577) in tab(45) and portal(0)
StackTrace:
at DotNetNuke.Services.Search.ModuleIndexer.ThrowLogError(ModuleInfo module,
Exception ex)
InnerMessage:Value cannot be null. Parameter name: type
InnerStackTrace:
at System.Activator.CreateInstance(Type type, Boolean nonPublic) at
system.Activator.CreateInstance(Type type) at
DotNetNuke.Services.Search.ModuleIndexer.GetModuleList(Int32 portalId)
I found this forum
https://www.dnnsoftware.com/forums/threadid/495897/scope/posts/help-with-rror-in-custom-module
so I added a parameterless constructor in my controller class but it didn't change anything
Why is type null in CreateInstance?

The value for businessControllerClass in the manifest must be in the format {FQCN}, {Assembly}. This is per The manifest schema
For your project, assuming the namespace is as you mentioned ProjectName.ListaNews.ListaController your assembly name is most likely ProjectName.ListaNews which would mean the proper value for the businessControllerClass is
ProjectName.ListaNews.ListaController, ProjectName.ListaNews
If that doesn't correct it, as Chris Hammond mentioned you can use the logs to get a more detailed error

Related

.net core 3.1 - View Components - Not finding my Default.cshtml

I'm just getting to grips with ViewComponents in my Razor pages application.
I have a ViewComponents folder within my project that contains my ViewComponent .cs code:
public class RemoveFromCartViewComponent : ViewComponent
{
public IViewComponentResult Invoke()
{
var result = "123";
return View(result);
}
}
I then have another folder within Pages/Shared/Components called RemoveFromCart. Within this folder I have my default.cshtml
#model string
<h2>
#Model
</h2>
Simply putting the string within a h2 tag.
In my projects Layout.cshtml file I am invoking this ViewComponent:
<div>
#await Component.InvokeAsync("RemoveFromCart")
</div>
When I start my project, the error I get is:
*InvalidOperationException: The view 'Components/RemoveFromCart/123' was not found. The following locations were searched:
/Pages/Components/RemoveFromCart/123.cshtml
/Pages/Shared/Components/RemoveFromCart/123.cshtml
/Views/Shared/Components/RemoveFromCart/123.cshtml*
This is indication my view should be called 123.cshtml which doesnt seem right. What am I doing wrong here? I should simply expect to see the text 123 appear
Thanks
By returning View("123"), you are using this overload:
public ViewViewComponentResult View (string viewName)
Returns a result which will render the partial view with name viewName.
So you are passing the view name, instead of a string value as the view’s model.
You can change that by explicitly calling the View<TModel>(TModel) overload instead:
public IViewComponentResult Invoke()
{
var result = "123";
return View<string>(result);
}
In the long run, I would suggest you to create a model class instead so that you can pass an object instead of just a string. This will avoid having this particular problem and you are also able to easily expand the model contents later on.

Resolving a TypeReference to a TypeDefinition in Mono.Cecil fails with Assembly Resolution Error

I'm trying to get a Mono.Cecil TypeDefinition from a .NET type and not having any luck.
I'm using code like this:
var type = typeof(MarkdownMonster.AppConfiguration);
var a = AssemblyDefinition.ReadAssembly(type.Assembly.Location);
var tr = a.MainModule.Import(type); // this seems to work
var td = tr.Resolve(); // fails
but it fails with an assembly resolution error:
GetConfigurationPropertiesTest [0:29.990] Failed: Mono.Cecil.AssemblyResolutionException : Failed to resolve assembly: 'MarkdownMonster, Version=1.18.11.0, Culture=neutral, PublicKeyToken=null'
Mono.Cecil.AssemblyResolutionException : Failed to resolve assembly: 'MarkdownMonster, Version=1.18.11.0, Culture=neutral, PublicKeyToken=null'
at Mono.Cecil.BaseAssemblyResolver.Resolve(AssemblyNameReference name, ReaderParameters parameters)
at Mono.Cecil.DefaultAssemblyResolver.Resolve(AssemblyNameReference name)
at Mono.Cecil.MetadataResolver.Resolve(TypeReference type)
at Mono.Cecil.TypeReference.Resolve()
at Westwind.TypeImporter.TypeParser.ParseObject(Type type, Boolean dontParseMembers)
The assembly is obviously there, since the TypeReference import seems to work and produces a valid TypeReference.
The assembly in question is an EXE, and just for kicks I renamed it to a DLL but that had no effect.
After a bit of back and forth experimenting I found one (ugly) solution is to create a custom type resolver and basically forcing a hard type reference into it. It seems Mono.Cecil is able to resolve transient dependencies once it's found the main assembly, but not the top level reference.
To make this work I basically pass in the already resolved assembly reference. In my case I know the only reference I will need to return will be the top level reference so I hard code this. A more realistic example will have to use AssemblyDefinition.ReadAssembly() to read an assembly off disk or from a stream.
Here's is the code to create the AssemblyResolver:
public class MonoAssemblyResolver : IAssemblyResolver
{
public AssemblyDefinition AssemblyDefinition;
public AssemblyDefinition Resolve(AssemblyNameReference name)
{
return AssemblyDefinition;
}
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
return AssemblyDefinition;
}
public void Dispose()
{
AssemblyDefinition = null;
}
}
Note the cheat to pass in an already resolved assembly reference.
To resolve I can now use the following code:
var a = AssemblyDefinition.ReadAssembly(type.Assembly.Location,
new ReaderParameters() { AssemblyResolver = resolver });
// assign the resolvedr
var resolver = new MonoAssemblyResolver();
resolver.AssemblyDefinition = a;
var tr = a.MainModule.Import(type: type);
var td = tr.Resolve(); // works now
This is crazy hacky, but can be adapted to be more generic (in my case not needed).
Still it would be much nicer if Mono.Cecil could automatically resolve the assembly - I don't understand why it's not finding the assembly in the first place since it lives in the current bin folder and the TypeReference can find it.
This is how it's normally done:
var assembly = #"c:\myassembly.dll";
var resolver = new DefaultAssemblyResolver();
// add .NET runtime dir for the sake of security
foreach (var dir in Directory.GetDirectories(RuntimeEnvironment.GetRuntimeDirectory(), "*", SearchOption.AllDirectories))
{
resolver.AddSearchDirectory(dir);
}
// add the assembly's directory
resolver.AddSearchDirectory(Path.GetDirectoryName(assembly));
var mod = AssemblyDefinition.ReadAssembly(assembly, new ReaderParameters { AssemblyResolver = resolver }).MainModule;
Regards

Assign custom methods in mongoose .d.ts so I can use them anywhere with mongoose in typescript

I am trying to create a custom function in mongoose Api hooks but the function is not defined as it has not declared. I have created index.d.ts separate file but don't know how to add that.
const exec = mongoose.Query.prototype.exec;
mongoose.Query.prototype.cache = function (options:ICacheOptions = {}) {
this.useCache = true;
this.hashKey = JSON.stringify(options.key || "");
return this;
}
link :: cache.ts!
error is: Property 'cache' does not exist on type 'Query'. Did you mean 'catch'?
what I have tried: I created .d.ts file and try to declare them.
declare module 'mongoose' {
interface DocumentQuery<T, DocType extends import("mongoose").Document, QueryHelpers = {}>{
mongooseCollection: {
name: any;
};
cache():DocumentQuery<T[], Document> & QueryHelpers;
useCache: boolean;
hashKey: string;
}
}
link:: index.d.ts!
My Errors are ::
src/lib/services/cache.ts(16,26): error TS2551: Property 'cache' does not exist on type 'Query<any>'. Did you mean 'catch'?
src/lib/services/cache.ts(17,8): error TS2339: Property 'useCache' does not exist on type 'Query<any>'.
src/lib/services/cache.ts(18,8): error TS2339: Property 'hashKey' does not exist on type 'Query<any>'.
src/lib/services/cache.ts(25,12): error TS2339: Property 'useCache' does not exist on type 'Query<any>'.
src/lib/services/cache.ts(30,41): error TS2339: Property 'mongooseCollection' does not exist on type 'Query<any>'.
src/lib/services/cache.ts(33,52): error TS2339: Property 'hashKey' does not exist on type 'Query<any>'.
src/lib/services/cache.ts(45,22): error TS2339: Property 'hashKey' does not exist on type 'Query<any>'.
src/lib/services/cache.ts(46,24): error TS2339: Property 'hashKey' does not exist on type 'Query<any>'.
I want somehow I can fix this and also want to know how can I extend some property in .d.ts if they are class. Thanks in advance.
Your augmentation does work. It needs to be placed somewhere that you have configured TypeScript to look for source files. The declare module style of type definition is called an "ambient declaration" which means that you don't have to put it in any particular directory or file. It can even be in a regular .ts file. The easiest thing to do is to put the declaration in cache.ts, the same file where you assign mongoose.Query.prototype.cache.
Edit: I forgot to address your specific question about augmenting a class. As you guessed you can augment a class by using the interface keyword. That is because in TypeScript defining a class does two things: it defines a value (the constructor function that is invoked when you call, e.g., new DocumentQuery), and a type for instances of the class which is really an interface. The value and the type both have the same name. Because the type part of a class is an interface you can augment it like any other interface.
So in this case you augment the DocumentQuery type, which is the superclass of Query, but you assign the cache method to the Query prototype. That works because Query extends DocumentQuery, so when you declare that DocumentQuery now has a cache method TypeScript assumes that subclasses, including Query, have the same method. This does lead to a discrepancy: TypeScript now assumes that DocumentQuery instances have a cache method, but you only really defined that method for Query. It would be more accurate to either change your type declaration to augment Query instead of DocumentQuery, or to assign the method implementation to DocumentQuery.prototype instead of to Query.prototype.

error TS2339: Property 'currentVersion' does not exist on type 'typeof ClientSchemaVersions'

I am using reference path="../typings/sharePoint/sharePoint.d.ts"
and the file has:
export class ClientSchemaVersions {
version14: string;
version15: string;
currentVersion: string;
But the statement parseInt(SP.ClientSchemaVersions.currentVersion) complains Property 'currentVersion' does not exist on type 'typeof ClientSchemaVersions'
but works right at run time.
On the other hand, the code:
let mySP = new SP.ClientSchemaVersions();
console.log(mySP.currentVersion)
Does not error in typescript but at run time, the value of mySP.currentVersion is undefined.
Is there a setting to set in the config files?
SP.ClientSchemaVersions.currentVersion is defined if and only if currentVersion is a static class variable of SP.ClientSchemaVersions. In this case, the definition file for sharePoint may be wrong. You should fire an issue.
As for let mySP = new SP.ClientSchemaVersions();, if currentVersion is a static variable of class SP.ClientSchemaVersions, it will not appear on SP.ClientSchemaVersions.prototype. I guess, SP.ClientSchemaVersions.currentVersion = "1.2.3" is in their js code. So, currentVersions is not on the prototype chains of mySP.

Liferay - Hook for GroupWrapper

I'm trying to override the getDescriptiveName() method in com.liferay.portal.model.Group
I found a wrapper (com.liferay.portal.model.GroupWrapper), so I tried to write a hook as written in the documentation :
liferay-hook.xml:
<service>
<service-type>com.liferay.portal.model.GroupWrapper</service-type>
<service-impl>fr.villedeniort.hook.expando.GroupWrapperImpl</service-impl>
</service>
fr.villedeniort.hook.expando.GroupWrapperImpl.java:
public class GroupWrapperImpl extends GroupWrapper {
public GroupWrapperImpl(Group group) {
super(group);
}
#Override
public java.lang.String getDescriptiveName()
throws com.liferay.portal.kernel.exception.PortalException,
com.liferay.portal.kernel.exception.SystemException {
return super.getDescriptiveName();
}
When the hook is deployed, it raises an exception :
java.lang.NoSuchMethodException: fr.villedeniort.hook.expando.GroupWrapperImpl.<init>(com.liferay.portal.model.GroupWrapper)
I browse the code I found out that it breaks at this part for a reason I ignore:
Constructor<?> serviceImplConstructor = serviceImplClass.getConstructor(new Class<?>[] {serviceTypeClass});
At this point, variables have theses values:
serviceType "com.liferay.portal.model.GroupWrapper" (id=14829)
serviceImpl "fr.villedeniort.hook.expando.GroupWrapperImpl" (id=14830)
serviceTypeClass Class<T> (com.liferay.portal.model.GroupWrapper) (id=14831)
serviceImplClass Class<T> (fr.villedeniort.hook.expando.GroupWrapperImpl) (id=14832)
Do you have any idea?
Thanks!
You should have also a constructor without any argument. Now you have one with constuctor arguments, but there is no pure class constructor that java searches when it makes class instance. After calling the pure constructor java then calls the argumented one.
I had similar case in some other context and this was the solution. <init> tag on the error message refers on this kind of issue.
Apparently, it's not possible to hook other classes than Services, so I had to find a different way. For my case, I hooked a JSP and wrote my own method to get the right descriptive name from the hook.

Resources