Typica is a free program for professional coffee roasters. https://typica.us
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

user.w 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. @** Typica User Management.
  2. \noindent Starting in version 1.8, the concepts of database user and \pn{} user
  3. are separated. This means that there must be controls for creating new users
  4. and for selecting the user to log in as. Other management interfaces can be
  5. implemented in configuration scripts.
  6. @* Application extensions for user handling.
  7. \noindent In order to present information about the currently logged in user
  8. globally, it was decided to provide a few methods in |Application| that can be
  9. used to report and change the current user.
  10. The first of these simply reports the currently logged in user.
  11. @<Application Implementation@>=
  12. QString Application::currentTypicaUser()
  13. {
  14. return currentUser;
  15. }
  16. @ Next is a method that can be used to force the login of a specified user
  17. without checking for an entered password. This is used for users that are set
  18. to login automatically.
  19. @<Application Implementation@>=
  20. void Application::setCurrentTypicaUser(const QString &user)
  21. {
  22. currentUser = user;
  23. emit userChanged(currentUser);
  24. }
  25. @ A login method is provided which determines if a user exists that matches the
  26. user name and password specified and reports if the login attempt was
  27. successful.
  28. @<Application Implementation@>=
  29. bool Application::login(const QString &user, const QString &password)
  30. {
  31. SqlQueryConnection h;
  32. QSqlQuery *dbquery = h.operator->();
  33. dbquery->prepare("SELECT 1 FROM typica_users WHERE name = :name AND password = :password AND active = TRUE");
  34. dbquery->bindValue(":name", user);
  35. dbquery->bindValue(":password", password);
  36. dbquery->exec();
  37. if(dbquery->next())
  38. {
  39. currentUser = user;
  40. emit userChanged(currentUser);
  41. return true;
  42. }
  43. return false;
  44. }
  45. @ A convenience method is also provided to attempt an automatic login if one is
  46. specified in the database.
  47. @<Application Implementation@>=
  48. bool Application::autoLogin()
  49. {
  50. SqlQueryConnection h;
  51. QSqlQuery *dbquery = h.operator->();
  52. dbquery->exec("SELECT name FROM typica_users WHERE auto_login = TRUE");
  53. if(dbquery->next())
  54. {
  55. currentUser = dbquery->value(0).toString();
  56. emit userChanged(currentUser);
  57. return true;
  58. }
  59. return false;
  60. }
  61. @* Login dialog.
  62. \noindent If there are no users set to log in automatically or any time a user
  63. change is requested, a login dialog should be presented.
  64. @<Class declarations@>=
  65. class LoginDialog : public QDialog
  66. {
  67. Q_OBJECT
  68. public:
  69. LoginDialog();
  70. public slots:
  71. void attemptLogin();
  72. private:
  73. QLineEdit *user;
  74. QLineEdit *password;
  75. QLabel *warning;
  76. QPushButton *login;
  77. };
  78. @ The constructor sets up the interface.
  79. @<LoginDialog implementation@>=
  80. LoginDialog::LoginDialog() : QDialog(),
  81. user(new QLineEdit), password(new QLineEdit),
  82. warning(new QLabel(tr("Log in failed."))),
  83. login(new QPushButton(tr("Log In")))
  84. {
  85. setModal(true);
  86. QVBoxLayout *mainLayout = new QVBoxLayout;
  87. warning->setVisible(false);
  88. password->setEchoMode(QLineEdit::Password);
  89. QFormLayout *form = new QFormLayout;
  90. form->addRow(tr("Name:"), user);
  91. form->addRow(tr("Password:"), password);
  92. form->addRow(warning);
  93. QHBoxLayout *buttonBox = new QHBoxLayout;
  94. buttonBox->addStretch();
  95. buttonBox->addWidget(login);
  96. mainLayout->addLayout(form);
  97. mainLayout->addLayout(buttonBox);
  98. connect(login, SIGNAL(clicked()), this, SLOT(attemptLogin()));
  99. setLayout(mainLayout);
  100. }
  101. @ The log in button attempts to log in with the specified credentials.
  102. @<LoginDialog implementation@>=
  103. void LoginDialog::attemptLogin()
  104. {
  105. if(AppInstance->login(user->text(), password->text()))
  106. {
  107. accept();
  108. }
  109. else
  110. {
  111. warning->setVisible(true);
  112. }
  113. }
  114. @ Scripts must be able to create login dialogs.
  115. @<Set up the scripting engine@>=
  116. constructor = engine->newFunction(constructLoginDialog);
  117. value = engine->newQMetaObject(&LoginDialog::staticMetaObject, constructor);
  118. engine->globalObject().setProperty("LoginDialog", value);
  119. @ The constructor is trivial.
  120. @<Functions for scripting@>=
  121. QScriptValue constructLoginDialog(QScriptContext *, QScriptEngine *engine)
  122. {
  123. QScriptValue object = engine->newQObject(new LoginDialog);
  124. return object;
  125. }
  126. @ A function prototype is required.
  127. @<Function prototypes for scripting@>=
  128. QScriptValue constructLoginDialog(QScriptContext *context, QScriptEngine *engine);
  129. @* Currently logged in user.
  130. \noindent Every main window in \pn{} should be able to report on the currently
  131. logged in user and it should be possible to bring up an interface to switch
  132. users. An easy way to do this is through a widget inserted into the status bar
  133. of every window that listens for user change data from the |Application|
  134. instance.
  135. @<Class declarations@>=
  136. class UserLabel : public QLabel
  137. {
  138. Q_OBJECT
  139. public:
  140. UserLabel();
  141. public slots:
  142. void updateLabel(const QString &user);
  143. protected:
  144. void mouseReleaseEvent(QMouseEvent *event);
  145. };
  146. @ On first instantiation, the constructor sets the displayed text to indicate
  147. the currently logged in user and starts listening for user change events.
  148. @<UserLabel implementation@>=
  149. UserLabel::UserLabel() : QLabel()
  150. {
  151. setTextFormat(Qt::PlainText);
  152. updateLabel(AppInstance->currentTypicaUser());
  153. connect(AppInstance, SIGNAL(userChanged(QString)),
  154. this, SLOT(updateLabel(QString)));
  155. }
  156. @ When the currently logged in user changes, the label text updates itself.
  157. @<UserLabel implementation@>=
  158. void UserLabel::updateLabel(const QString &user)
  159. {
  160. setText(QString(tr("Current operator: %1").arg(user)));
  161. }
  162. @ In order to handle clicks, |mouseReleaseEvent()| is implemented.
  163. @<UserLabel implementation@>=
  164. void UserLabel::mouseReleaseEvent(QMouseEvent *)
  165. {
  166. LoginDialog *dialog = new LoginDialog;
  167. dialog->exec();
  168. }
  169. @* User Creation.
  170. \noindent The first time \pn{} is started with a database connection and a
  171. multi-user aware configuration there will not be any user records in the
  172. database. An interface for adding new users is provided.
  173. @<Class declarations@>=
  174. class NewTypicaUser: public QDialog
  175. {
  176. Q_OBJECT
  177. public:
  178. NewTypicaUser();
  179. public slots:
  180. void createAndReset();
  181. void createAndClose();
  182. void validate();
  183. void cancelValidate();
  184. private:
  185. void createNewUser();
  186. QLineEdit *userField;
  187. QLineEdit *passwordField;
  188. QCheckBox *autoLogin;
  189. QPushButton *saveAndCloseButton;
  190. QPushButton *saveAndNewButton;
  191. QPushButton *cancelButton;
  192. };
  193. @ The constructor sets up the dialog.
  194. @<NewTypicaUser implementation@>=
  195. NewTypicaUser::NewTypicaUser() : QDialog(),
  196. userField(new QLineEdit), passwordField(new QLineEdit),
  197. autoLogin(new QCheckBox(tr("Log in automatically"))),
  198. saveAndCloseButton(new QPushButton(tr("Save and Close"))),
  199. saveAndNewButton(new QPushButton(tr("Save and Create Another"))),
  200. cancelButton(new QPushButton(tr("Cancel")))
  201. {
  202. setModal(true);
  203. QVBoxLayout *mainLayout = new QVBoxLayout;
  204. QFormLayout *form = new QFormLayout;
  205. QHBoxLayout *buttons = new QHBoxLayout;
  206. form->addRow(tr("Name:"), userField);
  207. passwordField->setEchoMode(QLineEdit::Password);
  208. form->addRow(tr("Password:"), passwordField);
  209. form->addRow(autoLogin);
  210. buttons->addWidget(cancelButton);
  211. buttons->addStretch();
  212. buttons->addWidget(saveAndNewButton);
  213. buttons->addWidget(saveAndCloseButton);
  214. mainLayout->addLayout(form);
  215. mainLayout->addLayout(buttons);
  216. setLayout(mainLayout);
  217. setWindowTitle(tr("Create New User"));
  218. connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
  219. connect(saveAndCloseButton, SIGNAL(clicked()), this, SLOT(createAndClose()));
  220. connect(saveAndNewButton, SIGNAL(clicked()), this, SLOT(createAndReset()));
  221. connect(userField, SIGNAL(textChanged(QString)), this, SLOT(validate()));
  222. }
  223. @ Slots handle basic operation.
  224. @<NewTypicaUser implementation@>=
  225. void NewTypicaUser::createAndReset()
  226. {
  227. createNewUser();
  228. userField->setText("");
  229. passwordField->setText("");
  230. autoLogin->setChecked(false);
  231. }
  232. void NewTypicaUser::createAndClose()
  233. {
  234. createNewUser();
  235. accept();
  236. }
  237. void NewTypicaUser::createNewUser()
  238. {
  239. SqlQueryConnection h;
  240. QSqlQuery *dbquery = h.operator->();
  241. dbquery->prepare("INSERT INTO typica_users (name, password, active, auto_login) VALUES (:name, :password, true, :auto)");
  242. dbquery->bindValue(":name", userField->text());
  243. dbquery->bindValue(":password", passwordField->text());
  244. dbquery->bindValue(":auto", autoLogin->isChecked());
  245. dbquery->exec();
  246. cancelButton->setEnabled(true);
  247. }
  248. void NewTypicaUser::validate()
  249. {
  250. if(!userField->text().isEmpty())
  251. {
  252. saveAndCloseButton->setEnabled(true);
  253. saveAndNewButton->setEnabled(true);
  254. }
  255. else
  256. {
  257. saveAndCloseButton->setEnabled(false);
  258. saveAndNewButton->setEnabled(false);
  259. }
  260. }
  261. void NewTypicaUser::cancelValidate()
  262. {
  263. SqlQueryConnection h;
  264. QSqlQuery *dbquery = h.operator->();
  265. dbquery->exec("SELECT count(1) FROM typica_users");
  266. if(dbquery->next())
  267. {
  268. if(dbquery->value(0).toInt() > 0)
  269. {
  270. cancelButton->setEnabled(true);
  271. return;
  272. }
  273. }
  274. cancelButton->setEnabled(false);
  275. }
  276. @ This is exposted to the host environment in the usual way.
  277. @<Set up the scripting engine@>=
  278. constructor = engine->newFunction(constructNewTypicaUser);
  279. value = engine->newQMetaObject(&NewTypicaUser::staticMetaObject, constructor);
  280. engine->globalObject().setProperty("NewTypicaUser", value);
  281. @ The constructor is trivial.
  282. @<Functions for scripting@>=
  283. QScriptValue constructNewTypicaUser(QScriptContext *, QScriptEngine *engine)
  284. {
  285. QScriptValue object = engine->newQObject(new NewTypicaUser);
  286. return object;
  287. }
  288. @ A function prototype is required.
  289. @<Function prototypes for scripting@>=
  290. QScriptValue constructNewTypicaUser(QScriptContext *context, QScriptEngine *engine);
  291. @ Add class implementations to generated source file.
  292. @<Class implementations@>=
  293. @<NewTypicaUser implementation@>
  294. @<UserLabel implementation@>
  295. @<LoginDialog implementation@>