Form uploading files to different server without following - php

How can I send a file in django to a different server without user being redirected to the server ? So all goes to rewriting this simple php function in django :
$filename = 'C:/tmp/myphoto.jpg';
$handler = 'http://www.example.com/upload.php';
$field = 'image';
$res = send_file($filename, $handler, $field);
if ($res) {
echo 'done.';
} else {
echo 'something went wrong.';
}
Function on the second server is just simple php func that reads files from $_FILES:
<?php
move_uploaded_file(
$_FILES['image']['tmp_name'],
'/var/www/image/uploaded-file.jpg'
);
echo 'file saved.';
?>
I've already tried django-filetransfers, and it works but I somehow cannot make it stay on the page from which I am uploading file. I have edited the upload_handler view and files are sent properly but after that I'm redirected to my second server :
def upload_handler(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect("/upload")
upload_url, upload_data = prepare_upload(request, "address of my server/")
form = UploadForm()
return direct_to_template(request, '/upload.html',
{'form': form, 'upload_url': upload_url, 'upload_data': upload_data,
'uploads': UploadModel.objects.all()})
And here's my approach. I'm using functions from httplib and also multipart_encode function from python-poster that creates me file headers :
def file_upload(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
f = request.FILES['file']
logging.debug(f)
status = send_file(request.FILES['file'])
c = RequestContext(request, {
"status" : status,
})
template = "management/status.html"
result = render_to_string(template, c)
return HttpResponse(result)
else:
form = UploadFileForm()
return render_to_response('management/file_upload.html', {'form': form})
def send_file(file):
datagen, headers = multipart_encode({"myfile": file})
conn = httplib.HTTPConnection(HOST)
conn.request('POST', '/rte/', file, headers)
res = conn.getresponse()
if res.status != 200:
logging.debug("error \n")
logging.debug(file)
logging.debug("\n")
logging.debug(headers)
return res.status
HTML:
<form action="{{ views.file_upload }}" method="POST" enctype="multipart/form-data">
{{ form.as_p }}
<input type="submit" value="Upload" />
</form>
As a result I get 'Error 500' and in debug :
2010-10-20 18:12:55,819 DEBUG thumb.php4.jpg
2010-10-20 18:14:55,968 DEBUG error
2010-10-20 18:14:55,968 DEBUG thumb.php4.jpg
2010-10-20 18:14:55,969 DEBUG
2010-10-20 18:14:55,969 DEBUG {'Content-Length': 15019, 'Content-Type': 'multipart/form-data; boundary=02cafbc1d080471284be55dc1095b399'}
My functions are based on python/django docs and few solutions I've found on the internet. Functionality looks the same but somehow it doesn't work. Should I take different approach ? In php I do not need to define headers etc.

Well, first of all I'd like to ask why are you doing this? Is it because of the storage or load or perhaps failover? In case it's storage, why are you sending the file to the second server via HTTP? I guess a simple SSH file transfer would be just fine (scp). In case it's failover you'll be better off with rsync.
If using your own second server is not mandatory you might as well go with a CDN service.

Related

Websocket server using php for flutter app [duplicate]

I am looking for a simple code to create a WebSocket server. I found phpwebsockets but it is outdated now and doesn't support the newest protocol. I tried updating it myself but it doesn't seem to work.
#!/php -q
<?php /* >php -q server.php */
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$master = WebSocket("localhost",12345);
$sockets = array($master);
$users = array();
$debug = false;
while(true){
$changed = $sockets;
socket_select($changed,$write=NULL,$except=NULL,NULL);
foreach($changed as $socket){
if($socket==$master){
$client=socket_accept($master);
if($client<0){ console("socket_accept() failed"); continue; }
else{ connect($client); }
}
else{
$bytes = #socket_recv($socket,$buffer,2048,0);
if($bytes==0){ disconnect($socket); }
else{
$user = getuserbysocket($socket);
if(!$user->handshake){ dohandshake($user,$buffer); }
else{ process($user,$buffer); }
}
}
}
}
//---------------------------------------------------------------
function process($user,$msg){
$action = unwrap($msg);
say("< ".$action);
switch($action){
case "hello" : send($user->socket,"hello human"); break;
case "hi" : send($user->socket,"zup human"); break;
case "name" : send($user->socket,"my name is Multivac, silly I know"); break;
case "age" : send($user->socket,"I am older than time itself"); break;
case "date" : send($user->socket,"today is ".date("Y.m.d")); break;
case "time" : send($user->socket,"server time is ".date("H:i:s")); break;
case "thanks": send($user->socket,"you're welcome"); break;
case "bye" : send($user->socket,"bye"); break;
default : send($user->socket,$action." not understood"); break;
}
}
function send($client,$msg){
say("> ".$msg);
$msg = wrap($msg);
socket_write($client,$msg,strlen($msg));
}
function WebSocket($address,$port){
$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
socket_bind($master, $address, $port) or die("socket_bind() failed");
socket_listen($master,20) or die("socket_listen() failed");
echo "Server Started : ".date('Y-m-d H:i:s')."\n";
echo "Master socket : ".$master."\n";
echo "Listening on : ".$address." port ".$port."\n\n";
return $master;
}
function connect($socket){
global $sockets,$users;
$user = new User();
$user->id = uniqid();
$user->socket = $socket;
array_push($users,$user);
array_push($sockets,$socket);
console($socket." CONNECTED!");
}
function disconnect($socket){
global $sockets,$users;
$found=null;
$n=count($users);
for($i=0;$i<$n;$i++){
if($users[$i]->socket==$socket){ $found=$i; break; }
}
if(!is_null($found)){ array_splice($users,$found,1); }
$index = array_search($socket,$sockets);
socket_close($socket);
console($socket." DISCONNECTED!");
if($index>=0){ array_splice($sockets,$index,1); }
}
function dohandshake($user,$buffer){
console("\nRequesting handshake...");
console($buffer);
//list($resource,$host,$origin,$strkey1,$strkey2,$data)
list($resource,$host,$u,$c,$key,$protocol,$version,$origin,$data) = getheaders($buffer);
console("Handshaking...");
$acceptkey = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
$upgrade = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $acceptkey\r\n";
socket_write($user->socket,$upgrade,strlen($upgrade));
$user->handshake=true;
console($upgrade);
console("Done handshaking...");
return true;
}
function getheaders($req){
$r=$h=$u=$c=$key=$protocol=$version=$o=$data=null;
if(preg_match("/GET (.*) HTTP/" ,$req,$match)){ $r=$match[1]; }
if(preg_match("/Host: (.*)\r\n/" ,$req,$match)){ $h=$match[1]; }
if(preg_match("/Upgrade: (.*)\r\n/",$req,$match)){ $u=$match[1]; }
if(preg_match("/Connection: (.*)\r\n/",$req,$match)){ $c=$match[1]; }
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key=$match[1]; }
if(preg_match("/Sec-WebSocket-Protocol: (.*)\r\n/",$req,$match)){ $protocol=$match[1]; }
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/",$req,$match)){ $version=$match[1]; }
if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
if(preg_match("/\r\n(.*?)\$/",$req,$match)){ $data=$match[1]; }
return array($r,$h,$u,$c,$key,$protocol,$version,$o,$data);
}
function getuserbysocket($socket){
global $users;
$found=null;
foreach($users as $user){
if($user->socket==$socket){ $found=$user; break; }
}
return $found;
}
function say($msg=""){ echo $msg."\n"; }
function wrap($msg=""){ return chr(0).$msg.chr(255); }
function unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }
function console($msg=""){ global $debug; if($debug){ echo $msg."\n"; } }
class User{
var $id;
var $socket;
var $handshake;
}
?>
and the client:
var connection = new WebSocket('ws://localhost:12345');
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
If there is anything wrong in my code can you help me fix it? Console in Firefox says:
Firefox can't establish a connection to the server at ws://localhost:12345/.
I was in the same boat as you recently, and here is what I did:
I used the phpwebsockets code as a reference for how to structure the server-side code. (You seem to already be doing this, and as you noted, the code doesn't actually work for a variety of reasons.)
I used PHP.net to read the details about every socket function used in the phpwebsockets code. By doing this, I was finally able to understand how the whole system works conceptually. This was a pretty big hurdle.
I read the actual WebSocket draft. I had to read this thing a bunch of times before it finally started to sink in. You will likely have to go back to this document again and again throughout the process, as it is the one definitive resource with correct, up-to-date information about the WebSocket API.
I coded the proper handshake procedure based on the instructions in the draft in #3. This wasn't too bad.
I kept getting a bunch of garbled text sent from the clients to the server after the handshake and I couldn't figure out why until I realized that the data is encoded and must be unmasked. The following link helped me a lot here: (original link broken) Archived copy.
Please note that the code available at this link has a number of problems and won't work properly without further modification.
I then came across the following SO thread, which clearly explains how to properly encode and decode messages being sent back and forth: How can I send and receive WebSocket messages on the server side?
This link was really helpful. I recommend consulting it while looking at the WebSocket draft. It'll help make more sense out of what the draft is saying.
I was almost done at this point, but had some issues with a WebRTC app I was making using WebSocket, so I ended up asking my own question on SO, which I eventually solved: What is this data at the end of WebRTC candidate info?
At this point, I pretty much had it all working. I just had to add some additional logic for handling the closing of connections, and I was done.
That process took me about two weeks total. The good news is that I understand WebSocket really well now and I was able to make my own client and server scripts from scratch that work great.
Hopefully the culmination of all that information will give you enough guidance and information to code your own WebSocket PHP script.
Good luck!
Edit: This edit is a couple of years after my original answer, and while I do still have a working solution, it's not really ready for sharing. Luckily, someone else on GitHub has almost identical code to mine (but much cleaner), so I recommend using the following code for a working PHP WebSocket solution:
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php
Edit #2: While I still enjoy using PHP for a lot of server-side related things, I have to admit that I've really warmed up to Node.js a lot recently, and the main reason is because it's better designed from the ground up to handle WebSocket than PHP (or any other server-side language). As such, I've found recently that it's a lot easier to set up both Apache/PHP and Node.js on your server and use Node.js for running the WebSocket server and Apache/PHP for everything else. And in the case where you're on a shared hosting environment in which you can't install/use Node.js for WebSocket, you can use a free service like Heroku to set up a Node.js WebSocket server and make cross-domain requests to it from your server. Just make sure if you do that to set your WebSocket server up to be able to handle cross-origin requests.
As far as I'm aware Ratchet is the best PHP WebSocket solution available at the moment. And since it's open source you can see how the author has built this WebSocket solution using PHP.
I've searched the minimal solution possible to do PHP + WebSockets during hours, until I found this article:
Super simple PHP WebSocket example
It doesn't require any third-party library.
Here is how to do it: create a index.html containing this:
<html>
<body>
<div id="root"></div>
<script>
var host = 'ws://<<<IP_OF_YOUR_SERVER>>>:12345/websockets.php';
var socket = new WebSocket(host);
socket.onmessage = function(e) {
document.getElementById('root').innerHTML = e.data;
};
</script>
</body>
</html>
and open it in the browser, just after you have launched php websockets.php in the command-line (yes, it will be an event loop, constantly running PHP script), with this websockets.php file.
I was in your shoes for a while and finally ended up using node.js, because it can do hybrid solutions like having web and socket server in one. So php backend can submit requests thru http to node web server and then broadcast it with websocket. Very efficiant way to go.
Need to convert the the key from hex to dec before base64_encoding and then send it for handshake.
$hashedKey = sha1($key. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true);
$rawToken = "";
for ($i = 0; $i < 20; $i++) {
$rawToken .= chr(hexdec(substr($hashedKey,$i*2, 2)));
}
$handshakeToken = base64_encode($rawToken) . "\r\n";
$handshakeResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $handshakeToken\r\n";

Unable to make POST request with files to another php website

Recently I have migrate my application from python2/pylons to python3/pyramid.
In my plyons app, I was using below code to make POST request to third party php website (also maintained by me).
register_openers()
datagen, headers = multipart_encode({
"biosamples_metadata": open(file_bs_md, "rb"),
"metastore": open(file_ds_md, "rb"),
"annotation-submit": "Validate Annotation Files"
})
# Create the Request object
url = config['php_website_url']
request = urllib.request.Request(url, datagen, headers)
# Actually do the request, and get the response
return_text = urllib.request.urlopen(request).read()
return return_text
This above code worked perfectly fine. However on python3, poster is not supported and I cannot use register_openers() which I don't even know do what.
In python3, I am using requests module.
import requests
from requests_toolbelt import MultipartEncoder
url = config['php_website_url']
m = MultipartEncoder(
fields={'biosamples_metadata': open(file_bs_md, 'rb'),
"metastore":open(file_ds_md, "rb"),
"annotation-submit": "Validate Annotation Files"
}
)
request = requests.post(url, data=m)
return_text = request.text
However, this code does not work properly. It goes to php app and executes the part of the code that is supposed to be executed when you do get request.
Here is what php code look like
public function handle_request () {
$TEMPLATE = 'content_main';
// Process POST
if ($this->isPOST()) {
$this->_annotationFiles = array();
return $this->error_span('This is Get');
}
else ($this->isGET()) {
$this->_annotationFiles = array();
return $this->error_span('This is Post')
}
Any help appreciated
I am not sure about your PHP code, but the below is the correct way to post files & data to a remote url with Python 3 and requests:
import requests
post_addr = "https://example.com/post_url"
data = {
'foo': 'bar',
'foo_a': 'bar_a':
}
files = {'file': open('report.xls', 'rb')}
r = requests.post(post_addr, data=data, files=files)

How to implement Authorize.NET Hosted Payments iFrame & Laravel

I found the official documentation and the github example provided by Authorize.NET to be a terribly confusing mess of stuff you don't need. This post is a summary of the last few hours work hoping it may help others.
This guide assumes you don't want the receipt page and you want to automatically move the user forward on successful payment.
The site back-end is Laravel (PHP) but there's little in here that's Laravel specific.
First thing to do is add the Authorize.NET SDK:
composer require authorizenet/authorizenet
I then setup a hosted payments repository that accepts the order but you could do this however you like, this returns the hosted form token to a controller ready to pass to the view.
<?php
namespace ShopApp\Repositories;
use ShopApp\Models\Order;
use ShopApp\Repositories\Contracts\hostedPaymentRepositoryContract;
use Illuminate\Support\Facades\Config;
use net\authorize\api\contract\v1\MerchantAuthenticationType;
use net\authorize\api\contract\v1\TransactionRequestType;
use net\authorize\api\controller\GetHostedPaymentPageController;
use net\authorize\api\contract\v1\GetHostedPaymentPageRequest;
use net\authorize\api\contract\v1\SettingType;
use net\authorize\api\constants\ANetEnvironment;
use net\authorize\api\contract\v1\CustomerAddressType;
use ShopApp\Models\Address;
/**
* Class hostedPaymentRepository
* #package ShopApp\Repositories
* #todo - Implement methods to talk to Authorize.NET and show form.
*/
class hostedPaymentRepository implements hostedPaymentRepositoryContract
{
public $response; //what did we get back?
public $paymentFormToken;
public function getHostedFormToken(Order $order){
$payment_amount = null;
foreach($order->items as $order_item){
$payment_amount += $order_item->price;
}
$billing_address = Address::findOrFail($order->billing_address_id);
// Common setup for API credentials
$merchantAuthentication = new MerchantAuthenticationType();
$merchantAuthentication->setName(Config::get('yoursite.payment_providers.authorize_dot_net.MERCHANT_LOGIN_ID'));
$merchantAuthentication->setTransactionKey(Config::get('yoursite.payment_providers.authorize_dot_net.MERCHANT_TRANSACTION_KEY'));
//create a transaction
$transactionRequestType = new TransactionRequestType();
$transactionRequestType->setTransactionType("authCaptureTransaction");
$transactionRequestType->setAmount($payment_amount);
// Create the Bill To info
$billto = new CustomerAddressType();
$billto->setFirstName($order->billing_first_name);
$billto->setLastName($order->billing_last_name);
$billto->setAddress($billing_address->address_1);
$billto->setCity($billing_address->city);
$billto->setState($billing_address->state);
$billto->setZip($billing_address->zip);
$billto->setCountry($billing_address->country);
if(!is_null($order->phone)){
$billto->setPhoneNumber($order->phone);
}
//#todo - implement user stuff and get email
//$billto->setEmail("changethis#later.com");
$transactionRequestType->setBillTo($billto);
// Set Hosted Form options
$setting1 = new SettingType();
$setting1->setSettingName("hostedPaymentButtonOptions");
$setting1->setSettingValue("{\"text\": \"Pay Now\"}");
$setting2 = new SettingType();
$setting2->setSettingName("hostedPaymentOrderOptions");
$setting2->setSettingValue("{\"show\": false}");
$setting3 = new SettingType();
$setting3->setSettingName("hostedPaymentReturnOptions");
$setting3->setSettingValue("{\"showReceipt\" : false }");
$setting4 = new SettingType();
$setting4->setSettingName("hostedPaymentIFrameCommunicatorUrl");
$setting4->setSettingValue("{\"url\": \"https://yoursite.local/checkout/payment/response\"}");
// Build transaction request
$request = new GetHostedPaymentPageRequest();
$request->setMerchantAuthentication($merchantAuthentication);
$request->setTransactionRequest($transactionRequestType);
$request->addToHostedPaymentSettings($setting1);
$request->addToHostedPaymentSettings($setting2);
$request->addToHostedPaymentSettings($setting3);
$request->addToHostedPaymentSettings($setting4);
//execute request
$controller = new GetHostedPaymentPageController($request);
$response = $controller->executeWithApiResponse(ANetEnvironment::SANDBOX);
if (($response == null) && ($response->getMessages()->getResultCode() != "Ok") )
{
return false;
}
return $response->getToken();
}
}
Note that I have set showReceipt to false and I have offered another setting called hostedPaymentIFrameCommunicatorUrl. Do not forget the hostedPaymentIFrameCommunicatorUrl otherwise you will get the reciept page regardless of setting showReceipt to false.
The hostedPaymentIFrameCommunicatorUrl MUST be on the same domain as your payment page and on the same port.
Then in your view you need to add the iFrame (mine just sits in the page, i didn't bother with the lightbox:
<div id="iframe_holder" class="center-block" style="width:90%;max-width: 1000px" data-mediator="payment-form-loader">
<iframe id="load_payment" class="embed-responsive-item" name="load_payment" width="750" height="900" frameborder="0" scrolling="no">
</iframe>
<form id="send_hptoken" action="https://test.authorize.net/payment/payment" method="post" target="load_payment">
<input type="hidden" name="token" value="{{ $hosted_payment_form_token }}" />
</form>
</div>
In the same view you need to load at least the following javascript (im using jQuery and have only half implemented the transactResponse method, but you get the idea):
$(document).ready(function(){
window.CommunicationHandler = {};
function parseQueryString(str) {
var vars = [];
var arr = str.split('&');
var pair;
for (var i = 0; i < arr.length; i++) {
pair = arr[i].split('=');
vars[pair[0]] = unescape(pair[1]);
}
return vars;
}
window.CommunicationHandler.onReceiveCommunication = function (argument) {
console.log('communication handler enter');
var params = parseQueryString(argument.qstr)
switch(params['action']){
case "resizeWindow" :
console.log('resize'); break;
case "successfulSave" :
console.log('save'); break;
case "cancel" :
console.log('cancel'); break;
case "transactResponse" :
sessionStorage.removeItem("HPTokenTime");
console.log('transaction complete');
var transResponse = JSON.parse(params['response']);
window.location.href = '/checkout/complete';
}
}
//send the token
$('#send_hptoken').submit();
});
The above code adds a function to handle the message that comes back from the iFrame Communicator (next step) and also submits the payment form token to get and load the actual payment form.
Next we need to setup an 'iframe communicator' This is basically just a way for Authorize.NET to get around the same-domain origin policy
To do this, create a new route and a view that just returns a simple HTML page (or a blade template, but it should have no content other than the scripts).
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>IFrame Communicator</title>
<script type="text/javascript">
function callParentFunction(str) {
if (str && str.length > 0 && window.parent.parent
&& window.parent.parent.CommunicationHandler && window.parent.parent.CommunicationHandler.onReceiveCommunication) {
var referrer = document.referrer;
window.parent.parent.CommunicationHandler.onReceiveCommunication({qstr : str , parent : referrer});
}
}
function receiveMessage(event) {
if (event && event.data) {
callParentFunction(event.data);
}
}
if (window.addEventListener) {
window.addEventListener("message", receiveMessage, false);
} else if (window.attachEvent) {
window.attachEvent("onmessage", receiveMessage);
}
if (window.location.hash && window.location.hash.length > 1) {
callParentFunction(window.location.hash.substring(1));
}
</script>
</head>
<body></body>
</html>
The key part here is the window.parent.parent
Authorize.NET grabs your hostedPaymentIFrameCommunicatorUrl that you gave it in the beginning and embeds this within another iFrame, inside their own payment iFrame. Which is why it's window.parent.parent.
Your hostedPaymentIFrameCommunicatorUrl script then can pass the payment response to your payment page and you can edit the functions above to do what you like after that.
Hope that helps someone.
Authorize.NET is seriously lacking examples and their docs are lightweight at best. The 'catch-all' example that has you sifting through loads of code you don't need is just plain lazy.
Their API doc isn't bad though, they just need decent examples...

Validating image file in custom jQuery, AJAX, and PHP form (SilverStripe)

I'm working on finishing a simple form for contest entry submissions. Normally we'd use the userforms SilverStripe extension (https://github.com/silverstripe/silverstripe-userforms) but that is a page type, and this form needs to be one section in a page type. I have got something setup right now that may not be be perfect but has done the job before. However, the inclusion of an image upload field is throwing me off.
Typically, outside of a CMS and without a plugin, I would use $_FILES['upload_field_name']['type or name or size'] to validate an upload field. But I am not sure I am able to use that here since I am working within a CMS.
This is what I have so far:
The jQuery + AJAX code:
(function ($) {
$(document).ready(function () {
var SubmitBtn = $('.SubmitEntry');
var FirstName = $('.FirstNameTxt');
var LastName = $('.LastNameTxt');
var EmailAddress = $('.EmailTxt');
var Image = $('.UploadImage');
SubmitBtn.on('click', function (e) {
var required = [FirstName, LastName, EmailAddress, Image];
var containsError = false;
e.preventDefault();
for (i = 0; i < required.length; i++) {
var input = required[i];
if ((input.val() == "")) {
containsError = true;
input.addClass('error-field');
$('.Error-Msg').show();
} else {
input.removeClass('error-field');
}
}
if (containsError == false) {
$('.Error-Msg').hide();
ajax_script();
}
});
function ajax_script() {
var form_data = {
firstname: FirstName.val(),
lastname: LastName.val(),
useremail: EmailAddress.val(),
image: Image.val()
};
$.ajax({
type: "POST",
url: "/home/ContestSubmission",
data: form_data,
dataType: "json"
}).done(function (response) {
for (var i = 0; i < response.length; i++) {
var status = response[i].status;
if (status == "error") {
var errorMessage = response[i].message;
if (response[i].field == "email") {
$('.Error-Msg').show();
$('.Error-Msg').html("<br />" + errorMessage.message);
EmailAddress.addClass("error-field");
}
/* else if image is not valid, show error message */
}
else if (status == "success") {
$('#contest-submissions')[0].reset();
alert("Thank you for your entry!");
}
}
});
}
});
}(jQuery));
$ = jQuery.noConflict();
This is the ContestSubmisson function located within Page.php:
public function ContestSubmission()
{
$fname = $this->getRequest()->postVar('firstname');
$lname = $this->getRequest()->postVar('lastname');
$useremail = $this->getRequest()->postVar('useremail');
$image = $this->getRequest()->postVar('image');
$errorField = "";
$return = array();
if (!filter_var($useremail, FILTER_VALIDATE_EMAIL)) {
$validatonStatus = "error";
$errorField = "email";
$errorList = "The email is not in the correct format. Please re-enter it.";
} /* else if $image is not valid i.e. size or file type */
else {
$contestEntry = new ContestEntrySubmission();
$contestEntry->FirstName = $fname;
$contestEntry->LastName = $lname;
$contestEntry->EmailAddress = $useremail;
$contestEntry->UploadedImage = $image;
$contestEntry->write();
$validatonStatus = "success";
$errorField = "";
$errorList = "";
$from = 'secret#secret.com';
$to = '[testing email address]';
$subject = 'Contest Entry Submission';
$body = "Below is the contest entry submission information:" . "<br /><br />";
$body .= "<strong>First Name:</strong> " . $fname . "<br/>" . "<strong>Last Name:</strong> " . $lname . "<br/>" . "<strong>Email:</strong> " . $useremail . "<br />" . "Image File: " . $image;
$email = new Email($from, $to, $subject, $body);
$email->replyTo($useremail);
$email->send();
}
$return[] = array(
"status" => $validatonStatus,
"field" => $errorField,
"message" => $errorList
);
return json_encode($return);
}
What is throwing me off is what to do with the image when it gets to the Page.php function shown above. Normally the setup above works fine in SilverStripe but now with the addition of an image upload field, I'm confused on what to change/add/edit to validate the file size and type. Examples I have found tend to deal with creating or working with an Upload Field inside of a page template type, which is not what I am looking for here.
Any suggestions would be great. I am just so used to not building these things inside a CMS. I may be making this much more difficult than needed therefore...any tips?
You mentioned that you just stick to JS, HTML, & PHP - that's cool because using the SilverStripe framework is all PHP! Learning how to make a custom form provides greater flexibility over UserForms, so it's handy to add to the ol' utility belt. It sounds like this is a good opportunity to learn something new, which is always fun :)
First and foremost, the tips to things you're missing is easy! There is a vast set of documentation for developers at https://docs.silverstripe.org/
I highly recommend the forms introduction section, which covers off basic form creation. Once you've defined the fields, SilverStripe will do all the rest for you (all the validation & all), leaving you just to deal with what happens after a valid submission.
Depending on your skill level, you may like to begin with one of the great tutorials - the text one is great and covers all the basics (even though it's quite old), you can access it from the tutorials link in the left menu. There's also a more modern video tutorial at http://www.silverstripe.org/learn/lessons/introduction-to-frontend-forms which can be useful for explaining some of the finer points that sometimes gets beginners a bit turned around. You choose which is best for you. There's also API documentation if you'd prefer to just jump straight into the deep end - you'll find that link on the top navigation.
It seems like you've got the front end part down pat, and SilverStripe doesn't really get in the way here at all. Plus it sounds like you're familiar with styling the UserForms, so that's a great bonus already. So at this point I'll assume that nothing here will be new to you on the front end - the default form rendering won't startle you.
Making the form itself is really easy. I'll use your example and try to cobble a very untested example using the inbuilt Form class (which is part of the Framework, not the CMS - a small but I think important distinction to make).
A form submits to itself, and takes care of all the validation for you. Neat! We'll use your contestSubmission function.
public function contestSubmission() {
return Form::create(
$this,
__FUNCTION__,
FieldList::create(
TextField::create('firstname', 'First Name'),
TextField::create('lastname', 'Last Name'),
EmailField::create('useremail', 'Email'),
FileField::create('image', 'Image')
),
FieldList::create(
FormAction::create('emailValidEntry', 'Submit Entry')
),
RequiredFields::create(array('firstname', 'lastname', 'useremail', 'image'))
);
}
You may be wondering what all this ClassName::create($stuff) business is. Think of it as new ClassName($stuff), but with the advantage that you can chain it (like how $('.selector') gives you a new jQuery object, and you can $('.selector').doThings().to().it() - using the ::create() method we can do the same in PHP, as opposed to creating into a variable then operating on it. A nice thing SilverStripe gives us :)
Once your form is all defined (including adjusting the required fields) you only need to worry about what to do with the info once it's collected, which removes a lot of the burden. Cool. We'll create another function for that: emailValidEntry (the name must match that given in the FormAction declared above) - it basically just contains the bit in your else block. I'll tidy it up for the Email bits to use templates too though.
public function emailValidEntry($data, $form) {
$contestEntry = ContestEntrySubmission::create();
$form->saveInto($contestEntry);
$contestEntry->write();
$data['ImageEmail'] = base64_encode(file_get_contents($data['UploadedImage']['tmp_name']));
Email::create(
'from#address', //from
'testing#address', //to
'Contest Entry Submission' //subject
)->setTemplate('ContestEntryEmail')
->populateTemplate(ArrayData::create($data))
->replyTo($data['EmailAddress'])
->attach($contestEntry->UploadedImage()->Filename)
->send();
$form->setMessage('Thank you for your submission!', 'good');
$this->redirectBack();
}
The 'do stuff with my form submission' method takes 2 arguements: an associative array of FieldName => Value, and the full instance of the Form object itself, with all the fields having their submitted values set.
OK, so there's a slight bit of complexity going on to get the base64 encode for the email embed, although I think attaching it is safer so I've shown that too.
The template ContestEntryEmail.ss (placed somewhere in your theme, maybe templates/emails or something) might look something like:
Below is the contest entry submission information: <br /><br />
<strong>First Name:</strong> $FirstName<br />
<strong>Last Name:</strong> $LastName<br />
<strong>Email:</strong> $EmailAddress<br />
<strong>Image File (see attached if not shown here):</strong><img src="$ImageEmail" alt="Submitted Image" />
Where $ImageEmail uses the base64 encoded image data to embed directly into the email.
But it seems like the recommended way would be to just attach it. From a quick google it seems that Outlook and Gmail both will block the inlined version shown in the template there.
Hopefully this is all rather not too TL;DR & helpful - good luck & happy form creating!
My top tip would be to check the MIME type of the image with PHP. This checks the actual file make-up to verify that it is in fact an image and not just a random file that has been renamed to have an image extension
Here is some sample code you could include
// Check for valid upload
if (($_FILES["picture"]["error"]) == "4") //no file detected
// error codes explained at http://php.net/manual/en/features.file-upload.errors.php
{
echo "<h3>No file has been detected. Please select a file to upload.</h3>";
exit();
}
if ($_FILES['picture']['error'] == "1" || $_FILES['picture']['error'] == "2" || ($_FILES['picture']['size'] > 71680)) //define your max_file_size 71680 is 70KB - not very big
//file too big
{
echo("<h3>Upload unsuccessful: File exceeds maximum allowed size</h3>");
echo("<p>Try compressing your photo or upload a different photo</p>");
exit();
}
//if file present and of acceptable size
if ($_FILES['picture']['error'] != "1" && $_FILES['picture']['error'] != "2" && $_FILES['picture']['error'] != "4" && $_FILES['picture']['size'] < 71680) {
//then check to see if the type of file is an image file
//check for mimetype as at http://php.net/manual/en/features.file-upload.php
$fileinfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$fileinfo->file($_FILES['picture']['tmp_name']),
array(
'png' => 'image/png','gif' => 'image/gif','jpg' => 'image/jpeg',
),
true
))
{
echo('<h3>The file chosen is not an acceptable image file (.jpg/.png or .gif)</h3>');
echo("Please upload a .jpg/.png or .gif image");
exit();
}
}

PHP get data from DELETE request

I am using jquery plugin for multiple file upload. Everything is working fine, except delete the images. Firebug say that JS it is sending DELETE request to the function. How can I get data from delete request?
PHP delete code:
public function deleteImage() {
//Get the name in the url
$file = $this->uri->segment(3);
$r = $this->session->userdata('id_user');
$q=$this->caffe_model->caffe_get_one_user($r);
$cff_name= $q->name;
$cff_id = $q->id_caffe;
$w = $this->gallery_model->gallery_get_one_user($gll_id);
$gll_name = $w->name;
$success = unlink("./public/img/caffe/$cff_name/$gll_name/" . $file);
$success_th = unlink("./public/img/caffe/$cff_name/$gll_name/thumbnails/" . $file);
//info to see if it is doing what it is supposed to
$info = new stdClass();
$info->sucess = $success;
$info->path = $this->getPath_url_img_upload_folder() . $file;
$info->file = is_file($this->getPath_img_upload_folder() . $file);
if (IS_AJAX) {//I don't think it matters if this is set but good for error checking in the console/firebug
echo json_encode(array($info));
} else { //here you will need to decide what you want to show for a successful delete
var_dump($file);
}
}
and JS is using jQuery-File-Upload plugin: link
Generally, if the DELETE request sends data in the request body, you can read the data by using the following code:
$data = file_get_contents("php://input");
Depending on the encoding of the data (usually JSON or form-encoded), you use json_decode or parse_str to read the data into usable variables.
For a simple example, see this article, where the author uses form-encoded data to handle a PUT request. DELETE works similarly.
In your case however, it looks like the file name is read from the request URL (the call to $this->uri->segment(3);). When I look at your code, it seems that the variable $gll_id is not initailized and you don't check if the resulting object $w and the variable $gll_name are empty. Maybe this is causing the delete to fail. Turn on error logging with ini_set("log_errors",1); and have a look at your server error log. If the unlink fails, the error log should contain the path PHP tried to unlink - it's likely that the path is not correct.

Categories