From 601fad02cca850754036735c34ddb1908aa7be2d Mon Sep 17 00:00:00 2001
From: Roel Standaert
Date: Wed, 1 Feb 2017 14:49:10 +0100
Subject: [PATCH] Several changes:
- Release notes for Wt 3.3.7, bump version
- Respond with 505 to strange versions, and only with HTTP/1.0 or HTTP/1.1
- WAxis constructor should be public
- Chart series selection: only select closest series if it is within 20px of the given point
- Added touch drag documentation
- Document new methods in WTimeEdit
- Install instructions: set minimum Boost to the lowest version we test with
- WFileDropWidget: use a different WResource for each upload, so we can't get a dataReceived() signal from a canceled upload, fixed cancel race condition
- added PDF mimetype
- fix unorthodox use of request to forward dataExceeded info broken by async handling in websession event queue
- Fixed progressive bootstrap for WFileDropWidget
---
CMakeLists.txt | 26 +++++------
Doxyfile | 2 +-
INSTALL | 38 ++++++++--------
INSTALL.html | 2 +-
INSTALL.win32.html | 2 +-
ReleaseNotes.html | 82 ++++++++++++++++++++++++++++++++++
examples/Doxyfile | 2 +-
src/Wt/Chart/WAxis | 6 ++-
src/Wt/Chart/WCartesianChart | 2 +-
src/Wt/Chart/WCartesianChart.C | 32 +++++++++----
src/Wt/WFileDropWidget | 5 +++
src/Wt/WFileDropWidget.C | 76 +++++++++++++++++++++++++------
src/Wt/WFileUpload | 1 +
src/Wt/WFileUpload.C | 35 +++++++--------
src/Wt/WInteractWidget | 3 ++
src/Wt/WPaintedWidget.C | 15 +++----
src/Wt/WResource | 3 ++
src/Wt/WTimeEdit | 27 +++++++++++
src/http/MimeTypes.C | 1 +
src/http/Reply.C | 14 +++---
src/http/Reply.h | 3 +-
src/http/Request.h | 2 +
src/http/RequestHandler.C | 2 +-
src/http/StockReply.C | 10 +++++
src/js/WFileDropWidget.js | 14 +++---
src/js/WFileDropWidget.min.js | 12 ++---
src/web/WebController.C | 10 +++--
27 files changed, 313 insertions(+), 114 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 80150beed5..e7fdbd488f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,19 +18,19 @@ SET(CMAKE_MODULE_PATH
SET(VERSION_SERIES 3)
SET(VERSION_MAJOR 3)
-SET(VERSION_MINOR 6)
-
-SET(WT_SOVERSION 40)
-SET(WTEXT_SOVERSION 40)
-SET(WTHTTP_SOVERSION 40)
-SET(WTFCGI_SOVERSION 40)
-SET(WTISAPI_SOVERSION 16)
-SET(WTDBO_SOVERSION 40)
-SET(WTDBOSQLITE3_SOVERSION 40)
-SET(WTDBOPOSTGRES_SOVERSION 40)
-SET(WTDBOFIREBIRD_SOVERSION 40)
-SET(WTDBOMYSQL_SOVERSION 40)
-SET(WTTEST_SOVERSION 10)
+SET(VERSION_MINOR 7)
+
+SET(WT_SOVERSION 41)
+SET(WTEXT_SOVERSION 41)
+SET(WTHTTP_SOVERSION 41)
+SET(WTFCGI_SOVERSION 41)
+SET(WTISAPI_SOVERSION 17)
+SET(WTDBO_SOVERSION 41)
+SET(WTDBOSQLITE3_SOVERSION 41)
+SET(WTDBOPOSTGRES_SOVERSION 41)
+SET(WTDBOFIREBIRD_SOVERSION 41)
+SET(WTDBOMYSQL_SOVERSION 41)
+SET(WTTEST_SOVERSION 11)
IF(NOT SHARED_LIBS)
IF(WIN32)
diff --git a/Doxyfile b/Doxyfile
index 795e282ab5..649946ce42 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -32,7 +32,7 @@ PROJECT_NAME = Wt
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER = 3.3.6
+PROJECT_NUMBER = 3.3.7
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer
diff --git a/INSTALL b/INSTALL
index eeb55cbbc6..9ee787fc9f 100644
--- a/INSTALL
+++ b/INSTALL
@@ -14,12 +14,10 @@ Requirements
you can have both of them.
The built-in web server is more convenient during development and is
- easier to setup.
+ easier to setup. It also allows you to use WebSockets.
- The FastCGI based solution provides more flexibility for deployment of
- the application. The built-in web server runs all sessions in a single
- process, while the FastCGI based solution allows different deployment
- schemes including dedicated processes per sessions.
+ The FastCGI based solution can be more convenient for deployment behind
+ another web server.
Each of these two choices correspond to a library, a so-called
connector library. Below it is outlined how to configure the build
@@ -38,7 +36,7 @@ Requirements
Preferably CMake 2.6, which comes with a usable script for finding
boost libraries, but CMake 2.4 is still supported using Wt's own
boost find script.
- * [5]C++ boost library (preferably version 1.41 or higher), with or
+ * [5]C++ boost library (preferably version 1.46.1 or higher), with or
without thread support. You can verify you have a thread-enabled
boost installation by locating the libboost_thread library. Thread
support is not essential: Wt functionality is not affected except
@@ -49,10 +47,8 @@ Requirements
features will be disabled that depend on the revised versions of
spirit, namely JSON parsing and improved SQL parsing (for Wt::Dbo).
* Optionally, [6]OpenSSL, which is used to support the HTTPS protocol
- in the web client, the HTTPS protocol in the built-in wthttpd
- connector, additional cryptographic hash functions in the Auth
- library, and WebSockets (which requires a SHA-1 hash
- implementation).
+ in the web client, and the HTTPS protocol in the built-in wthttpd
+ connector.
* Optionally, [7]Haru Free PDF Library, which is used to provide
support for painting to PDF (WPdfImage).
* Optionally, [8]GraphicsMagick, for supporting painting to raster
@@ -80,8 +76,7 @@ Requirements
to enable optional features (you can also build without them), but
otherwise no extra dependencies are required.
* Optionally, zlib (libz), for compression over HTTP.
- * Optionally, OpenSSL (libopenssl), for HTTPS and WebSockets (which
- requires a SHA-1 hash implementation).
+ * Optionally, OpenSSL (libopenssl), for HTTPS.
2 Additional and optional requirements for some of the examples
@@ -124,10 +119,14 @@ Building and installing the Wt library
may help CMake by setting some variables to help CMake locate the
libraries. This may be done on the command-line using -Dvar=value or
using the interactive program:
- $ ccmake .
+ $ ccmake ../
- Variables that you may set to configure Wt's built-in boost finding
- method:
+ or
+ $ cmake-gui ../
+
+ The GUI lists all variables that are configurable in Wt's build
+ process. Variables that you may set to configure Wt's built-in boost
+ finding method:
BOOST_COMPILER
The boost compiler signature. For a library
@@ -142,7 +141,8 @@ Building and installing the Wt library
lib/ and include/ are located for your boost installation.
Other variables specify several build and configuration aspects of Wt,
- of which the most relevant ones are:
+ of which the most relevant ones are (there are many more visible in the
+ GUI):
CMAKE_INSTALL_PREFIX
Installation prefix for the library and include files)
@@ -333,9 +333,9 @@ HTTPS server options:
References
- 1. file://localhost/home/koen/project/wt/git/wt/INSTALL.html#requirements
- 2. file://localhost/home/koen/project/wt/git/wt/INSTALL.html#build
- 3. file://localhost/home/koen/project/wt/git/wt/INSTALL.html#examples
+ 1. file:///home/roel/project/wt/git/wt/INSTALL.html#requirements
+ 2. file:///home/roel/project/wt/git/wt/INSTALL.html#build
+ 3. file:///home/roel/project/wt/git/wt/INSTALL.html#examples
4. http://www.cmake.org/
5. http://www.boost.org/
6. http://www.openssl.org/
diff --git a/INSTALL.html b/INSTALL.html
index aa07c17dd7..0a73d76f11 100644
--- a/INSTALL.html
+++ b/INSTALL.html
@@ -57,7 +57,7 @@ 1 Wt requirements
C++ boost library (preferably
- version 1.41 or higher), with or without thread support. You can
+ version 1.46.1 or higher), with or without thread support. You can
verify you have a thread-enabled boost installation by locating the
libboost_thread library. Thread support is not essential: Wt
functionality is not affected except for exotic things like server
diff --git a/INSTALL.win32.html b/INSTALL.win32.html
index 1ffa9c2c38..6d860d5a61 100644
--- a/INSTALL.win32.html
+++ b/INSTALL.win32.html
@@ -30,7 +30,7 @@ Requirements
build on the Express Edition, which is free (as in beer) to use.
CMake cross-platform build system (www.cmake.org): cmake-2.6.x,
Windows version (2.8 or newer recommended).
- Boost 1.36 (or later; a recent version is recommended)
+ Boost 1.46.1 (or later; a recent version is recommended)
diff --git a/ReleaseNotes.html b/ReleaseNotes.html
index 1648c2d2e9..d509399dbc 100644
--- a/ReleaseNotes.html
+++ b/ReleaseNotes.html
@@ -22,6 +22,88 @@ Wt Release notes
the way you build Wt, the way you configure Wt or the Wt API and
behaviour.
+Release 3.3.7 (February ?, 2017)
+
+ This release fixes many bugs, but also introduces some new features:
+
+
+ -
+ WFileDropWidget
+
+ -
+ The WFileDropWidget is a new widget that allows you to upload a file or multiple files by dragging them onto an area.
+
+ -
+ Scroll visibility
+
+ -
+ Some applications require you to know whether a widget is currently visible within the viewport, or whether it is
+ scrolled out of view, e.g. to load more content as you scroll down the page. You can now enable scroll visibility
+ detection with
+ WWidget::setScrollVisibilityEnabled(bool),
+ and react to changes in visibility with
+ WWidget::scrollVisibilityChanged().
+ A new scrollvisibility feature example has been added to demonstrate this infinite scrolling application.
+
+ -
+ Touch events
+
+ -
+ Although Wt already supported touch interactions in the charting library, touch events were previously
+ not exposed by Wt. Now,
+ we've added WTouchEvent, and the touchStarted, touchEnded, and touchMoved
+ events have been added to
+ WInteractWidget.
+ Also, draggable widgets can now also
+ be dragged after a long press, and you can select a range using a double touch in
+ WTableView and WTreeView.
+
+ -
+ Combined session tracking mode
+
+ -
+
+ The default session tracking method for Wt is URL rewriting, using JavaScript to hide the session id from
+ the address bar. Alternatively, cookies can be used with the Auto option, falling back to URL rewriting when
+ cookies are not available. However, the cookie-based method did not allow for multiple sessions within the
+ same browser.
+
+
+ In order to make the URL rewriting method with requirement 6.5.10 of the PCI Data Security Standard, while not
+ sacrificing the ability to have multiple sessions, a new Combined session tracking strategy has been added.
+ Wt already makes it difficult to steal a session when the session id is discovered, but resources are not as
+ protected. The Combined session tracking strategy uses URL rewriting in combination with a cookie that is shared
+ between sessions as an extra measure against session hijacking. This is the most secure strategy, but it will
+ deny access if cookie support is not available.
+
+
+ -
+ Wraparound for WSpinBox and WTimeEdit
+
+ -
+ WSpinBox will now wrap around from its maximum to its minimum if you enable wraparound. WTimeEdit will take advantage of this feature by default.
+
+ -
+ Some minor extra features:
+
+ -
+
+ -
+ It's now possible to retrieve a vector of all request headers with Wt::Http::Request::headers() in handleRequest when implementing a WResource. It is still recommended, and more efficient, to use headerValue, but retrieving a vector of all headers could be useful for debugging purposes.
+
+ -
+ In an effort to reduce the amount of JavaScript generated by the charting API, the
+ WPainter::drawStencilAlongPath()
+ method was added to WPainter.
+
+ -
+ Previously, WDialogs were movable by default. It's now possible to disable this with
+ WDialog::setMovable().
+
+
+
+
+
Release 3.3.6 (July 13, 2016)
This release has a focus on bug fixes and some new features:
diff --git a/examples/Doxyfile b/examples/Doxyfile
index 8f0ca606ee..5190763e71 100644
--- a/examples/Doxyfile
+++ b/examples/Doxyfile
@@ -4,7 +4,7 @@
# Project related configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = "Wt examples"
-PROJECT_NUMBER = 3.3.6
+PROJECT_NUMBER = 3.3.7
OUTPUT_DIRECTORY = ../doc/examples
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
diff --git a/src/Wt/Chart/WAxis b/src/Wt/Chart/WAxis
index ae8c79c18b..665885e4cd 100644
--- a/src/Wt/Chart/WAxis
+++ b/src/Wt/Chart/WAxis
@@ -188,6 +188,10 @@ public:
*/
static const double AUTO_MAXIMUM;
+ /*! \brief Constructor
+ */
+ WAxis();
+
/*! \brief Destructor
*/
virtual ~WAxis();
@@ -970,8 +974,6 @@ protected:
TickLabel(double v, TickLength length, const WString& l = WString());
};
- WAxis();
-
/*! \brief Returns the label (and ticks) information for this axis.
*/
virtual void getLabelTicks(std::vector& ticks, int segment, AxisConfig config) const;
diff --git a/src/Wt/Chart/WCartesianChart b/src/Wt/Chart/WCartesianChart
index d8f62be1d5..36025ad833 100644
--- a/src/Wt/Chart/WCartesianChart
+++ b/src/Wt/Chart/WCartesianChart
@@ -1194,7 +1194,7 @@ public:
/*! \brief A signal that notifies the selection of a new curve.
*
* This signal is emitted if a series is selected using a mouse
- * click or long press. The first argument is the model column of the selected
+ * click or long press. The first argument is the selected
* series. The second argument is the point that was selected, in model
* coordinates.
*
diff --git a/src/Wt/Chart/WCartesianChart.C b/src/Wt/Chart/WCartesianChart.C
index 45f68e2271..e523709c8f 100644
--- a/src/Wt/Chart/WCartesianChart.C
+++ b/src/Wt/Chart/WCartesianChart.C
@@ -46,6 +46,7 @@ namespace {
const int TICK_LENGTH = 5;
const int CURVE_LABEL_PADDING = 10;
const int DEFAULT_CURVE_LABEL_WIDTH = 100;
+const int CURVE_SELECTION_DISTANCE_SQUARED = 400; // Maximum selection distance in pixels, squared
inline int toZoomLevel(double zoomFactor)
{
@@ -4064,11 +4065,14 @@ void WCartesianChart::yTransformChanged()
void WCartesianChart::jsSeriesSelected(double x, double y)
{
- if (!seriesSelectionEnabled()) return;
- WPointF p = zoomRangeTransform(xTransformHandle_.value(), yTransformHandle_.value()).inverted().map(WPointF(x,y));
+ if (!seriesSelectionEnabled())
+ return;
+ WTransform transform = zoomRangeTransform(xTransformHandle_.value(), yTransformHandle_.value());
+ WPointF p = transform.inverted().map(WPointF(x,y));
double smallestSqDistance = std::numeric_limits::infinity();
const WDataSeries *closestSeries = 0;
- WPointF closestPoint;
+ WPointF closestPointPx;
+ WPointF closestPointBeforeSeriesTransform;
for (std::size_t i = 0; i < series_.size(); ++i) {
const WDataSeries &series = *series_[i];
if (!series.isHidden() && (series.type() == LineSeries || series.type() == CurveSeries)) {
@@ -4082,22 +4086,32 @@ void WCartesianChart::jsSeriesSelected(double x, double y)
WPointF segP = t.map(WPointF(seg.x(), seg.y()));
double dx = p.x() - segP.x();
double dy = p.y() - segP.y();
- double d = dx * dx + dy * dy;
- if (d < smallestSqDistance) {
- smallestSqDistance = d;
+ double d2 = dx * dx + dy * dy;
+ if (d2 < smallestSqDistance) {
+ smallestSqDistance = d2;
closestSeries = &series;
- closestPoint = p;
+ closestPointPx = segP;
+ closestPointBeforeSeriesTransform = WPointF(seg.x(), seg.y());
}
}
}
}
}
+ {
+ WPointF closestDisplayPoint = transform.map(closestPointPx);
+ double dx = closestDisplayPoint.x() - x;
+ double dy = closestDisplayPoint.y() - y;
+ double d2 = dx * dx + dy * dy;
+ if (d2 > CURVE_SELECTION_DISTANCE_SQUARED) {
+ return;
+ }
+ }
setSelectedSeries(closestSeries);
if (closestSeries) {
seriesSelected_.emit(closestSeries,
- mapFromDeviceWithoutTransform(closestPoint, closestSeries->axis()));
+ mapFromDeviceWithoutTransform(closestPointBeforeSeriesTransform, closestSeries->axis()));
} else {
- seriesSelected_.emit(0, mapFromDeviceWithoutTransform(closestPoint, YAxis));
+ seriesSelected_.emit(0, mapFromDeviceWithoutTransform(closestPointBeforeSeriesTransform, YAxis));
}
}
diff --git a/src/Wt/WFileDropWidget b/src/Wt/WFileDropWidget
index df459e70a4..8220183219 100644
--- a/src/Wt/WFileDropWidget
+++ b/src/Wt/WFileDropWidget
@@ -178,6 +178,9 @@ public:
*/
Signal& uploadFailed() { return uploadFailed_; }
+protected:
+ virtual void enableAjax() WT_CXX11ONLY(override);
+
private:
class WFileDropUploadResource : public WResource {
public:
@@ -194,12 +197,14 @@ private:
File *currentFile_;
};
+ void setup();
void handleDrop(const std::string& newDrops);
void handleTooLarge(::uint64_t size);
void handleSendRequest(int id);
void emitUploaded(int id);
void stopReceiving();
void onData(::uint64_t current, ::uint64_t total);
+ void onDataExceeded(::uint64_t dataExceeded);
// Functions for handling incoming requests
void setUploadedFile(Http::UploadedFile file);
diff --git a/src/Wt/WFileDropWidget.C b/src/Wt/WFileDropWidget.C
index dd682579c4..1d849e6d34 100644
--- a/src/Wt/WFileDropWidget.C
+++ b/src/Wt/WFileDropWidget.C
@@ -104,7 +104,7 @@ void NestedResource::handleRequest(const Http::Request& request,
WFileDropWidget::WFileDropWidget(WContainerWidget *parent)
: WContainerWidget(parent),
- resource_(new WFileDropUploadResource(this)),
+ resource_(0),
currentFileIdx_(0),
dropSignal_(this, "dropsignal"),
requestSend_(this, "requestsend"),
@@ -116,15 +116,26 @@ WFileDropWidget::WFileDropWidget(WContainerWidget *parent)
if (!app->environment().ajax())
return;
+ setup();
+}
+
+void WFileDropWidget::enableAjax()
+{
+ setup();
+}
+
+void WFileDropWidget::setup()
+{
+ WApplication *app = WApplication::instance();
+
LOAD_JAVASCRIPT(app, "js/WFileDropWidget.js", "WFileDropWidget", wtjs1);
std::string maxFileSize =
boost::lexical_cast(
WApplication::instance()->maximumRequestSize());
setJavaScriptMember(" WFileDropWidget", "new " WT_CLASS ".WFileDropWidget("
- + app->javaScriptClass() + "," + jsRef() + ",'"
- + resource_->generateUrl() + "', "
- + maxFileSize + ");");
+ + app->javaScriptClass() + "," + jsRef() + ","
+ + maxFileSize + ");");
dropSignal_.connect(this, &WFileDropWidget::handleDrop);
@@ -133,8 +144,6 @@ WFileDropWidget::WFileDropWidget(WContainerWidget *parent)
uploadFinished_.connect(this, &WFileDropWidget::emitUploaded);
doneSending_.connect(this, &WFileDropWidget::stopReceiving);
- resource_->dataReceived().connect(this, &WFileDropWidget::onData);
-
addStyleClass("Wt-filedropzone");
}
@@ -200,10 +209,15 @@ void WFileDropWidget::handleSendRequest(int id)
if (uploads_[i]->uploadId() == id) {
fileFound = true;
currentFileIdx_ = i;
- doJavaScript(jsRef() + ".send();");
+ delete resource_;
+ resource_ = new WFileDropUploadResource(this);
+ resource_->dataReceived().connect(this, &WFileDropWidget::onData);
+ resource_->dataExceeded().connect(this, &WFileDropWidget::onDataExceeded);
+ doJavaScript(jsRef() + ".send('" + resource_->url() + "');");
uploadStart_.emit(uploads_[currentFileIdx_]);
break;
} else {
+ // If a previous upload was not cancelled, it must have failed
if (!uploads_[i]->cancelled())
uploadFailed_.emit(uploads_[i]);
}
@@ -219,6 +233,12 @@ void WFileDropWidget::handleSendRequest(int id)
void WFileDropWidget::handleTooLarge(::uint64_t size)
{
+ if (currentFileIdx_ >= uploads_.size()) {
+ // This shouldn't happen, but a mischievous client might emit
+ // this signal a few times, causing currentFileIdx_
+ // to go out of bounds
+ return;
+ }
tooLarge_.emit(uploads_[currentFileIdx_], size);
currentFileIdx_++;
}
@@ -240,6 +260,13 @@ void WFileDropWidget::stopReceiving()
// Note: args by value, since this is handled after handleRequest is finished
void WFileDropWidget::setUploadedFile(Http::UploadedFile file)
{
+ if (currentFileIdx_ >= uploads_.size()) {
+ // This shouldn't happen, but a mischievous client might emit
+ // the filetoolarge signal too many times, causing currentFileIdx_
+ // to go out of bounds
+ return;
+ }
+
File *f = uploads_[currentFileIdx_];
currentFileIdx_++;
@@ -255,7 +282,7 @@ void WFileDropWidget::setUploadedFile(Http::UploadedFile file)
void WFileDropWidget::emitUploaded(int id)
{
- for (unsigned i=0; i < currentFileIdx_; i++) {
+ for (unsigned i=0; i < currentFileIdx_ && i < uploads_.size(); i++) {
File *f = uploads_[i];
if (f->uploadId() == id) {
f->uploaded().emit();
@@ -266,6 +293,12 @@ void WFileDropWidget::emitUploaded(int id)
bool WFileDropWidget::incomingIdCheck(int id)
{
+ if (currentFileIdx_ >= uploads_.size()) {
+ // This shouldn't happen, but a mischievous client might emit
+ // the filetoolarge signal too many times, causing currentFileIdx_
+ // to go out of bounds
+ return false;
+ }
if (uploads_[currentFileIdx_]->uploadId() == id)
return true;
else {
@@ -283,7 +316,7 @@ void WFileDropWidget::cancelUpload(File *file)
bool WFileDropWidget::remove(File *file)
{
- for (unsigned i=0; i < currentFileIdx_; i++) {
+ for (unsigned i=0; i < currentFileIdx_ && i < uploads_.size(); i++) {
if (uploads_[i] == file) {
uploads_.erase(uploads_.begin()+i);
currentFileIdx_--;
@@ -295,17 +328,32 @@ bool WFileDropWidget::remove(File *file)
void WFileDropWidget::onData(::uint64_t current, ::uint64_t total)
{
- WebSession::Handler *h = WebSession::Handler::instance();
-
- ::int64_t dataExceeded = h->request()->postDataExceeded();
- h->setRequest(0, 0); // so that triggerUpdate() will work
-
+ if (currentFileIdx_ >= uploads_.size()) {
+ // This shouldn't happen, but a mischievous client might emit
+ // the filetoolarge signal too many times, causing currentFileIdx_
+ // to go out of bounds
+ return;
+ }
File *file = uploads_[currentFileIdx_];
file->dataReceived().emit(current, total);
WApplication::instance()->triggerUpdate();
}
+void WFileDropWidget::onDataExceeded(::uint64_t dataExceeded)
+{
+ if (currentFileIdx_ >= uploads_.size()) {
+ // This shouldn't happen, but a mischievous client might emit
+ // the filetoolarge signal too many times, causing currentFileIdx_
+ // to go out of bounds
+ return;
+ }
+ tooLarge_.emit(uploads_[currentFileIdx_], dataExceeded);
+
+ WApplication *app = WApplication::instance();
+ app->triggerUpdate();
+}
+
void WFileDropWidget::setHoverStyleClass(const std::string& className)
{
doJavaScript(jsRef() + ".configureHoverClass('" + className + "');");
diff --git a/src/Wt/WFileUpload b/src/Wt/WFileUpload
index eaabca8f3d..76019470b0 100644
--- a/src/Wt/WFileUpload
+++ b/src/Wt/WFileUpload
@@ -342,6 +342,7 @@ private:
void create();
void onData(::uint64_t current, ::uint64_t total);
+ void onDataExceeded(::uint64_t dataExceeded);
virtual void setRequestTooLarge(::int64_t size);
diff --git a/src/Wt/WFileUpload.C b/src/Wt/WFileUpload.C
index a0321d85ad..e581d630ee 100644
--- a/src/Wt/WFileUpload.C
+++ b/src/Wt/WFileUpload.C
@@ -146,6 +146,7 @@ void WFileUpload::create()
fileUploadTarget_ = new WFileUploadResource(this);
fileUploadTarget_->setUploadProgress(true);
fileUploadTarget_->dataReceived().connect(this, &WFileUpload::onData);
+ fileUploadTarget_->dataExceeded().connect(this, &WFileUpload::onDataExceeded);
setJavaScriptMember(WT_RESIZE_JS,
"function(self, w, h) {"
@@ -179,26 +180,6 @@ void WFileUpload::onData(::uint64_t current, ::uint64_t total)
{
dataReceived_.emit(current, total);
- WebSession::Handler *h = WebSession::Handler::instance();
-
- ::int64_t dataExceeded = h->request()->postDataExceeded();
- h->setRequest(0, 0); // so that triggerUpdate() will work
-
- if (dataExceeded) {
- doJavaScript(WT_CLASS ".$('if" + id() + "').src='"
- + fileUploadTarget_->url() + "';");
- if (flags_.test(BIT_UPLOADING)) {
- flags_.reset(BIT_UPLOADING);
- handleFileTooLarge(dataExceeded);
-
- WApplication *app = WApplication::instance();
- app->triggerUpdate();
- app->enableUpdates(false);
- }
-
- return;
- }
-
if (progressBar_ && flags_.test(BIT_UPLOADING)) {
progressBar_->setRange(0, (double)total);
progressBar_->setValue((double)current);
@@ -208,6 +189,20 @@ void WFileUpload::onData(::uint64_t current, ::uint64_t total)
}
}
+void WFileUpload::onDataExceeded(::uint64_t dataExceeded)
+{
+ doJavaScript(WT_CLASS ".$('if" + id() + "').src='"
+ + fileUploadTarget_->url() + "';");
+ if (flags_.test(BIT_UPLOADING)) {
+ flags_.reset(BIT_UPLOADING);
+ handleFileTooLarge(dataExceeded);
+
+ WApplication *app = WApplication::instance();
+ app->triggerUpdate();
+ app->enableUpdates(false);
+ }
+}
+
void WFileUpload::enableAjax()
{
create();
diff --git a/src/Wt/WInteractWidget b/src/Wt/WInteractWidget
index ce72fb35a6..de62eb5906 100644
--- a/src/Wt/WInteractWidget
+++ b/src/Wt/WInteractWidget
@@ -310,6 +310,9 @@ public:
* The widget to be identified as source in the dropEvent may be given
* explicitly, and will default to this widget otherwise.
*
+ * When using a touch interface, the widget can also be dragged after
+ * a long press.
+ *
* \note When JavaScript is disabled, drag&drop does not work.
*
* \sa WWidget::dropEvent(), WWidget::acceptDrops(), WDropEvent
diff --git a/src/Wt/WPaintedWidget.C b/src/Wt/WPaintedWidget.C
index 9668811ef1..422054f3ec 100644
--- a/src/Wt/WPaintedWidget.C
+++ b/src/Wt/WPaintedWidget.C
@@ -204,16 +204,15 @@ void WPaintedWidget::defineJavaScript()
if (getMethod() == HtmlCanvas) {
LOAD_JAVASCRIPT(app, "js/WPaintedWidget.js", "WPaintedWidget", wtjs10);
LOAD_JAVASCRIPT(app, "js/WPaintedWidget.js", "gfxUtils", wtjs11);
- }
-
- if (app && getMethod() == HtmlCanvas && jsObjects_.size() > 0) {
- setFormObject(true);
+ if (jsObjects_.size() > 0) {
+ setFormObject(true);
- LOAD_JAVASCRIPT(app, "js/WJavaScriptObjectStorage.js", "WJavaScriptObjectStorage", wtjs20);
+ LOAD_JAVASCRIPT(app, "js/WJavaScriptObjectStorage.js", "WJavaScriptObjectStorage", wtjs20);
- jsDefined_ = true;
- } else {
- jsDefined_ = false;
+ jsDefined_ = true;
+ } else {
+ jsDefined_ = false;
+ }
}
}
diff --git a/src/Wt/WResource b/src/Wt/WResource
index 1ac8ab34b6..dc4a08fab7 100644
--- a/src/Wt/WResource
+++ b/src/Wt/WResource
@@ -328,6 +328,8 @@ public:
*/
Signal< ::uint64_t, ::uint64_t >& dataReceived() { return dataReceived_; }
+ Signal< ::uint64_t >& dataExceeded() { return dataExceeded_; }
+
/*! \brief Stream the resource to a stream.
*
* This is a convenience method to serialize to a stream (for
@@ -420,6 +422,7 @@ private:
Signal dataChanged_;
Signal< ::uint64_t, ::uint64_t > dataReceived_;
+ Signal< ::uint64_t > dataExceeded_;
bool trackUploadProgress_;
diff --git a/src/Wt/WTimeEdit b/src/Wt/WTimeEdit
index cfff41052e..edf9904784 100644
--- a/src/Wt/WTimeEdit
+++ b/src/Wt/WTimeEdit
@@ -81,19 +81,46 @@ public:
*/
WTime top() const;
+ /*! \brief Sets the step size for the hours
+ */
void setHourStep(int step);
+
+ /*! \brief Returns the step size for the hours
+ */
int hourStep() const;
+ /*! \brief Sets the step size for the minutes
+ */
void setMinuteStep(int step);
+
+ /*! \brief Returns the step size for the minutes
+ */
int minuteStep() const;
+ /*! \brief Sets the step size for the seconds
+ */
void setSecondStep(int step);
+
+ /*! \brief Returns the step size for the seconds
+ */
int secondStep() const;
+ /*! \brief Sets the step size for the milliseconds
+ */
void setMillisecondStep(int step);
+
+ /*! \brief Returns the step size for the milliseconds
+ */
int millisecondStep() const;
+ /*! \brief Enables or disables wraparound
+ *
+ * Wraparound is enabled by default
+ */
void setWrapAroundEnabled(bool rollover);
+
+ /*! \brief Returns whether wraparound is enabled
+ */
bool wrapAroundEnabled() const;
virtual void load();
diff --git a/src/http/MimeTypes.C b/src/http/MimeTypes.C
index 36b76c3b97..b3db8d84b3 100644
--- a/src/http/MimeTypes.C
+++ b/src/http/MimeTypes.C
@@ -42,6 +42,7 @@ struct mapping
{ "svg", "image/svg+xml" },
{ "webm", "video/webm" },
{ "xml", "application/xml" },
+ { "pdf", "application/pdf" },
{ 0, 0 } // Marks end of list.
};
diff --git a/src/http/Reply.C b/src/http/Reply.C
index 4e88529269..f7fe2aa973 100644
--- a/src/http/Reply.C
+++ b/src/http/Reply.C
@@ -159,6 +159,9 @@ void toText(S& stream, Reply::status_type status)
case Reply::service_unavailable:
stream << "503 Service Unavailable\r\n";
break;
+ case Reply::version_not_supported:
+ stream << "505 HTTP Version Not Supported\r\n";
+ break;
case Reply::no_status:
case Reply::internal_server_error:
stream << "500 Internal Server Error\r\n";
@@ -329,12 +332,11 @@ bool Reply::nextBuffers(std::vector& result)
* Status line.
*/
- buf_ << "HTTP/";
- buf_ << request_.http_version_major;
- buf_ << '.';
- buf_ << request_.http_version_minor;
-
- buf_ << ' ';
+ if (http10) {
+ buf_ << "HTTP/1.0 ";
+ } else {
+ buf_ << "HTTP/1.1 ";
+ }
status_strings::toText(buf_, status_);
diff --git a/src/http/Reply.h b/src/http/Reply.h
index b55d194ad7..e810e21b1b 100644
--- a/src/http/Reply.h
+++ b/src/http/Reply.h
@@ -84,7 +84,8 @@ class WTHTTP_API Reply : public boost::enable_shared_from_this
internal_server_error = 500,
not_implemented = 501,
bad_gateway = 502,
- service_unavailable = 503
+ service_unavailable = 503,
+ version_not_supported = 505
};
enum ws_opcode {
diff --git a/src/http/Request.h b/src/http/Request.h
index c812883948..afbd47eef5 100644
--- a/src/http/Request.h
+++ b/src/http/Request.h
@@ -89,6 +89,8 @@ class Request
#ifdef HTTP_WITH_SSL
ssl = 0;
#endif
+ http_version_major = -1;
+ http_version_minor = -1;
}
enum State { Partial, Complete, Error };
diff --git a/src/http/RequestHandler.C b/src/http/RequestHandler.C
index 9958b99e7f..0deb43274e 100644
--- a/src/http/RequestHandler.C
+++ b/src/http/RequestHandler.C
@@ -105,7 +105,7 @@ ReplyPtr RequestHandler::handleRequest(Request& req,
if ((req.http_version_major != 1)
|| (req.http_version_minor != 0
&& req.http_version_minor != 1))
- return ReplyPtr(new StockReply(req, Reply::not_implemented, "", config_));
+ return ReplyPtr(new StockReply(req, Reply::version_not_supported, "", config_));
// Decode url to path.
if (!url_decode(req.uri, req.request_path, req.request_query)) {
diff --git a/src/http/StockReply.C b/src/http/StockReply.C
index 88edc5fff6..274d609056 100644
--- a/src/http/StockReply.C
+++ b/src/http/StockReply.C
@@ -144,6 +144,12 @@ const std::string service_unavailable =
"503 Service Unavailable
"
"