@saltcorn/server 1.3.0-beta.9 → 1.3.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) hide show
  1. package/CHANGELOG.md +15 -2
  2. package/app.js +9 -2
  3. package/auth/routes.js +6 -1
  4. package/docs/assets/search.js +1 -1
  5. package/docs/classes/_saltcorn_common_code.Relation.html +10 -10
  6. package/docs/classes/_saltcorn_common_code.RelationsFinder.html +5 -5
  7. package/docs/classes/_saltcorn_data.models.Crash.html +17 -17
  8. package/docs/classes/_saltcorn_data.models.EventLog-1.html +19 -19
  9. package/docs/classes/_saltcorn_data.models.Field.html +74 -74
  10. package/docs/classes/_saltcorn_data.models.FieldRepeat.html +16 -16
  11. package/docs/classes/_saltcorn_data.models.File-1.html +48 -48
  12. package/docs/classes/_saltcorn_data.models.Form-1.html +46 -38
  13. package/docs/classes/_saltcorn_data.models.Library.html +13 -13
  14. package/docs/classes/_saltcorn_data.models.Page.html +24 -24
  15. package/docs/classes/_saltcorn_data.models.PageGroup-1.html +25 -25
  16. package/docs/classes/_saltcorn_data.models.Plugin.html +25 -25
  17. package/docs/classes/_saltcorn_data.models.Role.html +9 -9
  18. package/docs/classes/_saltcorn_data.models.Table-1.html +95 -93
  19. package/docs/classes/_saltcorn_data.models.TableConstraint.html +14 -14
  20. package/docs/classes/_saltcorn_data.models.Trigger.html +36 -36
  21. package/docs/classes/_saltcorn_data.models.User.html +48 -48
  22. package/docs/classes/_saltcorn_data.models.View-1.html +54 -54
  23. package/docs/classes/_saltcorn_data.models.Workflow.html +17 -17
  24. package/docs/classes/_saltcorn_data.models.WorkflowRun.html +35 -35
  25. package/docs/classes/_saltcorn_data.models.WorkflowStep.html +25 -25
  26. package/docs/classes/_saltcorn_data.models.WorkflowTrace.html +18 -18
  27. package/docs/classes/_saltcorn_mobile_builder.MobileBuilder.html +43 -36
  28. package/docs/enums/_saltcorn_common_code.RelationType.html +7 -7
  29. package/docs/enums/_saltcorn_common_code.ViewDisplayType.html +4 -4
  30. package/docs/functions/_saltcorn_admin_models.backup.create_backup.html +1 -1
  31. package/docs/functions/_saltcorn_admin_models.backup.create_csv_from_rows.html +1 -1
  32. package/docs/functions/_saltcorn_admin_models.backup.restore.html +1 -1
  33. package/docs/functions/_saltcorn_admin_models.pack.add_to_menu.html +1 -1
  34. package/docs/functions/_saltcorn_admin_models.pack.can_install_pack.html +1 -1
  35. package/docs/functions/_saltcorn_admin_models.pack.fetch_available_packs.html +1 -1
  36. package/docs/functions/_saltcorn_admin_models.pack.fetch_pack_by_name.html +1 -1
  37. package/docs/functions/_saltcorn_admin_models.pack.install_pack.html +1 -1
  38. package/docs/functions/_saltcorn_admin_models.pack.library_pack.html +1 -1
  39. package/docs/functions/_saltcorn_admin_models.pack.model_instance_pack.html +1 -1
  40. package/docs/functions/_saltcorn_admin_models.pack.model_pack.html +1 -1
  41. package/docs/functions/_saltcorn_admin_models.pack.page_group_pack.html +1 -1
  42. package/docs/functions/_saltcorn_admin_models.pack.page_pack.html +1 -1
  43. package/docs/functions/_saltcorn_admin_models.pack.plugin_pack.html +1 -1
  44. package/docs/functions/_saltcorn_admin_models.pack.role_pack.html +1 -1
  45. package/docs/functions/_saltcorn_admin_models.pack.table_pack.html +1 -1
  46. package/docs/functions/_saltcorn_admin_models.pack.trigger_pack.html +1 -1
  47. package/docs/functions/_saltcorn_admin_models.pack.uninstall_pack.html +1 -1
  48. package/docs/functions/_saltcorn_admin_models.pack.view_pack.html +1 -1
  49. package/docs/functions/_saltcorn_admin_models.tenant.copy_tenant_template.html +1 -1
  50. package/docs/functions/_saltcorn_admin_models.tenant.create_tenant.html +1 -1
  51. package/docs/functions/_saltcorn_admin_models.tenant.deleteTenant.html +1 -1
  52. package/docs/functions/_saltcorn_admin_models.tenant.domain_sanitize.html +1 -1
  53. package/docs/functions/_saltcorn_admin_models.tenant.eachTenant.html +1 -1
  54. package/docs/functions/_saltcorn_admin_models.tenant.getAllTenantRows.html +1 -1
  55. package/docs/functions/_saltcorn_admin_models.tenant.getAllTenants.html +1 -1
  56. package/docs/functions/_saltcorn_admin_models.tenant.insertTenant.html +1 -1
  57. package/docs/functions/_saltcorn_admin_models.tenant.switchToTenant.html +1 -1
  58. package/docs/functions/_saltcorn_common_code.buildTableCaches.html +1 -1
  59. package/docs/functions/_saltcorn_common_code.parseLegacyRelation.html +1 -1
  60. package/docs/functions/_saltcorn_common_code.parseRelationPath.html +1 -1
  61. package/docs/functions/_saltcorn_data.models.config.check_email_mask.html +1 -1
  62. package/docs/functions/_saltcorn_data.models.config.deleteConfig.html +1 -1
  63. package/docs/functions/_saltcorn_data.models.config.getAllConfig.html +1 -1
  64. package/docs/functions/_saltcorn_data.models.config.getConfig.html +1 -1
  65. package/docs/functions/_saltcorn_data.models.config.get_base_url.html +1 -1
  66. package/docs/functions/_saltcorn_data.models.config.get_latest_npm_version.html +1 -1
  67. package/docs/functions/_saltcorn_data.models.config.remove_from_menu.html +1 -1
  68. package/docs/functions/_saltcorn_data.models.config.save_menu_items.html +1 -1
  69. package/docs/functions/_saltcorn_data.models.config.setConfig.html +1 -1
  70. package/docs/functions/_saltcorn_data.models.discovery.discover_tables.html +1 -1
  71. package/docs/functions/_saltcorn_data.models.discovery.discoverable_tables.html +1 -1
  72. package/docs/functions/_saltcorn_data.models.discovery.findType.html +1 -1
  73. package/docs/functions/_saltcorn_data.models.discovery.get_existing_views.html +1 -1
  74. package/docs/functions/_saltcorn_data.models.discovery.implement_discovery.html +1 -1
  75. package/docs/functions/_saltcorn_data.models.email.getMailTransport.html +1 -1
  76. package/docs/functions/_saltcorn_data.models.email.send_verification_email.html +1 -1
  77. package/docs/functions/_saltcorn_data.models.email.viewToEmailHtml.html +1 -1
  78. package/docs/functions/_saltcorn_data.models.expression.apply_calculated_fields.html +1 -1
  79. package/docs/functions/_saltcorn_data.models.expression.apply_calculated_fields_stored.html +1 -1
  80. package/docs/functions/_saltcorn_data.models.expression.eval_expression.html +1 -1
  81. package/docs/functions/_saltcorn_data.models.expression.expressionValidator.html +1 -1
  82. package/docs/functions/_saltcorn_data.models.expression.get_async_expression_function.html +3 -3
  83. package/docs/functions/_saltcorn_data.models.expression.get_expression_function.html +1 -1
  84. package/docs/functions/_saltcorn_data.models.expression.jsexprToWhere.html +1 -1
  85. package/docs/functions/_saltcorn_data.models.expression.recalculate_for_stored.html +1 -1
  86. package/docs/functions/_saltcorn_data.models.expression.transform_for_async.html +1 -1
  87. package/docs/functions/_saltcorn_data.models.layout.eachView.html +1 -1
  88. package/docs/functions/_saltcorn_data.models.layout.getStringsForI18n.html +1 -1
  89. package/docs/functions/_saltcorn_data.models.layout.getViews.html +1 -1
  90. package/docs/functions/_saltcorn_data.models.layout.translateLayout.html +1 -1
  91. package/docs/functions/_saltcorn_data.models.layout.traverse.html +1 -1
  92. package/docs/functions/_saltcorn_data.models.layout.traverseSync.html +1 -1
  93. package/docs/functions/_saltcorn_data.models.random.all_views.html +1 -1
  94. package/docs/functions/_saltcorn_data.models.random.fill_table_row.html +1 -1
  95. package/docs/functions/_saltcorn_data.models.random.initial_view.html +1 -1
  96. package/docs/functions/_saltcorn_data.models.random.random_table.html +1 -1
  97. package/docs/functions/_saltcorn_db_common.buildInsertBulkSql.html +1 -1
  98. package/docs/functions/_saltcorn_db_common.buildInsertSql.html +1 -1
  99. package/docs/functions/_saltcorn_db_common.doCount.html +1 -1
  100. package/docs/functions/_saltcorn_db_common.doDeleteWhere.html +1 -1
  101. package/docs/functions/_saltcorn_db_common.doListScTables.html +1 -1
  102. package/docs/functions/_saltcorn_db_common.doListTables.html +1 -1
  103. package/docs/functions/_saltcorn_db_common.doListUserDefinedTables.html +1 -1
  104. package/docs/functions/_saltcorn_db_common.do_add_index.html +1 -1
  105. package/docs/functions/_saltcorn_db_common.do_drop_index.html +1 -1
  106. package/docs/functions/_saltcorn_db_common.ftsFieldsSqlExpr.html +1 -1
  107. package/docs/functions/_saltcorn_db_common.mkSelectOptions.html +1 -1
  108. package/docs/functions/_saltcorn_db_common.mkVal.html +1 -1
  109. package/docs/functions/_saltcorn_db_common.mkWhere.html +1 -1
  110. package/docs/functions/_saltcorn_db_common.multi_tenant.enable_multi_tenant.html +1 -1
  111. package/docs/functions/_saltcorn_db_common.multi_tenant.getRequestContext.html +1 -1
  112. package/docs/functions/_saltcorn_db_common.multi_tenant.getTenantSchema.html +1 -1
  113. package/docs/functions/_saltcorn_db_common.multi_tenant.init.html +1 -1
  114. package/docs/functions/_saltcorn_db_common.multi_tenant.is_it_multi_tenant.html +1 -1
  115. package/docs/functions/_saltcorn_db_common.multi_tenant.runWithTenant.html +1 -1
  116. package/docs/functions/_saltcorn_db_common.orderByIsObject.html +1 -1
  117. package/docs/functions/_saltcorn_db_common.orderByIsOperator.html +1 -1
  118. package/docs/functions/_saltcorn_db_common.prefixFieldsInWhere.html +1 -1
  119. package/docs/functions/_saltcorn_db_common.reprAsJson.html +1 -1
  120. package/docs/functions/_saltcorn_db_common.slugify.html +1 -1
  121. package/docs/functions/_saltcorn_db_common.sqlBinOp.html +1 -1
  122. package/docs/functions/_saltcorn_db_common.sqlFun.html +1 -1
  123. package/docs/functions/_saltcorn_db_common.sqlsanitize.html +1 -1
  124. package/docs/functions/_saltcorn_db_common.sqlsanitizeAllowDots.html +1 -1
  125. package/docs/functions/_saltcorn_db_common.subSelectWhere.html +1 -1
  126. package/docs/functions/_saltcorn_db_common.tryCatchInTransaction.html +1 -1
  127. package/docs/functions/_saltcorn_db_common.withTransaction.html +1 -1
  128. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.androidFeatures.html +1 -1
  129. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.androidPermissions.html +1 -1
  130. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.buildTablesFile.html +1 -1
  131. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.copyPrepopulatedDb.html +1 -1
  132. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.copyServerFiles.html +1 -1
  133. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.copyShareExtFiles.html +1 -1
  134. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.copySiteLogo.html +1 -1
  135. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.copyTranslationFiles.html +1 -1
  136. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.createSqliteDb.html +1 -1
  137. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.decodeProvisioningProfile.html +1 -1
  138. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.extractDomain.html +1 -1
  139. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.generateAndroidVersionCode.html +1 -1
  140. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.modifyAndroidManifest.html +5 -3
  141. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.modifyConfigXml.html +1 -1
  142. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.modifyGradleConfig.html +1 -1
  143. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.modifyInfoPlist.html +1 -1
  144. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.modifyXcodeProjectFile.html +1 -1
  145. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.prepAppIcon.html +1 -1
  146. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.prepareAppIcon.html +1 -1
  147. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.prepareBuildDir.html +7 -2
  148. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.prepareExportOptionsPlist.html +1 -1
  149. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.prepareSplashIcon.html +1 -1
  150. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.prepareSplashPage.html +1 -1
  151. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.writeCapacitorConfig.html +1 -1
  152. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.writeCfgFile.html +1 -1
  153. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.writeDataExtractionRules.html +1 -1
  154. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.writeNetworkSecurityConfig.html +1 -1
  155. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.writePodfile.html +1 -1
  156. package/docs/functions/_saltcorn_mobile_builder.common_build_utils.writePrivacyInfo.html +1 -1
  157. package/docs/functions/_saltcorn_mobile_builder.package_bundle_utils.bundleMobileAppCode.html +1 -1
  158. package/docs/functions/_saltcorn_mobile_builder.package_bundle_utils.bundlePackagesAndPlugins.html +1 -1
  159. package/docs/functions/_saltcorn_mobile_builder.package_bundle_utils.copyMobileAppDirs.html +1 -1
  160. package/docs/functions/_saltcorn_mobile_builder.package_bundle_utils.copyPublicDirs.html +1 -1
  161. package/docs/functions/_saltcorn_sqlite.add_index.html +1 -1
  162. package/docs/functions/_saltcorn_sqlite.add_unique_constraint.html +1 -1
  163. package/docs/functions/_saltcorn_sqlite.begin.html +1 -1
  164. package/docs/functions/_saltcorn_sqlite.changeConnection.html +1 -1
  165. package/docs/functions/_saltcorn_sqlite.close.html +1 -1
  166. package/docs/functions/_saltcorn_sqlite.commit.html +1 -1
  167. package/docs/functions/_saltcorn_sqlite.count.html +1 -1
  168. package/docs/functions/_saltcorn_sqlite.deleteWhere.html +1 -1
  169. package/docs/functions/_saltcorn_sqlite.dropTable.html +1 -1
  170. package/docs/functions/_saltcorn_sqlite.dropTables.html +1 -1
  171. package/docs/functions/_saltcorn_sqlite.drop_index.html +1 -1
  172. package/docs/functions/_saltcorn_sqlite.drop_reset_schema.html +1 -1
  173. package/docs/functions/_saltcorn_sqlite.drop_unique_constraint.html +1 -1
  174. package/docs/functions/_saltcorn_sqlite.getVersion.html +1 -1
  175. package/docs/functions/_saltcorn_sqlite.get_db_filepath.html +1 -1
  176. package/docs/functions/_saltcorn_sqlite.get_sql_logging.html +1 -1
  177. package/docs/functions/_saltcorn_sqlite.init.html +1 -1
  178. package/docs/functions/_saltcorn_sqlite.insert.html +1 -1
  179. package/docs/functions/_saltcorn_sqlite.listScTables.html +1 -1
  180. package/docs/functions/_saltcorn_sqlite.listTables.html +1 -1
  181. package/docs/functions/_saltcorn_sqlite.listUserDefinedTables.html +1 -1
  182. package/docs/functions/_saltcorn_sqlite.query.html +1 -1
  183. package/docs/functions/_saltcorn_sqlite.rollback.html +1 -1
  184. package/docs/functions/_saltcorn_sqlite.select.html +1 -1
  185. package/docs/functions/_saltcorn_sqlite.selectMaybeOne.html +1 -1
  186. package/docs/functions/_saltcorn_sqlite.selectOne.html +1 -1
  187. package/docs/functions/_saltcorn_sqlite.set_sql_logging.html +1 -1
  188. package/docs/functions/_saltcorn_sqlite.sql_log.html +1 -1
  189. package/docs/functions/_saltcorn_sqlite.update.html +1 -1
  190. package/docs/functions/_saltcorn_sqlite.updateWhere.html +1 -1
  191. package/docs/functions/_saltcorn_sqlite_mobile.add_index.html +1 -1
  192. package/docs/functions/_saltcorn_sqlite_mobile.add_unique_constraint.html +1 -1
  193. package/docs/functions/_saltcorn_sqlite_mobile.count.html +1 -1
  194. package/docs/functions/_saltcorn_sqlite_mobile.deleteWhere.html +1 -1
  195. package/docs/functions/_saltcorn_sqlite_mobile.drop_index.html +1 -1
  196. package/docs/functions/_saltcorn_sqlite_mobile.drop_reset_schema.html +1 -1
  197. package/docs/functions/_saltcorn_sqlite_mobile.drop_unique_constraint.html +1 -1
  198. package/docs/functions/_saltcorn_sqlite_mobile.init.html +1 -1
  199. package/docs/functions/_saltcorn_sqlite_mobile.insert.html +1 -1
  200. package/docs/functions/_saltcorn_sqlite_mobile.insertRows.html +1 -1
  201. package/docs/functions/_saltcorn_sqlite_mobile.listScTables.html +1 -1
  202. package/docs/functions/_saltcorn_sqlite_mobile.listTables.html +1 -1
  203. package/docs/functions/_saltcorn_sqlite_mobile.listUserDefinedTables.html +1 -1
  204. package/docs/functions/_saltcorn_sqlite_mobile.query.html +1 -1
  205. package/docs/functions/_saltcorn_sqlite_mobile.select.html +1 -1
  206. package/docs/functions/_saltcorn_sqlite_mobile.selectMaybeOne.html +1 -1
  207. package/docs/functions/_saltcorn_sqlite_mobile.selectOne.html +1 -1
  208. package/docs/functions/_saltcorn_sqlite_mobile.setConnectionObject.html +1 -1
  209. package/docs/functions/_saltcorn_sqlite_mobile.tableExists.html +1 -1
  210. package/docs/functions/_saltcorn_sqlite_mobile.time.html +1 -1
  211. package/docs/functions/_saltcorn_sqlite_mobile.update.html +1 -1
  212. package/docs/functions/_saltcorn_types.ModelAbstracts.abstract_field.instanceOfField.html +1 -1
  213. package/docs/functions/_saltcorn_types.ModelAbstracts.abstract_page.instanceOfPage.html +1 -1
  214. package/docs/functions/_saltcorn_types.ModelAbstracts.abstract_view.instanceOfView.html +1 -1
  215. package/docs/functions/_saltcorn_types.Types.base_types.instanceOWithHtmlFile.html +1 -1
  216. package/docs/functions/_saltcorn_types.Types.common_types.instanceOfErrorMsg.html +1 -1
  217. package/docs/functions/_saltcorn_types.Types.common_types.instanceOfSuccessMsg.html +1 -1
  218. package/docs/functions/_saltcorn_types.Types.common_types.instanceOfType.html +1 -1
  219. package/docs/functions/_saltcorn_types.generators.generateBool.html +1 -1
  220. package/docs/functions/_saltcorn_types.generators.generateString.html +1 -1
  221. package/docs/functions/_saltcorn_types.generators.num_between.html +1 -1
  222. package/docs/functions/_saltcorn_types.generators.oneOf.html +1 -1
  223. package/docs/interfaces/_saltcorn_mobile_builder.common_build_utils.ScCapacitorConfig.html +10 -10
  224. package/docs/interfaces/_saltcorn_types.ModelAbstracts.abstract_field.AbstractField.html +15 -15
  225. package/docs/interfaces/_saltcorn_types.ModelAbstracts.abstract_field.AbstractFieldRepeat.html +3 -3
  226. package/docs/interfaces/_saltcorn_types.ModelAbstracts.abstract_form.AbstractForm.html +33 -26
  227. package/docs/interfaces/_saltcorn_types.ModelAbstracts.abstract_page.AbstractPage.html +7 -7
  228. package/docs/interfaces/_saltcorn_types.ModelAbstracts.abstract_role.AbstractRole.html +3 -3
  229. package/docs/interfaces/_saltcorn_types.ModelAbstracts.abstract_table.AbstractTable.html +11 -11
  230. package/docs/interfaces/_saltcorn_types.ModelAbstracts.abstract_trigger.AbstractTrigger.html +16 -16
  231. package/docs/interfaces/_saltcorn_types.ModelAbstracts.abstract_view.AbstractView.html +10 -10
  232. package/docs/interfaces/_saltcorn_types.ModelAbstracts.abstract_workflow.AbstractWorkflow.html +13 -13
  233. package/docs/modules/_saltcorn_admin_models.backup.html +1 -1
  234. package/docs/modules/_saltcorn_admin_models.html +1 -1
  235. package/docs/modules/_saltcorn_admin_models.pack.html +1 -1
  236. package/docs/modules/_saltcorn_admin_models.tenant.html +1 -1
  237. package/docs/modules/_saltcorn_common_code.html +1 -1
  238. package/docs/modules/_saltcorn_data.html +3 -3
  239. package/docs/modules/_saltcorn_data.models.EventLog.html +2 -2
  240. package/docs/modules/_saltcorn_data.models.File.html +2 -2
  241. package/docs/modules/_saltcorn_data.models.Form.html +2 -2
  242. package/docs/modules/_saltcorn_data.models.PageGroup.html +2 -2
  243. package/docs/modules/_saltcorn_data.models.Table.html +2 -2
  244. package/docs/modules/_saltcorn_data.models.View.html +2 -2
  245. package/docs/modules/_saltcorn_data.models.config.html +1 -1
  246. package/docs/modules/_saltcorn_data.models.discovery.html +1 -1
  247. package/docs/modules/_saltcorn_data.models.email.html +1 -1
  248. package/docs/modules/_saltcorn_data.models.expression.html +1 -1
  249. package/docs/modules/_saltcorn_data.models.html +1 -1
  250. package/docs/modules/_saltcorn_data.models.layout.html +1 -1
  251. package/docs/modules/_saltcorn_data.models.random.html +1 -1
  252. package/docs/modules/_saltcorn_data.models.scheduler.html +1 -1
  253. package/docs/modules/_saltcorn_data.plugin_helper.html +1 -1
  254. package/docs/modules/_saltcorn_data.utils.html +1 -1
  255. package/docs/modules/_saltcorn_db_common.html +2 -2
  256. package/docs/modules/_saltcorn_db_common.multi_tenant.html +1 -1
  257. package/docs/modules/_saltcorn_markup.html +20 -10
  258. package/docs/modules/_saltcorn_mobile_builder.common_build_utils.html +1 -1
  259. package/docs/modules/_saltcorn_mobile_builder.html +1 -1
  260. package/docs/modules/_saltcorn_mobile_builder.package_bundle_utils.html +1 -1
  261. package/docs/modules/_saltcorn_sqlite.html +1 -1
  262. package/docs/modules/_saltcorn_sqlite_mobile.html +1 -1
  263. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_field.html +1 -1
  264. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_form.html +1 -1
  265. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_library.html +1 -1
  266. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_page.html +1 -1
  267. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_plugin.html +1 -1
  268. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_role.html +1 -1
  269. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_table.html +1 -1
  270. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_trigger.html +1 -1
  271. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_view.html +1 -1
  272. package/docs/modules/_saltcorn_types.ModelAbstracts.abstract_workflow.html +1 -1
  273. package/docs/modules/_saltcorn_types.ModelAbstracts.html +1 -1
  274. package/docs/modules/_saltcorn_types.Types.base_types.html +1 -1
  275. package/docs/modules/_saltcorn_types.Types.common_types.html +1 -1
  276. package/docs/modules/_saltcorn_types.Types.html +1 -1
  277. package/docs/modules/_saltcorn_types.generators.html +1 -1
  278. package/docs/modules/_saltcorn_types.html +1 -1
  279. package/docs/types/_saltcorn_data.models.EventLog.EventLogCfg.html +1 -1
  280. package/docs/types/_saltcorn_data.models.File.FileCfg.html +1 -1
  281. package/docs/types/_saltcorn_data.models.Form.AdditionalButton.html +1 -1
  282. package/docs/types/_saltcorn_data.models.Form.FormCfg.html +4 -2
  283. package/docs/types/_saltcorn_data.models.PageGroup.ScreenInfoParams.html +1 -1
  284. package/docs/types/_saltcorn_data.models.Table.ChildRelations.html +1 -1
  285. package/docs/types/_saltcorn_data.models.Table.ParentRelations.html +1 -1
  286. package/docs/types/_saltcorn_data.models.Table.RelationData.html +1 -1
  287. package/docs/types/_saltcorn_data.models.View.FindViewsPred.html +1 -1
  288. package/docs/types/_saltcorn_db_common.AggregationOptions.html +1 -1
  289. package/docs/types/_saltcorn_db_common.CoordOpts.html +1 -1
  290. package/docs/types/_saltcorn_db_common.DatabaseClient.html +1 -1
  291. package/docs/types/_saltcorn_db_common.JoinField.html +1 -1
  292. package/docs/types/_saltcorn_db_common.JoinFields.html +1 -1
  293. package/docs/types/_saltcorn_db_common.JoinOptions.html +1 -1
  294. package/docs/types/_saltcorn_db_common.JsonPath.html +1 -1
  295. package/docs/types/_saltcorn_db_common.JsonPathElem.html +1 -1
  296. package/docs/types/_saltcorn_db_common.Operator.html +1 -1
  297. package/docs/types/_saltcorn_db_common.PartialSome.html +1 -1
  298. package/docs/types/_saltcorn_db_common.PrimaryKeyValue.html +1 -1
  299. package/docs/types/_saltcorn_db_common.Row.html +1 -1
  300. package/docs/types/_saltcorn_db_common.SelectOptions.html +1 -1
  301. package/docs/types/_saltcorn_db_common.SqlAndValues.html +1 -1
  302. package/docs/types/_saltcorn_db_common.StrongRow.html +1 -1
  303. package/docs/types/_saltcorn_db_common.SubselectOptions.html +1 -1
  304. package/docs/types/_saltcorn_db_common.Value.html +1 -1
  305. package/docs/types/_saltcorn_db_common.Where.html +1 -1
  306. package/docs/types/_saltcorn_mobile_builder.IosCfg.html +1 -1
  307. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_field.FieldCfg.html +1 -1
  308. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_field.InputType.html +1 -1
  309. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_form.AdditionalButton.html +1 -1
  310. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_library.LibraryCfg.html +1 -1
  311. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_library.LibraryPack.html +1 -1
  312. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_page.PageCfg.html +1 -1
  313. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_page.PagePack.html +1 -1
  314. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_plugin.PluginCfg.html +1 -1
  315. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_plugin.PluginPack.html +1 -1
  316. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_role.RoleCfg.html +1 -1
  317. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_role.RolePack.html +1 -1
  318. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_table.TableCfg.html +1 -1
  319. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_table.TablePack.html +1 -1
  320. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_trigger.TriggerCfg.html +1 -1
  321. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_trigger.TriggerPack.html +1 -1
  322. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_view.ViewCfg.html +1 -1
  323. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_view.ViewPack.html +1 -1
  324. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_workflow.ConfigWorkflowStep.html +1 -1
  325. package/docs/types/_saltcorn_types.ModelAbstracts.abstract_workflow.RunResult.html +1 -1
  326. package/docs/types/_saltcorn_types.Types.base_types.Action.html +1 -1
  327. package/docs/types/_saltcorn_types.Types.base_types.AuthenticationMethod.html +1 -1
  328. package/docs/types/_saltcorn_types.Types.base_types.CalcJoinfield.html +1 -1
  329. package/docs/types/_saltcorn_types.Types.base_types.CapacitorPlugin.html +1 -1
  330. package/docs/types/_saltcorn_types.Types.base_types.CodePagePack.html +1 -1
  331. package/docs/types/_saltcorn_types.Types.base_types.Column.html +1 -1
  332. package/docs/types/_saltcorn_types.Types.base_types.ConnectObjType.html +1 -1
  333. package/docs/types/_saltcorn_types.Types.base_types.ConnectedObjects.html +1 -1
  334. package/docs/types/_saltcorn_types.Types.base_types.CopilotSkill.html +1 -1
  335. package/docs/types/_saltcorn_types.Types.base_types.ErrorObj.html +1 -1
  336. package/docs/types/_saltcorn_types.Types.base_types.FieldLike.html +1 -1
  337. package/docs/types/_saltcorn_types.Types.base_types.FieldView.html +1 -1
  338. package/docs/types/_saltcorn_types.Types.base_types.Header.html +1 -1
  339. package/docs/types/_saltcorn_types.Types.base_types.JoinFieldOption.html +1 -1
  340. package/docs/types/_saltcorn_types.Types.base_types.Layout.html +1 -1
  341. package/docs/types/_saltcorn_types.Types.base_types.MobileConfig.html +1 -1
  342. package/docs/types/_saltcorn_types.Types.base_types.ModelPattern.html +1 -1
  343. package/docs/types/_saltcorn_types.Types.base_types.Pack.html +1 -1
  344. package/docs/types/_saltcorn_types.Types.base_types.Plugin.html +1 -1
  345. package/docs/types/_saltcorn_types.Types.base_types.PluginFunction.html +1 -1
  346. package/docs/types/_saltcorn_types.Types.base_types.PluginLayout.html +1 -1
  347. package/docs/types/_saltcorn_types.Types.base_types.PluginRoute.html +1 -1
  348. package/docs/types/_saltcorn_types.Types.base_types.PluginSourceType.html +1 -1
  349. package/docs/types/_saltcorn_types.Types.base_types.PluginType.html +1 -1
  350. package/docs/types/_saltcorn_types.Types.base_types.PluginWrap.html +1 -1
  351. package/docs/types/_saltcorn_types.Types.base_types.PluginWrapArg.html +1 -1
  352. package/docs/types/_saltcorn_types.Types.base_types.RelationOption.html +1 -1
  353. package/docs/types/_saltcorn_types.Types.base_types.Req.html +1 -1
  354. package/docs/types/_saltcorn_types.Types.base_types.Res.html +1 -1
  355. package/docs/types/_saltcorn_types.Types.base_types.ResultType.html +1 -1
  356. package/docs/types/_saltcorn_types.Types.base_types.RouteAction.html +1 -1
  357. package/docs/types/_saltcorn_types.Types.base_types.RunExtra.html +1 -1
  358. package/docs/types/_saltcorn_types.Types.base_types.SlugStepType.html +1 -1
  359. package/docs/types/_saltcorn_types.Types.base_types.StepResType.html +1 -1
  360. package/docs/types/_saltcorn_types.Types.base_types.SubField.html +1 -1
  361. package/docs/types/_saltcorn_types.Types.base_types.TableProvider.html +1 -1
  362. package/docs/types/_saltcorn_types.Types.base_types.TableQuery.html +1 -1
  363. package/docs/types/_saltcorn_types.Types.base_types.Tablely.html +1 -1
  364. package/docs/types/_saltcorn_types.Types.base_types.ViewTemplate.html +1 -1
  365. package/docs/types/_saltcorn_types.Types.common_types.ErrorMessage.html +1 -1
  366. package/docs/types/_saltcorn_types.Types.common_types.GenObj.html +1 -1
  367. package/docs/types/_saltcorn_types.Types.common_types.ReqRes.html +1 -1
  368. package/docs/types/_saltcorn_types.Types.common_types.ResultMessage.html +1 -1
  369. package/docs/types/_saltcorn_types.Types.common_types.SuccessMessage.html +1 -1
  370. package/docs/types/_saltcorn_types.Types.common_types.Type.html +1 -1
  371. package/docs/variables/_saltcorn_data.db.html +1 -1
  372. package/docs/variables/_saltcorn_data.migrations.html +1 -1
  373. package/docs/variables/_saltcorn_data.models.config.available_languages.html +1 -1
  374. package/docs/variables/_saltcorn_data.models.config.configTypes.html +1 -1
  375. package/docs/variables/_saltcorn_data.models.scheduler.runScheduler.html +1 -1
  376. package/docs/variables/_saltcorn_data.plugin_helper.run_action_column.html +1 -1
  377. package/docs/variables/_saltcorn_data.utils.NotAuthorized.html +1 -1
  378. package/docs/variables/_saltcorn_data.utils.sleep.html +1 -1
  379. package/docs/variables/_saltcorn_db_common.multi_tenant.tenantNamespace.html +1 -1
  380. package/help/Firebase JSON key.tmd +20 -0
  381. package/help/Google Services File.tmd +30 -0
  382. package/help/VAPID configuration.tmd +1 -0
  383. package/locales/en.json +25 -1
  384. package/package.json +10 -10
  385. package/public/gridedit.js +2 -1
  386. package/public/saltcorn-common.js +114 -0
  387. package/public/saltcorn.js +14 -3
  388. package/public/serviceworker.js +11 -0
  389. package/routes/actions.js +19 -16
  390. package/routes/admin.js +112 -2
  391. package/routes/api.js +2 -2
  392. package/routes/fields.js +19 -3
  393. package/routes/list.js +3 -2
  394. package/routes/models.js +11 -0
  395. package/routes/notifications.js +196 -4
  396. package/routes/pageedit.js +4 -0
  397. package/routes/plugins.js +1 -3
  398. package/routes/search.js +23 -2
  399. package/routes/tables.js +1 -0
  400. package/routes/tenant.js +5 -1
  401. package/serve.js +29 -6
  402. package/tests/plugin_install.test.js +1 -1
  403. package/tests/tenant.test.js +2 -2
  404. package/wrapper.js +18 -0
