Trouble disaplying custom section in Umbraco 7.6 - umbraco7

I am trying to build a new section in Umbraco 7.6.
I had this working the 'old' way that use the tree controller extending from BaseTree but it was very ugly.
I'm now trying to do it using TreeController. I have followed the tutorials by:
Kevin Giszewski (https://github.com/kgiszewski/LearnUmbraco7/blob/master/Chapter%2016%20-%20Custom%20Sections%2C%20Trees%20and%20Actions/01%20-%20Create%20a%20Section.md)
and another by Tim Geyssens (https://github.com/TimGeyssens/UmbracoAngularBackofficePages)
but all I'm getting is an empty section without the tree and just with the title:
The controllers are not even hit on debugging, no console errors, no 500 errors, all compiles fine too.
Here's my code:
trees.config:
<add initialize="true" sortOrder="0" alias="UmbracoBookshelfTree" application="UmbracoBookshelf" title="Umbraco Bookshelf" iconClosed="icon-folder"
iconOpen="icon-folder-open" type="UmbracoBookshelf.Controllers.UmbracoBookshelfTreeController, MyWebsite.Backoffice"/>
applications.config:
<add alias="UmbracoBookshelf" name="Umbraco Bookshelf" icon="icon-globe-inverted-america" sortOrder="5"/>
Section:
using umbraco.businesslogic;
using umbraco.interfaces;
namespace UmbracoBookshelf.Applications
{
[Application("UmbracoBookshelf", "Umbraco Bookshelf", "icon-globe-inverted-america", 5)]
public class UmbracoBookshelfApplication : IApplication
{
}
}
Tree controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web;
using umbraco;
using umbraco.BusinessLogic.Actions;
using Umbraco.Core;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Mvc;
using Umbraco.Web.Trees;
namespace UmbracoBookshelf.Controllers
{
[PluginController("UmbracoBookshelf")]
[Umbraco.Web.Trees.Tree("UmbracoBookshelf", "UmbracoBookshelfTree", "Umbraco Bookshelf", iconClosed: "icon-folder")]
public class UmbracoBookshelfTreeController : TreeController
{
protected override Umbraco.Web.Models.Trees.MenuItemCollection GetMenuForNode(string id, System.Net.Http.Formatting.FormDataCollection queryStrings)
{
var menu = new MenuItemCollection();
if (id == Constants.System.Root.ToInvariantString())
{
// root actions
menu.Items.Add<CreateChildEntity, ActionNew>(ui.Text("actions", ActionNew.Instance.Alias));
menu.Items.Add<RefreshNode, ActionRefresh>(ui.Text("actions", ActionRefresh.Instance.Alias), true);
return menu;
}
else
{
//menu.DefaultMenuAlias = ActionDelete.Instance.Alias;
menu.Items.Add<ActionDelete>(ui.Text("actions", ActionDelete.Instance.Alias));
}
return menu;
}
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
var nodes = new TreeNodeCollection();
nodes.Add(CreateTreeNode("123", "456", queryStrings, "Some name to be shown"));
nodes.Add(CreateTreeNode("789", "456", queryStrings, "Some other name to be shown"));
return nodes;
}
}
}
It's pretty simple, what could wrong here?

I see the difference between my example and yours is , MyWebsite.Backoffice in trees.config I suggest to remove it.
<add initialize="true" sortOrder="0" alias="UmbracoBookshelfTree" application="UmbracoBookshelf" title="Umbraco Bookshelf" iconClosed="icon-folder"
iconOpen="icon-folder-open" type="UmbracoBookshelf.Controllers.UmbracoBookshelfTreeController"/>
I was running it via VS Code and IIS Express.
Tip: I found that using the code I mentioned above automatically adds App_Code to the config key and became like the following:
<add initialize="true" sortOrder="0" alias="UmbracoBookshelfTree" application="UmbracoBookshelf" title="Umbraco Bookshelf" iconClosed="icon-folder"
iconOpen="icon-folder-open" type="UmbracoBookshelf.Controllers.UmbracoBookshelfTreeController, App_Code"/>

Related

WPF Designer Extensibility: how to get the current designer zoom value?

I created a simple custom control with design-time support like the one described here
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Controls;
using System.Windows.Media;
using System.ComponentModel;
namespace CustomControlLibrary
{
public class ButtonWithDesignTime : Button
{
public ButtonWithDesignTime()
{
// The GetIsInDesignMode check and the following design-time
// code are optional and shown only for demonstration.
if (DesignerProperties.GetIsInDesignMode(this))
{
Content = "Design mode active";
}
}
}
}
Now I need to know what is the zoom factor set at design time by Visual Studio, but I cannot figure out how.
Is there an event that I can listen to for finding out this? Or is there a property that could reveal this?
I'm not sure if this is the most elegant way of doing it, but this is what I came up with. This problem has been thwarting me for ages!
private Point GetScaleFactor()
{
var rootVisual = PresentationSource.FromVisual(this)?.RootVisual;
if (rootVisual != null)
{
var transformToRoot = TransformToAncestor(rootVisual);
if (transformToRoot is Transform t)
{
return new Point(t.Value.M11, t.Value.M22);
}
}
return new Point(1, 1);
}

Show tooltip on hover over text

I want to create extension that allows to show custom message when I hover over a text.
E.g. "test-text" should give tooltip "OK" instead of current "ITrackin..."
I tried to follow https://learn.microsoft.com/en-us/visualstudio/extensibility/walkthrough-displaying-quickinfo-tooltips?view=vs-2019
but people are stating that it is not working and it's quite long way of doing this.
I cannot find any more docs on this. I know how to display it in on-click window/get currently selected text.
The sample send by Lance Li-MSFT was really helpful, but in order to get this working I had to spend some time.
Important steps:
Import LineAsyncQuickInfoSourceProvider.cs and LineAsyncQuickInfoSource.cs
Add reference to System.ComponentModel.Composition by add reference dialog (right click on the project name)
Get missing references by installing them using NuGet Package Manager
To initialize MEF components, you’ll need to add a new Asset to source.extension.vsixmanifest.
<Assets>
...
<Asset Type = "Microsoft.VisualStudio.MefComponent" d:Source="Project" d:ProjectName="%CurrentProject%" Path="|%CurrentProject%|" />
</Assets>
LineAsyncQuickInfoSourceProvider.cs
It's just used to display quick info/tooltip.
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Utilities;
using System.ComponentModel.Composition;
namespace JSONExtension
{
[Export(typeof(IAsyncQuickInfoSourceProvider))]
[Name("Line Async Quick Info Provider")]
[ContentType("any")]
[Order]
internal sealed class LineAsyncQuickInfoSourceProvider : IAsyncQuickInfoSourceProvider
{
public IAsyncQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) //creates instance of LineAsyncQuickInfoSource for displaying Quick Info
{
return textBuffer.Properties.GetOrCreateSingletonProperty(() => new LineAsyncQuickInfoSource(textBuffer)); //this ensures only one instance per textbuffer is created
}
}
}
LineAsyncQuickInfoSource.cs
Here you can customize what you want to display.
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Language.StandardClassification;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Adornments;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace JSONExtension
{
internal sealed class LineAsyncQuickInfoSource : IAsyncQuickInfoSource
{
private ITextBuffer _textBuffer;
public LineAsyncQuickInfoSource(ITextBuffer textBuffer)
{
_textBuffer = textBuffer;
}
// This is called on a background thread.
public Task<QuickInfoItem> GetQuickInfoItemAsync(IAsyncQuickInfoSession session, CancellationToken cancellationToken)
{
var triggerPoint = session.GetTriggerPoint(_textBuffer.CurrentSnapshot);
if (triggerPoint != null)
{
var line = triggerPoint.Value.GetContainingLine();
var lineSpan = _textBuffer.CurrentSnapshot.CreateTrackingSpan(line.Extent, SpanTrackingMode.EdgeInclusive);
var text = triggerPoint.Value.GetContainingLine().GetText(); //get whole line of current cursor pos
ContainerElement dataElm = new ContainerElement(
ContainerElementStyle.Stacked,
new ClassifiedTextElement(
new ClassifiedTextRun(PredefinedClassificationTypeNames.Keyword, "MESSAGE TO EDIT: " + text.ToString())
));
return Task.FromResult(new QuickInfoItem(lineSpan, dataElm)); //add custom text from above to Quick Info
}
return Task.FromResult<QuickInfoItem>(null); //do not add anything to Quick Info
}
public void Dispose()
{
// This provider does not perform any cleanup.
}
}
}

