diff options
-rw-r--r-- | controllers/auth.php | 32 | ||||
-rw-r--r-- | controllers/controllers.php | 89 | ||||
-rw-r--r-- | lib/helpers.php | 23 | ||||
-rw-r--r-- | public/js/script.js | 6 | ||||
-rw-r--r-- | views/dashboard.php | 1 | ||||
-rw-r--r-- | views/layout.php | 1 | ||||
-rw-r--r-- | views/new-post.php | 85 | ||||
-rw-r--r-- | views/photo.php | 56 |
8 files changed, 162 insertions, 131 deletions
diff --git a/controllers/auth.php b/controllers/auth.php index 0237c59..748f7ad 100644 --- a/controllers/auth.php +++ b/controllers/auth.php @@ -4,18 +4,18 @@ function buildRedirectURI() { return Config::$base_url . 'auth/callback'; } -function build_url($parsed_url) { - $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; - $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; - $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; - $user = isset($parsed_url['user']) ? $parsed_url['user'] : ''; - $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : ''; - $pass = ($user || $pass) ? "$pass@" : ''; - $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; - $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; - $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; - return "$scheme$user$pass$host$port$path$query$fragment"; -} +function build_url($parsed_url) { + $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; + $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; + $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; + $user = isset($parsed_url['user']) ? $parsed_url['user'] : ''; + $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : ''; + $pass = ($user || $pass) ? "$pass@" : ''; + $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; + $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; + $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; + return "$scheme$user$pass$host$port$path$query$fragment"; +} $app->get('/', function($format='html') use($app) { $res = $app->response(); @@ -38,7 +38,6 @@ $app->get('/auth/start', function() use($app) { // the "me" parameter is user input, and may be in a couple of different forms: // aaronparecki.com http://aaronparecki.com http://aaronparecki.com/ - // Normlize the value now (move this into a function in IndieAuth\Client later) if(!array_key_exists('me', $params) || !($me = IndieAuth\Client::normalizeMeURL($params['me']))) { $html = render('auth_error', array( 'title' => 'Sign In', @@ -68,12 +67,12 @@ $app->get('/auth/start', function() use($app) { $authorizationURL = false; } - // If the user has already signed in before and has a micropub access token, - // and the endpoints are all the same, skip the debugging screens and redirect + // If the user has already signed in before and has a micropub access token, + // and the endpoints are all the same, skip the debugging screens and redirect // immediately to the auth endpoint. // This will still generate a new access token when they finish logging in. $user = ORM::for_table('users')->where('url', $me)->find_one(); - if($user && $user->micropub_access_token + if($user && $user->micropub_access_token && $user->micropub_endpoint == $micropubEndpoint && $user->token_endpoint == $tokenEndpoint && $user->authorization_endpoint == $authorizationEndpoint @@ -251,4 +250,3 @@ $app->get('/signout', function() use($app) { unset($_SESSION['user_id']); $app->redirect('/', 301); }); - diff --git a/controllers/controllers.php b/controllers/controllers.php index 95d3aa5..b27c73e 100644 --- a/controllers/controllers.php +++ b/controllers/controllers.php @@ -384,20 +384,6 @@ function create_favorite(&$user, $url) { return $r; } -function create_photo(&$user, $params, $file) { - $error = validate_photo($file); - - if(!$error) { - $file_path = $file['tmp_name']; - $micropub_request = array('content' => $params['note_content']); - $r = micropub_post_for_user($user, $micropub_request, $file_path); - } else { - $r = array('error' => $error); - } - - return $r; -} - function create_repost(&$user, $url) { $micropub_request = array( 'repost-of' => $url @@ -452,40 +438,6 @@ $app->post('/favorite', function() use($app) { } }); -$app->post('/photo', function() use($app) { - if($user=require_login($app)) { - - // var_dump($app->request()->post()); - // - // Since $app->request()->post() with multipart is always - // empty (bug in Slim?) We're using the raw $_POST here - // until this gets fixed. - // PHP empties everything in $_POST if the file upload size exceeds - // that is why we have to test if the variables exist first. - - $note_content = isset($_POST['note_content']) ? $_POST['note_content'] : null; - $params = array('note_content' => $note_content); - $file = isset($_FILES['note_photo']) ? $_FILES['note_photo'] : null; - - $r = create_photo($user, $params, $file); - - // Populate the error if there was no location header. - if(empty($r['location']) && empty($r['error'])) { - $r['error'] = "No 'Location' header in response."; - } - - $html = render('photo', array( - 'title' => 'Photo posted', - 'note_content' => $params['note_content'], - 'location' => (isset($r['location']) ? $r['location'] : null), - 'error' => (isset($r['error']) ? $r['error'] : null), - 'response' => (isset($r['response']) ? htmlspecialchars($r['response']) : null), - 'authorizing' => false - )); - $app->response()->body($html); - } -}); - $app->post('/repost', function() use($app) { if($user=require_login($app)) { $params = $app->request()->params(); @@ -530,6 +482,47 @@ $app->post('/micropub/post', function() use($app) { } }); +$app->post('/micropub/multipart', function() use($app) { + if($user=require_login($app)) { + // var_dump($app->request()->post()); + // + // Since $app->request()->post() with multipart is always + // empty (bug in Slim?) We're using the raw $_POST here. + // PHP empties everything in $_POST if the file upload size exceeds + // that is why we have to test if the variables exist first. + + $file = isset($_FILES['photo']) ? $_FILES['photo'] : null; + + if($file) { + $error = validate_photo($file); + + unset($_POST['null']); + + if(!$error) { + $file_path = $file['tmp_name']; + correct_photo_rotation($file_path); + $r = micropub_post_for_user($user, $_POST, $file_path); + } else { + $r = array('error' => $error); + } + } else { + unset($_POST['null']); + $r = micropub_post_for_user($user, $_POST); + } + + // Populate the error if there was no location header. + if(empty($r['location']) && empty($r['error'])) { + $r['error'] = "No 'Location' header in response."; + } + + $app->response()->body(json_encode(array( + 'response' => (isset($r['response']) ? htmlspecialchars($r['response']) : null), + 'location' => (isset($r['location']) ? $r['location'] : null), + 'error' => (isset($r['error']) ? $r['error'] : null), + ))); + } +}); + $app->post('/micropub/postjson', function() use($app) { if($user=require_login($app)) { $params = $app->request()->params(); diff --git a/lib/helpers.php b/lib/helpers.php index c606dab..3939708 100644 --- a/lib/helpers.php +++ b/lib/helpers.php @@ -323,3 +323,26 @@ function validate_photo(&$file) { return $e->getMessage(); } } + +// Reads the exif rotation data and actually rotates the photo. +// Only does anything if the exif library is loaded, otherwise is a noop. +function correct_photo_rotation($filename) { + if(class_exists('IMagick')) { + $image = new IMagick($filename); + $orientation = $image->getImageOrientation(); + switch($orientation) { + case IMagick::ORIENTATION_BOTTOMRIGHT: + $image->rotateImage(new ImagickPixel('#00000000'), 180); + break; + case IMagick::ORIENTATION_RIGHTTOP: + $image->rotateImage(new ImagickPixel('#00000000'), 90); + break; + case IMagick::ORIENTATION_LEFTBOTTOM: + $image->rotateImage(new ImagickPixel('#00000000'), -90); + break; + } + $image->setImageOrientation(IMagick::ORIENTATION_TOPLEFT); + $image->writeImage($filename); + } +} + diff --git a/public/js/script.js b/public/js/script.js index ea86931..50496d3 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -15,7 +15,11 @@ } function csv_to_array(val) { - return val.split(/[, ]+/); + if(val.length > 0) { + return val.split(/[, ]+/); + } else { + return []; + } } diff --git a/views/dashboard.php b/views/dashboard.php index b5c4aa3..a20a44b 100644 --- a/views/dashboard.php +++ b/views/dashboard.php @@ -8,7 +8,6 @@ <li><a href="/bookmark"><img src="/images/bookmark.svg" width="60"></a></li> <li><a href="/favorite"><img src="/images/star.svg" width="60"></a></li> <li><a href="/repost"><img src="/images/repost.svg" width="60"></a></li> - <li><a href="/photo"><img src="/images/camera.svg" width="60"></a></li> <li><a href="/itinerary"><img src="/images/plane.svg" width="60"></a></li> <li><a href="/email"><img src="/images/email.svg" width="60"></a></li> </ul> diff --git a/views/layout.php b/views/layout.php index f853edd..da97687 100644 --- a/views/layout.php +++ b/views/layout.php @@ -69,7 +69,6 @@ if(property_exists($this, 'include_facebook')) { <li><a href="/new">Note</a></li> <li><a href="/bookmark">Bookmark</a></li> <li><a href="/favorite">Favorite</a></li> - <li><a href="/photo">Photo</a></li> <?php } ?> <li><a href="/docs">Docs</a></li> diff --git a/views/new-post.php b/views/new-post.php index 532a5ca..6c76f51 100644 --- a/views/new-post.php +++ b/views/new-post.php @@ -10,21 +10,28 @@ </div> <div class="form-group"> - <label for="note_in_reply_to"><code>in-reply-to</code> (optional, a URL you are replying to)</label> + <label for="note_in_reply_to"><code>in-reply-to</code> (a URL you are replying to)</label> <input type="text" id="note_in_reply_to" value="<?= $this->in_reply_to ?>" class="form-control"> </div> <div class="form-group"> - <label for="note_category"><code>category</code> (optional, comma-separated list of tags)</label> + <label for="note_category"><code>category</code> (comma-separated list of tags, will be posted as an array)</label> <input type="text" id="note_category" value="" class="form-control" placeholder="e.g. web, personal"> </div> <div class="form-group"> - <label for="note_slug"><code>slug</code> (optional)</label> + <label for="note_slug"><code>slug</code></label> <input type="text" id="note_slug" value="" class="form-control"> </div> <div class="form-group"> + <label for="note_photo"><code>photo</code></label> + <input type="file" name="note_photo" id="note_photo" accept="image/*" onchange="previewPhoto(event)"> + <br> + <img src="" id="photo_preview" style="max-width: 300px; max-height: 300px;"> + </div> + + <div class="form-group"> <label for="note_syndicate-to"><code>syndicate-to</code> <a href="javascript:reload_syndications()">(refresh)</a></label> <div id="syndication-container"> <?php @@ -72,7 +79,7 @@ <?php if($this->test_response): ?> <h4>Last response from your Micropub endpoint <span id="last_response_date">(<?= relative_time($this->response_date) ?>)</span></h4> <?php endif; ?> - <pre id="test_response" style="width: 100%; min-height: 240px;"><?= htmlspecialchars($this->test_response) ?></pre> + <pre id="test_response" class="<?= $this->test_response ? '' : 'hidden' ?>" style="width: 100%; min-height: 240px;"><?= htmlspecialchars($this->test_response) ?></pre> <div class="callout"> @@ -105,13 +112,13 @@ </div> <style type="text/css"> + #note_content_remaining { float: right; font-size: 0.8em; font-weight: bold; } - .pcheck206 { color: #6ba15c; } /* tweet fits within the limit even after adding RT @username */ .pcheck207 { color: #c4b404; } /* danger zone, tweet will overflow when RT @username is added */ .pcheck200,.pcheck208 { color: #59cb3a; } /* exactly fits 140 chars, both with or without RT */ @@ -120,6 +127,10 @@ </style> <script> +function previewPhoto(event) { + document.getElementById('photo_preview').src = URL.createObjectURL(event.target.files[0]); +} + $(function(){ $("#note_content").on('change keyup', function(e){ @@ -146,11 +157,69 @@ $(function(){ syndications.push($(btn).data('syndication')); }); - $.post("/micropub/post", { + var category = csv_to_array($("#note_category").val()); + + var formData = new FormData(); + if(v=$("#note_content").val()) { + formData.append("content", v); + } + if(v=$("#note_in_reply_to").val()) { + formData.append("in-reply-to", v); + } + if(v=$("#note_location").val()) { + formData.append("location", v); + } + if(category.length > 0) { + formData.append("category", category); + } + if(syndications.length > 0) { + formData.append("syndicate-to", syndications); + } + if(v=$("#note_slug").val()) { + formData.append("slug", v); + } + + if(document.getElementById("note_photo").files[0]) { + formData.append("photo", document.getElementById("note_photo").files[0]); + } + + // Need to append a placeholder field because if the file size max is hit, $_POST will + // be empty, so the server needs to be able to recognize a post with only a file vs a failed one. + // This will be stripped by Quill before it's sent to the Micropub endpoint + formData.append("null","null"); + + + var request = new XMLHttpRequest(); + request.open("POST", "/micropub/multipart"); + request.onreadystatechange = function() { + if(request.readyState == XMLHttpRequest.DONE) { + console.log(request.responseText); + try { + var response = JSON.parse(request.responseText); + if(response.location) { + window.location = response.location; + // console.log(response.location); + } else { + $("#test_response").html(response.response).removeClass('hidden'); + $("#test_success").addClass('hidden'); + $("#test_error").removeClass('hidden'); + } + } catch(e) { + $("#test_success").addClass('hidden'); + $("#test_error").removeClass('hidden'); + } + $("#btn_post").removeClass("loading disabled").text("Post"); + } + } + $("#btn_post").addClass("loading disabled").text("Working..."); + request.send(formData); + + /* + $.post("/micropub/multipart", { content: $("#note_content").val(), 'in-reply-to': $("#note_in_reply_to").val(), location: $("#note_location").val(), - category: csv_to_array($("#note_category").val()), + category: category, slug: $("#note_slug").val(), 'syndicate-to': syndications }, function(data){ @@ -180,6 +249,8 @@ $(function(){ $("#last_request_container").show(); $("#test_response").html(response.response); }); + */ + return false; }); diff --git a/views/photo.php b/views/photo.php deleted file mode 100644 index 25651dd..0000000 --- a/views/photo.php +++ /dev/null @@ -1,56 +0,0 @@ - <div class="narrow"> - <?= partial('partials/header') ?> - - <form method="POST" action="/photo" role="form" style="margin-top: 20px;" id="note_form" enctype="multipart/form-data"> - - <div class="form-group"> - <label for="note_photo"><code>photo</code></label> - <div class="uploadBtn btn btn-default"> - <span>Choose File</span> - <input type="file" name="note_photo" id="note_photo" accept="image/jpg,image/jpeg,image/gif,image/png"> - </div> - <div class="hidden" id="photo_filename_container"> - <input type="text" class="form-control" disabled="disabled" id="photo_filename"> - </div> - <p class="help-block">Photo JPEG, GIF or PNG.</p> - </div> - - <div class="form-group"> - <label for="note_content"><code>content</code> (optional)</label> - <textarea name="note_content" id="note_content" value="" class="form-control" style="height: 4em;"><?php if(isset($this->note_content)) echo $this->note_content ?></textarea> - </div> - - <button class="btn btn-success" id="btn_post">Post</button> - - <div style="clear:both;"></div> - </form> - - <?php if(!empty($this->location)): ?> - <div class="alert alert-success"> - <strong>Success!</strong> Photo posted to: <em><a href="<?= $this->location ?>"><?= $this->location ?></a></em> - </div> - <?php endif ?> - - <?php if(!empty($this->error)): ?> - <div class="alert alert-danger"> - <strong>Error:</strong> <em><?= $this->error ?></em> - </div> - <?php endif ?> - - <?php if(!empty($this->response)): ?> - <h4>Response:</h4> - <pre><?= $this->response ?></pre> - <?php endif ?> - </div> - <script> - $(function(){ - document.getElementById("note_photo").onchange = function () { - var filename = this.value; - if(filename.match(/[^\\]+$/)) { - filename = filename.match(/[^\\]+$/)[0]; - } - $("#photo_filename").val(filename); - $("#photo_filename_container").removeClass("hidden"); - }; - }); - </script>
\ No newline at end of file |