package/routes/actions.js CHANGED
@@ -1234,23 +1234,26 @@ router.get(
1234
1234
  { orderBy: "RANDOM()", forUser: req.user, forPublic: !req.user }
1235
1235
  );
1236
1236
  }
1237
- let runres;
1238
1237
 
1239
- try {
1240
- runres = await trigger.runWithoutRow({
1241
- console: fakeConsole,
1242
- table,
1243
- row,
1244
- req,
1245
- interactive: true,
1246
- ...(row || {}),
1247
- Table,
1248
- user: req.user,
1249
- });
1250
- } catch (e) {
1251
- console.error(e);
1252
- fakeConsole.error(e.message);
1253
- }
1238
+ const runres = await db.withTransaction(
1239
+ async () => {
1240
+ return await trigger.runWithoutRow({
1241
+ console: fakeConsole,
1242
+ table,
1243
+ row,
1244
+ req,
1245
+ interactive: true,
1246
+ ...(row || {}),
1247
+ Table,
1248
+ user: req.user,
1249
+ });
1250
+ },
1251
+ (e) => {
1252
+ console.error(e);
1253
+ fakeConsole.error(e.message);
1254
+ }
1255
+ );
1256
+
1254
1257
  if (output.length === 0) {
1255
1258
  req.flash(
1256
1259
  "success",
package/routes/admin.js CHANGED
@@ -2360,6 +2360,7 @@ router.get(
2360
2360
  const images = (await File.find({ mime_super: "image" })).filter((image) =>
2361
2361
  image.filename?.endsWith(".png")
2362
2362
  );
2363
+ const files = await File.find({ folder: "/" });
2363
2364
  const keystoreFiles = await File.find({ folder: "keystore_files" });
2364
2365
  const provisioningFiles = await File.find({ folder: "provisioning_files" });
2365
2366
  const withSyncInfo = await Table.find({ has_sync_info: true });
@@ -2377,6 +2378,7 @@ router.get(
2377
2378
  const xcodebuildVersion = xcodeCheckRes.version;
2378
2379
  const layout = getState().getLayout(req.user);
2379
2380
  const isSbadmin2 = layout === getState().layouts.sbadmin2;
2381
+ const pushEnabled = getState().getConfig("enable_push_notify", false);
2380
2382
  send_admin_page({
2381
2383
  res,
2382
2384
  req,
@@ -3245,7 +3247,50 @@ router.get(
3245
3247
  placeholder: "",
3246
3248
  })
3247
3249
  )
3248
- )
3250
+ ),
3251
+ // google-services.json file
3252
+ pushEnabled
3253
+ ? div(
3254
+ { class: "row pb-3" },
3255
+ div(
3256
+ { class: "col-sm-8" },
3257
+ label(
3258
+ {
3259
+ for: "googleServicesInputId",
3260
+ class: "form-label fw-bold",
3261
+ },
3262
+ req.__("Google Services File"),
3263
+ a(
3264
+ {
3265
+ href: "javascript:ajax_modal('/admin/help/Google Services File?')",
3266
+ },
3267
+ i({ class: "fas fa-question-circle ps-1" })
3268
+ )
3269
+ ),
3270
+ select(
3271
+ {
3272
+ class: "form-select",
3273
+ name: "googleServicesFile",
3274
+ id: "googleServicesInputId",
3275
+ },
3276
+ [
3277
+ option({ value: "" }, ""),
3278
+ ...files.map((file) =>
3279
+ option(
3280
+ {
3281
+ value: file.location,
3282
+ selected:
3283
+ builderSettings.googleServicesFile ===
3284
+ file.location,
3285
+ },
3286
+ file.filename
3287
+ )
3288
+ ),
3289
+ ].join("")
3290
+ )
3291
+ )
3292
+ )
3293
+ : ""
3249
3294
  )
3250
3295
  ),
