I have an issue with fwrite not properly overwriting a file - httprequest

For some reason, when I use fwrite to overwrite the contents of my file, it erases the data, but fails to write the new contents to it until fwrite is invoked a second time. Is there any way I can get around this?
My script uses HttpRequest to send a FormData object to an external script as if I had invoked a form submit, then the script saves the form data to a file on the server, and then uses another HttpRequest to get the responseText of the file on the server and apply it to the value of another element:
PHP.php
data = $_POST["name"];
fopen(filename, w);
fwrite(file, data);
fclose(file);
CORE.html
<script>
function functionName() {
obj = new XMLHttpRequest;
form = new FormData;
form.append("name", object.getElementById(elementId).value);
obj.open(POST, PHP.php, false);
obj.send(form);
obj2 = new XMLHttpRequest;
obj2.onreadystatechange = function() { if (obj2.readyState == 4 && obj2.status == 200) document.getElementById(elementId2).value = obj2.responseText };
obj2.open(POST, OUTPUT.txt, false);
obj2.send();
}
</script>
<textarea id=elementId onkeypress=functionName()>
</textarea>
<br>
<textarea id=elementId2>
</textarea>
OUTPUT.text
epty text document

Use fflush()
http://php.net/manual/en/function.fflush.php
This function forces a write of all buffered output to the resource pointed to by the file handle.

I figured it out myself.
For some reason, using the onkeypress event, the event was being triggered and the function called before any characters were inserted into the TextArea, resulting in the file being written with an empty string.
The reason for the successful writes on subsequent attempts was that on subsequent key presses, characters were then present within the TextArea, and were successfuly written to the file.
I simply used another method to call the function after characters were present.

Related

Vue component method behaving weirdly - loader animation not working while data is processing

I'm trying to figure out why this happens - loader for long processing of data does not show.. only after the processing is done. Huge (few thousands items) object of key-value items and want them to make filterable - that works - but takes few seconds. I'm using VueJS 2.
I wanted to show "please wait" message while it runs, using the isworking value. I have a span with v-if="isworking", defined with value false as initial value.
On first line I set the this.isworking prop, but instead of seeing the "please wait", the function hangs for few seconds to do a search, and THEN sets the isworking prop to true - I tried that by commenting the last isworking=false - can't figure out why it waits to change it to true for the huge processing to end.
That window.deaccent method is fn to replace all accented characters in string with basic ansii chars, nothing special.
In template, I have a simple:
<form #submit="searchmath">
<span v-if="working">please wait</span>
<input v-model=...>
<div v-for="(item,index) in searchmatchitems"> ... </div>
</form>
Method in component:
searchmatch: function($event){
this.isworking = true;
this.$forceUpdate(); // tried also this, does not help
$event.stopPropagation();$event.preventDefault();
try{
var searchid = window.deaccent(this.search_string.toLowerCase());
var searchobj = this.cdata;
let result = Object.keys(this.cdata).filter(function(el,i,c){
var elk = window.deaccent(el).toLowerCase();
var elv = window.deaccent(searchobj[el]);
return elk.indexOf(searchid) > -1 || elv.indexOf(searchid) > -1;
}, searchid);
this.searchmatchitems = result;
this.isworking = false;
} catch(e){ console.log(e); this.isworking = false; return [];}
}
I also tried moving the event.preventDefault() to bottom, just to be sure it does not affect anything, but no luck.
That cdata is a simple key-value object with many props like this, counting about 4000 items
data: {
cdata: {
"lorem": "aa",
"ipsum": "bc",
"dolor": "de",
....
},
isworking: false,
....
}
2 issues here.
First, you use working in your template, and isworking in your data and code.
Second, your method does not call any async code, so it sets isworking to true, does work, and then sets isworking to false. You template will never have a chance to refresh in this case, and the UI will freeze until the method returns.
For instance, if you made an async call to a network endpoint, and then set isworking to false in the callback, you would get the results you are expecting.
If you have long running code and wish to prevent the UI from freezing, you will need to use a web worker thread

How can I create a JavaScript global variable?

