I've two web content structures (foo and bar) in Liferay 7.0 and I want to store the web contents inside webcontents folders (webcontents/foo and webcontents/bar).
I added two asset publishers, one for each structure, and I also allow the user to create new webcontents through the asset publisher plus '+' icon. However, they are created in the web content root folder (webcontents/). There is any way to dynamicaly save the webcontent that are created through the '+' icon in the asset publisher to a specific folder (based on the template itself, tags, or any other field)?
I used a "ModelListener" for this exact scenario. https://dev.liferay.com/de/develop/tutorials/-/knowledge_base/7-0/model-listeners
If you extend Liferays BaseModelListener you can use the onBeforeCreate() Method for example.
First check the ddmStructure of the current journalArticle and get or create your Folder. Now set the Folder ID for your journalArticle and your done!
I don't think that this can be achieved without customization.
I'd create a service wrapper to determine the Folder e.g. by the Structure's name.
Posting the code as solution suggested by #Viergelenker
public class ArticleSetListenerPortlet extends BaseModelListener<JournalArticle> {
private static final Log LOGGER = LogFactoryUtil.getLog(ArticleSetListenerPortlet.class);
#Override
public void onBeforeCreate(JournalArticle model) throws ModelListenerException {
String structureName = model.getDDMStructure().getName(Locale.US);
long groupId = xxxxx;
List<JournalFolder> journalFolders = JournalFolderLocalServiceUtil.getFolders(groupId);
for(JournalFolder folder : journalFolders) {
if("Foo".equals(folder.getName())) {
model.setFolderId(folder.getFolderId());
LOGGER.info("Set folder as Foo");
}
}
super.onBeforeCreate(model);
}
Related
After I added versioning to a custom class (see https://devnet.kentico.com/articles/module-development-versioning-recycle-bin), the duplicated object is created (instead of just updating the existing one) when rollback feature is used.
Since I was asked for the exact steps that I took, here they are:
I have a custom class which is called StoreInfo. It contains a set of fields as Name, Address, Country, etc.
For enabling versioning, I added the following changes to the generated code:
public class StoreInfo : AbstractInfo<IndustryInfo>
{
...
public static ObjectTypeInfo TYPEINFO = new ObjectTypeInfo(...)
{
...
SupportsVersioning = true
};
...
protected override bool VersioningEnabled
{
get
{
return SettingsKeyInfoProvider.GetBoolValue("CMSEnableObjectsVersioning");
}
}
}
After this change was applied the Versions tab appeared in the UI interface:
Then I changed the name of the store from Test name to Test name 1, so Version 1.1 was added to the versions list:
The issue happened when I clicked this button:
Instead of updating the name of the existing Store back from Store name 1 to Store name it created a new store with the data of Version 1.0:
Any thoughts about why this happens would be helpful.
Kentico version is 11. (dev. approach - portal engine)
If you deleted Object 1 and then created a "new" Object 1 and tried to roll back the deletion of the original Object 1, it will create a new one because they don't have the same attributes, especially the GUID.
So you may need to specify exactly the steps you took when you deleted the object, what you did after you deleted the object and what you did when you rolled back the original object.
I've ServiceStack (V5.1.0) as a Windows Service, serving REST APIs, no problems. I would like to create a plugin that will serve static files from a specific physical directory, for any routes that start with /GUI.
I've read "Q: ServiceStack Razor with Multiple SPAs" here ServiceStack Razor with Multiple SPAs
But this seems to only handle individual files like index.html., and I need to serve not just files in the root of the physical path, but files in the subdirs of the physical path as well. For example, the route /GUI/css/site.css should serve the site.css file found in the css subdirectory below the root.
I looked at "Mapping static file directories in ServiceStack" here
https://forums.servicestack.net/t/mapping-static-file-directories-in-servicestack/3377/1
and based on this, tried overriding GetVirtualFileSources
public class AppHost : AppSelfHostBase {
...
// override GetVirtualFileSources to support multiple FileSystemMapping.
// Allow plugins to add their own FileSystemMapping
public override List<IVirtualPathProvider> GetVirtualFileSources()
{
var existingProviders = base.GetVirtualFileSources();
// Hardcoded now, will use a IoC collection populated by plugins in the future. Paths will be either absolute, or relative to the location at which the Program assembly is located.
existingProviders.Add(new FileSystemMapping("GUI",#"C:\Obfuscated\netstandard2.0\blazor"));
return existingProviders;
}
....
}
and using a FallBackRoute in the plugins' model,
[FallbackRoute("/GUI/{PathInfo*}")]
public class FallbackForUnmatchedGUIRoutes : IReturn<IHttpResult>
{
public string PathInfo { get; set; }
}
But I can't figure out how to get the interface method to change the PathInfo into an object that implements IVirtualFile.
public HttpResult Get(FallbackForUnmatchedGUIRoutes request)
{
// If no file is requested, default to "index.html"" file name
var cleanPathInfo = request.PathInfo ?? "index.html";
// Somehow, need to convert the cleanPathInfo into an IVirtualFile, that specifies the correct VirtualPathProvider (indexed by "GUI"")
// insert here the magic code to convert cleanPathInfo into an object that implements IVirtualFile
// var cleanVirtualPathInfo = cleanPathInfo as IVirtualFile
// to make use of ServiceStack enhanced functionality, wrap the cleanVirtualPathInfo in a HttpResult,
HttpResult httpresult = new HttpResult(cleanPathInfo,false); // this doesn't compile, because no overload with 2 parameters takes a string as the first parameter, but there is an overload that will take an IVirtualFile object
return httpresult;
}
Any suggestions to make the interface code return the correct files? Or a better way to allow for multiple plugins, each to support a different SPA, based on the first portion of the route? Hints, links, explicit instructions - any and all are welcome!
You just need to register a Virtual File System, you don't need to create your own Service as ServiceStack's static file handler will automatically return the first file it finds in the list of registered Virtual File Sources.
If you want to be able to register File Mappings in a Plugin you can add it to the AppHost's AddVirtualFileSources list in the plugin constructor, e.g:
public class GuiPlugin : IPlugin, IPreInitPlugin
{
public void Configure(IAppHost appHost)
{
appHost.AddVirtualFileSources.Add(
new FileSystemMapping("GUI", appHost.MapProjectPath("~/blazor")));
}
public void Register(IAppHost appHost) {}
}
The appHost.MapProjectPath() lets you resolve a physical file from your AppHost's projects Content Path. Then you can register the Plugin in your AppHost with:
public override void Configure(Container container)
{
Plugins.Add(new GuiPlugin());
}
Where your files in /blazor should now be resolvable from your registered path mapping, e.g:
/GUI/file.html -> C:\project\path\file.html
Note you don't need any Services and ServiceStack will automatically return the static files of registered file mappings, so you'll want to remove any [FallbackRoute] you've added.
I have a C# class that does an address lookup. I want to expose this as an Azure function. I've been going through the documentation but can't see how I can/if it's possible to do the following:
I have a Git repository in Team Services that contains a class library of my AddressLookup. Can my Function reference this project?
If I look at the folder structure of the site I can see it has copied over all the source files from the Git repository, can I get it to build the solution or does it literally just pull all the files?
Where in the solution do I put the function? Do I create a solution folder of the name of the function and place the relevant files in there?
My AddressLookup class returns an object that is defined in the class library. Will the function be able to use and return this?
Thanks
Alex
Follow-up to Q1: Are you trying to setup CI? For continuous integration with Azure Functions, you may reference the following:
https://azure.microsoft.com/en-us/documentation/articles/functions-continuous-deployment/#setting-up-continuous-deployment
http://flgmwt.github.io/azure/azure-functions/c-sharp/2016/04/04/azure-fns-with-ci.html
--Update 10/17--
Specific to Team Services, here are the steps:
Make sure that your VSTS account is linked to your Azure Subscription. Follow the instructions in this article.
Navigate to the Functions Portal for your Function App and click on Function app Settings -> Configure continuous integration.
In the Deployements blade, click on Setup and configure your Deployment source information (see sample snapshot below). Click on the OK button. Wait for the sync to succeed. Close the Deployments blade.
Give it a minute and refresh your Functions Portal session. You should now see the function added to your Function site. The snapshot below is my AddressLookup function that was synced from my Team Services project named MyFirstProject.
Note the disclaimer message above the Code editor. If you hook up CI for your Function, you will not be able to edit it in the Functions Portal. Since this particular example requires a request body, you will need to test it using Postman.
--End of update 10/17--
Answer to Q2:
Here's a good documentation describing the folder structure of Azure Functions:
https://azure.microsoft.com/en-us/documentation/articles/functions-reference/
I also recommend the follow-up documentation specific to C# development for Azure Functions:
https://azure.microsoft.com/en-us/documentation/articles/functions-reference-csharp/
Answer to Q3 & Q4: I will attempt to answer these by providing a sample implementation. I don't have any context on the implementation of your AddressLookup library, however, in the interest of providing an example, I am going to take a wild leap and assume that it is a library that will perform some Geocoding operations. Assuming again that you want to use this library in an HTTP-triggered Function, you may begin by first generating the AddressLookup.dll and then uploading it to the bin folder inside your Function. You may then reference that DLL from your Function script.
For instance, using this article as a reference, I generated a AddressLookup.dll library in Visual Studio that has the following implementation. This DLL will serve as a proxy for your AddressLookup library so that I can demonstrate how we can use it in a Function.
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Xml.Linq;
namespace AddressLookup
{
public class GeoLocation
{
public double Longitude { get; set; }
public double Latitude { get; set; }
}
public class GeoCoder
{
private const string geoCodeLookupUrlPattern =
"https://maps.googleapis.com/maps/api/geocode/xml?address={0}&key={1}";
private const string addressLookupUrlPattern =
"https://maps.googleapis.com/maps/api/geocode/xml?latlng={0},{1}&key={2}";
private string _apiKey = null;
public GeoCoder(string apiKey)
{
if (string.IsNullOrEmpty(apiKey))
{
throw new ArgumentNullException("apiKey");
}
_apiKey = apiKey;
}
public GeoLocation GetGeoLocation(string address)
{
GeoLocation loc = null;
string encodedAddress = HttpUtility.UrlEncode(address);
string url = string.Format(geoCodeLookupUrlPattern, encodedAddress, _apiKey);
WebRequest request = WebRequest.Create(url);
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
if (stream != null)
{
XDocument document = XDocument.Load(new StreamReader(stream));
XElement longitudeElement = document.Descendants("lng").FirstOrDefault();
XElement latitudeElement = document.Descendants("lat").FirstOrDefault();
if (longitudeElement != null && latitudeElement != null)
{
loc = new GeoLocation
{
Longitude = Double.Parse(longitudeElement.Value, CultureInfo.InvariantCulture),
Latitude = Double.Parse(latitudeElement.Value, CultureInfo.InvariantCulture)
};
}
}
}
}
return loc;
}
public string GetAddress(GeoLocation loc)
{
string address = null;
string url = string.Format(addressLookupUrlPattern, loc.Latitude, loc.Longitude, _apiKey);
WebRequest request = WebRequest.Create(url);
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
if (stream != null)
{
XDocument document = XDocument.Load(new StreamReader(stream));
XElement element = document.Descendants("formatted_address").FirstOrDefault();
if (element != null)
{
address = element.Value;
}
}
}
}
return address;
}
}
}
Now, let's create an HTTP-Triggered Function by performing the following steps:
Go to the Functions Portal. Create a Function using the HTTP
Trigger - C# template.
Fill in the name (e.g., AddressLookup) and authorization level (e.g., Anonymous). You should now see a Function named AddressLookup created with some pre-populated code.
On the left pane, click on the Function app settings button.
Optional: Click on Configure app Settings. Under the "App settings" section, add a value for the key GoogleMapsAPIKey with your api key, then click on the Save button. Note: If you skip this step, then you will need to hard-code the key in your function code later.
Next, use the Kudu console to upload your DLL. Click on the Go to Kudu button. This will launch a new browser window
with a cmd console. Type the following to navigate to your
Function directory,
cd site\wwwroot\AddressLookup
Create a bin folder by typing mkdir bin at the command prompt as follows,
Double-click on the bin folder and upload (see "Add files") the AddressLookup.dll into the folder. When you are done, you should a similar snapshot below,
Go back to the Functions Portal. In your Function's editor, at the bottom of the Code section, click on View Files. You should now see the newly created bin folder as follows,
Replace the contents of the pre-populated Function script with the following code
#r "AddressLookup.dll"
using System;
using AddressLookup;
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info($"C# HTTP trigger function processed a request. RequestUri={req.RequestUri}");
// Reading environment variable from App Settings, replace with hardcoded value if not using App settings
string apiKey = System.Environment.GetEnvironmentVariable("GoogleMapsAPIKey", EnvironmentVariableTarget.Process);
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
string address = data?.address;
string name = data?.name;
GeoCoder geoCoder = new GeoCoder(apiKey);
GeoLocation loc = geoCoder.GetGeoLocation(address);
string formattedAddress = geoCoder.GetAddress(loc);
HttpResponseMessage message = null;
if (name == null)
{
message = req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name in the request body");
}
else
{
var msg = $"Hello {name}. Lon: '{loc.Longitude}', Lat: '{loc.Latitude}', Formatted address: '{formattedAddress}'";
message = req.CreateResponse(HttpStatusCode.OK, msg);
}
return message;
}
Click on the Save button.
In the "Run" section, supply the following request body,
{
"name": "Azure",
"address": "One Microsoft Way Redmond WA 98052"
}
Click on the Run button.
You should see some log entries similar to the following,
2016-10-15T03:54:31.538 C# HTTP trigger function processed a request. RequestUri=https://myfunction.azurewebsites.net/api/addresslookup
2016-10-15T03:54:31.773 Function completed (Success, Id=e4308c0f-a615-4d43-8b16-3a6afc017f73)
and the following HTTP response message,
"Hello Azure. Lon: '-122.1283833', Lat: '47.6393225', Formatted address: '1 Microsoft Way, Redmond, WA 98052, USA'"
Since this is a HTTP-triggered Function, you may also test your Function using Postman. See snapshot below,
If you upload your own DLL in step 5 and edit the Function code to call your library, the Function should work just as well.
Is the assembly you want to reference frozen, or do you want to see updates? If you don't want to see updates, see the answer by Ling Toh.
But if you want to see updates when the assembly updates:
Link your function to a some form of continuous delivery. The official documentation explains how to do this. At the moment, you seem to need a separate git repository, VSTS project (or whatever) to do this easily. (It is possible to edit the deployment process in Kudu, but I would avoid that if you possibly can).
The functions project should contain only the functions code themselves. So it should contain something like the followling:
global.json
host.json
packages.config
Web.config
function1/
function1/run.csx
function1/project.json
function1/function.json
Where you should replace function1 by the name of your function.
Once you've configured this to push to your functions host, you're most of the way to where you want to be. Next, add a function1/run subdirectory where you place yourAssembly.dll. This should be automatically copied here on a successful build of the assembly project. I don't have experience of VSTS to know exactly the best way to do this, so you might need to ask another question.
You've now placed the assembly in the right place. You now refer to it by adding the assembly reference line to the top of your run.csx:
#r "yourAssembly.dll"
Note that all of the references have to be before everything else at the top of the run.csx. So you can put it after other references, but it has to be before anythign else, including load specifications.
Note that for custom assemblies you include the .dll and quote the file, whereas for framework assemblies that aren't referenced by default, you don't
In an ideal world, this will be enough.
But functions doesn't trigger a rebuild if the assembly is updated at the moment, only if project.json, run.csx or function.json is updated. So as part of this process, I then look at the end of the file and add or remove a blank line. It needs to be meaningless (so that you're not changing something important), but enough for your version control tool to think that the file has changed. Clearly, this step is not necessary if you've also made other changes to the file.
If you're using git, you'd now commit and push the changes up. Functions will see that function has changed, and recompile. You should see this in the log pane in the function itself.
Note that if you have two functions using the same dependent assembly, you need to copy it into both function folders; this can't be shared.
I am implementing Liferay 6.2 AuthVerifier. I developed it but it does not get called that is, TestAuthVerifier.verify() method.
I referred https://docs.liferay.com/portal/6.2/propertiesdoc/portal.properties.html the link to develop tthe est AuthVerifer. Here is what I do below
I make entries in portal-ext.properties file as below and develop the class further.
auth.verifier.pipeline=com.test.TestAuthVerifier
auth.verifier.TestAuthVerifier.version.supported=1.0
my code is as below just for reference.
package comt.test;
import com.liferay.portal.security.auth.*;
public class TestAuthVerifier implements AuthVerifier {
#Override
public String getAuthType() {
return PhAuthVerifier.class.getSimpleName();
}
#Override
public AuthVerifierResult verify(
AccessControlContext accessControlContext, Properties properties)
throws AuthException {
System.out.println("MyAuthVerifier.verify() invoked..")
try {
.....
return authVerifierResult;
} catch (AutoLoginException e) {
throw new AuthException(e);
}
}
On debugging from Liferay 6.2.3 source code I see the point when
the flow is broken is AuthVerifierPipeline._mergeAuthVerifierConfiguration() method.
the statement : Map settings = accessControlContext.getSettings(); returns zero size map.
Finally the actual place where the Verifier is called : AuthVerifierPipeline._verifyRequest() does not run as List authVerifierConfigurations is ZERO size.
I looked in AccessControlContext class and other classes, I could not see any setter method to set _settings or any references which set this var.
any help around this is much appreciated.
note : I verified that LifeRay does recognize my TestAuthVerifier impl.
to make it work you have to work with the hook plugin. First create a file liferay-hook.xml in WEB-INF folder to override the portal.properties
<?xml version="1.0"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 6.2.0//EN" "http://www.liferay.com/dtd/liferay-hook_6_2_0.dtd">
<hook>
<portal-properties>portal.properties</portal-properties>
</hook>
In the src folder put the file portal.properties or if you are using maven in the resource folder with the properties of the AuthVerifier in your case
auth.verifier.pipeline=com.test.TestAuthVerifier
auth.verifier.TestAuthVerifier.version.supported=1.0
This is a link of a sample in liferay git for more detail sample-authverifier-hook
ReSharper 8.X ships with a macro that fetches the "Containing Type Name", but what I want to do is manipulate that name. I'm using this in a Visual Studio 2013 Web API project, and I want a template that takes the class name and builds the URL that has to be called. So, for example, suppose I have this:
public class AnnouncementController : ApiController
{
//Want to put a template here!
[HttpGet]
public HttpResponseMessage GetActiveAnnouncements()
{
/// ...
}
}
now my ReSharper template will look something like this:
/// This sample shows how to call the <see cref="$METHOD$"/> method of controller $CLASS$ using the Web API.
/// https://myurl.mydomain.com/api/$CONTROLLER$/$METHOD$
$Controller$, by convention, is the class name minus the letters 'Controller'. This is because ASP.NET MVC Web API projects expect classes derived from ApiController to end with the string 'Controller',
Since this class is AnnouncementController, the template should output
https://myurl.mydomain.com/api/Announcement/GetActiveAnnouncements
Resharper's Built-In Macros can give me some of what I need, but I want to write a custom macro that fetches the containing type name and chops "Controller" off of it. I would like to do that directly, without storing the containing type name in another parameter.
Also, how do I install this custom macro? I've Googled around, and all I found was a lot of dead links and old walkthroughs written for ReSharper version 7 and below that do NOT work with ReSharper 8.x
After a lot of fighting, here is my solution.
[MacroImplementation(Definition = typeof (ControllerNameMacroDefinition))]
public class ControllerNameMacroImplementation : SimpleMacroImplementation
{
public ControllerNameMacroImplementation([Optional] IReadOnlyCollection<IMacroParameterValueNew> arguments)
{
}
public override HotspotItems GetLookupItems(IHotspotContext context)
{
var ret = "CONTROLLER";
var fileName = GetFileName(context);
if (!fileName.IsNullOrEmpty())
{
//Replace "Controller.cs" in two separate steps in case the extension is absent
ret = fileName.Replace("Controller", "").Replace(".cs", "");
}
return MacroUtil.SimpleEvaluateResult(ret);
}
/// <summary>
/// Returns the filename of the current hotspot context
/// </summary>
private string GetFileName(IHotspotContext context)
{
var psiSourceFile = context.ExpressionRange.Document.GetPsiSourceFile(context.SessionContext.Solution);
return psiSourceFile == null ? string.Empty : psiSourceFile.Name;
}
}
I wanted to do exactly this, but for JavaScript Jasmine tests -- SomethingViewModel.js, with a fixture of SomethingViewModelFixture.js, but wanted to be able to refer to SomethingViewModel in the file. A few slight modifications to the above made it possible.
Unfortunately, there's a ton more things you need to do in order to get your plugin to actually install. Here's a list. I hope it's comprehensive.
NuGet package install JetBrains.ReSharper.SDK, make sure you have the correct version installed!
Copy your Class Library DLL to C:\Users\<you>\AppData\Local\JetBrains\ReSharper\<version>\plugins\<your plugin name>, creating the plugins directory if needed.
You need the plugin Annotations in your AssemblyInfo.cs file:
[assembly: PluginTitle("Your extensions for ReSharper")]
[assembly: PluginDescription("Some description")] -- this is displayed in ReSharper->Options->Plugins
[assembly: PluginVendor("You")]
You need a class in your project that defines the MacroDefinition, as well as the above MacroImplementation
[MacroDefinition("MyNamespace.MyClassName", ShortDescription = "A short description of what it does.", LongDescription = "A long description of what it does.")]
"ShortDescription" - this is displayed in the "Choose Macro" dialog list.
"LongDescription" you'd think this would be in the "Choose Macro" description, but it isn't.
I just added this annotation to the above file.
The file you add the MacroDefinition to needs to implement IMacroDefinition, which has a method (GetPlaceholder) and a property (Parameters) on it. The former can return any string ("a") and the latter can return an empty array.
You can ignore the WiX/NuGet stuff if you want. Just for a local install.
In VS, the ReSharper->Options->Plugins section has some troubleshooting details on why your plugin might not be loading.
Good luck!