Web Api OData Controller returns blank page no error

I'm developing a library of OData queries using Web Api and ODataController. When I go to run my api from a web browser it returns nothing. I don't get an error of any kind. I can debug in Visual Studio and see clearly that the method runs and successfully returns my results as an IQueryable<>. Somewhere under the hood it's discarding my data. Has anyone else seen or encountered this? I've included my code below for reference:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.OData;
using Epm.Core.Model;
using System.Web.Http.OData.Query;
using Epm.Data.Access;
using Epm.Service.Assemblers;
namespace Epm.Service.Web.Controllers.OData
{
public class SearchActionsController : ODataController
{
private readonly EpmEntities context = new EpmEntities();
[Queryable(AllowedQueryOptions=AllowedQueryOptions.All)]
public IQueryable<ActionStepDisplay> Get(int planId, int? factorId, bool? showArchived)
{
var user = this.GetCurrentUser();
var results = (from p in context.SearchActions(user.SessionId, planId, factorId, showArchived, 1, null)
select p).ToModel().ToArray();
return results.AsQueryable();
}
protected override void Dispose(bool disposing)
{
context.Dispose();
base.Dispose(disposing);
}
}
}
My configuration:
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Epm.Core.Model.ActionStep>("SearchActions");
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
You are returning ActionStepDisplay from your method, but in builder you specified ActionStep as the entity.
May be Not Acceptable (406) in the response header
Possibly the problem lies with the MediaFormatter, which gets called after the controller has finished. When a media type formatter encounters a reference loop (where object A reference B and B references A) then you need to tell the media type formatter how to handle that, so in the Json Media Type Formatter you do something like...
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
See Documentation Here
I'd recommend you use Fiddler to see what is actually going on. You say your not getting a response in the browser, so what HTTP code is returned? You can use Fiddler to find out...