This is the part of my .html file, where I use an xmlhttp request, and then try to access the JSON object outside of a function.
''''
<html>
<head>
<title>make_activities_table.html</title>
</head>
<body>
<script>
var act_obj_array;
// Get file activities.json and create JSON object
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if ( this.readyState == 4 && this.status == 200 ) {
act_obj_array = JSON.parse( this.responseText);
}
console.log(this.responseText);
console.log('Inside function = ' + act_obj_array);
};
xmlhttp.open( "GET", "activities.json", true);
xmlhttp.send();
console.log( 'Outside function = ' + act_obj_array);
</script>
</body>
''''
I'm trying to access the JSON object 'act_obj_array' outside of the xmlhttp.onreadystatechange function. From what I read, in JavaScript, to make a variable global, you define it outside of any function, which, clearly I have done here. But when I console.log 'act_obj_array, outside the function, I get undefined.
activities.json, is a file, that exist in the same directory of my web hosting's server. It is a JSON formatted file. I can see the information in the file quite clearly when I console.log( this.responseText );
What do I need to do to create a JSON object from an xmlhttp request, and then be able to access the JSON object from the rest of my code?
Well, since no one wanted to answer, I came up with a solution, and, I want to work on a second solution.
The solution I have now, I added a function after the line, inside a function:
act_obj_array = JSON.parse( this.responseText);
which is a function, that builds my table from my JSON object. At first, I just added the script in the body of the document, but, then, I defined the function in an external JavaScript sheet. This looked much cleaner.
The other solution, and one I haven't played with yet, is to use the 'fetch' method to read in my JSON file. That way, I may not have to use a function to create my JSON object.

Vue2-Dropzone process form when files manually added

Is it possible to manually process the dropzone form (or queue) when the file is manually loaded?
We have the concept of a drivers license. The user uploads a photo and enters other information such as the license number, expiration date, etc.. The user clicks the save button and I call processQueue() which submit the entire form. This all works just fine.
Next, we display this license in a non-form way with an edit button. When they click the "Edit" button, I display the form again and populate the fields with previously entered data including manually adding the previously submitted photo of their license. Basically like this from the documentation:
mounted: () {
var file = { size: 300, name: "Icon", type: "image/png" };
var url = "https://example.com/img/logo_sm.png";
this.$refs.myVueDropzone.manuallyAddFile(file, url);
}
This appears to all work as expected. I see the dropzone with a thumbnail of the previously uploaded file. The input fields are all populated with previously entered data.
The problem occurs when I try to process this form again with:
onSubmit() {
this.$refs.myVueDropzone.processQueue()
}
If they only make changes to the input fields like the license number and do not upload a new file, the onSubmit() or processQueue() does not work. It only works if I remove and re-add a file or add a second file. It's as if it does not recognize a file has been added. Is manuallyAddFile only for displaying and not actually adding the file?
How can I submit this form when the file is manually added?
After a bit of research on Vue2 Dropzone
Manually adding files
Using the manuallyAddFile method allows you to programatically add files to your dropzone area. For example if you already have files on your server that you'd like to pre-populate your dropzone area with then simply use the function when the vdropzone-mounted event is fired.
source
So the solutions is to check and see if anything needs to be processed in your queue. Since you manually added the file you already have it, it does not need to be uploaded again. Only if the user adds a new file does it need to be uploaded.
You could do this a few ways, but I would recommend something like the example below for it's simplicity:
onSubmit() {
if (this.$refs.myVueDropzone.getQueuedFiles().length) {
this.$refs.myVueDropzone.processQueue()
}
}
If you need to wait until the queue has finished processing to send your form you can leverage vdropzone-queue-complete. Below is a quick example:
<template>
<!-- SOME FORM ELEMENTS HERE -->
<vue-dropzone
ref="myVueDropzone"
:options="dropzoneOptions"
#vdropzone-error="errorUploading"
#vdropzone-success="fileUploaded"
#vdropzone-queue-complete="submitForm"
/>
<button #click="saveForm">Save</button>
</template>
<script>
export default {
methods: {
saveForm () {
if (this.$refs.myVueDropzone.getQueuedFiles().length) {
this.$refs.myVueDropzone.processQueue()
} else {
this.submitForm()
}
},
errorUploading (file, message, xhr) {
// do something when a file errors
// perhaps show a user a message and create a data element to stop form from processing below?
},
fileUploaded (file, response) {
// do something with a successful file upload. response is the server response
// perhaps add it to your form data?
},
submitForm () {
// Queue is done processing (or nothing to process), you can now submit your form
// maybe check for errors before doing the below step
// do what ever you need to submit your form this.$axios.post(...) etc.
}
}
}
</script>

