AutoConnectAux.h 10.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
 * Declaration of AutoConnectAux basic class.
 * @file AutoConnectAux.h
 * @author hieromon@gmail.com
 * @version  1.2.0
 * @date 2029-04-17
 * @copyright  MIT license.
 */

#ifndef _AUTOCONNECTAUX_H_
#define _AUTOCONNECTAUX_H_

#include "AutoConnectDefs.h"
#include <vector>
#include <memory>
#include <functional>
#include <type_traits>
#ifdef AUTOCONNECT_USE_JSON
#include <Stream.h>
#endif // !AUTOCONNECT_USE_JSON
#include <PageBuilder.h>
#include "AutoConnectElement.h"
#include "AutoConnectTypes.h"

class AutoConnect;  // Reference to avoid circular
class AutoConnectAux;  // Reference to avoid circular

// Manage placed AutoConnectElement with a vector
typedef std::vector<std::reference_wrapper<AutoConnectElement>> AutoConnectElementVT;

// A type of callback function when  AutoConnectAux page requested.
typedef std::function<String(AutoConnectAux&, PageArgument&)>  AuxHandlerFunctionT;

// A type for the order in which callback functions are called.
typedef enum {
  AC_EXIT_AHEAD = 1,    /**< Callback before building HTML */
  AC_EXIT_LATER = 2,    /**< Callback after building HTML */
  AC_EXIT_BOTH = 3      /**< Callback twice before and after building HTML */
} AutoConnectExitOrder_t;

/**
 * A class that handles an auxiliary page with AutoConnectElement
 * that placed on it by binding it to the AutoConnect menu.
 * @param  uri     An uri string of this page.
 * @param  title   A title string of this page.
 * @param  addons  A set of AutoConnectElement vector.
 * @param  menu    A switch for item displaying in AutoConnect menu.
 */
class AutoConnectAux : public PageBuilder {
 public:
  explicit AutoConnectAux(const String& uri = String(""), const String& title = String(""), const bool menu = true, const AutoConnectElementVT addons = AutoConnectElementVT()) :
    chunk(PB_Chunk), _title(title), _menu(menu), _uriStr(String(uri)), _addonElm(addons), _handler(nullptr), _order(AC_EXIT_AHEAD), _uploadHandler(nullptr) { _uri = _uriStr.c_str(); }
  ~AutoConnectAux();
  AutoConnectElement& operator[](const String& name) { return *getElement(name); }
  void  add(AutoConnectElement& addon);                                 /**< Add an element to the auxiliary page */
  void  add(AutoConnectElementVT addons);                               /**< Add the element set to the auxiliary page */
  void  authentication(const AC_AUTH_t auth) { _httpAuth = auth; }      /**< Set certain page authentication */
  void  fetchElement(void);                                             /**< Fetch AutoConnectElements values from http query parameters */
  template<typename T>
  T&    getElement(const String& name);
  AutoConnectElement*   getElement(const String& name);                 /**< Get registered AutoConnectElement as specified name */
  AutoConnectElementVT& getElements(void) { return _addonElm; }         /**< Get vector of all elements */
  void  menu(const bool post) { _menu = post; }                         /**< Set or reset the display as menu item for this aux */
  bool  isMenu(void) { return _menu; }                                  /**< Return whether embedded in the menu or not */
  bool  isValid(void) const;                                            /**< Validate all AutoConnectInput value */
  bool  release(const String& name);                                    /**< Release an AutoConnectElement */
  bool  setElementValue(const String& name, const String value);        /**< Set value to specified element */
  bool  setElementValue(const String& name, std::vector<String> const& values);  /**< Set values collection to specified element */
  void  setTitle(const String& title) { _title = title; }               /**< Set a title of the auxiliary page */
  void  on(const AuxHandlerFunctionT handler, const AutoConnectExitOrder_t order = AC_EXIT_AHEAD) { _handler = handler; _order = order; }   /**< Set user handler */
  void  onUpload(PageBuilder::UploadFuncT uploadFunc) override { _uploadHandler = uploadFunc; }
  template<typename T>
  void  onUpload(T& uploadClass) {
    static_assert(std::is_base_of<AutoConnectUploadHandler, T>::value, "onUpload type must be inherited AutoConnectUploadHandler");
    _uploadHandler = std::bind(&T::upload, &uploadClass, std::placeholders::_1, std::placeholders::_2);
  }

#ifdef AUTOCONNECT_USE_JSON
  bool  load(PGM_P in);                                                 /**< Load whole elements to AutoConnectAux Page */
  bool  load(const __FlashStringHelper* in);                            /**< Load whole elements to AutoConnectAux Page */
  bool  load(const String& in);                                         /**< Load whole elements to AutoConnectAux Page */
  bool  load(Stream& in);                                               /**< Load whole elements to AutoConnectAux Page */
  bool  loadElement(PGM_P in, const String& name = String(""));         /**< Load specified element */
  bool  loadElement(PGM_P in, std::vector<String> const& names);        /**< Load any specified elements */
  bool  loadElement(const __FlashStringHelper* in, const String& name = String(""));  /**< Load specified element */
  bool  loadElement(const __FlashStringHelper* in, std::vector<String> const& names); /**< Load any specified elements */
  bool  loadElement(const String& in, const String& name = String("")); /**< Load specified element */
  bool  loadElement(const String& in, std::vector<String> const& names);/**< Load any specified elements */
  bool  loadElement(Stream& in, const String& name = String(""));       /**< Load specified element */
  bool  loadElement(Stream& in, std::vector<String> const& names);      /**< Load any specified elements */
  size_t  saveElement(Stream& out, std::vector<String> const& names = {});  /**< Write elements of AutoConnectAux to the stream */
#endif // !AUTOCONNECT_USE_JSON