LINQ not working on IEnumerable

I'm using Autofac (I've registered the base nuget package in a console app) and want to take a look at a list of registrations.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autofac;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
// First, create your application-level defaults using a standard
// ContainerBuilder, just as you are used to.
var builder = new ContainerBuilder();
var appContainer = builder.Build();
appContainer.ComponentRegistry.Registrations.Where(x => true);
}
}
}
The problem is the line
appContainer.ComponentRegistry.Registrations.Where(x => true);
Here intellisense is not giving me the Where linq method however it does compile as far as I can tell without any warnings, errors in messages.
I tried this further down
IEnumerable<string> list = new List<string>();
list.Where(x => true);
And intellisense is working correctly and giving me all the standard list methods.
I've tried this in a few different apps from scratch and I'm getting the same behaviour.
Any ideas as to whats going on?
EDIT:
The following works and shows correctly in intellisense
IEnumerable<IComponentRegistration> test = new List<IComponentRegistration>();
test.Where(x => true);
I'm using
<package id="Autofac" version="3.0.1" targetFramework="net45" /> from nuget.
and hovering over the ComponentRegistrations gives
and in this case scope is defined as
ILifetimeScope _scope;
However I get the same thing if i try directly off this
var builder = new ContainerBuilder();
var appContainer = builder.Build();
appContainer.ComponentRegistry.Registrations.Where(x => true);
Also IComponentRegistry is defined as (in Autofac)
public interface IComponentRegistry : IDisposable
{
...
IEnumerable<IComponentRegistration> Registrations { get; }
...
}
Comment copied to answer.
If I've understood you correctly and your problem is that intellisense isn't working on the line
appContainer.ComponentRegistry.Registrations.Where(x => true);
You should probably try and disable your addons and see if that takes care of it as it's working fine for me. Since you say you had someone else confirm it, maybe start with any addons that your installations have in common.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autofac;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
// First, create your application-level defaults using a standard
// ContainerBuilder, just as you are used to.
var builder = new ContainerBuilder();
var appContainer = builder.Build();
var registrations = appContainer.ComponentRegistry.Registrations.Where(x => x.Target.Equals("test"));
}
}
}
Try assigning the linq expression to a variable and see if it works