3251
3296
  div(
@@ -3685,6 +3730,7 @@ router.post(
3685
3730
  keystoreFile,
3686
3731
  keystoreAlias,
3687
3732
  keystorePassword,
3733
+ googleServicesFile,
3688
3734
  } = req.body || {};
3689
3735
  const receiveShareTriggers = Trigger.find({
3690
3736
  when_trigger: "ReceiveMobileShareData",
@@ -3825,6 +3871,8 @@ router.post(
3825
3871
  spawnParams.push("--androidKeyStoreAlias", keystoreAlias);
3826
3872
  if (keystorePassword)
3827
3873
  spawnParams.push("--androidKeystorePassword", keystorePassword);
3874
+ if (googleServicesFile)
3875
+ spawnParams.push("--googleServicesFile", googleServicesFile);
3828
3876
 
3829
3877
  // if builDir exists, remove it
3830
3878
  if (
@@ -4501,6 +4549,35 @@ admin_config_route({
4501
4549
  name: "pwa_icons",
4502
4550
  showIf: { pwa_enabled: true },
4503
4551
  },
4552
+ { section_header: "Push Notifications" },
4553
+ "enable_push_notify",
4554
+ {
4555
+ name: "push_notification_icon",
4556
+ showIf: { enable_push_notify: true },
4557
+ },
4558
+ {
4559
+ name: "push_notification_badge",
4560
+ showIf: { enable_push_notify: true },
4561
+ },
4562
+ { section_header: "Web push" },
4563
+ {
4564
+ name: "vapid_public_key",
4565
+ showIf: { enable_push_notify: true },
4566
+ },
4567
+ {
4568
+ name: "vapid_private_key",
4569
+ showIf: { enable_push_notify: true },
4570
+ },
4571
+ {
4572
+ name: "vapid_email",
4573
+ showIf: { enable_push_notify: true },
4574
+ },
4575
+ { section_header: "Native Android" },
4576
+ {
4577
+ name: "firebase_json_key",
4578
+ showIf: { enable_push_notify: true },
4579
+ help: { topic: "Firebas JSON key" },
4580
+ },
4504
4581
  ],
4505
4582
  response(form, req, res) {
4506
4583
  send_admin_page({
@@ -4511,7 +4588,40 @@ admin_config_route({
4511
4588
  type: "card",
4512
4589
  title: req.__("Notification settings"),
4513
4590
  titleAjaxIndicator: true,
4514
- contents: [renderForm(form, req.csrfToken())],
4591
+ contents: [
4592
+ renderForm(form, req.csrfToken()) +
4593
+ button(
4594
+ {
4595
+ id: "generate-vapid-keys-btn",
4596
+ class: "btn btn-primary d-none",
4597
+ onclick:
4598
+ "if (confirm('Are you sure? The old keys will be lost, and existing subscriptions will be deleted.')) ajax_post_btn('/notifications/generate-vapid-keys', true)",
4599
+ },
4600
+ "Generate VAPID keys"
4601
+ ) +
4602
+ script(
4603
+ domReady(`
4604
+ const fn = () => {
4605
+ const pushEnabled = document.getElementById("inputenable_push_notify");
4606
+ const generateBtn = document.getElementById("generate-vapid-keys-btn");
4607
+ if (pushEnabled && pushEnabled.checked) {
4608
+ generateBtn.classList.remove("d-none");
4609
+ generateBtn.classList.add("d-inline");
4610
+ }
4611
+ else {
4612
+ generateBtn.classList.remove("d-inline");
4613
+ generateBtn.classList.add("d-none");
4614
+ }
4615
+ };
4616
+ setTimeout(fn, 0);
4617
+ const mainForm = document.querySelector("form[action='/admin/notifications']");
4618
+ if (mainForm) {
4619
+ mainForm.addEventListener("change", (e) => {
4620
+ fn();
4621
+ });
4622
+ }`)
4623
+ ),
4624
+ ],
4515
4625
  },
4516
4626
  });
4517
4627
  },
package/routes/api.js CHANGED
@@ -391,8 +391,8 @@ router.get(
391
391
  aggregations: {
392
392
  _versions: {
393
393
  table: table.name + "__history",
394
- ref: "id",
395
- field: "id",
394
+ ref: table.pk_name,
395
+ field: table.pk_name,
396
396
  aggregate: "count",
397
397
  },
398
398
  },
package/routes/fields.js CHANGED
@@ -251,6 +251,7 @@ const fieldFlow = (req) =>
251
251
  attributes.include_fts = context.include_fts;
252
252
  attributes.on_delete_cascade = context.on_delete_cascade;
253
253
  attributes.on_delete = context.on_delete;
254
+ attributes.fkey_error_msg = context.fkey_error_msg;
254
255
  attributes.unique_error_msg = context.unique_error_msg;
255
256
  if (context.protected) attributes.min_role_write = context.min_role_write;
256
257
  else attributes.min_role_write = undefined;
@@ -307,7 +308,7 @@ const fieldFlow = (req) =>
307
308
  try {
308
309
  await db.withTransaction(async () => {
309
310
  if (context.id) {
310
- const field = await Field.findOne({ id: context.id });
311
+ const field = await Field.findOne({ id: context.id });
311
312
  await field.update(fldRow);
312
313
  Trigger.emitEvent(
313
314
  "AppChange",
@@ -628,7 +629,7 @@ const fieldFlow = (req) =>
628
629
  type: "String",
629
630
  class: "validate-expression",
630
631
  fieldview: "textarea",
631
- attributes: { rows: 2 },
632
+ attributes: { rows: 2, unsafe: true },
632
633
  validator: expressionValidator,
633
634
  showIf: { expression_type: "JavaScript expression" },
634
635
  }),
@@ -723,6 +724,15 @@ const fieldFlow = (req) =>
723
724
  "If the parent row is deleted, do this to the child rows."
724
725
  ),
725
726
  }),
727
+ new Field({
728
+ name: "fkey_error_msg",
729
+ label: req.__("Error message"),
730
+ type: "String",
731
+ sublabel: req.__(
732
+ "Shown to the user if foreign key constraint is violated"
733
+ ),
734
+ showIf: { on_delete: "Fail" },
735
+ }),
726
736
  ],
727
737
  });
728
738
  },
@@ -1417,7 +1427,13 @@ router.post(
1417
1427
  return;
1418
1428
  }
1419
1429
 
1420
- const field = table.getField(fieldName);
1430
+ let field = table.getField(fieldName);
1431
+ if (!field && fieldName.split(".").length === 3) {
1432
+ const [inboundTableName, inboundKey, refField] = fieldName.split(".");
1433
+ const inboundTable = Table.findOne(inboundTableName);
1434
+ field = inboundTable.getField(refField);
1435
+ }
1436
+
1421
1437
  if (!field) {
1422
1438
  res.send(req.query?.accept == "json" ? "[]" : "");
1423
1439
  return;
package/routes/list.js CHANGED
@@ -65,7 +65,7 @@ router.get(
65
65
 
66
66
  const fields = table.getFields();
67
67
  var tfields = fields.map((f) => ({ label: f.label, key: f.listKey }));
68
-
68
+ const pk_name = table.pk_name;
69
69
  tfields.push({
70
70
  label: req.__("Version"),
71
71
  key: (r) => r._version,
@@ -82,7 +82,7 @@ router.get(
82
82
  label: req.__("Restore"),
83
83
  key: (r) =>
84
84
  post_btn(
85
- `/list/_restore/${table.name}/${r.id}/${r._version}`,
85
+ `/list/_restore/${table.name}/${r[pk_name]}/${r._version}`,
86
86
  req.__("Restore"),
87
87
  req.csrfToken()
88
88
  ),
@@ -281,6 +281,7 @@ router.get(
281
281
  field: "_versions",
282
282
  title: "Versions",
283
283
  formatter: "__versionsFormatter",
284
+ formatterParams: { pk_name: table.pk_name },
284
285
  });
285
286
  }
286
287
  jsfields.push({
package/routes/models.js CHANGED
@@ -219,6 +219,7 @@ router.get(
219
219
  const table = await Table.findOne({ id: model.table_id });
220
220
  const instances = await ModelInstance.find({ model_id: model.id });
221
221
  const metrics = model.templateObj.metrics || {};
222
+ const rendered = await model.templateObj.renderModel?.({ table, ...model });
222
223
  const metricCols = Object.entries(metrics).map(([k, v]) => ({
223
224
  label: k,
224
225
  key: (inst) => inst.metric_values?.[k]?.toPrecision(6),
@@ -239,6 +240,16 @@ router.get(
239
240
  i({ class: "ms-1 fas fa-edit" })
240
241
  ),
241
242
  },
243
+ ...(rendered
244
+ ? [
245
+ {
246
+ type: "card",
247
+ class: "mt-0 mb-3",
248
+ title: req.__("Model configuration"),
249
+ contents: div(rendered),
250
+ },
251
+ ]
252
+ : []),
242
253
  {
243
254
  type: "card",
244
255
  class: "mt-0",
@@ -32,14 +32,23 @@ router.use(
32
32
  })
33
33
  );
34
34
 
35
- const notificationSettingsForm = () =>
36
- new Form({
35
+ const notificationSettingsForm = () => {
36
+ const fields = [{ name: "notify_email", label: "Email", type: "Bool" }];
37
+ if (getState().getConfig("enable_push_notify", false))
38
+ fields.push({
39
+ name: "notify_push",
40
+ label: "Push",
41
+ type: "Bool",
42
+ });
43
+
44
+ return new Form({
37
45
  action: `/notifications/settings`,
38
46
  noSubmitButton: true,
39
47
  onChange: `saveAndContinue(this)`,
40
48
  labelCols: 4,
41
- fields: [{ name: "notify_email", label: "Email", type: "Bool" }],
49
+ fields,
42
50
  });
51
+ };
43
52
 
44
53
  router.get(
45
54
  "/",
@@ -67,7 +76,10 @@ router.get(
67
76
 
68
77
  const form = notificationSettingsForm();
69
78
  const user = await User.findOne({ id: req.user?.id });
70
- form.values = { notify_email: user?._attributes?.notify_email };
79
+ form.values = {
80
+ notify_email: user?._attributes?.notify_email,
81
+ notify_push: user?._attributes?.notify_push,
82
+ };
71
83
  const notifyCards = nots.length
72
84
  ? nots.map((not) => ({
73
85
  type: "card",
@@ -185,6 +197,16 @@ router.post(
185
197
  form.validate(req.body || {});
186
198
  const _attributes = { ...user._attributes, ...form.values };
187
199
  await user.update({ _attributes });
200
+ const pushEnabled = !!_attributes.notify_push;
201
+ const allSubs = getState().getConfig("push_notification_subscriptions", {});
202
+ const newSubs = { ...allSubs };
203
+ if (!pushEnabled && newSubs[req.user.id]) {
204
+ delete newSubs[req.user.id];
205
+ await getState().setConfig("push_notification_subscriptions", newSubs);
206
+ } else if (pushEnabled && !newSubs[req.user.id]) {
207
+ newSubs[req.user.id] = [];
208
+ await getState().setConfig("push_notification_subscriptions", newSubs);
209
+ }
188
210
  res.json({ success: "ok" });
189
211
  })
190
212
  );
@@ -248,6 +270,176 @@ router.post(
248
270
  })
249
271
  );
250
272
 
273
+ router.post(
274
+ "/subscribe",
275
+ loggedIn,
276
+ error_catcher(async (req, res) => {
277
+ const enabled = getState().getConfig("enable_push_notify", false);
278
+ if (!enabled) {
279
+ res.status(403).json({
280
+ error: req.__("Notifications are not enabled on this server"),
281
+ });
282
+ } else {
283
+ const user = req.user;
284
+ const allSubs = getState().getConfig(
285
+ "push_notification_subscriptions",
286
+ {}
287
+ );
288
+ const userSubs = allSubs[user.id] || [];
289
+ const existingSub = userSubs.find(
290
+ (s) =>
291
+ s.endpoint === req.body.endpoint &&
292
+ s.keys.p256dh === req.body.keys.p256dh &&
293
+ s.keys.auth === req.body.keys.auth
294
+ );
295
+ if (existingSub) {
296
+ res.json({
297
+ success: "ok",
298
+ message: req.__("Already subscribed to notifications"),
299
+ });
300
+ } else {
301
+ userSubs.push({
302
+ type: "web-push",
303
+ endpoint: req.body.endpoint,
304
+ keys: {
305
+ auth: req.body.keys.auth,
306
+ p256dh: req.body.keys.p256dh,
307
+ },
308
+ });
309
+ await getState().setConfig("push_notification_subscriptions", {
310
+ ...allSubs,
311
+ [user.id]: userSubs,
312
+ });
313
+ res.json({
314
+ success: "ok",
315
+ message: req.__("Subscribed to notifications"),
316
+ });
317
+ }
318
+ }
319
+ })
320
+ );
321
+
322
+ router.post(
323
+ "/fcm-token",
324
+ loggedIn,
325
+ error_catcher(async (req, res) => {
326
+ const enabled = getState().getConfig("enable_push_notify", false);
327
+ if (!enabled) {
328
+ res.status(403).json({
329
+ error: req.__("Notifications are not enabled on this server"),
330
+ });
331
+ } else {
332
+ const { token, deviceId } = req.body || {};
333
+ if (!token) {
334
+ res.status(400).json({
335
+ error: req.__("FCM token is required"),
336
+ });
337
+ return;
338
+ }
339
+ const user = req.user;
340
+ const allSubs = getState().getConfig(
341
+ "push_notification_subscriptions",
342
+ {}
343
+ );
344
+ let userSubs = allSubs[user.id] || [];
345
+ const existingSub = userSubs.find(
346
+ (s) =>
347
+ s.type === "fcm-push" && s.token === token && s.deviceId === deviceId
348
+ );
349
+ if (existingSub) {
350
+ res.json({
351
+ success: "ok",
352
+ message: req.__("FCM token already uploaded"),
353
+ });
354
+ } else {
355
+ userSubs = userSubs.filter(
356
+ (s) => s.type !== "fcm-push" || s.deviceId !== deviceId
357
+ );
358
+ userSubs.push({
359
+ type: "fcm-push",
360
+ token: token,
361
+ deviceId: deviceId,
362
+ });
363
+ await getState().setConfig("push_notification_subscriptions", {
364
+ ...allSubs,
365
+ [user.id]: userSubs,
366
+ });
367
+ res.json({
368
+ success: "ok",
369
+ message: req.__("FCM token uploaded"),
370
+ });
371
+ }
372
+ }
373
+ })
374
+ );
375
+
376
+ router.post(
377
+ "/remove-subscription",
378
+ loggedIn,
379
+ error_catcher(async (req, res) => {
380
+ const enabled = getState().getConfig("enable_push_notify", false);
381
+ if (!enabled) {
382
+ res.status(403).json({
383
+ error: req.__("Notifications are not enabled on this server"),
384
+ });
385
+ } else {
386
+ const { subscription } = req.body;
387
+ const user = req.user;
388
+ const oldSubs = getState().getConfig(
389
+ "push_notification_subscriptions",
390
+ {}
391
+ );
392
+ let userSubs = oldSubs[user.id];
393
+ if (userSubs) {
394
+ userSubs = userSubs.filter(
395
+ (s) =>
396
+ s.endpoint !== subscription.endpoint ||
397
+ s.keys.p256dh !== subscription.keys.p256dh ||
398
+ s.keys.auth !== subscription.keys.auth
399
+ );
400
+ await getState().setConfig("push_notification_subscriptions", {
401
+ ...oldSubs,
402
+ [user.id]: userSubs,
403
+ });
404
+ }
405
+ res.json({
406
+ success: "ok",
407
+ message: req.__("Unsubscribed from notifications"),
408
+ });
409
+ }
410
+ })
411
+ );
412
+
413
+ router.post(
414
+ "/generate-vapid-keys",
415
+ isAdmin,
416
+ error_catcher(async (req, res) => {
417
+ const enabled = getState().getConfig("enable_push_notify", false);
418
+ if (!enabled) {
419
+ res.status(403).json({
420
+ error: req.__("Notifications are not enabled on this server"),
421
+ });
422
+ } else {
423
+ const webPush = require("web-push");
424
+ const vapidKeys = webPush.generateVAPIDKeys();
425
+ await getState().setConfig("vapid_public_key", vapidKeys.publicKey);
426
+ await getState().setConfig("vapid_private_key", vapidKeys.privateKey);
427
+ const allSubs = getState().setConfig(
428
+ "push_notification_subscriptions",
429
+ {}
430
+ );
431
+ const newSubs = {};
432
+ for (const k of Object.keys(allSubs)) {
433
+ newSubs[k] = [];
434
+ }
435
+ await getState().setConfig("push_notification_subscriptions", newSubs);
436
+ res.json({
437
+ success: "ok",
438
+ });
439
+ }
440
+ })
441
+ );
442
+
251
443
  router.get(
252
444
  "/manifest.json{:opt_cache_bust}",
253
445
  error_catcher(async (req, res) => {
@@ -183,6 +183,9 @@ const pageBuilderData = async (req, context) => {
183
183
  triggers.forEach((tr) => {
184
184
  actions.push(tr.name);
185
185
  });
186
+ const triggerActions = Trigger.trigger_actions({
187
+ apiNeverTriggers: true,
188
+ });
186
189
  const actionConfigForms = {};
187
190
  for (const name of actions) {
188
191
  const action = stateActions[name];
@@ -247,6 +250,7 @@ const pageBuilderData = async (req, context) => {
247
250
  page_groups,
248
251
  actions: actionsNotRequiringRow,
249
252
  builtInActions: ["GoBack"],
253
+ triggerActions,
250
254
  library,
251
255
  min_role: context.min_role,
252
256
  actionConfigForms,
package/routes/plugins.js CHANGED
@@ -1230,9 +1230,7 @@ router.get(
1230
1230
  const mod = await load_plugins.requirePlugin(plugin_db);
1231
1231
  const store_items = await get_store_items();
1232
1232
  const store_item = store_items.find((item) => item.name === name);
1233
- const update_permitted =
1234
- db.getTenantSchema() === db.connectObj.default_schema &&
1235
- plugin_db.source === "npm";
1233
+ const update_permitted = plugin_db.source === "npm";
1236
1234
 
1237
1235
  let latest =
1238
1236
  update_permitted &&
package/routes/search.js CHANGED
@@ -71,6 +71,14 @@ const searchConfigForm = (tables, views, req) => {
71
71
  input_type: "select",
72
72
  options: ["Cards", "Tabs"],
73
73
  });
74
+ fields.push({
75
+ name: "search_disable_fts",
76
+ label: req.__("Disable full-text search"),
77
+ type: "Bool",
78
+ sublabel: req.__(
79
+ "Use LIKE instead of tsvector. Use to match against phrase in the middle of a word."
80
+ ),
81
+ });
74
82
  const blurb1 = req.__(
75
83
  `Choose views for <a href="/search">search results</a> for each table.<br/>Set to blank to omit table from global search.`
76
84
  );
@@ -112,6 +120,10 @@ router.get(
112
120
  "search_results_decoration",
113
121
  "Cards"
114
122
  );
123
+ form.values.search_disable_fts = getState().getConfig(
124
+ "search_disable_fts",
125
+ true
126
+ );
115
127
  send_infoarch_page({
116
128
  res,
117
129
  req,
@@ -154,6 +166,10 @@ router.post(
154
166
  "search_results_decoration",
155
167
  result.success.search_results_decoration || "Cards"
156
168
  );
169
+ await getState().setConfig(
170
+ "search_disable_fts",
171
+ result.success.search_disable_fts || false
172
+ );
157
173
  await getState().setConfig("search_use_websearch", +dbversion >= 11.0);
158
174
  delete result.success.search_table_description;
159
175
  delete result.success.search_results_decoration;
@@ -235,7 +251,9 @@ const runSearch = async ({ q, _page, table }, req, res) => {
235
251
  viewName === "search_table_description" ||
236
252
  tableName === "search_table_description" ||
237
253
  viewName === "search_results_decoration" ||
238
- tableName === "search_results_decoration"
254
+ tableName === "search_results_decoration" ||
255
+ viewName === "search_disable_fts" ||
256
+ tableName === "search_disable_fts"
239
257
  )
240
258
  continue;
241
259
  tablesConfigured += 1;
@@ -367,7 +385,10 @@ router.get(
367
385
  "/",
368
386
  error_catcher(async (req, res, next) => {
369
387
  const state = getState();
370
- const maintenanceModeEnabled = state.getConfig("maintenance_mode_enabled", false);
388
+ const maintenanceModeEnabled = state.getConfig(
389
+ "maintenance_mode_enabled",
390
+ false
391
+ );
371
392
 
372
393
  if (maintenanceModeEnabled && (!req.user || req.user.role_id > 1)) {
373
394
  res.status(503).send("Page Unavailable: in maintenance mode");
package/routes/tables.js CHANGED
@@ -748,6 +748,7 @@ const attribBadges = (f) => {
748
748
  "summary_field",
749
749
  "importance",
750
750
  "on_delete_cascade",
751
+ "fkey_error_msg",
751
752
  "on_delete",
752
753
  "unique_error_msg",
753
754
  "ref",
package/routes/tenant.js CHANGED
@@ -435,7 +435,10 @@ router.get(
435
435
  {
436
436
  label: req.__("Subdomain"),
437
437
  key: (r) =>
438
- link(getNewURL(req, r.subdomain, get_cfg_tenant_base_url(req)), text(r.subdomain)),
438
+ link(
439
+ getNewURL(req, r.subdomain, get_cfg_tenant_base_url(req)),
440
+ text(r.subdomain)
441
+ ),
439
442
  },
440
443
  {
441
444
  label: req.__("Description"),
@@ -909,6 +912,7 @@ router.post(
909
912
  const { sub } = req.params;
910
913
  // todo warning before deletion
911
914
  await deleteTenant(sub);
915
+ await User.destroy_all_tenant_sessions(sub);
912
916
  res.redirect(`/tenant/list`);
913
917
  })
914
918
  );