  TransferEncoding_t    chunk;                                          /**< Chunked transfer specified */

 protected:
  void  upload(const String& requestUri, const HTTPUpload& upload);     /**< Uploader wrapper */
  void  _concat(AutoConnectAux& aux);                                   /**< Make up chain of AutoConnectAux */
  void  _join(AutoConnect& ac);                                         /**< Make a link to AutoConnect */
  PageElement*  _setupPage(const String& uri);                          /**< AutoConnectAux page builder */
  const String  _insertElement(PageArgument& args);                     /**< Insert a generated HTML to the page built by PageBuilder */
  const String  _insertStyle(PageArgument& args);                       /**< Insert CSS style */
  const String  _injectTitle(PageArgument& args) const { (void)(args); return _title; } /**< Returns title of this page to PageBuilder */
  const String  _injectMenu(PageArgument& args);                        /**< Inject menu title of this page to PageBuilder */
  const String  _indicateUri(PageArgument& args);                       /**< Inject the uri that caused the request */
  const String  _indicateEncType(PageArgument& args);                   /**< Inject the ENCTYPE attribute */
  void  _storeElements(WebServerClass* webServer);                      /**< Store element values from contained in request arguments */
  static AutoConnectElement&  _nullElement(void);                       /**< A static returning value as invalid */

#ifdef AUTOCONNECT_USE_JSON
  template<typename T>
  bool  _parseJson(T in);
  bool  _load(JsonObject& in);                                          /**< Load all elements from JSON object */
  bool  _loadElement(JsonVariant& in, const String& name);              /**< Load an element as specified name from JSON object */
  bool  _loadElement(JsonVariant& in, std::vector<String> const& names);  /**< Load any elements as specified name from JSON object */
  AutoConnectElement& _loadElement(JsonObject& in, const String& name); /**< Load an element as specified name from JSON object */
  AutoConnectElement* _createElement(const JsonObject& json);           /**< Create an AutoConnectElement instance from JSON object */
  static ACElement_t  _asElementType(const String& type);               /**< Convert a string of element type to the enumeration value */
  /**
   * Parse and load a JSON document which declares one of the AutoConnectElement.
   * The compiler instantiates this template according to the stored data type that contains the JSON document.
   * This template also generates different parsing function calls depending on the ArduinoJson version.
   * @param  T  An object type of the JSON document which must be a passable object to ArduinoJson.
   * @param  U  An instance of a source name to load.
   */
  template<typename T, typename U,
  typename std::enable_if<std::is_same<U, const String&>::value || std::is_same<U, std::vector<String> const&>::value>::type* = nullptr>
  bool _parseElement(T in, U name) {
    ArduinoJsonBuffer jsonBuffer(AUTOCONNECT_JSONBUFFER_PRIMITIVE_SIZE);
    JsonVariant jb;
  #if ARDUINOJSON_VERSION_MAJOR<=5
    jb = jsonBuffer.parse(in);
    if (!jb.success()) {
      AC_DBG("JSON parse error\n");
      return false;
    }
  #else
    DeserializationError  err = deserializeJson(jsonBuffer, in);
    if (err) {
      AC_DBG("Deserialize:%s\n", err.c_str());
      return false;
    }
    jb = jsonBuffer.as<JsonVariant>();
  #endif
    return _loadElement(jb, name);
  }
#endif // !AUTOCONNECT_USE_JSON

  String  _title;                             /**< A title of the page */
  bool    _menu;                              /**< Switch for menu displaying */
  bool    _deletable = false;                 /**< Allow deleting itself. */
  AC_AUTH_t _httpAuth = AC_AUTH_NONE;         /**< Applying HTTP authentication */
  String  _uriStr;                            /**< uri as String */
  AutoConnectElementVT  _addonElm;            /**< A vector set of AutoConnectElements placed on this auxiliary page */
  AutoConnectAux*       _next = nullptr;      /**< Auxiliary pages chain list */
  AutoConnect*          _ac = nullptr;        /**< Hosted AutoConnect instance */
  AuxHandlerFunctionT   _handler;             /**< User sketch callback function when AutoConnectAux page requested. */
  AutoConnectExitOrder_t  _order;             /**< The order in which callback functions are called. */
  PageBuilder::UploadFuncT    _uploadHandler; /**< The AutoConnectFile corresponding to current upload */
  AutoConnectFile*      _currentUpload;       /**< AutoConnectFile handling the current upload */
  static const char _PAGE_AUX[] PROGMEM;      /**< Auxiliary page template */

  // Protected members can be used from AutoConnect which handles AutoConnectAux pages.
  friend class AutoConnect;
};

#endif // !_AUTOCONNECTAUX_H_