I have a story model that serves as a wrapper for many posts. Posts may have photo attachments, and I use remotipart and paperclip to handle the image uploading and processing.
This works perfectly fine when a user POSTS a new post. I want to allow post authors to edit their posts/uploaded images; and this is where the problem occurs.
When a user clicks an edit button I alter the form that I originally used when users create posts so that that same form can be used to edit the post. As part of that I change I modify the <form> tag from:
<form id="new_post_for_story_241" class="new_post" accept_charset="UTF-8" action="/stories/241/posts.json" data-remote="true" enctype="multipart/form-data" method="post" novalidate="novalidate" target="">
to
<form id="new_post_for_story_241" class="new_post" accept_charset="UTF-8" action="/stories/241/posts/287.json" data-remote="true" enctype="multipart/form-data" method="put" novalidate="novalidate" target="">
Again, this works fine to PUT edits if the user doesn't include an image. It breaks, however, if the form contains a new image. For whatever reason, if the user wants to upload a new image as part of the altered form, the form is submitted with a GET action. Triggering this:
Started GET "/stories/241/posts/287.json?post%5Bphoto%5D=champcourse15thgreen.jpg&post%5Bcontents%5D=another+pic%0D%0A&remotipart_submitted=true&X-Requested-With=IFrame&X-Http-Accept=text%2Fjavascript%2C+application%2Fjavascript%2C+application%2Fecmascript%2C+application%2Fx-ecmascript%2C+*%2F*%3B+q%3D0.01" for 127.0.0.1 at 2013-05-22 10:54:50 -0700
ActionController::RoutingError (No route matches [GET] "/stories/241/posts/287.json"):
Anyone know why this is happening, and more importantly, have a fix for it?
You likely have an old version of the remotipart dependency jquery-iframe-transport which has incorrect code for setting the form's method (it is hardcoded instead of copying the original form's method). It's been fixed since.
Related
I got an ASP NET Core RazorPage having a button which asynchronously replaces a part of the given HTML using an AJAX request.
Besides some text content it renders another button which is intended to post back the side when clicked. It is surrounded by a form element.
However, clicking the button I receive an HTTP 400 with the information "This page isn't working" (Chrome). Other browsers like Firefox return an HTTP 400 as well.
The relevant HTML with the button which has been created by the AJAX call is the one below:
<form method="post">
<button class="btnIcon" title="Todos" id="btnTodos" formaction="PersonManagement/Parts/MyPageName?handler=PerformTodos">Execute action</button>
</form>
As the url exists (I doublechecked it using the browser with a simple GET) I wonder whether the issue could be due to some security settings along with the browser or is there anything I am perhaps missing out here?
Thank you for any hint
Two things here first add this attribute to your form asp-antiforgery="true", then send it's value to the server in your AJAX post request.
jQuery magic starts here :)
token: $('[name=__RequestVerificationToken]').val(),
Antiforgery is ON by default since .net core 2.0 (as far as I remember), so if you do AJAX post you need to send the antiforgery token with each request.
Let us know if it helps. Spread knowledge don't hide it just for yourself :P
Finally I came across a very interesting article from Matthew Jones at https://exceptionnotfound.net/using-anti-forgery-tokens-in-asp-net-core-razor-pages/ about Anti-Forgery Tokens in Razor pages. Worth reading, indeed.
However, independently from that article what solved my issue was simply not to add the <form .. element at the client-side, but already at the server-side. As there is no need for me to explicitly adding it at the client-side, but only the button itself, this is a solution for me which works properly.
A brief summary of my scenario now:
There is a Razor Page containing usual cshtml content along with a <form method="post"..
Some anchor elements also are included, one is triggering a JQuery AJAX call to the server
The JQuery call comes back from the server with some additional HTML including the post button which which I add to the existing HTML.
The button is being rendered inside the now already existing
Clicking the button causes the page to post back in the wanted manner and executes the handler as intended.
Thanks again Stoyan for your input and help with that.
Copying one issue and its child issues is a natively built-in feature and thus works just fine.
But is there a way to do this multiple times?
Like re-creating one issue (including its children) twenty or fifty times?
Edit 2
This new functionality should be accessible via the Redmine interface and compatible to any browser.
It does not matter whether it is a completely new Plugin, an extension to the built-in copy feature, a call to a PHP-script or anything else.
Due to compatibility (networking, browsers etc.) I guess a completely server-side modification is the only way to go here.
What parts of the default plugin (as created in the voting tutorial) or a core element would have to be changed?
Where can I find the code for the native issue copy function?
Or - if all this is too complicated - how would I write my plugin to point to a PHP file that manipulates the SQL database directly?
Edit:
To clarify: just like the normal copy function (either in the context menu or the top-right link, I don't care) I want to copy one issue and its sub-issues n times.
To let the user set the amount n, any user number input may suffice, like a textbox, a pop-up etc.
I think the simplest way to do this is to start with redmine source modification.
Once it works you can move on and try to extract this feature into plugin.
Note, that I am not a ruby developer, so some things below are just my guesses. But I did few small redmine modifications like this before and hope that my thoughts can be useful.
It will also be easier if you familiar with some of MVC frameworks (for any language), because they mostly have a similar structure with routes, controllers, views and models.
The Idea
The link to copy single issue looks like this: //redmine.myserver.com/projects/myapp/issues/12407/copy.
My idea is to add a num_copies parameter to this link and use it in the code to create many copies.
You need no UI for that, once implemented the feature will work like this:
find the issue you need
choose the copy action for it
once the form opened, manually add ?num_copies=XX parameter into the URL (//redmine.myserver.com/projects/myapp/issues/12407/copy?num_copies=50) and press 'Enter' to reload the form
check the details and submit the form - it will create multiple copies according to the num_copies parameter
The Implementation Plan
Now, how to do this.
I am referring to the redmine mirror on github which looks fresh.
1) Find where the .../copy link is handled
When you open the form to copy the issue, you'll see form like this:
<form action="/projects/myapp/issues" class="new_issue" id="issue-form" method="post">
<input id="copy_from" name="copy_from" type="hidden" value="12407">
<div class="box tabular">
<div id="all_attributes">
...
</form>
Note the form action, it points to the /issues link and it will submit the copy_from parameter (this is ID of the issue we are copying).
2) Find the code which handles the form submission
We could first go and check through the config/routes.rb, but we can just guess that we need the controllers/issues_controller.rb
Search for the place where copy_from parameter is used.
You'll see the build_new_issue_from_params method.
Now search for its usages and you'll find this:
before_filter :build_new_issue_from_params, :only => [:new, :create]
From how it looks, I guess that it is called before both new and create actions.
Looking at new and create definitions, the new action renders the new issue form and the create action handles the form post.
3) Add the num_copies parameter to the form
Find the view file used by new issue action.
Here there is a template for the new issue form, try to add num_copies parameter similar to the copy_from:
<%= title l(:label_issue_new) %>
<%= call_hook(:view_issues_new_top, {:issue => #issue}) %>
...
<%= error_messages_for 'issue' %>
<%= hidden_field_tag 'copy_from', params[:copy_from] if params[:copy_from] %>
Here I am not 100% sure if it will just work if you add a similar line for `num_copies. You may also need to modify the route.
When done, you should have the new issue form like this:
<form action="/projects/myapp/issues" class="new_issue" id="issue-form" method="post">
<input id="copy_from" name="copy_from" type="hidden" value="12407">
<input id="copy_from" name="num_copies" type="hidden" value="50">
<div class="box tabular">
<div id="all_attributes">
...
</form>
4) Handle the num_copies parameter
It should be done in the create action:
def create
...
call_hook(:controller_issues_new_before_save, { :params => params, :issue => #issue })
#issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
if #issue.save
...
end
Here you already have the #issue variable created in the build_new_issue_from_params method and what you need to do is to check if num_copies parameter is set and if it is set then copy / save the #issue in a loop to create additional copies.
I can't provide the exact code snippet for this, but it should not be very complex.
Check this code in the bulk_update method, it looks like what you need:
issue = orig_issue.copy({},
:attachments => copy_attachments,
:subtasks => copy_subtasks,
:link => link_copy?(params[:link_copy])
)
I think this specific plugin is not high priority for Redmine community.
But, you can write very easy API calling for Java, Python or other language to do what you exactly want.
Here, you can see API documentation how to list, create, update issues.
Issue API documentation
PS: You can leave your request in redmine community,
maybe you are lucky https://redmine.org/projects/redmine/issues
I have searched everywhere for help on this, but the only answers I come up with are to the question of why an image won't show up at all. Here is my problem:
In my Rails CRM app, I created a scaffold for "Contacts." I purchased a template on ThemeForest that uses Bootstrap, and have copied all the CSS and javascript files into my Vendor directory. The template comes with a number of HTML files. I selected one and used it as my main Contacts List. I used the "datatables.net" gem to display the contact list. This particular HTML file contains a number of images, mainly used for buttons. All the images display correctly on this page.
On my Edit Contacts page, I copied and pasted all of the HTML from the Contact List page, and inserted a Form For partial in place of the contact list table to display editable fields for a particular contact. At first, all of the images on this page did not work. I did some reading about the asset pipeline, and discovered that the image source should be "asset/image_name.png" instead of "images/image_name.png". (I had copied all my images to the assets/images folder.) I was puzzled why the images on the main contact list page would work, but the Edit page would not. Regardless, I did a find and replace for "/images/" and replaced it with "/assets/". This fixed about 2/3 of the images on the Edit Contacts page. The rest, I am still getting the following error as an example on Rails Server:
Started GET "/contacts/assets/icons/quickstats/user.png" for 127.0.0.1 at 2012-08-15 23:40:42 -0700
ActionController::RoutingError (No route matches [GET] "/contacts/assets/icons/quickstats/user.png"):
This is the same error I got before I did the find/replace.
The problem is you are probably using "assets/blah.png" instead of "/assets/blah.png". This will work for root urls but not for anything deeper, eg. "/contacts/new".
Instead of typing the URL out, use the image_tag helper, which will take care of this stuff for you.
The problem is that i have a remote form that, based on condition, id like to convert to a non-remote form (using UJS), and then submit.
note the form has a file upload.
Here's the details: I have initially rendered the remote form using
= form_for #myobj, :url => {:action=>"remoteAction", :controller=>"myobjects"}, :remote => true do |f|
... (f.fields....)
which produces the HTML:
<form id="new_myobj" class="new_myobj" method="post" accept-charset="UTF-8" data-remote="true" action="/remoteAction">
when i click submit, as expected, the form is submitted 'AS JS'.
in the controller action, i am doing some validation of the fields inside the submitted form.
If all the validations pass, i execute the following .js.haml template:
$('form#new_myobj').removeAttr("data-remote");
$('form#new_myobj').attr('enctype', 'multipart/form-data');
$('form#new_myobj').attr('action', '/myobjects/regularAction');
which successfully changes the HTML on the page (witnessed via Firebug) to:
<form id="new_myobj" class="new_myobj" method="post" accept-charset="UTF-8" enctype="multipart/form-data" action="/myobjects/regularAction">
since the form contains an f.file_field, i have to submit as multipart so the image can be uploaded, and i cannot submit 'AS JS'
now, when i click submit, the controller action 'regularAction' is indeed called, but its still 'AS JS'
the question is, what else do i need to change in the HTML so the form can be submitted non-xhr? is it related to the headers?
jQuery is a bit tricky with the data attributes since it both
reads the HTML5 data tags as well as its own storage bound to the
DOM element, that is also called data. When writing to an attribute
that value gets copied into jQuerys own data storage (presumably
when data("remote") is being called).
However, this only happens
if jQuery’s data is empty for that name. Thus setting the attribute will only work once, after that the "cached" value is being used
even if the attribute changes. In order to really get rid of the
value, we need to remove the attribute and jQuerys own storage
method in that order. The reason is that there’s a high-level
(element.removeData(…)) function and a low level one (jQuery.
removeData(element, …)). The former re-reads the HTML5 data
attribute and stores it in jQuery’s own storage. Using the rather
unusual low level function obviously works as well.
Also, we do really need to remove the attribute -- setting it to
false is not enough since Rails only checks if form.data('remote')
is not undefined (look for it in jquery_ujs.js).
TL;DR:
attr("data-remote") != data("remote")
These two lines make a form non-remote (again). Order matters.
$("form").removeAttr("data-remote");
$("form").removeData("remote");
It’s documented, if you actually know what you’re looking for:
http://api.jquery.com/jQuery.data/ (low level function)
http://blog.madebydna.com/all/code/2011/12/05/ajax-in-rails-3.html
StackOverflow doesn’t allow me to post more than two links, but you can guess the removeData one. The high-level functions are linked from the low level ones.
Avoiding the token authenticity error in Rails 4+:
As Stan commented below, just doing the above will fail with an InvalidAuthenticityToken error. The workaround is easy though, see here for details: https://stackoverflow.com/a/19858504/1684530
The problem is that your approach to disable the Ajax submission isn't quite correct. You need to unbind the JavaScript events that have already been added by rails.js (Rails UJS adapter) to the form.
You can do that by:
$('form#new_myobj').unbind() to unbind all events attached to the form. You also need to $('form#new_myobj').removeAttr('data-remote') and $('form#new_myobj').removeAttr('data-type') to remove data-remote and data-type attributes (if existent).
I'm building a website with very small amounts of Javascript, just to add things to the page (like a contact form) without having to go to a new page.
I understand that I should build the contact page anyways (just in case the user doesn't have javascript turned on) and then use javascript if they've got it.
So where do I store the HTML for the form if I don't want to have it in two places?
(Normally I'm not so picky, but I'm curious on this one.)
If you have access to a server-side language, you can keep a separate snippet of the form in an external page. Then, you can include the snippet into the HTML content page with an appropriate include call. This has the added benefit that, for your JavaScript, you can pull the contact form from this snippet file using AJAX. In fact, many plugins allow you to display DHTML windows with HTML content. For example, check out ThickBox.
Without a server-side language, you can do something similar with frames. Just display the form snippet in a frame when you need to reference it. Personally, I don't like frames very much, so this isn't a very attractive solution for me, but you can use it if you choose (and style the frames appropriately).
Just put your HTML for the contact form in a .html file. Assuming you're using PHP or something, just include the file in your contact page and include it in the section for your dynamic contact form. The form should still submit to the same server-side page and have the same look and feel..
e.g. contactForm.html
<div class="contact-form">
<input ....>
</div>