Renaming config files with Visual Studio setup project

My applications have a handful of config files, each one particular to the machine it's being deployed to (dev, production, etc.) I'm wanting to add them to the setup project, use radio buttons to select one and then have a script (or maybe custom action?) rename the selected files to connectionStrings.config, settings.config, etc.
Is this feasible/possible with a setup project?
To give you an idea, my configs might look like this:
DEV connectionStrings.config
PROD connectionStrings.config
After the user chooses DEV or PROD in the installer radiobutton UI, I would like the chosen config to be renamed to
connectionStrings.config
Considering it's a VS setup project, I have a feeling I'm asking for way too much and that I will get an interesting response as most setup project questions do :)
I created a setup project to set connection strings and i used the following which works perfectly for me.
Create a installer.cs file for the setup.
using System;
using System.Data.SqlClient;
using System.Xml;
using System.Configuration;
using System.Reflection;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using System.Configuration.Install;
namespace YOURNAMESPACE
{
[RunInstaller(true)]
public partial class installer : System.Configuration.Install.Installer
{
public installer()
{
InitializeComponent();
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
}
public override void Commit(IDictionary savedState)
{
base.Commit(savedState);
try
{
string DatabaseString1 = "FULL NAME OF CONNECTION STRING";
ConnectionConfigure(DatabaseString1);
}
catch (Exception e)
{
base.Rollback(savedState);
}
}
public override void Rollback(IDictionary savedState)
{
base.Rollback(savedState);
}
public override void Uninstall(IDictionary savedState)
{
base.Uninstall(savedState);
}
private void ConnectionConfigure(string DatabaseString)
{
string dataSource = "";
dataSource = "Provider="+ Context.Parameters["InitialCatalog"]+ ";" + "Data Source=" + Context.Parameters["DataSource"];
ExeConfigurationFileMap map = new ExeConfigurationFileMap();
string configFile = string.Concat(Assembly.GetExecutingAssembly().Location, ".config");
map.ExeConfigFilename = configFile;
System.Configuration.Configuration config = System.Configuration.ConfigurationManager.
OpenMappedExeConfiguration(map, System.Configuration.ConfigurationUserLevel.None);
string connectionsection = config.ConnectionStrings.ConnectionStrings
[DatabaseString].ConnectionString;
ConnectionStringSettings connectionstring = null;
if (connectionsection != null)
{
config.ConnectionStrings.ConnectionStrings.Remove(DatabaseString);
}
connectionstring = new ConnectionStringSettings(DatabaseString, dataSource);
config.ConnectionStrings.ConnectionStrings.Add(connectionstring);
config.Save(ConfigurationSaveMode.Modified, true);
ConfigurationManager.RefreshSection("connectionStrings");
}
}
}
Add project output to your setup project then begin to setup this setup project.
Right click setup project
Add text boxes and create the UI
Set custom actions
Create project output actions
Custom action properties
That is how i setup mine (I have attached screenshots to help explain my process but in short. Create setup project and installer.cs file. Add project output to setup project, add a UI so that the user can input a connection string and or provider for connection string, add custom actions so that the inputs can be read by the installer.cs file and then congratulations it should change the connection string.
Hope this helps.
I've come to the conclusion that this is impossible due to VS severely lacking on setup project configuration options. Instead I added a radio button control during the setup process and assigned the choices a variable name. In the file system I added all of my config files and then set conditions to each one. They referenced values to my radio button choices in order to copy during deployment.
I've done this many times, and basically I just install both files with different names. The application can ask the user which file to use, and change it anytime they want because many users don't know enough about the application to make this choice at install time.
You get interesting answers to setup questions like this because many people want to configure the application during the installation. Why not just let the setup install the files and have the app do its own configuration?

Resources