I'm trying to write a Liferay portlet that sends a file upload over AJAX to a JSONWS endpoint, but I keep getting the following errors in the logfile:
DEBUG [http-bio-8080-exec-70][JSONWebServiceActionsManagerImpl:121] Request JSON web service action with path /document/upload-file and method POST for //document-portlet
DEBUG [http-bio-8080-exec-70][JSONWebServiceActionsManagerImpl:455] Found 1 JSON web service actions with path /document-portlet/document/upload-file in for //document-portlet
DEBUG [http-bio-8080-exec-70][JSONWebServiceActionsManagerImpl:501] Unable to match parameters to a JSON web service action with path /document-portlet/document/upload-file for //document-portlet
ERROR [http-bio-8080-exec-70][JSONWebServiceServiceAction:97] No JSON web service action associated with path /document/upload-file and method POST for //document-portlet
Following a couple of tutorials, my JSONWS implementation looks like this:
class DocumentServiceImpl extends DocumentServiceBaseImpl {
#JSONWebService(method="POST")
public String uploadFile(File content) {
// ...
}
}
And here's my client-side code:
<aui:form name="files" enctype="multipart/form-data">
<aui:input type="file" name="content" label="File" />
<aui:button type="submit" name="btnUploadFile" value="Upload file" />
</aui:form>
<script>
AUI().use(
'aui-io-request',
function(A) {
var btnUploadFile = A.one("#<portlet:namespace />btnUploadFile");
btnUploadFile.on("click",
function(event) {
event.preventDefault();
var myForm = A.one("#<portlet:namespace/>files");
var ajaxURL = "http://localhost:8080/api/jsonws/document-portlet.document/upload-file";
var configs = {
method : 'POST',
form : {
id : myForm,
upload : true
},
sync : true,
on : {
complete : function() {
// ...
}
}
};
A.io.request(ajaxURL, configs);
}
)
}
);
</script>
It looks to me like Liferay discovers the uploadFile method, but it can't match the signature of the request to the parameters of the method. How do I match parameters for a multipart/form-data request? Or am I going about this the wrong way?
Related
I am having an ASP.NET Core Razor application, where I am using the JavaScript AppInsights component for the ckient-side (configured in _Layout.cshtml) and the nuget package for the server-side.
Unfortunately, I am not able to correlate page views on the client-side to requests on the server-side. Also, the application map is not getting drawn correctly, no matter what I try. As you can see, they are "disconnected".
I have tried the following settings on the front end without luck. Any idea?
disableFetchTracking: false,
enableCorsCorrelation: true,
enableRequestHeaderTracking: true,
enableResponseHeaderTracking: true,
I figured it out. The documentation only lists XMLHttpRequest calls under the auto-collected dependency items for JavaScript.
So that implies I have to change views like this
// MySite.cshtml
#page
#model ...
#{
ViewData["Title"] = "My Site";
}
<h1>#ViewData["Title"]</h1>
<form method="post" class="mt-1">
<button class="btn btn-primary">Do something</button>
<input type="hidden" name="id" value="doSomething" />
</form>
// MySite.cshtml.cs
public class MySiteModel : PageModel
{
// ...
public void OnPost(string id)
{
// ...
}
}
To views that make use of AJAX, e.g like so
// MySite.cshtml
#page
#model ...
#{
ViewData["Title"] = "Exceptions";
}
<h1>#ViewData["Title"]</h1>
<button class="btn btn-primary" id="doSomething">Do Something</button>
#section scripts
{
<script>
$(document).click(e => {
var id = e.target.id;
if (id == "doSomething") {
$.ajax({
url: '?handler=DoSomething
});
}
});
</script>
}
// MySite.cshtml.cs
public MySiteModel : PageModel
{
...
public void OnGetDoSomething()
{
...
}
}
And now everything looks as it should
I use ASP.NET MVC 5 and SignalR 2 .
I have one chat page in one view,
and one chat page in another partialview,
i have different style for sent message and received message.
how to set this styles to messages that sent and received ?
all letters in internet is chat in one view.so not have different style.
i use this link : https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc
thanks :)
#{
ViewBag.Title = "Chat";
}
<h2>Chat</h2>
<div class="container">
<input type="text" id="message" />
<input type="button" id="sendmessage" value="Send" />
<input type="hidden" id="displayname" />
<ul id="discussion">
</ul>
</div>
#section scripts {
<!--Script references. -->
<!--The jQuery library is required and is referenced by default in _Layout.cshtml. -->
<!--Reference the SignalR library. -->
<script src="~/Scripts/jquery.signalR-2.1.0.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="~/signalr/hubs"></script>
<!--SignalR script to update the chat page and send messages.-->
<script>
$(function () {
// Reference the auto-generated proxy for the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call back to display messages.
chat.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '</li>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
// This optional function html-encodes messages for display in the page.
function htmlEncode(value) {
var encodedValue = $('<div />').text(value).html();
return encodedValue;
}
</script>
}
You need a flag according which you can different between messages from and to server. And you need also some css to mark different messages.
Sample:
chat.client.addNewMessageToPage = function (name, message, fromServer) {
// Add the message to the page.
if(fromServer){
$('#discussion').append('<li class="messageFromServer">strong>'+htmlEncode(name)+'</strong>: '+htmlEncode(message) + '</li>');
}
else{
$('#discussion').append('<li class="messageToServer"><strong>'+htmlEncode(name)+'</strong>: '+htmlEncode(message)+'</li>');
}
};
Sample css:
.messageFromServer{
color:purple
}
.messageToServer{
color:red
(You can test css stuff on fiddler:
https://jsfiddle.net/ugrf0jea/7/)
Your hub:
public class ChatHub : Hub
{
public void Send(string name, string message)
{
// Mark the message, that this is from other client. Only call methode "addNewMessageToPage" with this params on other clients(not on caller)
Clients.Others.addNewMessageToPage(name, message, false);
// Mark message that this is from current client. Only call method addNewMessageToPage" on caller cliebt
Clients.Caller.addNewMessageToPage(name, message, false);
}
}
Here's the JSP from where my Javascript function is being called:
JSP code
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="continueTour" onclick="showTutorial()">Take a Quick Tour</button>
Skip Tour
</div>
Here's the Javascript function from where I need to render another JSP, and hence need to get to the Render method in the controller. Notice the 'simulate' method that I'm calling to simulate the click of the hyperlink (!Not sure if this is right or not!):
Javascript Code showTutorial() method:
function showTutorial(){
launchTutorial();
}
function launchTutorial(){
var enjoyhint_instance = new EnjoyHint({
onEnd: function(){
AUI().use('liferay-portlet-url', function(A) {
var plid = Liferay.ThemeDisplay.getPlid();
var url=Liferay.PortletURL.createRenderURL();
/*url.setPortletId(plid);*/
url.setPortletName(Liferay.ThemeDisplay.getp)
url.setParameter('render','redirectToEmpInfo');
alert(url);
A.one(document.createElement('a')).attr('href',url).simulate('click');
});
}
});
var enjoyhint_script_steps = [
{
"next #newAuthorizationActive": 'To create an authorization form'
}
];
enjoyhint_instance.set(enjoyhint_script_steps);
enjoyhint_instance.run();
}
Here's the controller method which I've written to catch the render request from the Javascript.
Controller Method (Not getting to this method)
#RenderMapping(params = "render=redirectToEmpInfo")
protected ModelAndView redirectToEmpInfoForAuthTour(ModelMap map, RenderRequest renderRequest, RenderResponse response) {
LiferayPortal.logInfo(_log, "Inside the render method for Emp Info");
return null;
/*return new ModelAndView("emailsuccess", map);*/
}
You add this code in head of jsp:
<%# taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<%# taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme"%>
<liferay-theme:defineObjects/>
<portlet:defineObjects />
Also in you code:
var plid = Liferay.ThemeDisplay.getPlid();
var url=Liferay.PortletURL.createRenderURL();
/*url.setPortletId(plid);*/
url.setPortletName(Liferay.ThemeDisplay.getp)
url.setParameter('render','redirectToEmpInfo');
alert(url);
Replace, similar to this:
var plid = Liferay.ThemeDisplay.getPlid();
var url = Liferay.PortletURL.createRenderURL();
url.setPortletId('<%=themeDisplay.getPortletDisplay().getId() %>');
url.setParameter('render', 'redirectToEmpInfo');
alert(url);
I have a form like this:
<form method='post' action='next' onsubmit="return validateMyForm();" >
<input type="text" id="data" />
<input type="submit" />
<p id="result"></p>
and this code for interact with node.js:
socket = io.connect();
function vlidateMyForm(){
var data = $('data').val();
socket.emit('login',{data: data}); // check dataBase in server
return false;
}
socket.on('responseLogin',function(result){
if(result){ // if data is valid
// submit form
}
else{ // data invalid
$('#result').html('field is not valid')
}
});
I want to submit my form, when the result is true. What should I do to solve this problem?
Change socket.on('responseLogin',function(result){... to socket.on('login',function(result){... should fix your problem
You can use Jquery submit to submit the form :
$("form#formID").submit();
Your form must have action attribute like action='url_to_post_to' for this.
Or if you like to use AJAX so that you can process the data, you can do :
$.ajax({
type: "POST",
url: 'url_to_post_to',
data: $("#formID").serialize(),
success: function(data)
{
alert(data);
}
});
None of the answers I have found anywhere have worked. I am trying to extend the example in "Developing Backbone.js Applications" to upload files. Although the form has enctype="multipart/form-data," request.files is always undefined.
The form HTML is:
<form id="addBook" action="..." enctype="multipart/form-data">
<div>
<label for="coverImage">CoverImage: </label><input id="coverImage" name="coverImage" type="file" />
<label for="title">Title: </label><input id="title" type="text" />
<label for="author">Author: </label><input id="author" type="text" />
<label for="releaseDate">Release date: </label><input id="releaseDate" type="text" />
<label for="keywords">Keywords: </label><input id="keywords" type="text" />
<button id="add">Add</button>
</div>
</form>
The backbone that saves the new record is
addBook: function( e ) {
e.preventDefault();
var formData = {};
var reader = new FileReader();
$( '#addBook div' ).children( 'input' ).each( function( i, el ) {
if( $( el ).val() != '' )
{
if( el.id === 'keywords' ) {
formData[ el.id ] = [];
_.each( $( el ).val().split( ' ' ), function( keyword ) {
formData[ el.id ].push({ 'keyword': keyword });
});
} else if( el.id === 'releaseDate' ) {
formData[ el.id ] = $( '#releaseDate' ).datepicker( 'getDate' ).getTime();
} else {
formData[ el.id ] = $( el ).val();
}
}
});
console.log(formData);
this.collection.create( formData );
}
The Node being called.
//Insert a new book
app.post( '/api/books', function( request, response ) {
console.log(request.body);
console.log(request.files);
});
The value of coverimage send to node is correct, I just never get anything in request.files. I have a cool drag and drop I would like to use instead, but until I get this working I am stuck.
I tried the JQuery-file-upload, that got me nowhere.
If I had hair, I would be pulling it out right now.
I wouldn't be submitting the file as part of the model.save/collection.create(model).
What I've used is Plupload for a file upload manager, submitting a file to an upload handler. This upload handler either returns the path to the uploaded file, or fileId if a reference is stored in a database table.
From there I populate a property in my backbone model, then persist the model. You can have your model listenTo plupload, for an upload completed event or similar.
I'm also following the sample of the book "Developing Backbone.js Applications", I extended the functionality to upload images to a folder in the server and save the path in my model to show the correct images. It is working fine. I tried to use Plupload and other jquery plugins but I didn't like them. My sample is using ajax to upload images to the server and then using them. I read many posts referencing the use of iframes to have ajax functionality. The best approach for this I found is using the jquery.form.js to avoid postbacks and load the images in a nice way.
The running sample working fine with nodeJS:
https://github.com/albertomontellano/bookLibrarySampleNodeJS
I based my solution in the post of Mark Dawson:
http://markdawson.tumblr.com/post/18359176420/asynchronous-file-uploading-using-express-and-node-js
However, I had to correct a method of this post to make it work correctly:
app.post('/api/photos', function(req, res) {
var responseServerPath = 'images/' + req.files.userPhoto.name;
var serverPath = 'site/images/' + req.files.userPhoto.name;
console.log(req.files.userPhoto.path);
require('fs').rename(
req.files.userPhoto.path,
serverPath,
function(error) {
if(error) {
console.log(error);
res.send({
error: 'Ah crap! Something bad happened'
});
return;
}
res.send({
path: responseServerPath
});
}
);
});
I hope it helps.
Turned out I had to end up hiring someone to do it, because I can't find any examples online of anybody uploading a file through backbone, even without updating any database interaction. Everybody has the same basic advice about what tools to use to upload the files, but I can't for the life of me find ONE example of someone implementing it.
I am planning on making the code available to everybody, so they can see how it works.