How to add an uploaded file to a list of already uploaded files in Vue?

I currently have a file uploader that accepts a single CSV file. Then with axios I POST such file to the server and everything works just fine. What I'm not being able to achieve is being able to upload another CSV that will get added to the list of CSVs uploaded. I'm not talking about uploading various files at once, I'm taking about uploading different files at different points in time.
This is the method that is used to select a CSV file in the .vue file.
staticCampaignCSVSelected: function (file) {
console.log('campaign-detail.vue#staticCampaignCSVSelected', file)
let vc = this
vc.selectedHeuristicId = -1
Campaign.uploadStaticCSV(vc.campaign, file[0])
.then(
function (data) {
alert('CSV cargado con exito')
}
)
.catch(
function (err, data) {
console.log("campaign-detail#staticCampaignCSVSelected - catch", err.response)
alert(err.response.data.error)
}
)
},
This is the function that I have in some other JS file to POST to the API:
function uploadStaticCSV (campaign, csv) {
console.log('Campaign#uploadStaticCSV', campaign, csv)
//long list of assertions
let formData = new FormData()
formData.append('csv', csv)
return axios.post(API.campaignUploadStaticCSV(campaign.id), formData)
}
And this is the function I have in my endpoints.js file:
campaignUploadStaticCSV: function (id) { return this.campaign(id) + '' + '/csv' },
I haven't found a way to properly pass a[file] array as a parameter to the functions, which is what I believe I need to somehow do.
Any help would be appreciated :)
As far as i understood your question you need a way to pass a file from browser interface to your staticCampaignCSVSelected(file) method. If so why not to use an input model or a simple event or a watcher. E.g.
<input type="file" #input="staticCampaignCSVSelected($event.target.files[0])" />
But also i see a mistake in your code. You should append .then().catch() callbacks to axios.post() itself but not to Campaign.uploadStaticCSV() method.
And
return axios.post()
will not return a server response. You have to handle it in
axios.post().then(response => {})
callback

jquery .live on form submit not picking up dynamically added inputs

when my ajaxupload script finishes it adds a read-only input w/ the value of the image's URL.
it is a long script, but i think this is the relevant part that fires on successful completion:
var location = '<div id="'+ID+'_location" class="img_location">' + '<input name="'+ID+'" class="location regular-text" type="text" size="50" readonly="readonly" value="'+response+'" />';
$(container).append(location).show(); //create readonly input
$(container) is defined just as the parent div of the upload button. that part seems to work... the image is uploaded, it is saved properly, and the input w/ the image's location is added to to the DOM. but i've discovered a bug that if I click my SAVE button (which triggers my ajax save function) then this new input is NOT captured.
here is my save function:
$('form#childoptions').live('submit', function(e) {
e.preventDefault();
var values = $(this).serialize();
alert(values);
var data = {
action: 'save_function',
type : 'save',
_nonce: '<?php echo $nonce; ?>',
formdata: values
};
$.post(ajaxurl, data, function(response) {
//alert(response);
if(response == 1) {
show_message(1);
t = setTimeout('fade_message()', 2000);
} else {
show_message(99);
t = setTimeout('fade_message()', 2000);
}
});
//return false;
});
only the new input is not captured. the rest works properly. there is also no problem if i refresh in between as I presume the input is part of the DOM. which is why i thought to use .live. i thought i had solved the issue twice- 1. i wasn't using a "name" on the dynamic input and 2. i wasn't using .live on the form. but now i am doing both and not getting anywhere.
all help is much appreciated. let me know if there is more information I can provide.
It appears that your using live on the whole form, not on inputs. So the live event binding would try to pickup new forms with id childoptions. This won't work. You'd be better off using bind() instead. Have you tried:
$('form#childoptions').bind('submit', function(e) {…}
I'm curious if this will fix your issue.