commit 12de4ceb6109196827bf6058b838e39c4a9db5f5
parent e5ce4d688650ccb41b83c5a7001d63586fd6bfbc
Author: Felicitus <felicitus@felicitus.org>
Date: Sun, 10 Jul 2011 03:50:40 +0200
Added file upload support via webcam
Diffstat:
6 files changed, 299 insertions(+), 1 deletion(-)
diff --git a/frontend/index.php b/frontend/index.php
@@ -34,7 +34,8 @@ include("config.php");
<?php
}
?>
- <script type="text/javascript" src="js/org.phpjs.lib/php.default.min.js" defer="defer"></script>
+ <script type="text/javascript" src="js/org.phpjs.lib/php.default.min.js"></script>
+ <script type="text/javascript" src="js/webcam.js"></script>
</head>
<body>
diff --git a/frontend/js/Components/Widgets/AttachmentGrid.js b/frontend/js/Components/Widgets/AttachmentGrid.js
@@ -42,6 +42,13 @@ Ext.define('PartKeepr.AttachmentGrid', {
disabled: true
});
+ this.webcamButton = Ext.create("Ext.button.Button", {
+ text: i18n("Take image"),
+ handler: this.onWebcamClick,
+ scope: this,
+ icon: 'resources/fugue-icons/icons/webcam.png'
+ });
+
this.dockedItems = [{
xtype: 'toolbar',
items: [{
@@ -50,6 +57,7 @@ Ext.define('PartKeepr.AttachmentGrid', {
icon: 'resources/silkicons/attach.png',
handler: this.onAddClick
},
+ this.webcamButton,
this.viewButton,
this.deleteButton
]
@@ -90,6 +98,20 @@ Ext.define('PartKeepr.AttachmentGrid', {
this.getSelectionModel().on('selectionchange', this.onSelectChange, this);
this.on("itemdblclick", this.onDoubleClick, this);
},
+ onWebcamClick: function () {
+ var wp = Ext.create("PartKeepr.WebcamPanel");
+ wp.on("uploadComplete", this.onFileUploaded, this);
+
+ var j = Ext.create("Ext.window.Window", {
+ items: [
+ wp
+ ]
+ });
+
+ wp.on("uploadComplete", function () { j.close(); });
+
+ j.show();
+ },
onDoubleClick: function (view, record) {
if (record) {
this.viewAttachment(record);
diff --git a/frontend/js/Components/Widgets/WebcamPanel.js b/frontend/js/Components/Widgets/WebcamPanel.js
@@ -0,0 +1,60 @@
+/**
+ * Creates a panel with a webcam widget. The webcam widget is
+ * a flash (jpegcam).
+ */
+Ext.define('PartKeepr.WebcamPanel', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.WebcamPanel',
+ initComponent: function () {
+ // Create a toolbar with the "take photo" button
+ this.bbar = Ext.create("Ext.toolbar.Toolbar", {
+ enableOverflow: true,
+ items: [
+ {
+ xtype: 'button',
+ text: i18n("Take picture and upload"),
+ icon: 'resources/fugue-icons/icons/webcam.png',
+ handler: this.takePhoto
+ }
+ ]
+ });
+
+ // Render the SWF
+ this.on("afterrender", this.renderWebcam, this);
+
+ // Fires when the image upload is complete
+ this.addEvents("uploadComplete");
+ this.callParent();
+ },
+ /**
+ * Renders the webcam swf.
+ * @param e The element for this component
+ */
+ renderWebcam: function (e) {
+ webcam.set_swf_url("resources/webcam.swf");
+ webcam.set_quality(90);
+ webcam.set_api_url(PartKeepr.getBasePath()+"?service=TempFile&call=uploadCam&session="+PartKeepr.getApplication().getSession());
+ webcam.set_shutter_sound(false);
+ webcam.set_hook('onComplete', Ext.bind(this.onUploadComplete, this));
+
+ e.body.insertHtml('beforeEnd', webcam.get_html(640,480, 640, 480));
+ },
+ /**
+ * Takes a photo using the webcam.
+ */
+ takePhoto: function () {
+ webcam.snap();
+ },
+ /**
+ * Called when the upload is complete. Resumes webcam operation
+ * and fires the event. 'uploadComplete'
+ * @param message The server side message
+ */
+ onUploadComplete: function (message) {
+ var response = Ext.decode(message);
+
+ webcam.reset();
+ this.fireEvent("uploadComplete", response.response);
+
+ }
+});+
\ No newline at end of file
diff --git a/frontend/js/webcam.js b/frontend/js/webcam.js
@@ -0,0 +1,197 @@
+/* JPEGCam v1.0.9 */
+/* Webcam library for capturing JPEG images and submitting to a server */
+/* Copyright (c) 2008 - 2009 Joseph Huckaby <jhuckaby@goldcartridge.com> */
+/* Licensed under the GNU Lesser Public License */
+/* http://www.gnu.org/licenses/lgpl.html */
+
+/* Usage:
+ <script language="JavaScript">
+ document.write( webcam.get_html(320, 240) );
+ webcam.set_api_url( 'test.php' );
+ webcam.set_hook( 'onComplete', 'my_callback_function' );
+ function my_callback_function(response) {
+ alert("Success! PHP returned: " + response);
+ }
+ </script>
+ <a href="javascript:void(webcam.snap())">Take Snapshot</a>
+*/
+
+// Everything is under a 'webcam' Namespace
+window.webcam = {
+ version: '1.0.9',
+
+ // globals
+ ie: !!navigator.userAgent.match(/MSIE/),
+ protocol: location.protocol.match(/https/i) ? 'https' : 'http',
+ callback: null, // user callback for completed uploads
+ swf_url: 'webcam.swf', // URI to webcam.swf movie (defaults to cwd)
+ shutter_url: 'shutter.mp3', // URI to shutter.mp3 sound
+ api_url: '', // URL to upload script
+ loaded: false, // true when webcam movie finishes loading
+ quality: 90, // JPEG quality (1 - 100)
+ shutter_sound: true, // shutter sound effect on/off
+ stealth: false, // stealth mode (do not freeze image upon capture)
+ hooks: {
+ onLoad: null,
+ onComplete: null,
+ onError: null
+ }, // callback hook functions
+
+ set_hook: function(name, callback) {
+ // set callback hook
+ // supported hooks: onLoad, onComplete, onError
+ if (typeof(this.hooks[name]) == 'undefined')
+ return alert("Hook type not supported: " + name);
+
+ this.hooks[name] = callback;
+ },
+
+ fire_hook: function(name, value) {
+ // fire hook callback, passing optional value to it
+ if (this.hooks[name]) {
+ if (typeof(this.hooks[name]) == 'function') {
+ // callback is function reference, call directly
+ this.hooks[name](value);
+ }
+ else if (typeof(this.hooks[name]) == 'array') {
+ // callback is PHP-style object instance method
+ this.hooks[name][0][this.hooks[name][1]](value);
+ }
+ else if (window[this.hooks[name]]) {
+ // callback is global function name
+ window[ this.hooks[name] ](value);
+ }
+ return true;
+ }
+ return false; // no hook defined
+ },
+
+ set_api_url: function(url) {
+ // set location of upload API script
+ this.api_url = url;
+ },
+
+ set_swf_url: function(url) {
+ // set location of SWF movie (defaults to webcam.swf in cwd)
+ this.swf_url = url;
+ },
+
+ get_html: function(width, height, server_width, server_height) {
+ // Return HTML for embedding webcam capture movie
+ // Specify pixel width and height (640x480, 320x240, etc.)
+ // Server width and height are optional, and default to movie width/height
+ if (!server_width) server_width = width;
+ if (!server_height) server_height = height;
+
+ var html = '';
+ var flashvars = 'shutter_enabled=' + (this.shutter_sound ? 1 : 0) +
+ '&shutter_url=' + escape(this.shutter_url) +
+ '&width=' + width +
+ '&height=' + height +
+ '&server_width=' + server_width +
+ '&server_height=' + server_height;
+
+ if (this.ie) {
+ html += '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+this.protocol+'://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+width+'" height="'+height+'" id="webcam_movie" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+this.swf_url+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/></object>';
+ }
+ else {
+ html += '<embed id="webcam_movie" src="'+this.swf_url+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="webcam_movie" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" />';
+ }
+
+ this.loaded = false;
+ return html;
+ },
+
+ get_movie: function() {
+ // get reference to movie object/embed in DOM
+ if (!this.loaded) return alert("ERROR: Movie is not loaded yet");
+ var movie = document.getElementById('webcam_movie');
+ if (!movie) alert("ERROR: Cannot locate movie 'webcam_movie' in DOM");
+ return movie;
+ },
+
+ set_stealth: function(stealth) {
+ // set or disable stealth mode
+ this.stealth = stealth;
+ },
+
+ snap: function(url, callback, stealth) {
+ // take snapshot and send to server
+ // specify fully-qualified URL to server API script
+ // and callback function (string or function object)
+ if (callback) this.set_hook('onComplete', callback);
+ if (url) this.set_api_url(url);
+ if (typeof(stealth) != 'undefined') this.set_stealth( stealth );
+
+ this.get_movie()._snap( this.api_url, this.quality, this.shutter_sound ? 1 : 0, this.stealth ? 1 : 0 );
+ },
+
+ freeze: function() {
+ // freeze webcam image (capture but do not upload)
+ this.get_movie()._snap('', this.quality, this.shutter_sound ? 1 : 0, 0 );
+ },
+
+ upload: function(url, callback) {
+ // upload image to server after taking snapshot
+ // specify fully-qualified URL to server API script
+ // and callback function (string or function object)
+ if (callback) this.set_hook('onComplete', callback);
+ if (url) this.set_api_url(url);
+
+ this.get_movie()._upload( this.api_url );
+ },
+
+ reset: function() {
+ // reset movie after taking snapshot
+ this.get_movie()._reset();
+ },
+
+ configure: function(panel) {
+ // open flash configuration panel -- specify tab name:
+ // "camera", "privacy", "default", "localStorage", "microphone", "settingsManager"
+ if (!panel) panel = "camera";
+ this.get_movie()._configure(panel);
+ },
+
+ set_quality: function(new_quality) {
+ // set the JPEG quality (1 - 100)
+ // default is 90
+ this.quality = new_quality;
+ },
+
+ set_shutter_sound: function(enabled, url) {
+ // enable or disable the shutter sound effect
+ // defaults to enabled
+ this.shutter_sound = enabled;
+ this.shutter_url = url ? url : 'shutter.mp3';
+ },
+
+ flash_notify: function(type, msg) {
+ // receive notification from flash about event
+ switch (type) {
+ case 'flashLoadComplete':
+ // movie loaded successfully
+ this.loaded = true;
+ this.fire_hook('onLoad');
+ break;
+
+ case 'error':
+ // HTTP POST error most likely
+ if (!this.fire_hook('onError', msg)) {
+ alert("JPEGCam Flash Error: " + msg);
+ }
+ break;
+
+ case 'success':
+ // upload complete, execute user callback function
+ // and pass raw API script results to function
+ this.fire_hook('onComplete', msg.toString());
+ break;
+
+ default:
+ // catch-all, just in case
+ alert("jpegcam flash_notify: " + type + ": " + msg);
+ break;
+ }
+ }
+};
diff --git a/frontend/resources/webcam.swf b/frontend/resources/webcam.swf
Binary files differ.
diff --git a/src/de/RaumZeitLabor/PartKeepr/TempFile/TempFileService.php b/src/de/RaumZeitLabor/PartKeepr/TempFile/TempFileService.php
@@ -27,5 +27,22 @@ class TempFileService extends Service {
return array("id" => $tmpFile->getId(), "extension" => $tmpFile->getExtension(), "size" => $tmpFile->getSize(), "originalFilename" => $tmpFile->getOriginalFilename());
}
+
+ /**
+ * Processes data via HTTP POST. Reads php://input and creates a temporary image out of it.
+ */
+ public function uploadCam () {
+ $tempFile = tempnam("/tmp", "PWC") . ".jpg";
+ $result = file_put_contents( $tempFile, file_get_contents('php://input') );
+
+ $image = new TempUploadedFile();
+ $image->replace($tempFile);
+ $image->setOriginalFilename(sprintf(PartKeepr::i18n("Cam photo of %s"), date("Y-m-d H:i:s")).".jpg");
+
+ PartKeepr::getEM()->persist($image);
+ PartKeepr::getEM()->flush();
+
+ return array("id" => $image->getId(), "extension" => $image->getExtension(), "size" => $image->getSize(), "originalFilename" => $image->getOriginalFilename());
+ }
}
\ No newline at end of file