eliom-distillery -name daegsrv -template os.pgocaml

This commit is contained in:
Paul Campbell 2022-10-30 18:24:41 +00:00
parent 10f486040a
commit d5d40a02c7
85 changed files with 9217 additions and 0 deletions

26
daegsrv/.gitignore vendored Normal file
View file

@ -0,0 +1,26 @@
# Database
local_db
# Local server configuration
local
# Eliom
.depend
_client
_deps
_server
# Mobile application
cordova
# Style
.sass-cache
static/css/daegsrv.css.map
static/css/daegsrv.css
# NPM
node_modules
# Make
Makefile.local

31
daegsrv/.ocamlformat Normal file
View file

@ -0,0 +1,31 @@
break-cases = fit
break-collection-expressions = fit-or-vertical
break-fun-decl = wrap
break-fun-sig = wrap
break-infix = wrap
break-infix-before-func = false
break-sequences = false
break-separators = before
break-string-literals = never
break-struct = force
cases-matching-exp-indent = compact
doc-comments = after-when-possible
dock-collection-brackets = false
indicate-multiline-delimiters = no
infix-precedence = indent
let-and = compact
let-binding-spacing = compact
module-item-spacing = compact
parens-tuple = multi-line-only
parens-tuple-patterns = multi-line-only
sequence-style = terminator
sequence-blank-line = compact
single-case = compact
type-decl = compact
if-then-else = keyword-first
field-space = loose
space-around-arrays = false
space-around-records = false
space-around-lists = false
space-around-variants = false
ocp-indent-compat = true

4
daegsrv/.ocp-indent Normal file
View file

@ -0,0 +1,4 @@
normal
with=0
syntax=lwt mll
max_indent=2

26
daegsrv/Makefile Normal file
View file

@ -0,0 +1,26 @@
#----------------------------------------------------------------------
# GLOBAL MAKEFILE
#----------------------------------------------------------------------
# Local settings (optional). See Makefile.local.example for an example.
# WARNING: do not commit to a repository!
-include Makefile.local
# Eliom settings. Contains all variables.
include Makefile.options
# I18N rules.
include Makefile.i18n
# Database rules.
include Makefile.db
# Styles (CSS, SASS...).
include Makefile.style
# Ocsigen-start makefile
# Makefile.PROJECT_NAME is also included in this makefile
include Makefile.os
# Mobile app makefile
include Makefile.mobile

10
daegsrv/Makefile.daegsrv Normal file
View file

@ -0,0 +1,10 @@
#----------------------------------------------------------------------
# SETTINGS FOR THE ELIOM PROJECT daegsrv
#----------------------------------------------------------------------
## Here you can add specific rules for your project. It's recommended to add
## variables in Makefile.options (which is supposed to contain all variables and
## no rules).
## This file is included in Makefile.os. You can also use any variable defined
## in Makefile.options.

152
daegsrv/Makefile.db Normal file
View file

@ -0,0 +1,152 @@
## ---------------------------------------------------------------------
## This Makefile contains the rules about the database management.
##
## Ocsigen Start uses PostgreSQL so you need to install it.
##
## Ocsigen Start uses pg_ctl. If this executable is not found, an error occurs.
##
## Some variables like the PostgreSQL directory for log, the PostgreSQL
## directory or the variables to access the database (like the database name or
## the user) can be found and changed in the file Makefile.options
## ---------------------------------------------------------------------
## The following part defines some variables by adding the environment variables
## to the used binaries.
export PGHOST := $(DB_HOST)
export PGDATABASE := $(DB_NAME)
export PGPORT := $(DB_PORT)
export PGUSER := $(DB_USER)
export PGPASSWORD := $(DB_PASSWORD)
# Ocsigenserver uses the DB settings from daegsrv.conf.in (which
# should be the same as the exported variables above)
OCSIGENSERVER := $(OCSIGENSERVER)
OCSIGENSERVER.OPT := $(OCSIGENSERVER.OPT)
OPAM_LIB_DIR := $(shell opam config var lib)
OS_UPGRADE_FILE := $(OPAM_LIB_DIR)/eliom/templates/os.pgocaml/upgrade.sql
## ---------------------------------------------------------------------
##----------------------------------------------------------------------
pg_dump = pg_dump
# Rule to get the pg_ctl binary.
ifeq ($(shell psql --version 2> /dev/null),)
$(error "PostgreSQL is not installed")
else
pg_ctl = $(shell which pg_ctl || \
ls /usr/lib/postgresql/*/bin/pg_ctl | \
sort -nr -t / -k 5 | head -n 1)
endif
##----------------------------------------------------------------------
## ---------------------------------------------------------------------
## Here some rules to easily manage the database.
# - db-init:
# Initialise the database. It creates the directory PSQL_DIR and
# start the database.
#
# - db-start:
# Start the database.
#
# - db-stop:
# Stop the database.
#
# - db-status:
# Get the database status.
#
# - db-create:
# Create the database and use UNICODE.
#
# - db-schema:
# Execute the SQL file contained in the PSQL_FILE
# variable to create the schema and the tables. By default, the PSQL_FILE is
# PROJECTNAME.sql. See Makefile.options.
#
# - db-upgrade:
# Upgrade the database schema related to Ocsigen Start to the newer database
# schema.
# It uses $(OS_UPGRADE_FILE) SQL file. You must not change it.
# Please read the upgrade information before calling this rule.
# Use it carefully!
#
# - db-drop:
# Drop the database but doesn't remove the database directory PSQL_DIR.
#
# - db-psql:
# Connect to the database.
#
# - db-delete:
# Stop the database (without error if it's not running) and remove
# the database directory containing all database data and the log file.
#
# Depending on the value of the LOCAL variable (defined in Makefile.options),
# the database is created locally or globally. By default, the database is
# local.
##----------------------------------------------------------------------
##----------------------------------------------------------------------
$(PSQL_DIR):
-mkdir -p $@
ifeq ($(LOCAL),yes)
db-init: $(PSQL_DIR)
$(pg_ctl) initdb -o --encoding=UNICODE -D $(PSQL_DIR)
echo unix_socket_directories = \'/tmp\' >> $(PSQL_DIR)/postgresql.conf
$(pg_ctl) -o "-p $(DB_PORT)" -D $(PSQL_DIR) -l $(PSQL_LOG) start
db-start:
$(pg_ctl) -o "-p $(DB_PORT)" -D $(PSQL_DIR) -l $(PSQL_LOG) start
db-stop:
$(pg_ctl) -D $(PSQL_DIR) -l $(PSQL_LOG) stop
db-status:
$(pg_ctl) -D $(PSQL_DIR) -l $(PSQL_LOG) status
db-delete:
$(pg_ctl) -D $(PSQL_DIR) -l $(PSQL_LOG) stop || true
rm -f $(PSQL_LOG)
rm -rf $(PSQL_DIR)
db-snapshot:
@echo "# Creating $(DB_SNAPSHOT)"
$(pg_dump) --clean --create --no-owner --encoding=utf8 \
$(DB_NAME) | gzip > $(DB_SNAPSHOT)
else
db-start:
$(pg_ctl) -o "-p $(DB_PORT)" start
db-status:
$(pg_ctl) status
db-stop:
$(pg_ctl) stop
endif
db-create:
createdb --encoding UNICODE $(DB_NAME)
db-schema:
psql -d $(DB_NAME) -f $(PSQL_FILE)
db-upgrade:
psql -d $(DB_NAME) -f $(OS_UPGRADE_FILE)
db-drop:
dropdb $(DB_NAME)
dropdb ocsipersist_daegsrv
db-psql:
psql $(DB_NAME)
##----------------------------------------------------------------------

47
daegsrv/Makefile.i18n Normal file
View file

@ -0,0 +1,47 @@
##----------------------------------------------------------------------
## The following part defines rules for i18n.
## See https://github.com/besport/ocsigen-i18n for more information.
I18N_CHECKER := ocsigen-i18n-checker
I18N_GENERATOR := ocsigen-i18n-generator
## The i18n generated file.
## IMPROVEME: Due to daegsrv_language, the module
## defining all translations must be static.
I18N_ELIOM_FILE := $(PROJECT_NAME)_i18n.eliom
## PPX extension to rewrite each file while compiling.
I18N_PPX_REWRITER := "ocsigen-i18n-rewriter --prefix 'Daegsrv_' --suffix '_i18n' --default-module Daegsrv_i18n"
## This rule will update the primary I18N module
$(I18N_ELIOM_FILE): $(I18N_PRIMARY_FILE)
# use LC_ALL=C so that all $(I18N_ELIOM_FILE) files are generated the same
# way.
LC_ALL=C $(I18N_GENERATOR) \
--languages $(I18N_LANGUAGES) \
--default-language $(I18N_DEFAULT_LANGUAGE) \
< $(I18N_PRIMARY_FILE) \
> $(I18N_ELIOM_FILE)
## This rule will update all other I18N modules
$(PROJECT_NAME)_%_i18n.eliom: $(I18N_TSV_DIR)$(PROJECT_NAME)_%_i18n.tsv
# use LC_ALL=C so that all I18N files are generated the same
# way.
LC_ALL=C $(I18N_GENERATOR) \
--languages $(I18N_LANGUAGES) \
--default-language $(I18N_DEFAULT_LANGUAGE) \
--primary $(notdir $(I18N_PRIMARY_FILE)) \
< $< \
> $@
i18n-check:
$(I18N_CHECKER) ./*.eliom < $(I18N_TSV_FILE)
.PHONY: i18n-clean
i18n-clean:
-rm -f $(notdir $(patsubst %.tsv,%.eliom,$(I18N_PRIMARY_FILE) \
$(I18N_EXTRA_FILES)))
## end of i18n
##----------------------------------------------------------------------

View file

@ -0,0 +1,18 @@
## This is an example of a Makefile.local file. It contains some example of
## variables you can define.
##
## Makefile.local must be only local: you must not distribute it. It must
## contain the local configuration for your development workflow.
# The URL address of your Web app (without / at the end).
# See Makefile.mobile for more information about this variable.
APP_SERVER := http://YOUR_IP_ADDRESS:PORT
# Use to decide if JS and CSS files must be retrieved from the server or not
# while building the mobile application.
# See Makefile.mobile for more information about this variable.
APP_REMOTE := yes
# Use to declare the app as a dev build that should access cleartext data
# over `http`. A release should be anything else than `dev`.
APP := dev

466
daegsrv/Makefile.mobile Normal file
View file

@ -0,0 +1,466 @@
#----------------------------------------------------------------------
# This Makefile contains rules about the mobile application.
#----------------------------------------------------------------------
WWW_PATH := local/var/www/$(PROJECT_NAME)
mobile-all: assets android browser # ios
##----------------------------------------------------------------------
## Errors definition
define ERROR_APP_SERVER
Error: APP_SERVER environment is not set!
You need to define the environment variable APP_SERVER in order to build the
mobile app. This server is the one your mobile app is connecting to and should
be running when building the app, so the matching Eliom JS and CSS files can be
retrieved.
Example:
$$ make APP_SERVER=http://eliomapp.example.com APP_REMOTE=yes android
endef
define ERROR_APP_REMOTE
Error: APP_REMOTE environment is not set!
You need to define the environment variable APP_REMOTE to 'yes' or 'no' in
order to build the mobile app. If set to 'yes', JS and CSS files will be
retrieved from the remote server defined in APP_SERVER; if set to 'no', locally
generated ones will be used instead.
Example:
$$ make APP_REMOTE=yes APP_SERVER=http://eliomapp.example.com android
endef
define ERROR_DOWNLOAD_JS
Error: Downloading of Eliom JS file from server $(APP_SERVER) has failed.
Please check that $(APP_SERVER) is running properly and try again.
endef
define ERROR_DOWNLOAD_CSS
Error: Downloading of Eliom CSS file from server $(APP_SERVER) has failed.
Please check that $(APP_SERVER) is running properly and try again.
endef
##----------------------------------------------------------------------
## Warnings definition
define WARNING_NO_CORDOVA
WARNING: 'cordova' command not found in PATH. Assuming we don't need it for the
current build. It makes sense if you only need Hot Code Push update files on a
server, for example. If you want to build the actual app, you need to install
Cordova. See the documentation at the beginning of 'Makefile.mobile' for
detailed instructions.
endef
##----------------------------------------------------------------------
## Environment variables definition.
APPJS := $(CORDOVAPATH)/www/$(PROJECT_NAME).js
APPCSS := $(CORDOVAPATH)/www/css/$(PROJECT_NAME).css
##----------------------------------------------------------------------
## Mobile app related ## variables checking
check-app-env:
ifeq ($(strip $(APP_SERVER)),)
$(error $(ERROR_APP_SERVER))
endif
ifeq ($(strip $(APP_REMOTE)),)
$(error $(ERROR_APP_REMOTE))
endif
# Rules to get the JS and CSS files when building. If APP_REMOTE is set to yes,
# the JS and CSS files are retrieved from APP_SERVER. Else, it copies the local
# JS and CSS files generated during the server compilation.
$(APPJS): $(JS_PREFIX).js
ifeq ($(APP_REMOTE),yes)
APPJS_FILE=$$(curl -s -f $(APP_SERVER)$(APP_PATH)/ | cat | grep -E -o -m 1 '$(PROJECT_NAME)_[a-f0-9]*?\.js') &&\
curl -s -o $@ $(APP_SERVER)$(APP_PATH)/$$APPJS_FILE
else
cp -f $(WWW_PATH)/`readlink $(JS_PREFIX).js` $@
endif
$(APPCSS):
ifeq ($(APP_REMOTE),yes)
APPCSS_FILE=$$(curl -s -f $(APP_SERVER)$(APP_PATH)/ | cat | grep -E -o -m 1 '$(PROJECT_NAME)_[a-f0-9]*?\.css') &&\
curl -s -o $@ $(APP_SERVER)$(APP_PATH)/css/$$APPCSS_FILE
else
cp -f $(WWW_PATH)/css/`readlink $(CSS_PREFIX).css` $@
endif
##----------------------------------------------------------------------
## Regenerate config files at each build, in case an environment variable has
## changed.
.PHONY: clean \
icons spashes $(IOS_SPLASHES) \
chcp app-config check-app-env \
$(APPJS) $(APPCSS) \
$(ADD_PLATFORMS) $(PLATFORMS) $(EMULATE_PLATFORMS) $(RUN_PLATFORMS) \
$(CORDOVAPATH)/www/chcp.json \
$(CORDOVAPATH)/www/eliom.html \
$(CORDOVAPATH)/www/index.html \
$(CORDOVAPATH)/config.xml
ifeq ($(USE_NPM), yes)
CORDOVA = PATH=$(PWD)/node_modules/cordova/bin:$$PATH cordova
CORDOVA_HCP = PATH=$(PWD)/node_modules/cordova-hot-code-push-cli/bin:$$PATH cordova-hcp
else
CORDOVA = cordova
CORDOVA_HCP = cordova-hcp
endif
# Necessary Cordova and static files directories. They are created when
# creating the Cordova project.
CORDOVA_DIRS := www www/css www/fonts www/images www/img www/js
# If Cordova is installed, use it to create a new project
# If not, we assume we don't need a full fledge project and simply create
# necessary directories (typically on a server where we only want to set up
# Hot Code Push).
# The CSS, JS, logo, index and config.xml created by the Cordova CLI are removed
# to be replaced later.
$(CORDOVAPATH):
ifeq ($(USE_NPM), yes)
npm install cordova git+https://github.com/besport/cordova-hot-code-push-cli.git read-package-json xml2js
endif
$(CORDOVA) create $(CORDOVAPATH) $(MOBILE_APP_ID) "$(MOBILE_APP_NAME)"
rm -f $(CORDOVAPATH)/www/css/index.css
rm -f $(CORDOVAPATH)/www/js/index.js
rm -f $(CORDOVAPATH)/www/img/logo.png
rm -f $(CORDOVAPATH)/www/index.html
rm -f $(CORDOVAPATH)/config.xml
$(foreach dir,$(CORDOVA_DIRS),mkdir -p $@/$(dir);)
##----------------------------------------------------------------------
## Rules for static files.
# Function to list files in a given directory $(1), and change prefix directory
# to the target one $(2)
# Example: $(call static_targets,$(SOURCE_DIR),$(TARGET_DIR))
static_targets = $(patsubst $(1)%,$(2)%,$(shell find $(1) -type f | grep -v .well-known | grep -v "/fonts/.*\\([.]svg\\|[.]eot\\|webfont[.]ttf\\)$$"))
# Cordova static files which needs to be copied to the Cordova project
# directory.
CORDOVA_STATIC_FILES := \
$(CORDOVAPATH)/.chcpignore \
$(call static_targets,$(MOBILESTATICPATH)/www,$(CORDOVAPATH)/www)
LOCAL_STATIC_FILES := $(call static_targets,$(LOCAL_STATIC)/css,$(CORDOVAPATH)/www/css) $(call static_targets,$(LOCAL_STATIC)/images,$(CORDOVAPATH)/www/images) $(call static_targets,$(LOCAL_STATIC)/fonts,$(CORDOVAPATH)/www/fonts)
# Static files dependencies: if a file changes in these directory, a new copy
# of static files will be triggered
# The rules related to static files are essentially copy in the appropriate
# mobile directory.
$(CORDOVA_STATIC_FILES): $(CORDOVAPATH)/%: $(MOBILESTATICPATH)/%
cp -rf $< $@
$(LOCAL_STATIC_FILES): $(CORDOVAPATH)/www/%: $(LOCAL_STATIC)/%
cp -rf $< $@
##----------------------------------------------------------------------
## Rules for cordova config files like index.html, eliom_loader.js, etc.
# Parse APP_SERVER and extract host, scheme, port. A bit rudimentary,
# but it should work for URLs starting with http:// or https://.
APP_SCHEME = $(shell echo $(APP_SERVER) | cut -d : -f 1)
APP_HOST = $(shell echo $(APP_SERVER) | cut -d / -f 3 | cut -d : -f 1)
APP_PORT = $(shell echo $(APP_SERVER) | cut -d / -f 3 | cut -d : -f 2)
ifneq ($(APP_PORT),"")
APP_PORT_ARG="port=\"$(APP_PORT)\""
else
APP_PORT_ARG=
endif
ifeq ($(APP),dev)
MOBILE_USE_CLEARTEXT_TRAFFIC?=<edit-config file=\"app/src/main/AndroidManifest.xml\" mode=\"merge\" target=\"/manifest/application\"><application android:usesCleartextTraffic=\"true\"/></edit-config>
else
MOBILE_USE_CLEARTEXT_TRAFFIC?=
endif
# This rule generates the config.xml file from mobile/config.xml.in.
$(CORDOVAPATH)/config.xml: mobile/config.xml.in $(CORDOVAPATH)
sed -e "s,%%APPSERVER%%,$(APP_SERVER),g" \
-e "s,%%APPPATH%%,$(APP_PATH),g" \
-e "s,%%APPHOST%%,$(APP_HOST),g" \
-e "s,%%APPPORTARG%%,$(APP_PORT_ARG),g" \
-e "s,%%APPSCHEME%%,$(APP_SCHEME),g" \
-e "s,%%APPID%%,$(MOBILE_APP_ID),g" \
-e "s,%%MOBILE_APP_NAME%%,$(MOBILE_APP_NAME),g" \
-e "s,%%MOBILE_APP_VERSION%%,$(MOBILE_APP_VERSION),g" \
-e "s,%%MOBILE_DESCRIPTION%%,$(MOBILE_DESCRIPTION),g" \
-e "s,%%MOBILE_AUTHOR_EMAIL%%,$(MOBILE_AUTHOR_EMAIL),g" \
-e "s,%%MOBILE_AUTHOR_HREF%%,$(MOBILE_AUTHOR_HREF),g" \
-e "s,%%MOBILE_AUTHOR_DESCRIPTION%%,$(MOBILE_AUTHOR_DESCRIPTION),g" \
-e "s,%%MOBILE_ANDROID_SDK_VERSION%%,$(MOBILE_ANDROID_SDK_VERSION),g" \
-e "s,%%MOBILE_NOTIFICATIONS_SENDER_ID%%,$(MOBILE_NOTIFICATIONS_SENDER_ID),g" \
-e "s,%%MOBILE_USE_CLEARTEXT_TRAFFIC%%,$(MOBILE_USE_CLEARTEXT_TRAFFIC),g" \
mobile/config.xml.in > $@
# This rule generates index.html. md5sum is used to set the right JavaScript
# filename in the page.
$(CORDOVAPATH)/www/index.html: $(CORDOVAPATH) $(APPJS) mobile/index.html.in
HASH=$$(md5sum $(APPJS) | cut -d ' ' -f 1) && \
sed -e "s,%%APPNAME%%,$(PROJECT_NAME)_$$HASH,g" \
-e "s,%%APPSERVER%%,$(APP_SERVER),g" \
-e "s,%%APPPATH%%,$(APP_PATH),g" \
-e "s,%%MOBILE_APP_NAME%%,$(MOBILE_APP_NAME),g" \
mobile/index.html.in > \
$(CORDOVAPATH)/www/index.html
# This rule generates eliom.html. md5sum is used to set the right JavaScript and
# CSS filenames in the page.
$(CORDOVAPATH)/www/eliom.html: $(CORDOVAPATH) \
$(APPJS) $(APPCSS) mobile/eliom.html.in
JS_HASH=$$(md5sum $(APPJS) | cut -d ' ' -f 1) && \
CSS_HASH=$$(md5sum $(APPCSS) | cut -d ' ' -f 1) && \
sed -e "s,%%APPNAME%%,$(PROJECT_NAME)_$$JS_HASH,g" \
-e "s,%%APPPATH%%,$(APP_PATH),g" \
-e "s,%%PROJECTNAME%%,$(PROJECT_NAME),g" \
-e "s,%%APPSERVER%%,$(APP_SERVER),g" \
mobile/eliom.html.in > $@
# Eliom loader is used in the index.html to retrieve update from the server.
mobile/eliom_loader.byte: mobile/eliom_loader.ml
ocamlfind ocamlc \
-package js_of_ocaml,js_of_ocaml-ppx,js_of_ocaml-lwt,lwt_ppx \
-linkpkg -o mobile/eliom_loader.byte \
$<
$(CORDOVAPATH)/www/eliom_loader.js: mobile/eliom_loader.byte
js_of_ocaml $< -o $@
# app-config builds all needed files by the mobile application.
app-config: $(CORDOVAPATH)/www/index.html \
$(CORDOVAPATH)/www/eliom.html \
$(CORDOVAPATH)/www/eliom_loader.js \
$(CORDOVAPATH)/config.xml \
$(CORDOVAPATH)/www/chcp.manifest \
$(CORDOVAPATH)/www/chcp.json
##----------------------------------------------------------------------
## Rules to update the mobile applications. For the moment, Cordova Hot Code
## Push is used.
# Get the actual timestamp which will be used by chcp.json.
TIMESTAMP := $(shell date +%y%m%d-%H%M%S)
# Build the chcp.manifest with cordova-hcp build for updates. First, it updates
# and copies all files for the mobile application.
$(CORDOVAPATH)/www/chcp.manifest: $(APPJS) $(APPCSS) \
$(CORDOVAPATH) $(CORDOVA_STATIC_FILES) \
$(LOCAL_STATIC_FILES)
cd $(CORDOVAPATH) ; $(CORDOVA_HCP) build
$(RM) $(CORDOVAPATH)/www/chcp.json
# Build the chcp.json based on mobile/chcp.json.in after creating the
# chcp.manifest.
$(CORDOVAPATH)/www/chcp.json: mobile/chcp.json.in \
$(CORDOVAPATH)/www/chcp.manifest
sed -e "s,%%APPSERVER%%,$(APP_SERVER),g" \
-e "s,%%APPPATH%%,$(APP_PATH),g" \
-e "s,%%DATE%%,$(TIMESTAMP),g" \
$< > $@
# Create the $(TIMESTAMP) directory where updates will be copied. First, it
# needs to generate and copy all files needed by the mobile application.
$(WWW_PATH)/update/$(TIMESTAMP): app-config check-app-env \
$(CORDOVA_STATIC_FILES) $(LOCAL_STATIC_FILES)
mkdir -p $(WWW_PATH)/update
cp -r $(CORDOVAPATH)/www $@
# Build the chcp configuration based on the timestamp. First, it calls the rule
# to create the update/TIMESTAMP directory where updated files will be copied
# and secondly, it copies the chcp.json generated previously in the update/conf
# directory.
$(WWW_PATH)/update/conf/chcp.json: $(WWW_PATH)/update/$(TIMESTAMP)
mkdir -p $(WWW_PATH)/update/conf
cp $</chcp.json $@
chcp: $(WWW_PATH)/update/conf/chcp.json
assets: $(CORDOVAPATH) chcp
##----------------------------------------------------------------------
## Cordova platform rules
# Add the platform to the Cordova project.
# FIXME/IMPROVEME: true is used to avoid to stop building
# the application if the platform is already added. The disadvantage of
# this method is any error will be ignored.
$(CORDOVAPATH)/platforms/%: check-app-env assets icons-cp-% $(CORDOVAPATH)
cd $(CORDOVAPATH) ;\
$(CORDOVA) platform add $* ; true
ADD_PLATFORMS := add-android add-browser add-ios add-windows
$(ADD_PLATFORMS): add-%: $(CORDOVAPATH)/platforms/%
##----------------------------------------------------------------------
## Cordova build rules.
# debug build by default
PLATFORMS := android ios browser
# before building, environment variables are checked and assets are copied.
$(PLATFORMS): %: byte check-app-env icons-% assets icons-cp-% add-%
cd $(CORDOVAPATH) ;\
$(CORDOVA) build $(CORDOVA_FLAGS) $*
# release build
RELEASE_PLATFORMS := android-release ios-release
$(RELEASE_PLATFORMS): CORDOVA_FLAGS = -release
$(RELEASE_PLATFORMS): %-release: %
##----------------------------------------------------------------------
## Cordova emulate rules.
EMULATE_PLATFORMS := emulate-android emulate-ios
$(EMULATE_PLATFORMS): emulate-%: byte check-app-env icons-% assets icons-cp-% %
cd $(CORDOVAPATH) ;\
$(CORDOVA) emulate --nobuild $*
##----------------------------------------------------------------------
## Cordova run rules.
RUN_PLATFORMS := run-android run-ios run-browser
# Run the mobile app on a platform
# run-(android|ios|windows)
$(RUN_PLATFORMS): run-%: byte check-app-env icons-% assets %
cd $(CORDOVAPATH) ;\
$(CORDOVA) run --nobuild $*
##----------------------------------------------------------------------
## Rules for the icons and splashes generation.
CORDOVA_RES_PATH = $(CORDOVAPATH)/res
## Android icons names. It corresponds to ldpi, mdpi, hdpi, xhdpi, xxhdpi and
## xxxhdpi.
ANDROID_ICON_NAMES = \
icon-36 \
icon-48 \
icon-72 \
icon-96 \
icon-144 \
icon-192
ANDROID_ICONS_PATH = $(MOBILESTATICPATH)/res/android
ANDROID_ICONS = $(foreach name,$(ANDROID_ICON_NAMES),$(ANDROID_ICONS_PATH)/$(name).png)
IOS_ICON_NAMES = \
icon-20 icon-20@2x \
icon-40 icon-40@2x \
icon-50 icon-50@2x \
icon-60 icon-60@2x icon-60@3x \
icon-72 icon-72@2x \
icon-76 icon-76@2x \
icon-83.5@2x \
icon icon@2x \
icon-small icon-small@2x icon-small@3x
IOS_ICONS_PATH = $(MOBILESTATICPATH)/res/ios
IOS_ICONS = $(foreach name,$(IOS_ICON_NAMES),$(IOS_ICONS_PATH)/$(name).png)
# Icons creation (ImageMagick is necessary)
ICON_PNG := assets/images/icon.png
convert_android = convert -background none -resize
convert_ios = convert -background white -resize
$(ANDROID_ICONS_PATH) $(IOS_ICONS_PATH):
mkdir -p $@
# Simple icons (Android)
$(ANDROID_ICONS_PATH)/icon-%.png: $(ICON_PNG) $(ANDROID_ICONS_PATH)
$(convert_android) $*x$* $< $@
# Triple size icons (iOS)
$(IOS_ICONS_PATH)/icon-%@3x.png: $(ICON_PNG) $(IOS_ICONS_PATH)
size=$$((3 * $*)) ;\
$(convert_ios) $${size}x$${size} $< $@
# Double size icons (iOS)
$(IOS_ICONS_PATH)/icon-%@2x.png: $(ICON_PNG) $(IOS_ICONS_PATH)
size=$$((2 * $*)) ;\
$(convert_ios) $${size}x$${size} $< $@
# Simple icons (iOS)
$(IOS_ICONS_PATH)/icon-%.png: $(ICON_PNG) $(IOS_ICONS_PATH)
$(convert_ios) $*x$* $< $@
# iOS icons with special names
# To add one, use a rule like this:
# $(ICON_WITH_SPECIAL_NAME): $(ICON_WITH_PIXELS.png); cp $< $@
$(IOS_ICONS_PATH)/icon-small.png: $(IOS_ICONS_PATH)/icon-29.png; cp $< $@
$(IOS_ICONS_PATH)/icon-small@2x.png: $(IOS_ICONS_PATH)/icon-29@2x.png; cp $< $@
$(IOS_ICONS_PATH)/icon-small@3x.png: $(IOS_ICONS_PATH)/icon-29@3x.png; cp $< $@
$(IOS_ICONS_PATH)/icon.png: $(IOS_ICONS_PATH)/icon-57.png; cp $< $@
$(IOS_ICONS_PATH)/icon@2x.png: $(IOS_ICONS_PATH)/icon-57@2x.png; cp $< $@
$(IOS_ICONS_PATH)/icon-83.5@2x.png: $(IOS_ICONS_PATH)/icon-167.png; cp $< $@
# Launch images (iOS)
# Format: SIZE.NAME
IOS_SPLASHES = \
640x1136.Default-568h@2x~iphone \
750x1334.Default-667h \
1242x2208.Default-736h \
2208x1242.Default-Landscape-736h \
2048x1536.Default-Landscape@2x~ipad \
1024x768.Default-Landscape~ipad \
1536x2048.Default-Portrait@2x~ipad \
768x1024.Default-Portrait~ipad \
640x960.Default@2x~iphone \
320x480.Default~iphone
convert_splash_ios = convert -background white -gravity center -extent
$(IOS_SPLASHES): $(ICON_SVG) $(IOS_ICON_PATH)
splash=$@; \
size=$${splash%%.*}; \
name=$${splash#*.}; \
$(convert_splash_ios) $$size $< $(IOS_ICONS_PATH)/$$name.png
splashes: $(IOS_SPLASHES)
# Only this rule must be used to generate the icons.
icons: $(ANDROID_ICONS) $(IOS_ICONS)
icons-android: $(ANDROID_ICONS)
icons-ios: $(IOS_ICONS)
$(CORDOVA_RES_PATH): $(CORDOVAPATH)
mkdir -p $(CORDOVA_RES_PATH)
icons-cp-%: $(MOBILESTATICPATH)/res/% $(CORDOVA_RES_PATH)
cp -r $< $(CORDOVA_RES_PATH)
icons-clean:
rm -rf $(IOS_ICONS_PATH) $(ANDROID_ICONS_PATH)
##----------------------------------------------------------------------
## Cleaning rules.
mobile-clean: icons-clean
rm -rf $(CORDOVAPATH)
rm -f mobile/eliom_loader.byte mobile/eliom_loader.cmi \
mobile/eliom_loader.cmo

253
daegsrv/Makefile.options Normal file
View file

@ -0,0 +1,253 @@
#----------------------------------------------------------------------
# SETTINGS FOR THE ELIOM PROJECT daegsrv
#----------------------------------------------------------------------
# Required binaries
OPAM := opam
PROJECT_NAME := daegsrv
##----------------------------------------------------------------------
## The following part is used for the internationalization (i18n).
## The list of supported language, separated by a comma. Each language will
## generate a value in a sum type defined in the generate i18n file.
I18N_LANGUAGES := en,fr
## The default language.
I18N_DEFAULT_LANGUAGE := en
## The TSV files containing all translations.
I18N_TSV_DIR := assets/
I18N_PRIMARY_FILE := $(I18N_TSV_DIR)$(PROJECT_NAME)_i18n.tsv
# The demo entries are in their own file, for convenience.
I18N_EXTRA_FILES := $(I18N_TSV_DIR)$(PROJECT_NAME)_Demo_i18n.tsv
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## The following part contains the settings for the compilation process like the
## server/client OPAM packages and the server/client files.
# Source files for the server. By default, it's all eliom, eliomi and ml files
# in the project root directory.
SERVER_FILES := $(wildcard *.eliomi *.eliom *.ml) \
$(notdir $(patsubst %.tsv,%.eliom,$(I18N_EXTRA_FILES))) \
daegsrv_i18n.eliom
# Source files for the client. By default, it's all eliom and eliomi files
# in the project root directory.
CLIENT_FILES := $(wildcard *.eliomi *.eliom) \
$(notdir $(patsubst %.tsv,%.eliom,$(I18N_EXTRA_FILES))) \
daegsrv_i18n.eliom
# OCamlfind packages for the server (for modules which defines services)
SERVER_ELIOM_PACKAGES := ocsigen-start.server
# OCamlfind packages for the server
SERVER_PACKAGES := lwt_ppx js_of_ocaml-ppx_deriving_json ppx_deriving.std pgocaml ocsigen-ppx-rpc
# OCamlfind packages for database access
SERVER_DB_PACKAGES := pgocaml pgocaml_ppx
# OCamlfind packages for the client
CLIENT_PACKAGES := lwt_ppx js_of_ocaml-ppx js_of_ocaml-ppx_deriving_json \
ppx_deriving.std ocsigen-start.client base ocsigen-ppx-rpc
# Automatically install packages via NPM
USE_NPM := yes
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## The following part contains the configuration for the ocsigenserver.
# User to run server with (make run.*)
WWWUSER := www-data
WWWGROUP := www-data
# Port for running the server (make run.*)
PORT := 80
# Port for testing (make test.*)
TEST_PORT := 8080
# Root of installation (must end with /)
PREFIX := /usr/local/
# Local folder for make test.* (must end with /)
TEST_PREFIX := local/
## The installation tree (relative to $(PREFIX) when installing/running or
## $(TEST_PREFIX) when testing).
# Configuration file $(PROJECT_NAME).conf
ETCDIR := etc/${PROJECT_NAME}
# Project's library $(PROJECT_NAME).cma (cmxs)
LIBDIR := lib/${PROJECT_NAME}
# Command pipe, eg. $ echo restart > $(INSTALL_PREFIX)$(CMDPIPE)
CMDPIPE := var/run/${PROJECT_NAME}-cmd
# Ocsigenserver's logging files
LOGDIR := var/log/${PROJECT_NAME}
# Ocsigenserver's persistent data files
DATADIR := var/data/${PROJECT_NAME}
# Project's static files
FILESDIR := var/www/${PROJECT_NAME}
# Project's JavaScript file directory
ELIOMSTATICDIR := var/www/${PROJECT_NAME}
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## The following part contains the settings for CSS.
# Directory with files to be statically served. All external CSS files will be
# copied in this directory.
LOCAL_STATIC := static
LOCAL_STATIC_CSS := $(LOCAL_STATIC)/css
LOCAL_STATIC_IMAGES := $(LOCAL_STATIC)/images
LOCAL_STATIC_FONTS := $(LOCAL_STATIC)/fonts
# Default CSS if not using SASS:
LOCAL_STATIC_DEFAULTCSS := $(LOCAL_STATIC)/defaultcss
# Main CSS generated file.
# If you use SASS, this is the compiled file.
# If you don't use SASS, all CSS files in $(CSS_FILES) will be concatenate in
# this file.
LOCAL_CSS := $(LOCAL_STATIC_DEFAULTCSS)/$(PROJECT_NAME).css
# The OPAM share directory.
SHAREDIR := $(shell $(OPAM) config var share)
# The ocsigen-toolkit directory containing CSS files.
SHAREDIR_OT_CSS := $(SHAREDIR)/ocsigen-toolkit/css
# CSS files to copy from other projects into the working directory.
# By default, ocsigen-start needs the CSS files of ocsigen-toolkit.
# If you don't need ocsigen-toolkit CSS files, remote CSS files related to
# ocsigen-toolkit in this variable.
# The order is important because it will be used when concataining all CSS files
# if SASS is deactivated.
# These files are copied with every run of make so they must not be modified.
EXTERNAL_CSS_FILES := $(SHAREDIR_OT_CSS)/ot_buttons.css \
$(SHAREDIR_OT_CSS)/ot_carousel.css \
$(SHAREDIR_OT_CSS)/ot_tongue.css \
$(SHAREDIR_OT_CSS)/ot_sticky.css \
$(SHAREDIR_OT_CSS)/ot_datetime.css \
$(SHAREDIR_OT_CSS)/ot_drawer.css \
$(SHAREDIR_OT_CSS)/ot_icons.css \
$(SHAREDIR_OT_CSS)/ot_picture_uploader.css \
$(SHAREDIR_OT_CSS)/ot_popup.css \
$(SHAREDIR_OT_CSS)/ot_spinner.css \
$(SHAREDIR_OT_CSS)/ot_page_transition.css
# All CSS files which must be concatenated.
# This variable is only used if SASS is deactivated.
# The order is important because it will be used when concataining all CSS
# files.
CSS_FILES := $(LOCAL_STATIC_CSS)/font-awesome.min.css \
$(EXTERNAL_CSS_FILES) \
$(LOCAL_STATIC_DEFAULTCSS)/os.css
# CSS destination
CSSDIR := $(TEST_PREFIX)$(ELIOMSTATICDIR)/css
CSS_PREFIX := $(CSSDIR)/$(PROJECT_NAME)
CSS_DEST := $(CSS_PREFIX).css
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## The following part contains the settings for SASS.
# Set to yes if you want to use SASS (http://sass-lang.com). Any other value
# will disable SASS.
# It is recommended to use SASS to write your styles. By default, SASS is
# activated.
USE_SASS := yes
# The directory where SASS files are.
SASSDIR := sass
# The filename of the main SASS file.
# You can import other stylesheets from this file
# (with @import "path/filename[.css]").
SASS_SRC := $(SASSDIR)/$(PROJECT_NAME).scss
# A separated-colon list of path containing CSS and SCSS files. It allows to
# avoid to write the entire path to a SCSS/CSS file in a @import rule.
SASS_PATH := $(SASSDIR)/lib:$(LOCAL_STATIC_CSS)
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## The following part contains the settings for debugging
# Debug application (yes/no): Debugging info in compilation,
# JavaScript, ocsigenserver
DEBUG := yes
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## The following part contains the settings for the mobile application.
# The directory where the Cordova application will be copied.
CORDOVAPATH := cordova
# The directory containing the files about the mobile application like the
# config.xml.in, index.html.in, eliom_loader.ml, ...
MOBILESTATICPATH := mobile
MOBILE_APP_ID := com.daegsrv.mobile
MOBILE_APP_NAME := daegsrv
MOBILE_APP_VERSION := 0.1
MOBILE_DESCRIPTION := daegsrv mobile application
MOBILE_AUTHOR_EMAIL := developer@domain.com
MOBILE_AUTHOR_HREF := http://domain.com
MOBILE_AUTHOR_DESCRIPTION := daegsrv team
MOBILE_ANDROID_SDK_VERSION := 19
MOBILE_NOTIFICATIONS_SENDER_ID := 424242424242
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## The following part is only used for PGOcaml (ie for the database).
## The host database.
DB_HOST := localhost
## The port of the database server
DB_PORT := 3000
## The database name for the project. By default, it's the project name.
DB_NAME := daegsrv
## The database user. By default, it will use the username of the current user.
DB_USER := $(USER)
## The password to access the database. By default it's empty.
DB_PASSWORD := ""
## The main SQL file. This file will be used by the rule 'db-schema' (defined
## in Makefile.db) to initialise the database schema.
PSQL_FILE := $(DB_NAME).sql
## The filename for the database snapshot. This variable is used
## by 'db-snapshot'.
DB_SNAPSHOT := daegsrv-$$(date '+%Y%m%d%H%M%S').sql.gz
## Choose if the database will be installed locally or globally
# - yes: will create the database in the $(TEST_PREFIX)/db (which has the value
# 'local' by default).
# - no: will use the default database daemon
LOCAL := yes
## If the LOCAL variable is set to yes, PSQL_DIR is the database directory.
PSQL_DIR := local_db
## If the LOCAL variable is set to yes, PSQL_LOG is the log directory.
PSQL_LOG := $(PSQL_DIR)/log
##----------------------------------------------------------------------

350
daegsrv/Makefile.os Normal file
View file

@ -0,0 +1,350 @@
#----------------------------------------------------------------------
# OCSIGEN-START MAKEFILE, NOT TO BE MODIFIED
#----------------------------------------------------------------------
##----------------------------------------------------------------------
## DISCLAIMER
##
## This file contains the rules to make an Eliom project. The project is
## configured through the variables in the file Makefile.options.
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## Internals
## Required binaries
ELIOMC := eliomc -w +A-4-7-9-37-38-39-41-42-44-45-48-70
ELIOMOPT := eliomopt
JS_OF_ELIOM := js_of_eliom -w +A-4-7-9-37-38-39-41-42-44-45-48-70 -jsopt +base/runtime.js
JS_OF_OCAML := js_of_ocaml
ELIOMDEP := eliomdep
OCSIGENSERVER := ocsigenserver
OCSIGENSERVER.OPT := ocsigenserver.opt
## Where to put intermediate object files.
## - ELIOM_{SERVER,CLIENT}_DIR must be distinct
## - ELIOM_CLIENT_DIR must not be the local dir.
## - ELIOM_SERVER_DIR could be ".", but you need to
## remove it from the "clean" rules...
export ELIOM_SERVER_DIR := _server
export ELIOM_CLIENT_DIR := _client
export ELIOM_TYPE_DIR := _server
DEPSDIR := _deps
ifeq ($(DEBUG),yes)
GENERATE_DEBUG ?= -g
RUN_DEBUG ?= "-v"
DEBUG_JS ?= --noinline --disable=shortvar --pretty
# --debuginfo
endif
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## General
.PHONY: all css byte opt
DIST_DIRS := $(ETCDIR) $(DATADIR) $(LIBDIR) $(LOGDIR) \
$(FILESDIR)/avatars/tmp $(ELIOMSTATICDIR) \
$(shell dirname $(CMDPIPE))
JS_PREFIX := $(TEST_PREFIX)$(ELIOMSTATICDIR)/$(PROJECT_NAME)
CONF_IN := $(wildcard *.conf.in)
CONFIG_FILES := $(patsubst %.conf.in,$(TEST_PREFIX)$(ETCDIR)/%.conf,$(CONF_IN))
TEST_CONFIG_FILES := $(patsubst %.conf.in,$(TEST_PREFIX)$(ETCDIR)/%-test.conf,$(CONF_IN))
all: css byte opt
byte:: $(TEST_PREFIX)$(LIBDIR)/${PROJECT_NAME}.cma
opt:: $(TEST_PREFIX)$(LIBDIR)/${PROJECT_NAME}.cmxs
byte opt:: ${JS_PREFIX}.js
byte opt:: $(CONFIG_FILES)
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## The following part has been generated with os template.
## This will overload the default required binaries.
## DO NOT MOVE IT ON TOP OF THE `all` RULE!
include Makefile.$(PROJECT_NAME)
## end of `include Makefile.$(PROJECT_NAME)`
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## Testing
DIST_FILES = $(ELIOMSTATICDIR)/$(PROJECT_NAME).js $(LIBDIR)/$(PROJECT_NAME).cma
.PHONY: test.byte test.opt staticfiles
test.byte: $(TEST_CONFIG_FILES) staticfiles $(addprefix $(TEST_PREFIX),$(DIST_DIRS) $(DIST_FILES)) css
@echo "==== The website is available at http://localhost:$(TEST_PORT) ===="
$(OCSIGENSERVER) $(RUN_DEBUG) -c $<
test.opt: $(TEST_CONFIG_FILES) staticfiles $(addprefix $(TEST_PREFIX),$(DIST_DIRS) $(patsubst %.cma,%.cmxs, $(DIST_FILES))) css
@echo "==== The website is available at http://localhost:$(TEST_PORT) ===="
$(OCSIGENSERVER.OPT) $(RUN_DEBUG) -c $<
$(addprefix $(TEST_PREFIX), $(DIST_DIRS)):
mkdir -p $@
staticfiles:
cp -rf $(LOCAL_STATIC_CSS) $(LOCAL_STATIC_IMAGES) $(LOCAL_STATIC_FONTS) $(TEST_PREFIX)$(ELIOMSTATICDIR)
##----------------------------------------------------------------------
## Installing & Running
.PHONY: install install.byte install.byte install.opt install.static install.etc install.lib install.lib.byte install.lib.opt run.byte run.opt
install: install.byte install.opt
install.byte: install.lib.byte install.etc install.static | $(addprefix $(PREFIX),$(DATADIR) $(LOGDIR) $(shell dirname $(CMDPIPE)))
install.opt: install.lib.opt install.etc install.static | $(addprefix $(PREFIX),$(DATADIR) $(LOGDIR) $(shell dirname $(CMDPIPE)))
install.lib: install.lib.byte install.lib.opt
install.lib.byte: $(TEST_PREFIX)$(LIBDIR)/$(PROJECT_NAME).cma | $(PREFIX)$(LIBDIR)
install $< $(PREFIX)$(LIBDIR)
install.lib.opt: $(TEST_PREFIX)$(LIBDIR)/$(PROJECT_NAME).cmxs | $(PREFIX)$(LIBDIR)
install $< $(PREFIX)$(LIBDIR)
install.static: $(TEST_PREFIX)$(ELIOMSTATICDIR)/$(PROJECT_NAME).js | $(PREFIX)$(STATICDIR) $(PREFIX)$(ELIOMSTATICDIR)
cp -r $(LOCAL_STATIC_CSS) $(PREFIX)$(FILESDIR)
cp -r $(LOCAL_STATIC_IMAGES) $(PREFIX)$(FILESDIR)
cp -r $(LOCAL_STATIC_FONTS) $(PREFIX)$(FILESDIR)
[ -z $(WWWUSER) ] || chown -R $(WWWUSER) $(PREFIX)$(FILESDIR)
install $(addprefix -o ,$(WWWUSER)) $< $(PREFIX)$(ELIOMSTATICDIR)
install.etc: $(TEST_PREFIX)$(ETCDIR)/$(PROJECT_NAME).conf | $(PREFIX)$(ETCDIR)
install $< $(PREFIX)$(ETCDIR)/$(PROJECT_NAME).conf
.PHONY:
print-install-files:
@echo $(PREFIX)$(LIBDIR)
@echo $(PREFIX)$(ELIOMSTATICDIR)
@echo $(PREFIX)$(ETCDIR)
$(addprefix $(PREFIX),$(ETCDIR) $(LIBDIR)):
install -d $@
$(addprefix $(PREFIX),$(DATADIR) $(LOGDIR) $(ELIOMSTATICDIR) $(shell dirname $(CMDPIPE))):
install $(addprefix -o ,$(WWWUSER)) -d $@
run.byte:
@echo "==== The website is available at http://localhost:$(PORT) ===="
$(OCSIGENSERVER) $(RUN_DEBUG) -c ${PREFIX}${ETCDIR}/${PROJECT_NAME}.conf
run.opt:
@echo "==== The website is available at http://localhost:$(PORT) ===="
$(OCSIGENSERVER.OPT) $(RUN_DEBUG) -c ${PREFIX}${ETCDIR}/${PROJECT_NAME}.conf
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## Aux
# Use `eliomdep -sort' only in OCaml>4
#ifeq ($(shell ocamlc -version|cut -c1),4)
#eliomdep=$(shell $(ELIOMDEP) $(1) -ppx -sort $(2) $(filter %.eliom %.ml,$(3))))
#else
#eliomdep=$(3)
#endif
objs=$(patsubst %.ml,$(1)/%.$(2),$(patsubst %.eliom,$(1)/%.$(2),$(filter %.eliom %.ml,$(3))))
#depsort=$(call objs,$(1),$(2),$(call eliomdep,$(3),$(4),$(5)))
depsort=$(shell ocaml tools/sort_deps.ml .depend $(patsubst %.ml,$(1)/%.$(2),$(patsubst %.eliom,$(1)/%.$(2),$(filter %.eliom %.ml,$(5)))))
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## Config files
ELIOM_MODULES=$(patsubst %,\<eliommodule\ findlib-package=\"%\"\ /\>,$(SERVER_ELIOM_PACKAGES))
FINDLIB_PACKAGES=$(patsubst %,\<extension\ findlib-package=\"%\"\ /\>,$(SERVER_PACKAGES))
EDIT_WARNING=DON\'T EDIT THIS FILE! It is generated from $(PROJECT_NAME).conf.in, edit that one, or the variables in Makefile.options
SED_ARGS = -e "/^ *%%%/d"
SED_ARGS += -e "s|%%PROJECT_NAME%%|$(PROJECT_NAME)|g"
SED_ARGS += -e "s|%%DB_NAME%%|$(DB_NAME)|g"
SED_ARGS += -e "s|%%DB_HOST%%|$(DB_HOST)|g"
SED_ARGS += -e "s|%%DB_PORT%%|$(DB_PORT)|g"
SED_ARGS += -e "s|%%DB_USER%%|$(DB_USER)|g"
SED_ARGS += -e "s|%%DB_PASSWORD%%|$(DB_PASSWORD)|g"
SED_ARGS += -e "s|%%CMDPIPE%%|%%PREFIX%%$(CMDPIPE)|g"
SED_ARGS += -e "s|%%LOGDIR%%|%%PREFIX%%$(LOGDIR)|g"
SED_ARGS += -e "s|%%DATADIR%%|%%PREFIX%%$(DATADIR)|g"
SED_ARGS += -e "s|%%LIBDIR%%|%%PREFIX%%$(LIBDIR)|g"
SED_ARGS += -e "s|%%WARNING%%|$(EDIT_WARNING)|g"
SED_ARGS += -e "s|%%PACKAGES%%|$(FINDLIB_PACKAGES)|g"
SED_ARGS += -e "s|%%ELIOM_MODULES%%|$(ELIOM_MODULES)|g"
SED_ARGS += -e "s|%%FILESDIR%%|%%PREFIX%%$(FILESDIR)|g"
SED_ARGS += -e "s|%%ELIOMSTATICDIR%%|%%PREFIX%%$(ELIOMSTATICDIR)|g"
SED_ARGS += -e "s|%%APPNAME%%|$(shell basename `readlink $(JS_PREFIX).js` .js)|g"
SED_ARGS += -e "s|%%CSSNAME%%|$(shell readlink $(CSS_PREFIX).css)|g"
ifeq ($(DEBUG),yes)
SED_ARGS += -e "s|%%DEBUGMODE%%|\<debugmode /\>|g"
else
SED_ARGS += -e "s|%%DEBUGMODE%%||g"
endif
LOCAL_SED_ARGS := -e "s|%%PORT%%|$(TEST_PORT)|g"
LOCAL_SED_ARGS += -e "s|%%USERGROUP%%||g"
GLOBAL_SED_ARGS := -e "s|%%PORT%%|$(PORT)|g"
ifeq ($(WWWUSER)$(WWWGROUP),)
GLOBAL_SED_ARGS += -e "s|%%USERGROUP%%||g"
else
GLOBAL_SED_ARGS += -e "s|%%USERGROUP%%|<user>$(WWWUSER)</user><group>$(WWWGROUP)</group>|g"
endif
ifneq ($(DO_NOT_RECOMPILE),yes)
JS_AND_CSS=$(JS_PREFIX).js $(CSS_PREFIX).css
endif
$(CONFIG_FILES): $(TEST_PREFIX)$(ETCDIR)/%.conf: %.conf.in Makefile.options $(JS_AND_CSS) | $(TEST_PREFIX)$(ETCDIR)
sed $(SED_ARGS) $(GLOBAL_SED_ARGS) $< | sed -e "s|%%PREFIX%%|$(PREFIX)|g" > $@
$(TEST_CONFIG_FILES): $(TEST_PREFIX)$(ETCDIR)/%-test.conf: %.conf.in Makefile.options $(JS_AND_CSS) | $(TEST_PREFIX)$(ETCDIR)
sed $(SED_ARGS) $(LOCAL_SED_ARGS) $< | sed -e "s|%%PREFIX%%|$(TEST_PREFIX)|g" > $@
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## Server side compilation
SERVER_INC_DEP := ${addprefix -package ,${SERVER_PACKAGES} ${SERVER_ELIOM_PACKAGES}}
SERVER_INC := ${addprefix -package ,${SERVER_PACKAGES} ${SERVER_ELIOM_PACKAGES}}
SERVER_DB_INC := ${addprefix -package ,${SERVER_PACKAGES} ${SERVER_DB_PACKAGES} ${SERVER_ELIOM_PACKAGES}}
${ELIOM_TYPE_DIR}/%.type_mli: %.eliom
${ELIOMC} -ppx -ppx ${I18N_PPX_REWRITER} -infer ${SERVER_INC} $<
$(TEST_PREFIX)$(LIBDIR)/$(PROJECT_NAME).cma: $(call objs,$(ELIOM_SERVER_DIR),cmo,$(SERVER_FILES)) | $(TEST_PREFIX)$(LIBDIR)
${ELIOMC} -a -o $@ $(GENERATE_DEBUG) \
$(call depsort,$(ELIOM_SERVER_DIR),cmo,-server,$(SERVER_DB_INC),$(SERVER_FILES))
$(TEST_PREFIX)$(LIBDIR)/$(PROJECT_NAME).cmxa: $(call objs,$(ELIOM_SERVER_DIR),cmx,$(SERVER_FILES)) | $(TEST_PREFIX)$(LIBDIR)
${ELIOMOPT} -a -o $@ $(GENERATE_DEBUG) \
$(call depsort,$(ELIOM_SERVER_DIR),cmx,-server,$(SERVER_DB_INC),$(SERVER_FILES))
%.cmxs: %.cmxa
$(ELIOMOPT) -shared -linkall -o $@ $(GENERATE_DEBUG) $<
${ELIOM_SERVER_DIR}/%_db.cmi: %_db.mli
${ELIOMC} -c -ppx ${SERVER_DB_INC} $(GENERATE_DEBUG) $<
${ELIOM_SERVER_DIR}/%.cmi: %.mli
${ELIOMC} -c ${SERVER_INC} $(GENERATE_DEBUG) $<
${ELIOM_SERVER_DIR}/%.cmi: %.eliomi
${ELIOMC} -ppx -ppx ${I18N_PPX_REWRITER} -c ${SERVER_INC} $(GENERATE_DEBUG) $<
${ELIOM_SERVER_DIR}/%_db.cmo: %_db.ml
${ELIOMC} -c -ppx ${SERVER_DB_INC} $(GENERATE_DEBUG) $<
${ELIOM_SERVER_DIR}/%.cmo: %.ml
${ELIOMC} -c ${SERVER_INC} $(GENERATE_DEBUG) $<
${ELIOM_SERVER_DIR}/%.cmo: %.eliom
${ELIOMC} -ppx -ppx ${I18N_PPX_REWRITER} -c ${SERVER_INC} $(GENERATE_DEBUG) $<
${ELIOM_SERVER_DIR}/%_db.cmx: %_db.ml
${ELIOMOPT} -c -ppx ${SERVER_DB_INC} $(GENERATE_DEBUG) $<
${ELIOM_SERVER_DIR}/%.cmx: %.ml
${ELIOMOPT} -c ${SERVER_INC} $(GENERATE_DEBUG) $<
${ELIOM_SERVER_DIR}/%.cmx: %.eliom
${ELIOMOPT} -ppx -ppx ${I18N_PPX_REWRITER} -c ${SERVER_INC} $(GENERATE_DEBUG) $<
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## Client side compilation
CLIENT_LIBS := ${addprefix -package ,${CLIENT_PACKAGES}}
CLIENT_INC := ${addprefix -package ,${CLIENT_PACKAGES}}
CLIENT_OBJS := $(filter %.eliom %.ml, $(CLIENT_FILES))
CLIENT_OBJS := $(patsubst %.eliom,${ELIOM_CLIENT_DIR}/%.cmo, ${CLIENT_OBJS})
CLIENT_OBJS := $(patsubst %.ml,${ELIOM_CLIENT_DIR}/%.cmo, ${CLIENT_OBJS})
$(ELIOM_CLIENT_DIR)/os_prologue.js: \
$(shell ocamlfind query -r -predicates byte -a-format $(CLIENT_PACKAGES))
${JS_OF_ELIOM} -jsopt --dynlink -o $@ $(GENERATE_DEBUG) $(CLIENT_INC) \
${addprefix -jsopt ,$(DEBUG_JS)}
ifeq ($(DEBUG),yes)
$(JS_PREFIX).js: $(call objs,$(ELIOM_CLIENT_DIR),js,$(CLIENT_FILES)) | $(TEST_PREFIX)$(ELIOMSTATICDIR) $(ELIOM_CLIENT_DIR)/os_prologue.js
cat $(ELIOM_CLIENT_DIR)/os_prologue.js $(call depsort,$(ELIOM_CLIENT_DIR),js,-client,$(CLIENT_INC),$(CLIENT_FILES)) > $(JS_PREFIX)_tmp.js && \
HASH=`md5sum $(JS_PREFIX)_tmp.js | cut -d ' ' -f 1` && \
mv $(JS_PREFIX)_tmp.js $(JS_PREFIX)_$$HASH.js && \
ln -sf $(PROJECT_NAME)_$$HASH.js $@
else
$(JS_PREFIX).js: $(call objs,$(ELIOM_CLIENT_DIR),cmo,$(CLIENT_FILES)) | $(TEST_PREFIX)$(ELIOMSTATICDIR)
${JS_OF_ELIOM} -ppx -o $(JS_PREFIX)_tmp.js $(GENERATE_DEBUG) $(CLIENT_INC) ${addprefix -jsopt ,$(DEBUG_JS)} \
$(call depsort,$(ELIOM_CLIENT_DIR),cmo,-client,$(CLIENT_INC),$(CLIENT_FILES))
HASH=`md5sum $(JS_PREFIX)_tmp.js | cut -d ' ' -f 1` && \
mv $(JS_PREFIX)_tmp.js $(JS_PREFIX)_$$HASH.js && \
ln -sf $(PROJECT_NAME)_$$HASH.js $@
endif
${ELIOM_CLIENT_DIR}/%.cmi: %.mli
${JS_OF_ELIOM} -c ${CLIENT_INC} $(GENERATE_DEBUG) $<
${ELIOM_CLIENT_DIR}/%.cmo: %.eliom
${JS_OF_ELIOM} -ppx -ppx ${I18N_PPX_REWRITER} -c ${CLIENT_INC} $(GENERATE_DEBUG) $<
${ELIOM_CLIENT_DIR}/%.cmo: %.ml
${JS_OF_ELIOM} -c ${CLIENT_INC} $(GENERATE_DEBUG) $<
${ELIOM_CLIENT_DIR}/%.cmi: %.eliomi
${JS_OF_ELIOM} -ppx -ppx ${I18N_PPX_REWRITER} -c ${CLIENT_INC} $(GENERATE_DEBUG) $<
${ELIOM_CLIENT_DIR}/%.js: ${ELIOM_CLIENT_DIR}/%.cmo
${JS_OF_OCAML} $(DEBUG_JS) +base/runtime.js $<
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## Dependencies
# DO NOT include `.depend' for the following commands: db-*, clean, distclean
is_db_command=$(shell echo $(1) | grep -q "db-" && echo "true" || echo "false")
ifneq ($(call is_db_command,$(MAKECMDGOALS)),true)
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),i18n-update)
ifneq ($(MAKECMDGOALS),distclean)
include .depend
endif
endif
endif
endif
.depend: $(patsubst %,$(DEPSDIR)/%.server,$(SERVER_FILES)) $(patsubst %,$(DEPSDIR)/%.client,$(CLIENT_FILES))
@cat $^ > $@
$(DEPSDIR)/%.ml.server: %.ml | $(DEPSDIR) $(SERVER_FILES)
$(ELIOMDEP) -server -ppx $(SERVER_DB_INC) $< > $@.tmp && mv $@.tmp $@
$(DEPSDIR)/%.mli.server: %.mli | $(DEPSDIR) $(SERVER_FILES)
$(ELIOMDEP) -server -ppx $(SERVER_DB_INC) $< > $@.tmp && mv $@.tmp $@
$(DEPSDIR)/%.eliom.server: %.eliom | $(DEPSDIR) $(SERVER_FILES)
$(ELIOMDEP) -server -ppx -ppx ${I18N_PPX_REWRITER} $(SERVER_INC_DEP) $< > $@.tmp && mv $@.tmp $@
$(DEPSDIR)/%.eliomi.server: %.eliomi | $(DEPSDIR) $(SERVER_FILES)
$(ELIOMDEP) -server -ppx -ppx ${I18N_PPX_REWRITER} $(SERVER_INC_DEP) $< > $@.tmp && mv $@.tmp $@
$(DEPSDIR)/%.ml.client: %.ml | $(DEPSDIR)
$(ELIOMDEP) -client $(CLIENT_INC) $< > $@.tmp && mv $@.tmp $@
$(DEPSDIR)/%.eliom.client: %.eliom | $(DEPSDIR)
$(ELIOMDEP) -client -ppx -ppx ${I18N_PPX_REWRITER} $(CLIENT_INC) $< > $@.tmp && mv $@.tmp $@
$(DEPSDIR)/%.eliomi.client: %.eliomi | $(DEPSDIR)
$(ELIOMDEP) -client -ppx -ppx ${I18N_PPX_REWRITER} $(CLIENT_INC) $< > $@.tmp && mv $@.tmp $@
$(DEPSDIR):
mkdir $@
##----------------------------------------------------------------------
##----------------------------------------------------------------------
## Clean up
clean:: clean-style mobile-clean i18n-clean
-rm -f *.cm[ioax] *.cmxa *.cmxs *.o *.a *.annot
-rm -f *.type_mli
-rm -rf ${ELIOM_CLIENT_DIR} ${ELIOM_SERVER_DIR}
distclean: clean
-rm -rf $(TEST_PREFIX) $(DEPSDIR) .depend

119
daegsrv/Makefile.style Normal file
View file

@ -0,0 +1,119 @@
# The following condition is used to determinate if the sed version is BSD or
# GNU. It is useful because `sed -i` has a different implementation (and this
# command is needed by $(CSS_DEST). It uses the fact that `sed --version`
# doesn't exist for the BSD version.
.PHONY: css check_sass clean-style
check_sed:
ifneq ($(shell sed --version 2> /dev/null),)
SED = sed -i
else
SED = sed -i .css
endif
css: $(CSS_DEST)
define ERROR_SASS
Error: SASS not found.
Ocsigen Start gives the choice to use SASS (a CSS preprocessor) to write
stylesheets. We encourage you to use SASS and the template needs it by default.
See https://sass-lang.com for more information.
If you don't really want to use it, you can change the value of the variable
USE_SASS to "no" in Makefile.options to use CSS.
endef
check_sass:
ifeq ($(strip $(USE_SASS)),yes)
ifeq ($(shell which sassc),)
ifeq ($(shell which sass),)
$(error $(ERROR_SASS))
endif
endif
endif
ifeq ($(USE_NPM), yes)
PACKAGE_JSON = package.json
NPM_POSTCSS = node_modules/postcss-cli
NPM_AUTOPREFIXER = node_modules/autoprefixer
POSTCSS = node_modules/.bin/postcss
$(NPM_POSTCSS): $(PACKAGE_JSON)
npm install postcss-cli@7.1.2
$(NPM_AUTOPREFIXER): $(PACKAGE_JSON)
npm install autoprefixer@9.8.6
$(PACKAGE_JSON):
npm init --yes
else
PACKAGE_JSON =
NPM_POSTCSS =
NPM_AUTOPREFIXER =
POSTCSS = postcss
endif
##----------------------------------------------------------------------
## SASS rules
# If $(USE_SASS) (see Makefile.options) is set to yes, it will compile and
# compress all SASS files and save it in $(LOCAL_CSS).
# If SASS is not activated, it will concatenate all CSS files (listed in
# $(CSS_FILES)) in $(LOCAL_CSS).
# In both cases, external CSS files ($(EXTERNAL_CSS_FILES)) are copied.
$(LOCAL_CSS): $(PACKAGE_JSON) $(NPM_POSTCSS) $(NPM_AUTOPREFIXER) $(LOCAL_STATIC_CSS)/.import-external-css | check_sass
ifeq "$(USE_SASS)" "yes"
ifeq ($(shell which sassc),)
[ -d $(SASSDIR) ] && \
SASS_PATH=$(SASS_PATH) sass --style compressed $(SASS_SRC) $@
else
[ -d $(SASSDIR) ] && \
sassc -t compressed $(addprefix -I ,$(subst :, ,$(SASS_PATH))) $(SASS_SRC) $@
endif
$(POSTCSS) --use autoprefixer --replace $@
else
cat $(CSS_FILES) > $@
endif
##----------------------------------------------------------------------
## CSS rules
$(CSSDIR):
mkdir -p $@
# Copy the CSS file $(LOCAL_CSS) in $(CSS_DEST) after adding a hash in the name
# and make a symlink for $(PROJECT_NAME).css which is used in index.html.
# FIXME: md5sum is not by default on Mac OSX: it must be installed with brew.
# Instead of md5sum, md5 is present but the output is different.
$(CSS_DEST): $(LOCAL_CSS) | $(CSSDIR) check_sed
HASH=`cat $< | md5sum | cut -d ' ' -f 1` && \
cp $< $(CSS_PREFIX)_$$HASH.css && \
$(SED) '1s/^/@charset "UTF-8";/' $(CSS_PREFIX)_$$HASH.css && \
ln -sf $(PROJECT_NAME)_$$HASH.css $@
# Charset is necessary for iOS.
# Including it in scss does not work because sass removes it.
##----------------------------------------------------------------------
## External CSS
# Copy files from other projects into the working directory.
# By default, it imports all CSS files from ocsigen-toolkit because the template
# needs it.
# See EXTERNAL_CSS_FILES definition in Makefile.options for more information.
# It is executed with every run of make to be sure external CSS files are
# up-to-date and it allows to add other external CSS files between two
# compilation processes.
$(LOCAL_STATIC_CSS)/.import-external-css: $(EXTERNAL_CSS_FILES)
ifneq "$(EXTERNAL_CSS_FILES)" ""
cp $(EXTERNAL_CSS_FILES) $(LOCAL_STATIC_CSS)
endif
touch $(LOCAL_STATIC_CSS)/.import-external-css
clean-style:
$(RM) $(LOCAL_CSS) $(LOCAL_STATIC_CSS)/$(PROJECT_NAME).css.map
$(RM) -r .sass-cache

351
daegsrv/README.md Normal file
View file

@ -0,0 +1,351 @@
Instructions
============
This project is (initially) generated by `eliom-distillery` as the basic
project `daegsrv`.
Note that external dependencies are required prior to building the
project. Postgres is mandatory. By default, NPM is used for
automatically installing various NPM packages; you can disable this
via the `USE_NPM` variable in `Makefile.options` if you prefer to use
a system-wide NPM installation. SASS is optional, but not installing
it may negatively impact the rendering of the pages generated. All
needed packages (Postgres, NPM, SASS, ...) and required OPAM packages can be
installed via the command (from the daegsrv directory):
```shell
opam install .
```
If you have issues with the NPM provided by your distribution, you can
use [NVM](https://github.com/creationix/nvm). If NPM is too old (< 2.0),
you can try updating it with `sudo npm install -g npm`. Depending on your
setup, you may have to update your `$PATH` for the new `npm` to become
visible.
Generally, you can compile it and run ocsigenserver on it by
```shell
make db-init
make db-create
make db-schema
make test.byte (or test.opt)
```
Then connect to `http://localhost:8080` to see the running app skeleton.
Registration will work only if sendmail if configured on your system.
But the default template will print the activation link on the standard
output to make it possible for you to create your first users (remove this!).
See below for other useful targets for make.
Generated files
---------------
The following files in this directory have been generated by
`eliom-distillery`:
- `daegsrv*.eliom[i]`
Initial source file of the project.
All Eliom files (*.eliom, *.eliomi) in this directory are
automatically compiled and included in the application.
To add a .ml/.mli file to your project,
append it to the variable `SERVER_FILES` or `CLIENT_FILES` in
Makefile.options.
- `static/`.
This folder contains the static data for your app.
The content will be copied into the static file directory
of the server and included in the mobile app.
Put your CSS or additional JavaScript files here.
- `Makefile.options`
Configure your project here.
- `daegsrv.conf.in`.
This file is a template for the configuration file for
Ocsigen Server. You will rarely have to edit it yourself - it takes its
variables from the Makefile.options. This way, the installation
rules and the configuration files are synchronized with respect to
the different folders.
- `mobile`
The files needed by Cordova mobile apps
- `Makefile`
This contains all rules necessary to build, test, and run your
Eliom application. See below for the relevant targets.
- `README.md`
Makefile targets
----------------
Here's some help on how to work with this basic distillery project:
- Initialize, start, create, stop, delete a local db, or show status:
```Shell
make db-init
make db-start
make db-create
make db-stop
make db-delete
make db-status
```
- Test your application by compiling it and running ocsigenserver locally
```
make test.byte (or test.opt)
```
- Compile it only
```Shell
make all (or byte or opt)
```
- Deploy your project on your system
```Shell
sudo make install (or install.byte or install.opt)
```
- Run the server on the deployed project
```Shell
sudo make run.byte (or run.opt)
```
If `WWWUSER` in the `Makefile.options` is you, you don't need the
`sudo`. If Eliom isn't installed globally, however, you need to
re-export some environment variables to make this work:
```Shell
sudo PATH=$PATH OCAMLPATH=$OCAMLPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH make run.byte/run.opt
```
- If you need a findlib package in your project, add it to the
variables `SERVER_PACKAGES` and/or `CLIENT_PACKAGES`. The configuration
file will be automatically updated.
Build the mobile applications
-----------------------------
## Prepare the mobile infrastructure.
### For all mobile platforms:
Make sure you have a working NPM installation. The needed NPM packages
(like Cordova) will be installed automatically.
Warning: NPM packages (and especially Cordova plugins) are very sensitive to
version changes. You may have to change version numbers in
`mobile/config.xml.in` if something goes wrong during app generation.
You may also have problems with old versions of `gradle` or wrong versions
of Android packages ...
If npm is causing a lot of errors (on Debian) in the following parts of the installation, an advice would be to uninstall nodejs and npm and do a clean installation of them **with aptitude**.
This installation was tested with those versions:
```
npm : 6.14.12
nodejs : v10.24.1
```
**Be prepared! You're entering an unstable world!**
### For Android:
- Install JDK 11 (`openjdk-11-jdk` package in Debian/Ubuntu)
Run those commands and look carefully if the checked option for java and javac are from the same repository:
```
sudo update-alternatives --config java
sudo update-alternatives --config javac
```
- Install Gradle (`gradle` package in Debian/Ubuntu)
- Download and untar the [Android SDK](http://developer.android.com) (the smaller version without Android Studio is sufficent), rename it so that you have a `$HOME/android-sdk-linux/tools` folder.
- Using the Android package management interface (or sdkmanager):
* List All System Images Available for Download: `sdkmanager --list | grep system-images`\
(*As an example we're going to choose "system-images;android-26;default;x86" but you can choose your way.*)
* Download Image: sdkmanager --install "system-images;android-26;default;x86"\
(*Be aware that version > android-26 may not work.*)
If you want to emulate an Android device, you need to create an emulator :
```
echo "no" | avdmanager --verbose create avd --force --name "generic_10" --package "system-images;android-26 default;x86" --tag "default" --abi "x86"
# Check every available options that offers avdmanager to customize your emulator as you wish.
```
There is a couple more steps to follow:
Unfortunately there are two named emulator binary file, which are located under `$ANDROID_SDK/tools/emulator` and the other is under `$ANDROID_SDK/emulator/`.\
Make sure you have the right emulator configure (you need to add `$ANDROID_SDK/emulator` to your env PATH).
In order to do this:
1. Add in your `~/.bashrc` (or `~/.zshrc`) file:
```sh
export ANDROID_SDK=$HOME'your_path_to_android_sdk'
export PATH=$ANDROID_SDK/emulator:$PATH
export PATH=$ANDROID_SDK/tools:$PATH
export PATH=$ANDROID_SDK/tools/bin:$PATH
export PATH=$ANDROID_SDK/platform-tools:$PATH
export ANDROID_SDK_ROOT=$ANDROID_SDK
export ANDROID_AVD_HOME=$HOME/.android/and
alias emulator='$ANDROID_SDK/emulator/emulator'
```
2. Then execute this command in your shell: `source ~/.bash_profile`
3. And show the installed emulators with: `emulator -list-avds`\
You should have something displaying like:
```sh
generic_10
# Or even something like :
Pixel_2_API_29
Pixel_3a_API_29
Pixel_C_API_29
```
### For iOS:
- Xcode installs all dependencies you need.
- Some iOS-specific code exists. You should check it out. For instance, looking carefully at the [`PROJECT_NAME.conf.in`](PROJECT_NAME.conf.in) file is mandatory if you're building an iOS app.
### For Windows:
Ocsigen Start uses
[cordova-hot-code-push-plugin](https://github.com/nordnet/cordova-hot-code-push)
to upload local files (like CSS and JavaScript files, images and logo) when the
server code changes.
Unfortunately, this plugin is not yet available for Windows Phone. However, as
ocsigen Start also builds the website part, an idea is to run the website into a
WebView on Windows Phones.
Even if Cordova allows you to build Windows app, it doesn't authorize you to
load an external URL without interaction with the user.
Another solution is to build an [Hosted Web
App](https://developer.microsoft.com/en-us/windows/bridges/hosted-web-apps). It
makes it possible to create easily an application based on your website. You can
also use Windows JavaScript API (no OCaml binding available for the moment) to
get access to native components. You can create the APPX package (package format
for Windows app) by using [Manifold JS](http://manifoldjs.com/), even if you are on MacOS X or Linux.
If you are on Windows, you can
use [Visual Studio Community](https://www.visualstudio.com/fr/vs/community/).
The Visual Studio Community solution is recommended to test and debug. You can
see all errors in the JavaScript console provided in Visual Studio.
[Here](https://blogs.windows.com/buildingapps/2016/02/17/building-a-great-hosted-web-app/#3mlzw0giKcuGZDeq.97) a
complete tutorial from the Windows blog for both versions (with Manifold JS and
Visual Studio).
If you use the Manifold JS solution, you need to sign the APPX before installing it on a device.
## Launching the mobile app
The following examples are described for Android but they are also available
for iOS: you only need to replace `android` by `ios`.
- Launch an Ocsigen server serving your app:
```
make test.opt
```
In the following commands, if `APP_REMOTE` is `yes`, the mobile app will
be created by getting all the necessary files (js, etc) from a server.
This may be used to create a mobile app for an which has not been
compiled locally. With `APP_REMOTE=no`, the local files will be used.
The remote server address is given in the variable `APP_SERVER`.
Replace `${YOUR_SERVER}` by `${YOUR_IP_ADDRESS}:8080` in the following
commands if you want to test on your local machine.
- To run the application in the emulator, use:
```
make APP_SERVER=http://${YOUR_SERVER} APP_REMOTE=no APP=dev emulate-android
```
The above command will attempt to launch your app in the Android emulator that
you have configured previously. Depending on your setup, you may need to start
the emulator before running the command.
Note: If the emulator does not start on your Linux system because of
a library problem, you can try to set the environment variable
`ANDROID_EMULATOR_USE_SYSTEM_LIBS` to `1` to make it start (see
https://developer.android.com/studio/command-line/variables.html for
details).
To run the application on a connected device, use:
```
make APP_SERVER=http://${YOUR_SERVER} APP_REMOTE=no APP=dev run-android
```
Notice that the `APP_SERVER` argument needs to point to your LAN or public
address (e.g., `192.168.1.x`), not to `127.0.0.1` (neither to `localhost`). The
reason is that the address will be used by the Android emulator/device, inside
which `127.0.0.1` has different meaning; it points to the Android host itself.
If you only want to build the mobile application, you can use:
```
make APP_SERVER=http://${YOUR_SERVER} APP_REMOTE=no APP=dev android
```
Before uploading on Google Play Store, check the variables in Makefile.options
(MOBILE_APP_IP, version number, etc).
You'll need to build a release version (default is debug version):
```
make APP_SERVER=http://${YOUR_SERVER} APP_REMOTE=no android-release
```
then sign it (see Android documentation).
If you want the application URL to include a path
(`http://${YOUR_SERVER}${PATH}`),
you need to provide an additional `APP_PATH` argument, e.g.,
`APP_PATH=/foo`. You need to include the leading `/`, but no trailing
`/`. You also need to modify the `daegsrv.conf.in` with a
[`<site>` tag](http://ocsigen.org/ocsigenserver/manual/config#h5o-31).
Note: if any of the mobile-related targets fails due to the inexistent
`node` command, you may need to create a symlink from `node` to
`nodejs`, e.g., as follows:
```
ln -s /usr/bin/nodejs /usr/local/bin/node
```
## Update the mobile application.
The mobile app is updated automatically at launch time, every time the
server has been updated. To do that, Ocsigen Start is using Cordova Hot
Code Push.
In order to make it work, you MUST use the following command every time
you update the server:
```
make APP_SERVER=http://${YOUR_SERVER} APP_REMOTE={yes|no} chcp
```
## Use Makefile.local file.
You need to define `APP_REMOTE` and `APP_SERVER` each time you want to build
the mobile application or to update it. The `APP` variable is not mandatory per
say but when set to `dev` it enables cleartext traffic, so you might want to
keep it on while working on dev builds.
If you don't want to pass the variables `App`, `APP_SERVER` and
`APP_REMOTE` every time, you can change the values of these variables in
`Makefile.local.example` and rename this file to `Makefile.local`. This way,
the variables `App`, `APP_REMOTE` and `APP_SERVER` are not mandatory when you build
or update the mobile application. You can use:
```
make chcp
make run-android
make run-ios
...
```
This file is meant for rules and variables that are only relevant for local development
and it must not be deployed or shared (by default, this file is ignored by Git).

View file

@ -0,0 +1,121 @@
allows_get_information_currently_connected_user provides information about the currently connected user (server or client side). vous autorise à obtenir les information de l'utilisateur courant connecté (côté serveur ou côté client).
always_get_current_user_using_module Always get the current user id using module Récupérez toujours l'ID de l'utilisateur courant en utilisant le module
cache Cache Cache
cache_1 Caching the data Mise en cache des données
cache_2 Module {{eliom_cscache}} implements a cache of data that is designed for Eliom's client-server programming model. It permits saving a client-side copy of the data. Have a look at the module {{os_user_proxy}} to see how it works (and use this module for getting information about Ocsigen Start's users). Le module {{eliom_cscache}} implémente un cache de données construit pour le modèle client-serveur d'Eliom. Il permet de sauvegarder une copie des données du client. Jetez un oeil au module {{os_user_proxy}} pour comprendre son fonctionnement (et utilisez ce module pour obtenir des informations sur les utilisateurs d'Ocsigen Start).
cache_3 When you get a piece of data through {{eliom_cscache}} from client-side, the request to the server is done only if the data is not already in the client-side cache. On server-side, {{eliom_cscache}} is using a temporary cache (with \"request\" scope) to avoid fetching the data several times from the database during the same request. This server-side cache is automatically sent to the client to fill the client-side cache. If you want to avoid too many requests from the client, prefill the server-side cache with the data the client program will need. Quand une donnée du client est obtenue via {{eliom_cscache}}, la requête vers le serveur est faite uniquement si la donnée ne se trouve pas déjà dans le cache côté client. Du côté serveur, {{eliom_cscache}} utilise un cache temporaire (avec une portée de type "request") afin d'éviter de récupérer les données de la base de données plusieurs fois sur une même requête. Ce cache côté serveur est automatiquement envoyé au client pour remplir son cache. Pour éviter un trop grand nombre de requêtes côté client, pré-remplissez le cache côté serveur avec les données dont le programme côté client a besoin.
cache_4 In the near future, {{eliom_cscache}} will enable saving persistent data, which is useful for implementing off-line applications. Prochainement, {{eliom_cscache}} permettra de sauvegarder des données de manière persistante, ce qui peut être utile pour implémenter des applications hors ligne.
calendar Calendar Calendrier
carousel_1 Carousel Carousel
carousel_2 Carousel: page with tabs Carousel : page avec onglets
carousel_third_example_1 Example of a vertical circular carousel (wheel). Try with a touch screen. Exemple de carousel vertical circulaire (wheel). Essayez avec un écran tactile.
carousel_wheel Wheel carousel Carousel avec roulette
eliom_ref Eliom references + OS dates Références Eliom + dates OS
eliom_ref_1 We use an Eliom reference to record the last time you visited this page. Eliom references make it possible to save, server-side, data specific to one user, one browser, or one tab. Nous utilisons les références Eliom pour sauvegarder la date de la dernière fois que vous avez visité cette page. Les références Eliom permettent de sauvegarder côté serveur des données propres à un utilisateur, à un navigateur ou à un onglet.
eliom_ref_2 The value is different for each user. La valeur est différente pour chaque utilisateur.
eliom_ref_3 The reference has been updated. Come back later! La référence a été mise à jour. Revenez plus tard !
eliom_ref_first_visit This is your first visit. Ceci est votre première visite.
eliom_ref_last_visit The last time you visited was: La dernière fois que vous avez visité ce lien était :
example_tip This is an example of tip. Ceci est un exemple de tip.
exchange_msg_between_users Module {{os_notif}} enables sending information to client applications (notifications, new messages ...). Le module {{os_notif}} permet d'envoyer des données aux applications client (notifications, nouveaux messages,...).
external_service external service service externe
fill_input_form_send_message Fill in the input form to send a message to all other tabs. Remplissez le formulaire pour envoyer un message à tous vos autres onglets ouverts.
friday Friday Vendredi
general_principles General principles Principes généraux
internal_link internal link lien interne
internationalization {{{capitalize?I||i}}}nternationalization {{{capitalize?I||i}}}nternationalisation
internationalization_1 Ocsigen Start uses Ocsigen-i18n for internationalizing your app. Ocsigen-i18n defines a PPX syntax extension for automatically selecting language-dependent text for each user. The user can choose his preferred language from the settings page. By default the browser's language is used. Ocsigen Start utilise Ocsigen-i18n for internationaliser les applications. Ocsigen-i18n définit une extension de syntaxe PPX qui sélectionne automatiquement les textes en fonction de la langue de l'utilisateur courant. L'utilisateur peut choisir sa langue préférée dans la page de paramètres. Par défaut, la langue du navigateur est utilisée.
internationalization_2 Write your translations (as tab-separated-values) in file {{f1}}. File {{f2}} is generated automatically from this file. Écrivez vos traductions (au format "tab-separated-values") dans le fichier {{f1}}. Le fichier {{f2}} est généré automatiquement à partir de ce fichier.
internationalization_3 Have a look at the OCaml code of this page to discover some features of the module Ocsigen-i18n. Jetez un coup d'œil au code OCaml de cette page pour découvrir quelques astuces du module Ocsigen-i18n.
internationalization_4 To make your life easier, the entries used for this whole demo sub-site have been split into their own file {{f}}. When you feel ready to make this project your own, you can freely delete all the {{demo_prefix}} files, and {{f}}. Pour rendre votre vie un peu plus facile, les entrées utilisées dans tout ce sous-site démo ont été séparées dans leur propre fichier {{f}}. Quand vous vous sentez prêt à faire de ce projet le votre, vous pourrez librement supprimer tous les fichiers {{demo_prefix}}, ainsi que {{f}}.
intro Demo: introduction Demo : introduction
intro_1 Ocsigen provides a set of largely independent tools for implementing Web and mobile applications (OCaml to JS compiler, Web server, typed HTML, etc.). Ocsigen can be used to implement, depending on your needs, either traditional Web sites (server-side), or client-side apps running in a browser, or full client-server apps, running both in a browser and as mobile apps. Ocsigen Start is a template for quickly writing such a client-server app. Ocsigen fournit un ensemble d'outils largement indépendants pour programmer des applications Web et mobiles (compilateur OCaml vers Javascript, serveur Web, HTML typé, etc.). Cela vous permet d'écrire, selon vos besoins, des sites Web traditionnels (côté serveur), des applications clientes s'exécutant dans une page Web, ou de véritables applications client-serveur, pouvant s'exécuter dans un navigateur ou comme application mobile. Ocsigen Start est un template prêt à utiliser pour ce type d'applications client-serveur.
intro_2 Ocsigen Eliom is a set of libraries for Web programming in OCaml: sessions, services, client-server communication, etc. It also contains an extension of the OCaml language to write a client-server program as a single app. Code annotations permit distinguishing between the code to be included in the server app, the code for the client app, and the code to be included in both of them. Have a look at the code of this app to learn how to generate typed HTML pages, how to call server function from client side, or how to send information to client applications (notifications). Ocsigen Eliom est un ensemble de bibliothèques pour la programmation Web en OCaml : sessions, services, communication client-serveur, etc. Il contient aussi une extension du langage OCaml permettant d'écrire des applications client-serveur. Des annotations du code permettent de distinguer le code devant être inclus dans l'application serveur, du code qui doit être inclus dans l'application cliente. Regardez le code source de cette application pour apprendre comment générer des pages HTML bien typées, comment appeler une fonction serveur depuis un programme client, ou encore comment envoyer des informations aux clients connectés (notifications).
intro_3 Read tutorials on Ocsigen's Web site for a more detailed introduction. Lisez les tutoriels du site d'Ocsigen pour une introduction plus détaillée.
links_and_forms Links and forms Liens et formulaires
links_and_forms_1 Here is an example of an {{t1}}, and an example of link towards an {{t2}}. Voici un exemple de {{t1}}, et un exemple de lien vers un {{t2}}.
links_and_static_files Links, services and static files Liens, services et fichiers statiques
log_in_to_see_demo Log in to see the demo. Connectez-vous pour voir la démonstration.
look_module_tip Look at the code to see how it is defined. Regardez le code pour voir comment c'est défini.
monday Monday Lundi
never_trust_client_pending_user_id Never trust a client sending its own user id! Ne faites jamais confiance à un client envoyant son propre ID d'utilisateur !
no_user_create_accounts No user. Create some accounts to test. Aucun utilisateur. Créez quelques comptes pour tester.
notification Notifications Notifications
notification_got got Reçu
open_multiple_tabs_browsers Open this page in multiple tabs or browsers. Ouvrez cette page dans plusieurs onglets et fenêtres.
ot_carousel_first_example_1 This is a first example of Ocsigen Toolkit's carousel. Voici le premier exemple du carousel d'Ocsigen Toolkit.
ot_carousel_first_example_2 The carousel displays a number of blocks side-by-side (or vertically stacked). Le carousel sert à afficher des blocs côte-à-côte (ou empilés verticalement).
ot_carousel_first_example_3 To switch to a different block, use the buttons in the carousel. Pour vous rendre sur un autre bloc, utilisez les boutons dans le carousel.
ot_carousel_first_example_4 On touch screens you can also swipe with your fingers. Sur les écrans tactiles, swipez avec les doigts.
ot_carousel_second_example_1 This page demonstrates how to use Ocsigen Toolkit's carousel to display a page with several tabs. Cette page montre comment utiliser le carousel d'Ocsigen Toolkit pour afficher une page avec des onglets.
ot_carousel_second_example_2 Try to swipe on a touch screen. Sur écran tactile, glissez pour changer d'onglet.
ot_carousel_second_example_3 Try on a small screen or browser window to see how the tabs stick on top while scrolling the page. Essayez sur un petit écran pour voir comment la barre d'onglets se fixe en haut de la page quand vous faites défiler la page vers le bas.
ot_tongue_1 This is an example of page with a tongue coming from the bottom of the screen. try to slide it with your finger on a mobile screen. Ceci est un exemple de page avec une languette partant du bas de l'écran. Essayez de la faire glisser vers le haut avec le doigt sur un téléphone mobile.
pagetransition Page transitions Transition de pages
pagetransition_add_button Add Ajouter
pagetransition_back_button Go back Retourner
pagetransition_detail_page Detail Page Page de Détails
pagetransition_intro This demo illustrates smooth page transitions and the retention of a page's scroll position. To see the effects scroll a bit and click on one of the links. When you return to this page by hitting the back button the DOM of the page along with its scroll position will be restored from the cache without being charged from the server or generated on the client. Cette démo présente des changement de page animés et la mémorisation des positions de scroll. Pour voir ces effets faites défiler la page un peu vers le bas et cliquez sur un des liens de la liste. Quand vous retournerez sur cette page en appuyant sur le bouton «retour», le DOM de la page sera servi directement du cache sans être généré une nouvelle fois. La position du défilement aura été sauvegardé.
pagetransition_list_page List Page Page Liste
pgocaml Database request Requête à la base de données.
pgocaml_description_1 This page shows signed-up users fetched from the database. Cette page montre tous les utilisateurs inscrits qui ont été récupérés de la base de données.
pgocaml_description_2 Have a look at the source code to see how to make a DB request with PGOCaml. Regardez dans le code source comment réaliser une requête à la base de données en utilisant PGOcaml.
pgocaml_description_3 We are using Ot_spinner to display the list, which means that, in the case the page is generated client-side, the page will be displayed immediately with a spinner, that will be replaced by the contents when ready. The code contains a 2s sleep to demonstrate the spinner. Nous utilisons Ot_spinner pour afficher la liste : dans le cas où une page est générée côté client, cette page est affichée immédiatement avec une icône de chargement qui sera remplacée par le contenu quand il sera prêt. Pour la démonstration, nous avons ajouté une pause de 2s pour laisser le temps de voir l'icône de chargement.
pgocaml_users Users: Utilisateurs :
popup Popup Button Bouton popup
popup_click Click for a popup! Cliquez pour afficher un popup !
popup_content Here is a button showing a simple popup window when clicked: Voici un bouton affichant une simple fenêtre popup quand vous cliquez dessus :
popup_message Popup message Message du popup
pull_to_refresh Pull to refresh Tirer pour rafraîchir
pull_to_refresh_1 This is an example of a page with refreshable content. It is a very common feature in mobile applications. You will need to view this page on your phone to see it work. Cette démo présente une page avec du contenu actualisable. C'est une fonctionnalité très présente dans les applications mobiles. Pour voir les effets de cette page, ouvrez-la dans l'application mobile.
pull_to_refresh_2 This page contains a counter that increases every time you "refresh" by pulling down the page. This feature is called "pull to refresh", but you give it your own action to be performed after the motion. Here, it updates a reactive signal after a second, but in your application, you will probably fetch data and update a more complicated signal than a number to rebuild a part of or the whole page, or do anything else you want. Cette page contient un compteur qui s'incrémente chaque fois que vous "rechargez" la page en tirant vers le bas avec votre doigt. Cette fonctionnalité s'appelle "Tirer pour rafraîchir", mais vous fournissez votre propre action à effectuer à la fin du geste. Ici, nous mettons un simple signal réactif à jour, mais dans votre application, vous récupérerez probablement des données depuis le serveur pour mettre à jour un signal plus compliqué qu'un nombre pour reconstruire toute ou une partie de la page, ou faire ce que vous voulez d'autre.
pull_to_refresh_counter You refreshed the page {{n}} times. Vous avez rafraîchi la page {{n}} fois.
reactive_programming Reactive pages Pages réactives
reactive_programming_1 This is an example of a page with reactive content. It is a very convenient solution to update pages when data changes. Ceci est un exemple d'une page avec du contenu réactif. C'est une solution extrêment simple pour mettre à jour une page quand les données changent.
reactive_programming_2 It defines a (client-side) reactive OCaml list. You can add elements in this list via the input form. The page is updated automatically when the value of the reactive list changes. Il définit une liste OCaml réactive (côté client). Vous pouvez ajouter des élements dans cette liste via le formulaire. Cette page sera automatique mise à jour quand la valeur de la liste réactive changera.
reactive_programming_3 The reactive page is generated either server-side (for example when you are using a Web browser and you reload this page) or client-side (in a mobile app or if you were already in the app before coming to this page). La page réactive est générée aussi bien du côté serveur (par exemple quand vous utilisez le navigateur web et que vous rechargez cette page) que du côté client (sur mobile ou si vous étiez déjà sur dans cette application avant de venir sur cette page).
reactive_programming_button add ajouter
rpc_button RPC button Bouton RPC
rpc_button_click_increase Click to increase server-side value Cliquez pour augmenter la valeur côté serveur.
rpc_button_description This button performs an RPC to increase a server-side value. Ce bouton réalise un appel de fonction distante (RPC) pour augmenter une valeur côté serveur.
saturday Saturday Samedi
send_message send message envoyer le message
services Services Services
services_1 Have a look at file {{f1}} to see some examples of service definitions. Most service handlers are defined in file {{f2}}. Service registration is done in {{f3}}. Have a look to see how to define a service returning an application page, an action or a redirection, etc. Read Ocsigen's tutorials and Eliom's manual for more information about services. Vous trouverez des exemples de définition de services dans le fichier {{f1}}. La plupart des handlers de services sont définis dans le fichier {{f2}}. L'enregistrement des services est fait dans le fichier {{f3}}. Jetez-y un œil pour voir comment définir une nouvelle page pour cette application, une action, une redirection, etc. Lisez les tutoriels d'Ocsigen et le manuel d'Eliom pour plus d'informations sur les services.
spinner Spinner Icône de chargement
spinner_content_ready The content is ready. Le contenu est prêt.
spinner_description_1 We use this widget to integrate into the page an HTML block that takes a long time to produce, e.g., because of a slow server call. Nous utilisons ce widget pour intégrer dans notre page HTML un bloc qui prend un long moment à produire, par exemple, à cause d'un appel serveur lent.
spinner_description_2 A spinner is displayed, which is then replaced with the actual content when this content is ready. Une icône de chargement est d'abord affichée, puis remplacée par le vrai contenu quand celui-ci est prêt.
spinner_description_3 For the demo we just sleep for 5 seconds to simulate waiting for the content. Pour la démonstration, nous avons ajouté un délai de 5 secondes pour simuler l'attente du contenu.
spinner_description_ot This is a demo of the Ocsigen Toolkit spinner widget. Ceci est la démonstration de l'icône de chargement d'Ocsigen Toolkit.
spinner_generated_client_side The spinner is generated client-side. L'icône de chargement est générée côté client.
spinner_message_replace_spinner This message has replaced the spinner. Ce message a remplacé l'icône de chargement.
static_files Static files Fichiers statiques
static_files_1 Use service {{static_dir}} (predefined in Eliom) to create links towards static files (images, fonts, etc.). Put static files you want to include in the mobile app in directory {{static}}. They will be stored locally on the mobile device. By default, links are relative on the Web app and absolute on the mobile app. For example, here is an example of an image stored locally in the mobile app: Utilisez le service {{static_dir}} (prédéfini dans Eliom) pour faire des liens vers des fichiers statiques (images, fontes, etc.). Les fichiers statiques que vous voulez inclure dans l'application mobile doivent être placés dans le répertoire {{static}}. Ils seront stockés en local sur l'appareil mobile. Par défaut les liens sont relatifs dans l'application Web et absolus dans l'application mobile. Forcez les liens relatifs pour faire des liens vers des fichiers locaux dans l'application mobile. Par exemple voici une image stockée localement dans l'application mobile :
static_files_2 and a remote image: et une image distante :
sunday Sunday Dimanche
the_module The module Le module
these_functions_called_server_or_client_side These functions can be called from either server- or client-side. Ces fonctions peuvent être appelées aussi bien côté client que côté serveur.
this_page_show_calendar This page shows Ocsigen Toolkit's date picker. Cette page montre le sélecteur de date d'Ocsigen Toolkit.
thursday Thursday Jeudi
timepicker Time picker Sélecteur d'heure
timepicker_back_to_hours Back to hours Revenir aux heures
timepicker_description This page shows the Ocsigen Toolkit's time picker. Cette page montre le sélecteur d'heure d'Ocsigen Toolkit.
tips Tips Astuces
tips1 Tips for new users and new features Astuces pour les nouveaux utilisateurs et nouvelles fonctionnalités
tips2 Module {{os_tips}} implements a way to display tips in the page to the users who haven't already seen them. Le module {{os_tips}} implémente une façon d'afficher des astuces dans la page aux utilisateurs qui ne les ont pas déjà vues.
tips3 This page contains a tip, that you will see only as connected user, until you close it. Cette page contient une astuce, que vous allez voir seulement en tant qu'utilisateur connecté, jusqu'à ce que vous la fermiez.
tips4 It is possible to reset the set of already seen tips from the {{set_page}}. Il est possible de réinitialiser l'ensemble des astuces déjà vues depuis la page {{set_page}}.
tips5 settings page page Paramètres
tongue_1 Tongue Languette
tuesday Tuesday Mardi
users Users Utilisateurs
wednesday Wednesday Mercredi
widget_feel_free Feel free to modify the generated code and use it or redistribute it as you want. Vous êtes libres de modifier le code généré et de l'utiliser ou de le redistribuer comme vous souhaitez.
widget_ot This app also contains demos for some widgets from Ocsigen Toolkit. Cette application contient également des démonstrations de quelques widgets d'Ocsigen Toolkit.
widget_see_drawer The different demos are accessible through the drawer menu. To open it click the top left button on the screen. Les différentes démonstrations sont accessibles à travers le menu. Pour l'ouvrir, cliquez sur le bouton en haut à gauche de l'écran.
you_are You are Vous êtes
you_are_not_connected You are not connected. Vous n'êtes pas connecté.
you_click_on_date You clicked on {{y}}/{{m}}/{{d}} Vous avez cliqué sur {{d}}/{{m}}/{{y}}
you_click_on_time You clicked on {{h}}:{{m}} Vous avez cliqué sur {{h}}:{{m}}
your_user_id Your user id Votre ID utilisateur
Can't render this file because it contains an unexpected character in line 6 and column 242.

View file

@ -0,0 +1,64 @@
welcome_text1 Welcome to Ocsigen Start. This is a template for applications based on Ocsigen (Eliom, Js_of_ocaml, etc.). Bienvenue dans Ocsigen Start ! Ceci est un template d'application écrite avec Ocsigen (Eliom, Js_of_ocaml, etc.).
welcome_text2 Use it: Utilisez-le :
welcome_text3 As a basis for your own applications. Comme point de départ pour vos propres applications ;
welcome_text4 To learn the most important concepts of client-server programming with Ocsigen. Pour apprendre les principaux concepts de la programmation client-serveur avec Ocsigen.
welcome_text5 This application contains: Cette application contient :
welcome_text6 Features for user management (log-in form, user registration, activation links, password recovery, settings page, etc.). Des fonctionnalités de gestion des utilisateurs (connexion, création d'utilisateur, liens d'activation, récupération de mot de passe, paramètres de l'utilisateur,...) ;
welcome_text7 An extensive demo of the most important features you need to implement your own app. Read the source code to learn! And remove the demo part when you're ready to start with your own app. Une démo des plus importantes fonctionnalités dont vous avez besoin pour écrire votre propre application. Lisez le code source pour apprendre ! Ensuite enlevez la partie demo quand vous êtes prêts à commencer votre propre application ;
welcome_text8 A library with useful features (tips, notifications, etc.). Une bibliothèque avec de nombeuses fonctionnalités utiles (tips, notifications, etc.) ;
welcome_text9 All the features you need to create a multilingual app. Tous les outils pour créer une application multilingue ;
welcome_text10 A basic responsive CSS. Une feuille de style "responsive" basique.
welcome_text11 This application is multi-platform: it can run as a client-server Web application (with server-side generated pages) and as a mobile app (with client-side generated pages) for Android, iOS or Windows. Have a look at the README file to learn how to generate the mobile apps, which you will be able to upload on Google Play or Apple App Store. Cette application est multi-plateforme : elle peut tourner comme application Web client-serveur (avec des pages générées côté serveur) ou bien comme application mobile pour iOS, Android ou Windows (avec des pages générées côté client). Regardez le fichier README pour apprendre comment générer les applications mobiles que vous pourrez envoyer sur Google Play ou Apple App Store.
about_handler_template This template provides a skeleton for an Ocsigen application. Ce template fournit une base pour une application Ocsigen.
about_handler_license Feel free to modify the generated code and use it or redistribute it in any way you want. Vous êtes libres de modifier le code généré et de l'utiliser ou le redistribuer comme vous le souhaitez.
footer_generated This application has been generated using the Cette application a été générée en utilisant le template d'
footer_eliom_distillery template for Eliom-distillery and uses the avec Eliom-distillery et utilise les technologies
footer_technology technology. .
home {{{capitalize?H||h}}}ome {{{capitalize?H||h}}}ome
about {{{capitalize?A||a}}}bout {{{capitalize?À||à}}} propos
demo Demo Démo
password {{{capitalize?P||p}}}assword {{{capitalize?M||m}}}ot de passe
retype_password retype your password retapez votre mot de passe
your_email {{{capitalize?Y||y}}}our email {{{capitalize?V||v}}}otre e-mail
your_password {{{capitalize?Y||y}}}our password {{{capitalize?V||v}}}otre mot de passe
keep_logged_in keep me logged in rester connecté
sign_in {{{capitalize?S||s}}}ign in {{{capitalize?S||s}}}e connecter
forgot_your_password_q {{{capitalize?F||f}}}orgot your password? {{{capitalize?M||m}}}ot de passe oublié ?
sign_up {{{capitalize?S||s}}}ign up {{{capitalize?S||s}}}'enregistrer
logout {{{capitalize?L||l}}}ogout {{{capitalize?S||s}}}e déconnecter
set_as_main_email {{{capitalize?S||s}}}et as main email {{{capitalize?D||d}}}éfinir comme e-mail principal
validated {{{capitalize?V||v}}}alidated {{{capitalize?V||v}}}alidé{{{f?e||}}}
waiting_confirmation {{{capitalize?W||w}}}aiting for confirmation {{{capitalize?E||e}}}n attente de confirmation
main_email {{{capitalize?M||m}}}ain email {{{capitalize?E||e}}}-mail principal
change_password {{{capitalize?C||c}}}hange your password: {{{capitalize?C||c}}}hanger votre mot de passe :
link_new_email Link a new email to your account: Ajouter une adresse e-mail à votre compte :
currently_registered_emails Currently registered emails: E-mails actuellement enregistrés :
settings {{{capitalize?S||s}}}ettings {{{capitalize?P||p}}}aramètres
error {{{capitalize?E||e}}}rror {{{capitalize?E||e}}}rreur
passwords_do_not_match Passwords do not match Les mots de passe ne correspondent pas
generate_action_link_key_subject_email creation création
sign_up_email_msg Welcome!\r\nTo confirm your email address, please click on this link: Bienvenue !\r\nPour confirmer votre adresse e-mail, cliquer sur ce lien :
email_already_exists Email already exists Cet e-mail existe déjà
user_does_not_exist user does not exist Cet utilisateur n'existe pas
account_not_activated Account not activated Ce compte n'est pas activé
wrong_password Wrong password Mauvais mot de passe
no_such_user No such user Cet utilisateur n'existe pas
add_email_msg Welcome!\r\nTo confirm your email address, please click on this link: Bienvenue !\r\nPour confirmer votre adresse e-mail, cliquez sur ce lien :
invalid_action_key Invalid action key, please ask for a new one. Clef d'action invalide. Demandez en une nouvelle svp.
forgot_pwd_email_msg Hi,\r\nTo set a new password, please click on this link: Bonjour,\r\nPour mettre à jour votre mot de passe, cliquez sur ce lien :
must_be_connected_to_see_page You must be connected to see this page. Vous devez être connecté pour voir cette page.
email_address Email address Adresse e-mail
your_first_name Your first name Votre prénom
your_last_name Your last name Votre nom
submit {{{capitalize?S||s}}}ubmit {{{capitalize?E||e}}}nvoyer
see_help_again_from_beginning See help again from beginning Revoir l'aide depuis le début
personal_information_not_set Your personal information has not been set yet. Vous n'avez pas encore entré vos données personnelles.
take_time_enter_name_password Please take time to enter your name and to set a password. Veuillez entrer votre nom et choisir un mot de passe svp.
wrong_data_fix Wrong data. Please fix. Données incorrectes. Veuillez corriger.
send {{{capitalize?S||s}}}end {{{capitalize?E||e}}}nvoyer
recover_password {{{capitalize?R||r}}}ecover password {{{capitalize?R||r}}}écupérer le mot de passe.
welcome {{{capitalize?W||w}}}elcome! {{{capitalize?B||b}}}ienvenue !
log_in_to_see_page {{{capitalize?L||l}}}og in to see this page. {{{capitalize?C||c}}}onnectez-vous pour voir cette page.
change_profile_picture Change profile picture Changer votre photo de profil.
change_language Change language Changer la langue
disconnect_all Logout on all my devices Me déconnecter sur tous mes appareils
Can't render this file because it contains an unexpected character in line 10 and column 61.

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

82
daegsrv/daegsrv.conf.in Normal file
View file

@ -0,0 +1,82 @@
%%% This is the template for your configuration file. The %%VALUES%% below are
%%% taken from the Makefile to generate the actual configuration files.
%%% This comment will disappear.
<!-- %%WARNING%% -->
<ocsigen>
<server>
<port>%%PORT%%</port>
%%% Only set for running, not for testing
%%USERGROUP%%
<logdir>%%LOGDIR%%</logdir>
<datadir>%%DATADIR%%</datadir>
<charset>utf-8</charset>
<uploaddir>/tmp</uploaddir> <!-- Customize this -->
<usedefaulthostname/>
%%% Only set when debugging
%%DEBUGMODE%%
<extension findlib-package="ocsigenserver.ext.accesscontrol"/>
<extension findlib-package="ocsigenserver.ext.cors"/>
<commandpipe>%%CMDPIPE%%</commandpipe>
<extension findlib-package="ocsigenserver.ext.staticmod"/>
<!-- we use the PostgreSQL backend for ocsipersist,
but there is also a DBM and an SQLite backend -->
<!--
<extension findlib-package="ocsipersist.dbm">
<delayloading val="false"/>
<store dir="%%DATADIR%%"/>
<ocsidbm name="ocsidbm"/>
</extension>
-->
<!--
<extension findlib-package="ocsipersist.sqlite">
<database file="%%DATADIR%%/ocsidb"/>
</extension>
-->
<extension findlib-package="ocsipersist.pgsql">
<database
host="%%DB_HOST%%"
port="%%DB_PORT%%"
user="%%DB_USER%%"
password="%%DB_PASSWORD%%"
database="ocsipersist_daegsrv"
/>
</extension>
<extension findlib-package="eliom.server">
<!-- Ask Eliom to ignore UTM parameters and others: -->
<ignoredgetparams regexp="utm_[a-z]*|[a-z]*clid|li_fat_id"/>
<!-- Uncomment if you are using https only and want secure cookies:
<securecookies value="true" />
-->
</extension>
%%% This will include the packages defined as SERVER_PACKAGES in your Makefile:
%%PACKAGES%%
<host hostfilter="*">
<static dir="%%FILESDIR%%" />
%%ELIOM_MODULES%%
<eliommodule module="%%LIBDIR%%/%%PROJECT_NAME%%.cma">
<app name="%%APPNAME%%" css="%%CSSNAME%%" />
<avatars dir="%%FILESDIR%%/avatars" />
<os-db
host="%%DB_HOST%%"
port="%%DB_PORT%%"
user="%%DB_USER%%"
password="%%DB_PASSWORD%%"
database="%%DB_NAME%%"
/>
</eliommodule>
<eliom/>
<if>
<!-- Remove the condition (or change it to true) if you're serving an iOS app and having
"Origin null is not allowed by Access-Control-Allow-Origin.",
since with iOS WkWebView, your origin is a local HTML file on the iOS platform. -->
<header name="origin" regexp="http://localhost:8000"/>
<then>
<cors max_age="86400"
credentials="true"
methods="POST,GET,HEAD"
exposed_headers="x-eliom-application,x-eliom-location,x-eliom-set-process-cookies,x-eliom-set-cookie-substitutes"/>
</then>
</if>
</host>
</server>
</ocsigen>

76
daegsrv/daegsrv.eliom Normal file
View file

@ -0,0 +1,76 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
let%client add_email_notif () = ()
let%server add_email_notif () =
if Eliom_reference.Volatile.get Os_user.user_already_exists
then Os_msg.msg ~level:`Err ~onload:true [%i18n S.email_already_exists]
let%shared () =
(* Registering services. Feel free to customize handlers. *)
Eliom_registration.Action.register
~service:Os_services.set_personal_data_service
Daegsrv_handlers.set_personal_data_handler;
Eliom_registration.Redirection.register
~service:Os_services.set_password_service
Daegsrv_handlers.set_password_handler;
Eliom_registration.Action.register
~service:Os_services.forgot_password_service
Daegsrv_handlers.forgot_password_handler;
Eliom_registration.Action.register ~service:Os_services.preregister_service
Daegsrv_handlers.preregister_handler;
Eliom_registration.Action.register ~service:Os_services.sign_up_service
Os_handlers.sign_up_handler;
Eliom_registration.Action.register ~service:Os_services.connect_service
Os_handlers.connect_handler;
Eliom_registration.Unit.register ~service:Os_services.disconnect_service
(Os_handlers.disconnect_handler ~main_page:true);
Eliom_registration.Any.register ~service:Os_services.action_link_service
(Os_session.Opt.connected_fun Daegsrv_handlers.action_link_handler);
Eliom_registration.Action.register ~service:Os_services.add_email_service
(fun () email ->
let%lwt () = Os_handlers.add_email_handler () email in
add_email_notif (); Lwt.return_unit);
Eliom_registration.Action.register
~service:Os_services.update_language_service
Daegsrv_handlers.update_language_handler;
Daegsrv_base.App.register ~service:Os_services.main_service
(Daegsrv_page.Opt.connected_page
Daegsrv_handlers.main_service_handler);
Daegsrv_base.App.register
~service:Daegsrv_services.about_service
(Daegsrv_page.Opt.connected_page
Daegsrv_handlers.about_handler);
Daegsrv_base.App.register
~service:Daegsrv_services.settings_service
(Daegsrv_page.Opt.connected_page
Daegsrv_handlers.settings_handler)
let%server () =
Eliom_registration.Ocaml.register
~service:Daegsrv_services.upload_user_avatar_service
(Os_session.connected_fun
Daegsrv_handlers.upload_user_avatar_handler)
(* Print more debugging information when <debugmode/> is in config file
(DEBUG = yes in Makefile.options).
Example of use:
let section = Lwt_log.Section.make "Daegsrv:sectionname"
...
Lwt_log.ign_info ~section "This is an information";
(or ign_debug, ign_warning, ign_error etc.)
*)
let%server _ =
if Eliom_config.get_debugmode ()
then (
ignore
[%client
((* Eliom_config.debug_timings := true; *)
(* Lwt_log_core.add_rule "eliom:client*" Lwt_log_js.Debug; *)
(* Lwt_log_core.add_rule "os*" Lwt_log_js.Debug; *)
Lwt_log_core.add_rule "Daegsrv*" Lwt_log_js.Debug
(* Lwt_log_core.add_rule "*" Lwt_log_js.Debug *)
: unit)];
(* Lwt_log_core.add_rule "*" Lwt_log.Debug *)
Lwt_log_core.add_rule "Daegsrv*" Lwt_log.Debug)

10
daegsrv/daegsrv.opam Normal file
View file

@ -0,0 +1,10 @@
opam-version: "2.0"
name: "daegsrv"
version: "0.1"
synopsis: "Pseudo package for defining project dependencies"
depends: [
"eliom" {>= "10.0.0" & < "11.0.0"}
"ocsipersist-pgsql" {>= "1.0" & < "2.0"}
"ocsigen-start"
]

148
daegsrv/daegsrv.sql Normal file
View file

@ -0,0 +1,148 @@
-- README:
-- Do not remove the field with a `-- DEFAULT` suffix.
-- That's the default tables/fields needed by Ocsigen-start
CREATE DATABASE ocsipersist_daegsrv;
CREATE EXTENSION citext; --DEFAULT
-- You may remove the above line if you use the type TEXT for emails instead of CITEXT
CREATE SCHEMA ocsigen_start
-- Note that `main_email` is not an `emails` foreign key to prevent a circular
-- dependency. Triggers on table `emails` defined below make sure this column
-- stays in sync
CREATE TABLE users ( -- DEFAULT
userid bigserial primary key, -- DEFAULT
firstname text NOT NULL,
lastname text NOT NULL,
main_email citext,
password text,
avatar text,
language text
)
CREATE TABLE emails ( -- DEFAULT
email citext primary key, -- DEFAULT
userid bigint NOT NULL references users(userid), -- DEFAULT
validated boolean NOT NULL DEFAULT(false)
)
CREATE TABLE activation ( -- DEFAULT
activationkey text primary key, -- DEFAULT
userid bigint NOT NULL references users(userid), -- DEFAULT
email citext NOT NULL,
autoconnect boolean NOT NULL,
validity bigint NOT NULL default 1,
action text NOT NULL,
data text NOT NULL,
creationdate timestamp NOT NULL default (now() at time zone 'utc'),
expiry timestamp
)
CREATE TABLE groups ( -- DEFAULT
groupid bigserial primary key, -- DEFAULT
name text UNIQUE NOT NULL, -- DEFAULT
description text -- DEFAULT
)
CREATE TABLE user_groups ( -- DEFAULT
userid bigint NOT NULL references users(userid), -- DEFAULT
groupid bigint NOT NULL references groups(groupid) -- DEFAULT
)
CREATE TABLE preregister (
email citext NOT NULL
)
CREATE TABLE phones (
number citext primary key,
userid bigint NOT NULL references users(userid)
);
CREATE OR REPLACE FUNCTION can_delete_email ()
RETURNS TRIGGER AS $$
BEGIN
IF (EXISTS (SELECT 1
FROM ocsigen_start.emails, ocsigen_start.users
WHERE emails.userid = old.userid
AND users.userid = old.userid
AND emails.email <> old.email
AND users.main_email = emails.email
AND validated))
THEN
RETURN old;
ELSE
RETURN NULL;
END IF;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION can_delete_phone ()
RETURNS TRIGGER AS $$
BEGIN
IF (EXISTS (SELECT 1
FROM ocsigen_start.phones
WHERE userid = old.userid AND number <> old.number) OR
EXISTS (SELECT 1
FROM ocsigen_start.emails
WHERE userid = old.userid
AND validated))
THEN
RETURN old;
ELSE
RETURN NULL;
END IF;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION set_main_email ()
RETURNS TRIGGER AS $$
BEGIN
IF (EXISTS (SELECT 1
FROM ocsigen_start.users
WHERE users.userid = NEW.userid
AND (users.main_email IS NULL OR
users.main_email NOT SIMILAR TO '%@%')))
THEN
UPDATE users
SET main_email = NEW.email WHERE users.userid = NEW.userid;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE
FUNCTION trigger_exists (t_name text)
RETURNS boolean
STABLE AS $$
SELECT EXISTS
(SELECT 1 FROM pg_trigger
WHERE NOT tgisinternal
AND tgname = t_name)
$$ LANGUAGE SQL;
DO $$
BEGIN
IF NOT trigger_exists('can_delete_phone') THEN
CREATE TRIGGER can_delete_phone
BEFORE DELETE on ocsigen_start.phones
FOR EACH ROW
EXECUTE PROCEDURE can_delete_phone();
END IF;
IF NOT trigger_exists('can_delete_email') THEN
CREATE TRIGGER can_delete_email
BEFORE DELETE on ocsigen_start.emails
FOR EACH ROW
EXECUTE PROCEDURE can_delete_email();
END IF;
IF NOT trigger_exists('set_main_email') THEN
CREATE TRIGGER set_main_email
AFTER INSERT on ocsigen_start.emails
FOR EACH ROW
EXECUTE PROCEDURE set_main_email();
END IF;
END;
$$;

View file

@ -0,0 +1,37 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
let%server application_name = !Daegsrv_config.app_name
let%client application_name = Eliom_client.get_application_name ()
let%shared displayed_app_name = "daegsrv"
(* Database initialization *)
let () =
Os_db.init
?host:!Daegsrv_config.os_db_host
?port:!Daegsrv_config.os_db_port
?user:!Daegsrv_config.os_db_user
?password:!Daegsrv_config.os_db_password
?database:!Daegsrv_config.os_db_database
?unix_domain_socket_dir:
!Daegsrv_config.os_db_unix_domain_socket_dir
()
let () = Os_email.set_mailer "/usr/sbin/sendmail"
let () =
Os_email.set_from_addr ("daegsrv team", "noreply@DEFAULT.DEFAULT")
(* Create a module for the application. See
https://ocsigen.org/eliom/manual/clientserver-applications for more
information. *)
[%%shared
module App = Eliom_registration.App (struct
let application_name = application_name
let global_data_path = Some ["__global_data__"]
end)]
(* As the headers (stylesheets, etc) won't change, we ask Eliom not to
update the <head> of the page when changing page. (This also avoids
blinking when changing page in iOS). *)
let%client _ = Eliom_client.persist_document_head ()

View file

@ -0,0 +1,74 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* This module contains the configuration of your Eliom application.
You can take some configuration options from Ocsigen server's
configuration file, as shown below.
See https://ocsigen.org/ocsigenserver/ for more information about
the configuration file and how to get the information of the
config file in an Eliom project. *)
(* Variable definitions *)
(* The following variables are changed by the ocsigenserver
configuration file. *)
(* Configuration of the application itself. *)
let app_name = ref ""
let css_name = ref ""
(* The name of the avatar directory. *)
let avatar_dir = ref []
(* Database configuration. *)
let os_db_host = ref None
let os_db_port = ref None
let os_db_user = ref None
let os_db_password = ref None
let os_db_database = ref None
let os_db_unix_domain_socket_dir = ref None
(* Get variables values from the ocsigenserver configuration file *)
(* Application configuration *)
let app =
let open Ocsigen_extensions.Configuration in
let attributes =
[ attribute ~name:"name" ~obligatory:true (fun h -> app_name := h)
; attribute ~name:"css" ~obligatory:true (fun h -> css_name := h) ]
in
element ~name:"app" ~obligatory:true ~attributes ()
(* Avatars configuration *)
let avatars =
let open Ocsigen_extensions.Configuration in
let attributes =
[ attribute ~name:"dir" ~obligatory:true (fun h ->
avatar_dir := Eliom_lib.String.split '/' h) ]
in
element ~name:"avatars" ~obligatory:true ~attributes ()
(* Database configuration *)
let os_db =
let open Ocsigen_extensions.Configuration in
let attributes =
[ attribute ~name:"host" (fun h -> os_db_host := Some h)
; attribute ~name:"port" (fun h ->
os_db_port :=
try Some (int_of_string h)
with Failure _ ->
raise
@@ Ocsigen_extensions.Error_in_config_file
"port is not an integer")
; attribute ~name:"user" (fun h -> os_db_user := Some h)
; attribute ~name:"password" (fun h -> os_db_password := Some h)
; attribute ~name:"database" (fun h -> os_db_database := Some h)
; attribute ~name:"unix_domain_socket_dir" (fun h ->
os_db_unix_domain_socket_dir := Some h) ]
in
element ~name:"os-db" ~attributes ()
let _ = Eliom_config.parse_config [app; avatars; os_db]

View file

@ -0,0 +1,15 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
val app_name : string ref
val css_name : string ref
val avatar_dir : string list ref
val os_db_host : string option ref
val os_db_port : int option ref
val os_db_user : string option ref
val os_db_password : string option ref
val os_db_database : string option ref
val os_db_unix_domain_socket_dir : string option ref
val app : Ocsigen_extensions.Configuration.element
val avatars : Ocsigen_extensions.Configuration.element
val os_db : Ocsigen_extensions.Configuration.element

View file

@ -0,0 +1,90 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
let%shared os_header ?user () =
let open Eliom_content.Html.F in
let%lwt user_box =
Os_user_view.user_box ~a_placeholder_email:[%i18n S.your_email]
~a_placeholder_pwd:[%i18n S.your_password]
~text_keep_me_logged_in:[%i18n S.keep_logged_in]
~content_popup_forgotpwd:[%i18n S.recover_password ~capitalize:true]
~text_button_forgotpwd:[%i18n S.forgot_your_password_q ~capitalize:true]
~text_sign_in:[%i18n S.sign_in ~capitalize:true]
~text_sign_up:[%i18n S.sign_up ~capitalize:true]
~text_send_button:[%i18n S.send ~capitalize:true] ?user ()
in
Lwt.return
(header
~a:[a_class ["os-page-header"]]
[ a
~a:[a_class ["os-page-header-app-name"]]
~service:Os_services.main_service
[txt Daegsrv_base.displayed_app_name]
()
; user_box ])
let%shared os_footer () =
let open Eliom_content.Html.F in
footer
~a:[a_class ["os-page-footer"]]
[ p
[ txt [%i18n S.footer_generated]
; a ~service:Daegsrv_services.os_github_service
[txt " Ocsigen Start "] ()
; txt [%i18n S.footer_eliom_distillery]
; a ~service:Daegsrv_services.ocsigen_service
[txt " Ocsigen "] ()
; txt [%i18n S.footer_technology] ] ]
let%rpc get_wrong_pdata ()
: ((string * string) * (string * string)) option Lwt.t
=
Lwt.return @@ Eliom_reference.Volatile.get Os_msg.wrong_pdata
let%shared connected_welcome_box () =
let open Eliom_content.Html.F in
let%lwt wrong_pdata = get_wrong_pdata () in
let info, ((fn, ln), (p1, p2)) =
match wrong_pdata with
| None ->
( p
[ txt [%i18n S.personal_information_not_set]
; br ()
; txt [%i18n S.take_time_enter_name_password] ]
, (("", ""), ("", "")) )
| Some wpd -> p [txt [%i18n S.wrong_data_fix]], wpd
in
Lwt.return
@@ div
~a:[a_class ["os-welcome-box"]]
[ div [h2 [%i18n welcome ~capitalize:true]; info]
; Os_user_view.information_form
~a_placeholder_password:[%i18n S.password]
~a_placeholder_retype_password:[%i18n S.retype_password]
~a_placeholder_firstname:[%i18n S.your_first_name]
~a_placeholder_lastname:[%i18n S.your_last_name]
~text_submit:[%i18n S.submit] ~firstname:fn ~lastname:ln
~password1:p1 ~password2:p2 () ]
let%shared get_user_data = function
| None -> Lwt.return_none
| Some myid ->
let%lwt u = Os_user_proxy.get_data myid in
Lwt.return_some u
let%shared page ?html_a ?a ?title ?head myid_o content =
let%lwt me = get_user_data myid_o in
let%lwt content =
match me with
| Some me when not (Os_user.is_complete me) ->
let%lwt cwb = connected_welcome_box () in
Lwt.return @@ (cwb :: content)
| _ -> Lwt.return @@ content
in
let%lwt h = os_header ?user:me () in
Lwt.return
(Os_page.content ?html_a ?a ?title ?head
[ h
; Eliom_content.Html.F.(div ~a:[a_class ["os-body"]] content)
; os_footer ()
; Daegsrv_drawer.make ?user:me () ])

View file

@ -0,0 +1,42 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%shared.start]
(** This module defines the default template for application pages *)
val os_header
: ?user:Os_types.User.t
-> unit
-> [> `Header] Eliom_content.Html.F.elt Lwt.t
(** [os_header ?user ()] defines the header for all pages. In this
template, it's a userbox and the user name is displayed. *)
val os_footer : unit -> [> `Footer] Eliom_content.Html.F.elt
(** [os_footer ()] defines a footer for the page. *)
val connected_welcome_box
: unit
-> [> Html_types.div] Eliom_content.Html.F.elt Lwt.t
val get_user_data : Os_types.User.id option -> Os_types.User.t option Lwt.t
val page
: ?html_a:Html_types.html_attrib Eliom_content.Html.attrib list
-> ?a:Html_types.body_attrib Eliom_content.Html.attrib list
-> ?title:string
-> ?head:[< Html_types.head_content_fun] Eliom_content.Html.elt list
-> Os_types.User.id option
-> [< Html_types.div_content_fun > `Div] Eliom_content.Html.F.elt
Eliom_content.Html.F.list_wrap
-> Os_page.content Lwt.t
(** [page userid_o content] returns a page personalized for the user
with id [myid_o] and with the content [content]. It adds a header,
a footer, and a drawer menu. If the user profile is not
completed, a connected welcome box is added. *)
[%%shared.start]
val get_wrong_pdata
: unit
-> ((string * string) * (string * string)) option Lwt.t

View file

@ -0,0 +1,39 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%shared open Eliom_content.Html.F]
(** This module defines the drawer menu *)
let%shared item text service =
li [a ~a:[a_class ["os-drawer-item"]] ~service [txt text] ()]
let%shared user_menu () =
[ item
[%i18n S.settings ~capitalize:true]
Daegsrv_services.settings_service
; Eliom_content.Html.F.li
[ Os_user_view.disconnect_link
~text_logout:[%i18n S.logout ~capitalize:true]
~a:[a_class ["os-drawer-item"]]
() ] ]
let%shared make ?user () =
let items = if user = None then [] else user_menu () in
let items =
item [%i18n S.home ~capitalize:true] Os_services.main_service
:: item [%i18n S.about ~capitalize:true]
Daegsrv_services.about_service
:: Demo_tools.drawer_contents ()
:: items
in
let menu = ul ~a:[a_class ["os-drawer-menu"]] items in
let contents =
match user with
| None -> [menu]
| Some user ->
let user_box = Os_user_view.connected_user_box ~user in
[user_box; menu]
in
let drawer, _, _ = Ot_drawer.drawer contents in
drawer

View file

@ -0,0 +1,181 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%shared open Eliom_content.Html.F]
(* Upload user avatar *)
let upload_user_avatar_handler myid () ((), (cropping, photo)) =
let avatar_dir =
List.fold_left Filename.concat
(List.hd !Daegsrv_config.avatar_dir)
(List.tl !Daegsrv_config.avatar_dir)
in
let%lwt avatar =
Os_uploader.record_image avatar_dir ~ratio:1. ?cropping photo
in
let%lwt user = Os_user.user_of_userid myid in
let old_avatar = Os_user.avatar_of_user user in
let%lwt () = Os_user.update_avatar ~userid:myid ~avatar in
match old_avatar with
| None -> Lwt.return_unit
| Some old_avatar -> Lwt_unix.unlink (Filename.concat avatar_dir old_avatar)
(* Set personal data *)
let%server set_personal_data_handler =
Os_session.connected_fun Os_handlers.set_personal_data_handler
let%rpc set_personal_data_rpc (data : (string * string) * (string * string))
: unit Lwt.t
=
set_personal_data_handler () data
let%client set_personal_data_handler () = set_personal_data_rpc
(* Forgot password *)
let%server forgot_password_handler =
Os_handlers.forgot_password_handler
Daegsrv_services.settings_service
let%rpc forgot_password_rpc (email : string) : unit Lwt.t =
forgot_password_handler () email
let%client forgot_password_handler () = forgot_password_rpc
(* Action links are links created to perform an action. They are used
for example to send activation links by email, or links to reset a
password. You can create your own action links and define their
behavior here. *)
let%shared action_link_handler myid_o akey () =
(* We try first the default actions (activation link, reset
password) *)
try%lwt Os_handlers.action_link_handler myid_o akey () with
| Os_handlers.No_such_resource | Os_handlers.Invalid_action_key _ ->
Os_msg.msg ~level:`Err ~onload:true [%i18n S.invalid_action_key];
Eliom_registration.(appl_self_redirect Action.send) ()
| e ->
let%lwt email, phantom_user =
match e with
| Os_handlers.Account_already_activated_unconnected
{ Os_types.Action_link_key.userid = _
; email
; validity = _
; action = _
; data = _
; autoconnect = _ } ->
Lwt.return (email, false)
| Os_handlers.Custom_action_link
( { Os_types.Action_link_key.userid = _
; email
; validity = _
; action = _
; data = _
; autoconnect = _ }
, phantom_user ) ->
Lwt.return (email, phantom_user)
| _ -> Lwt.fail e
in
(* Define here your custom action links. If phantom_user is true,
it means the link has been created for an email that does not
correspond to an existing user. By default, we just display a
sign up form or phantom users, a login form for others. You
don't need to modify this if you are not using custom action
links.
Perhaps personalise the intended behavior for when you meet
[Account_already_activated_unconnected]. *)
if myid_o = None (* Not currently connected, and no autoconnect *)
then
if phantom_user
then
let page =
[ div
~a:[a_class ["login-signup-box"]]
[ Os_user_view.sign_up_form
~a_placeholder_email:[%i18n S.your_email]
~text:[%i18n S.sign_up] ~email () ] ]
in
Daegsrv_base.App.send
(Daegsrv_page.make_page (Os_page.content page))
else
let page =
[ div
~a:[a_class ["login-signup-box"]]
[ Os_user_view.connect_form
~a_placeholder_email:[%i18n S.your_email]
~a_placeholder_pwd:[%i18n S.your_password]
~text_keep_me_logged_in:[%i18n S.keep_logged_in]
~text_sign_in:[%i18n S.sign_in] ~email () ] ]
in
Daegsrv_base.App.send
(Daegsrv_page.make_page (Os_page.content page))
else
(*VVV In that case we must do something more complex. Check
whether myid = userid and ask the user what he wants to
do. *)
let open Eliom_registration in
appl_self_redirect Redirection.send
(Redirection Eliom_service.reload_action)
(* Set password *)
let%server set_password_handler =
Os_session.connected_fun (fun myid () (pwd, pwd2) ->
let%lwt () = Os_handlers.set_password_handler myid () (pwd, pwd2) in
Lwt.return (Eliom_registration.Redirection Eliom_service.reload_action))
let%client set_password_handler () (pwd, pwd2) =
let%lwt () = Os_handlers.set_password_rpc (pwd, pwd2) in
Lwt.return (Eliom_registration.Redirection Eliom_service.reload_action)
(* Preregister *)
let%server preregister_handler = Os_handlers.preregister_handler
let%rpc preregister_rpc (email : string) : unit Lwt.t =
preregister_handler () email
let%client preregister_handler () = preregister_rpc
let%shared main_service_handler myid_o () () =
Daegsrv_container.page
~a:[a_class ["os-page-main"]]
myid_o
[ p [%i18n welcome_text1]
; p [%i18n welcome_text2]
; ul [li [%i18n welcome_text3]; li [%i18n welcome_text4]]
; p [%i18n welcome_text5]
; ul
[ li [%i18n welcome_text6]
; li [%i18n welcome_text7]
; li [%i18n welcome_text8]
; li [%i18n welcome_text9]
; li [%i18n welcome_text10] ]
; p [%i18n welcome_text11] ]
let%shared about_handler myid_o () () =
let open Eliom_content.Html.F in
Daegsrv_container.page
~a:[a_class ["os-page-about"]]
myid_o
[ div
[ p [%i18n about_handler_template]
; br ()
; p [%i18n about_handler_license] ] ]
let%shared settings_handler myid_o () () =
let%lwt content =
match myid_o with
| Some _ -> Daegsrv_settings.settings_content ()
| None -> Lwt.return [p [%i18n log_in_to_see_page ~capitalize:true]]
in
Daegsrv_container.page myid_o content
let%server update_language_handler () language =
Os_session.connected_wrapper Daegsrv_language.update_language
(Daegsrv_i18n.language_of_string language)
let%client update_language_handler () language =
Daegsrv_i18n.(set_language (language_of_string language));
Os_current_user.update_language language

View file

@ -0,0 +1,85 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(** This module defines handlers to upload avatar, upload personal
data, set a new password, and also main handlers (main page, about
page, and settings page). In addition to including all default
handlers from OS (see {!Os_handlers}), it overrides some of them
for the purposes of this template. *)
[%%server.start]
val upload_user_avatar_handler
: Os_types.User.id
-> unit
-> unit
* ((float * float * float * float) option * Ocsigen_extensions.file_info)
-> unit Lwt.t
(** Update new user avatar with cropping option. The new avatar is saved
and the old one is removed. *)
[%%shared.start]
val set_personal_data_handler
: unit
-> (string * string) * (string * string)
-> unit Lwt.t
(** Update personal data. It uses the default OS handler
{!Os_handlers.set_personal_data_handler} and gets the user information
with {!Os_session.connected_fun}. *)
val forgot_password_handler : unit -> string -> unit Lwt.t
(** Reset forgotten password. It uses the default OS handler
{!Os_handlers.forgot_password_handler} with the main service. *)
val action_link_handler
: Os_types.User.id option
-> string
-> unit
-> Daegsrv_base.App.result Lwt.t
val set_password_handler
: unit
-> string * string
-> Eliom_service.non_ocaml Eliom_registration.redirection Lwt.t
(** Set a new password. It uses the default OS handler
{!Os_handlers.set_password_handler} and gets the user information
with {!Os_session.connected_fun}. *)
val preregister_handler : unit -> string -> unit Lwt.t
(** The following functions are the handlers for the three main pages.
They are created with {!Daegsrv_container.page} which
means that a header and a footer will be displayed in addition to
the main content.
For each of them, you can personalize the page for a specific user
by sending the userid as first parameter. *)
val main_service_handler
: Os_types.User.id option
-> unit
-> unit
-> Os_page.content Lwt.t
(** The first page of the application *)
val about_handler
: Os_types.User.id option
-> unit
-> unit
-> Os_page.content Lwt.t
(** About page *)
val settings_handler
: Os_types.User.id option
-> unit
-> unit
-> Os_page.content Lwt.t
(** Settings page. If the user is connected (see
{!Daegsrv_container.get_user_data}), a settings
container will be created. *)
val update_language_handler
: unit
-> string
-> Eliom_registration.Action.page Lwt.t

View file

@ -0,0 +1,38 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%shared.start]
(** This module defines an interface to create icons HTML element with
predefined style/value. We assume "Font Awesome" icons are used by
default (fa CSS class is added when using [icon classes]). See
http://fontawesome.io/ for more information and for the complete
list of CSS classes values. *)
module Make (A : module type of Eliom_content.Html.F) = struct
(** [icon classes ~a:other_css_classes ()] create an icon HTML
attribute with "fa" and [classes] as CSS classes. The HTML tag
"i" is used because it is the de facto standard for icons. The
optional parameter ~a is at the end to be able to add other CSS
classes with predefined icons. *)
let icon classes
?(a = ([] : Html_types.i_attrib Eliom_content.Html.attrib list)) ()
=
A.i ~a:(A.a_class ("fa" :: classes) :: a) []
(** Icons used by Ocsigen Start's library *)
let user = icon ["fa-user"; "fa-fw"]
let signout = icon ["fa-sign-out"; "fa-fw"]
let close = icon ["fa-close"; "fa-fw"]
let trash = icon ["fa-trash-o"; "fa-fw"]
(* Add your own icons here. See http://fontawesome.io/icons/ for the
complete list of CSS classes available by default. *)
end
module F = Make (Eliom_content.Html.F)
module D = Make (Eliom_content.Html.D)
(* Register this module for use by Os_icon. *)
module Empty = Os_icons.Register (F) (D)

View file

@ -0,0 +1,58 @@
(* This file was generated by Ocsigen-start.
Feel free to use it, modify it, and redistribute it as you wish. *)
let%server best_matched_language () =
(* lang contains a list of (language_as_string, quality_value) *)
let lang = Eliom_request_info.get_accept_language () in
(* If no quality is given, we suppose it's 1 *)
let lang =
List.map (fun (s, q) -> s, match q with Some q -> q | None -> 1.) lang
in
(* Increasingly sort based on the quality *)
let lang = List.sort (fun (_, q1) (_, q2) -> compare q2 q1) lang in
Lwt.return
@@
(* The first language of the list is returned. If the list is empty,
the default language is returned. *)
let rec aux = function
| (l, _) :: tl -> (
try Daegsrv_i18n.guess_language_of_string l
with Daegsrv_i18n.Unknown_language _ -> aux tl)
| [] -> Daegsrv_i18n.default_language
in
aux lang
let%server update_language lang =
let language = Daegsrv_i18n.string_of_language lang in
let myid_o = Os_current_user.Opt.get_current_userid () in
(* Update the server and client values *)
Daegsrv_i18n.set_language lang;
ignore [%client (Daegsrv_i18n.set_language ~%lang : unit)];
(* Update in the database if a user is connected *)
match myid_o with
| None -> Lwt.return_unit
| Some userid -> Os_user.update_language ~userid ~language
let%server _ =
Os_session.on_start_process (fun _ ->
(* Guess a default language. *)
let%lwt lang = best_matched_language () in
ignore (update_language lang);
Lwt.return_unit);
Os_session.on_start_connected_process (fun userid ->
(* Set language according to user preferences. *)
let%lwt language =
match%lwt Os_user.get_language userid with
| Some lang ->
Lwt.return (Daegsrv_i18n.guess_language_of_string lang)
| None ->
let%lwt best_language = best_matched_language () in
ignore
(Os_user.update_language ~userid
~language:
(Daegsrv_i18n.string_of_language best_language));
Lwt.return best_language
in
Daegsrv_i18n.set_language language;
ignore [%client (Daegsrv_i18n.set_language ~%language : unit)];
Lwt.return_unit)

View file

@ -0,0 +1,14 @@
(* This file was generated by Ocsigen-start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%server.start]
(** This module is used for i18n (internationalization). I18n allows
to have an application in multiple languages. The rule [make
i18n-update] uses this module to create the i18n file for
translations (see [Makefile.options]). *)
val update_language : Daegsrv_i18n.t -> unit Lwt.t
(** [update_language language] updates the language (client and server
side) for the current user with the value [language]. It also
updates the value in the database if an user is connected. *)

View file

@ -0,0 +1,159 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%client.start]
[@@@ocaml.warning "-33"]
open Daegsrv (* for dependency reasons *)
[@@@ocaml.warning "+33"]
[%%client open Js_of_ocaml]
[%%client open Js_of_ocaml_lwt]
(* This RPC is called when client application is initialized. This
way, the server sends necessary cookies to the client (the mobile
app) early on and subsequent requests from the client will contain
the proper cookies.
The RPC only initializes Os_date by default, but you can add your
own actions to be performed server side on first client request, if
necessary. *)
let%rpc init_request myid_o (tz : string) : unit Lwt.t =
ignore myid_o; Os_date.initialize tz; Lwt.return_unit
let to_lwt f =
let wait, wakeup = Lwt.wait () in
f (Lwt.wakeup wakeup);
wait
let ondeviceready =
to_lwt (fun cont ->
ignore
@@ Js_of_ocaml.Dom.addEventListener Js_of_ocaml.Dom_html.document
(Js_of_ocaml.Dom_html.Event.make "deviceready")
(Js_of_ocaml.Dom_html.handler (fun _ ->
cont (); Js_of_ocaml.Js._true))
Js_of_ocaml.Js._false)
let app_started = ref false
let initial_change_page = ref None
let change_page_gen action =
if !app_started
then Lwt.async action
else if !initial_change_page = None
then initial_change_page := Some action
let change_page_uri uri =
change_page_gen (fun () -> Eliom_client.change_page_uri uri)
let handle_initial_url () =
let tz = Os_date.user_tz () in
let%lwt () = init_request tz in
let%lwt () = ondeviceready in
app_started := true;
match !initial_change_page with
| None ->
Eliom_client.change_page ~replace:true ~service:Os_services.main_service
() ()
| Some action -> action ()
let () =
Lwt.async @@ fun () ->
if Eliom_client.is_client_app ()
then (
(* Initialize the application server-side; there should be a
single initial request for that. *)
Os_date.disable_auto_init ();
let%lwt _ = Lwt_js_events.onload () in
handle_initial_url ())
else Lwt.return_unit
(* Reactivate comet on resume and online events *)
let () =
Firebug.console##log (Js_of_ocaml.Js.string "adding resume/online listeners");
let activate ev =
ignore
@@ Js_of_ocaml.Dom.addEventListener Js_of_ocaml.Dom_html.document
(Js_of_ocaml.Dom_html.Event.make ev)
(Js_of_ocaml.Dom_html.handler (fun _ ->
Firebug.console##log (Js_of_ocaml.Js.string ev);
Eliom_comet.activate ();
Js_of_ocaml.Js._true))
Js_of_ocaml.Js._false
in
activate "online"; activate "resume"
(* Restart on a given URL *)
let storage () =
Js_of_ocaml.Js.Optdef.case
Js_of_ocaml.Dom_html.window##.localStorage
(fun () -> failwith "Browser storage not supported")
(fun v -> v)
let () =
let st = storage () in
let lc = Js_of_ocaml.Js.string "__os_restart_url" in
Js_of_ocaml.Js.Opt.case
(st##getItem lc)
(fun () -> ())
(fun url ->
st##removeItem lc;
change_page_uri (Js_of_ocaml.Js.to_string url))
(* Handle universal links *)
type event =
< url : Js_of_ocaml.Js.js_string Js_of_ocaml.Js.t Js_of_ocaml.Js.readonly_prop
; scheme :
Js_of_ocaml.Js.js_string Js_of_ocaml.Js.t Js_of_ocaml.Js.readonly_prop
; host :
Js_of_ocaml.Js.js_string Js_of_ocaml.Js.t Js_of_ocaml.Js.readonly_prop
; path :
Js_of_ocaml.Js.js_string Js_of_ocaml.Js.t Js_of_ocaml.Js.readonly_prop
; params : 'a. 'a Js_of_ocaml.Js.t Js_of_ocaml.Js.readonly_prop >
let universal_links () =
let%lwt () = ondeviceready in
Lwt.return @@ Js_of_ocaml.Js.Optdef.to_option
@@ (Js_of_ocaml.Js.Unsafe.global##.universalLinks
: < subscribe :
Js_of_ocaml.Js.js_string Js_of_ocaml.Js.opt
-> (event Js_of_ocaml.Js.t -> unit) Js_of_ocaml.Js.callback
-> unit Js_of_ocaml.Js.meth
; unsubscribe :
Js_of_ocaml.Js.js_string Js_of_ocaml.Js.opt
-> unit Js_of_ocaml.Js.meth >
Js_of_ocaml.Js.t
Js_of_ocaml.Js.Optdef.t)
let _ =
match%lwt universal_links () with
| Some universal_links ->
Js_of_ocaml.Firebug.console##log
(Js_of_ocaml.Js.string "Universal links: registering");
universal_links##subscribe Js_of_ocaml.Js.null
(Js_of_ocaml.Js.wrap_callback (fun (ev : event Js_of_ocaml.Js.t) ->
Js_of_ocaml.Firebug.console##log_2
(Js_of_ocaml.Js.string "Universal links: got link")
ev##.url;
change_page_uri (Js_of_ocaml.Js.to_string ev##.url)));
Js_of_ocaml.Firebug.console##log
(Js_of_ocaml.Js.string "Universal links: registered");
Lwt.return_unit
| None -> Lwt.return_unit
(* Debugging *)
(* Enable debugging messages.
If you need to display debugging messages in the client side JS
debugger console, you can do so by uncommenting the following
lines. *)
(* let () = Eliom_config.debug_timings := true *)
(* let () = Lwt_log_core.add_rule "eliom:client*" Lwt_log_js.Debug *)
(* let () = Lwt_log_core.add_rule "os*" Lwt_log_js.Debug *)

View file

@ -0,0 +1,2 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)

View file

@ -0,0 +1,66 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
open Eliom_content.Html.F]
[%%client
module Ocsigen_config = struct
let get_debugmode () = false
end]
let%server css_name = !Daegsrv_config.css_name
let%client css_name =
try Js_of_ocaml.Js.to_string Js_of_ocaml.Js.Unsafe.global##.___css_name_
with _ -> ""
let%server css_name_script =
[script (cdata_script (Printf.sprintf "var __css_name = '%s';" css_name))]
let%client css_name_script = []
(* Warning: either we use exactly the same global node (and make sure
global nodes work properly on client side), or we do not add the
script on client side. We chose the second solution. *)
let%server app_js =
[Daegsrv_base.App.application_script ~defer:true ()]
let%client app_js = []
let%server the_local_js = []
let%client the_local_js = [] (* in index.html *)
let%shared the_local_css = [[css_name]]
[%%shared
module Page_config = struct
include Os_page.Default_config
let title = "daegsrv"
let local_js = the_local_js
let local_css = the_local_css
let other_head =
meta
~a:
[ a_name "viewport"
; a_content "width=device-width, initial-scale=1, user-scalable=no" ]
()
:: css_name_script
@ app_js
let default_predicate _ _ = Lwt.return_true
let default_connected_predicate _ _ _ = Lwt.return_true
let default_error_page _ _ exn =
Daegsrv_container.page None
(if Ocsigen_config.get_debugmode ()
then [p [txt (Printexc.to_string exn)]]
else [p [txt "Error"]])
let default_connected_error_page myid_o _ _ exn =
Daegsrv_container.page myid_o
(if Ocsigen_config.get_debugmode ()
then [p [txt (Printexc.to_string exn)]]
else [p [txt "Error"]])
end
include Os_page.Make (Page_config)]

View file

@ -0,0 +1,63 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%shared.start]
val css_name : string
val css_name_script : [> Html_types.script] Eliom_content.Html.F.elt list
val app_js : [> `Script] Eliom_content.Html.elt list
val the_local_js : 'a list
val the_local_css : string list list
module Page_config : sig
val js : string list list
val css : string list list
val title : string
val local_js : 'a list
val local_css : string list list
val other_head : [> Html_types.head_content] Eliom_content.Html.F.elt list
val default_predicate : 'a -> 'b -> bool Lwt.t
val default_connected_predicate : 'a -> 'b -> 'c -> bool Lwt.t
val default_error_page : 'a -> 'b -> exn -> Os_page.content Lwt.t
val default_connected_error_page
: Os_types.User.id option
-> 'a
-> 'b
-> exn
-> Os_page.content Lwt.t
end
val make_page : Os_page.content -> [> Html_types.html] Eliom_content.Html.elt
val page
: ?predicate:('a -> 'b -> bool Lwt.t)
-> ?fallback:('a -> 'b -> exn -> Os_page.content Lwt.t)
-> ('a -> 'b -> Os_page.content Lwt.t)
-> 'a
-> 'b
-> Html_types.html Eliom_content.Html.elt Lwt.t
module Opt : sig
val connected_page
: ?allow:Os_types.Group.t list
-> ?deny:Os_types.Group.t list
-> ?predicate:(Os_types.User.id option -> 'a -> 'b -> bool Lwt.t)
-> ?fallback:
(Os_types.User.id option -> 'a -> 'b -> exn -> Os_page.content Lwt.t)
-> (Os_types.User.id option -> 'a -> 'b -> Os_page.content Lwt.t)
-> 'a
-> 'b
-> Html_types.html Eliom_content.Html.elt Lwt.t
end
val connected_page
: ?allow:Os_types.Group.t list
-> ?deny:Os_types.Group.t list
-> ?predicate:(Os_types.User.id option -> 'a -> 'b -> bool Lwt.t)
-> ?fallback:
(Os_types.User.id option -> 'a -> 'b -> exn -> Os_page.content Lwt.t)
-> (Os_types.User.id -> 'a -> 'b -> Os_page.content Lwt.t)
-> 'a
-> 'b
-> Html_types.html Eliom_content.Html.elt Lwt.t

View file

@ -0,0 +1,36 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%shared.start]
(* Edit this file to enable phone connectivity.
[enable] has to be set to [true].
Use [Os_connect_phone.set_send_sms_handler] to register your
SMS-sending function, e.g., by using Amazon SNS or Twilio.
You can remove this file if you don't need this functionality. *)
let enable = false
let%server () =
if enable
then
Os_connect_phone.set_send_sms_handler (fun ~number message ->
Printf.printf "Send SMS %s to %s\n%!" message number;
Lwt.return (Ok ()))
let () =
if enable
then (
Os_user_view.enable_phone ();
Eliom_registration.Action.register
~service:Os_services.confirm_code_recovery_service
Os_handlers.confirm_code_recovery_handler;
Eliom_registration.Action.register
~service:Os_services.confirm_code_extra_service
Os_handlers.confirm_code_extra_handler;
Eliom_registration.Action.register
~service:Os_services.confirm_code_signup_service
Os_handlers.confirm_code_signup_handler)

View file

@ -0,0 +1,40 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
let%server about_service =
Eliom_service.create
~path:(Eliom_service.Path ["about"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
let%server upload_user_avatar_service : (unit, unit) Ot_picture_uploader.service
=
Ot_picture_uploader.mk_service "upload_user_avatar_service" [%json: unit]
let%server demo_service =
Eliom_service.create
~path:(Eliom_service.Path ["demo"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
let%server settings_service =
Eliom_service.create
~path:(Eliom_service.Path ["settings"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
let%server os_github_service =
Eliom_service.extern ~prefix:"http://github.com"
~path:["ocsigen"; "ocsigen-start"]
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
let%server ocsigen_service =
Eliom_service.extern ~prefix:"http://ocsigen.org" ~path:[]
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
let%client about_service = ~%about_service
let%client upload_user_avatar_service = ~%upload_user_avatar_service
let%client demo_service = ~%demo_service
let%client settings_service = ~%settings_service
let%client ocsigen_service = ~%ocsigen_service
let%client os_github_service = ~%os_github_service
(* The OS lib needs access to the settings service to perform
redirections to it. We need to register it *)
let%server () = Os_services.register_settings_service settings_service

View file

@ -0,0 +1,76 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%shared.start]
val about_service
: ( unit
, unit
, Eliom_service.get
, Eliom_service.att
, Eliom_service.non_co
, Eliom_service.non_ext
, Eliom_service.reg
, [`WithoutSuffix]
, unit
, unit
, Eliom_service.non_ocaml )
Eliom_service.t
val upload_user_avatar_service : (unit, unit) Ot_picture_uploader.service
val demo_service
: ( unit
, unit
, Eliom_service.get
, Eliom_service.att
, Eliom_service.non_co
, Eliom_service.non_ext
, Eliom_service.reg
, [`WithoutSuffix]
, unit
, unit
, Eliom_service.non_ocaml )
Eliom_service.t
val settings_service
: ( unit
, unit
, Eliom_service.get
, Eliom_service.att
, Eliom_service.non_co
, Eliom_service.non_ext
, Eliom_service.reg
, [`WithoutSuffix]
, unit
, unit
, Eliom_service.non_ocaml )
Eliom_service.t
val os_github_service
: ( unit
, unit
, Eliom_service.get
, Eliom_service.att
, Eliom_service.non_co
, Eliom_service.ext
, Eliom_service.non_reg
, [`WithoutSuffix]
, unit
, unit
, Eliom_service.non_ocaml )
Eliom_service.t
val ocsigen_service
: ( unit
, unit
, Eliom_service.get
, Eliom_service.att
, Eliom_service.non_co
, Eliom_service.ext
, Eliom_service.non_reg
, [`WithoutSuffix]
, unit
, unit
, Eliom_service.non_ocaml )
Eliom_service.t

View file

@ -0,0 +1,158 @@
[%%client
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
open Js_of_ocaml_lwt]
let%shared update_main_email_button email =
let open Eliom_content.Html in
let button =
D.button
~a:[D.a_class ["button"]]
[D.txt [%i18n S.set_as_main_email ~capitalize:true]]
in
ignore
[%client
(Lwt.async (fun () ->
Lwt_js_events.clicks (Eliom_content.Html.To_dom.of_element ~%button)
(fun _ _ ->
let%lwt () = Os_current_user.update_main_email ~%email in
Eliom_client.change_page
~service:Daegsrv_services.settings_service () ()))
: unit)];
button
(* A button to remove the email from the database *)
let%shared delete_email_button email =
let open Eliom_content.Html in
let button =
D.button
~a:[D.a_class ["button"; "os-remove-email-button"]]
[Daegsrv_icons.D.trash ()]
in
ignore
[%client
(Lwt.async (fun () ->
Lwt_js_events.clicks (Eliom_content.Html.To_dom.of_element ~%button)
(fun _ _ ->
let%lwt () = Os_current_user.remove_email_from_user ~%email in
Eliom_client.change_page
~service:Daegsrv_services.settings_service () ()))
: unit)];
button
(* A list of buttons to update or to remove the email depending on the
email properties *)
let%shared buttons_of_email is_main_email is_validated email =
if is_main_email
then []
else if is_validated
then [update_main_email_button email; delete_email_button email]
else [delete_email_button email]
(* A list of labels describing the email properties. *)
let%shared labels_of_email is_main_email is_validated =
let open Eliom_content.Html.F in
let valid_label =
span
~a:[a_class ["os-settings-label"; "os-validated-email"]]
[ (txt
@@
if is_validated
then [%i18n S.validated ~capitalize:true]
else [%i18n S.waiting_confirmation ~capitalize:true]) ]
in
if is_main_email
then
[ span
~a:[a_class ["os-settings-label"; "os-main-email"]]
[%i18n main_email ~capitalize:true]
; valid_label ]
else [valid_label]
let%shared li_of_email main_email (email, is_validated) =
let is_main_email =
match main_email with
| Some main_email -> main_email = email
| None -> false
in
let open Eliom_content.Html.D in
let labels = labels_of_email is_main_email is_validated
and buttons = buttons_of_email is_main_email is_validated email
and email = span ~a:[a_class ["os-settings-email"]] [txt email] in
Lwt.return (li ((email :: labels) @ buttons))
let%shared ul_of_emails (main_email, emails) =
let li_of_email = li_of_email main_email in
let%lwt li_list = Lwt_list.map_s li_of_email emails in
Lwt.return Eliom_content.Html.D.(div ~a:[a_class ["os-emails"]] [ul li_list])
(* List with information about emails *)
let%rpc get_emails myid () : (string option * (string * bool) list) Lwt.t =
let%lwt main_email = Os_db.User.email_of_userid myid in
let%lwt emails = Os_db.User.emails_of_userid myid in
let%lwt emails =
Lwt_list.map_s
(fun email ->
let%lwt v = Os_current_user.is_email_validated email in
Lwt.return (email, v))
emails
in
Lwt.return (main_email, emails)
let%shared select_language_form select_language_name =
let open Eliom_content.Html in
let current_language = Daegsrv_i18n.get_language () in
let all_languages_except_current =
List.filter
(fun l -> l <> current_language)
Daegsrv_i18n.languages
in
let form_option_of_language language is_current_language =
D.Form.Option
( []
, (* No attributes *)
Daegsrv_i18n.string_of_language language
, None
, is_current_language )
in
[ D.p [D.txt [%i18n S.change_language]]
; D.Form.select ~name:select_language_name D.Form.string
(form_option_of_language current_language true)
(List.map
(fun l -> form_option_of_language l false)
all_languages_except_current)
; D.Form.input ~input_type:`Submit ~value:[%i18n S.send ~capitalize:true]
D.Form.string ]
let%shared settings_content () =
let%lwt emails = get_emails () in
let%lwt emails = ul_of_emails emails in
Lwt.return
@@ Eliom_content.Html.D.
[ div
~a:[a_class ["os-settings"]]
[ p [%i18n change_password ~capitalize:true]
; Os_user_view.password_form ~a_placeholder_pwd:[%i18n S.password]
~a_placeholder_confirmation:[%i18n S.retype_password]
~text_send_button:[%i18n S.send]
~service:Os_services.set_password_service ()
; br ()
; Os_user_view.upload_pic_link
~submit:([a_class ["button"]], [txt "Submit"])
~content:[%i18n change_profile_picture]
Daegsrv_services.upload_user_avatar_service
; br ()
; Os_user_view.reset_tips_link
~text_link:[%i18n S.see_help_again_from_beginning] ()
; br ()
; Os_user_view.disconnect_all_link
~text_link:[%i18n S.disconnect_all] ()
; br ()
; p [%i18n link_new_email]
; Os_user_view.generic_email_form
~a_placeholder_email:[%i18n S.email_address] ~text:[%i18n S.send]
~service:Os_services.add_email_service ()
; p [%i18n currently_registered_emails]
; div ~a:[a_class ["os-emails"]] [emails]
; Form.post_form ~service:Os_services.update_language_service
select_language_form () ] ]

43
daegsrv/demo.eliom Normal file
View file

@ -0,0 +1,43 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%shared open Eliom_content.Html.D]
(* drawer / demo welcome page ***********************************************)
let%shared handler myid_o () () =
Daegsrv_container.page
~a:[a_class ["os-page-demo"]]
myid_o
[ h2 [%i18n Demo.general_principles]
; p [%i18n Demo.intro_1]
; p [%i18n Demo.intro_2]
; p [%i18n Demo.widget_ot]
; p [%i18n Demo.widget_see_drawer]
; p [%i18n Demo.widget_feel_free]
; p [%i18n Demo.intro_3] ]
let%shared () =
let registerDemo (module D : Demo_tools.Page) =
Daegsrv_base.App.register ~service:D.service
( Daegsrv_page.Opt.connected_page @@ fun myid_o () () ->
let%lwt p = D.page () in
Daegsrv_container.page ~a:[a_class [D.page_class]] myid_o p )
in
List.iter registerDemo Demo_tools.demos;
Daegsrv_base.App.register
~service:Daegsrv_services.demo_service
(Daegsrv_page.Opt.connected_page handler)
(* [detail_page_handler] is not registered in [Demo_tools] because we
- don't want to show detail pages in the menu. *)
let%shared () =
let detail_page_handler myid_o page () =
Daegsrv_container.page
~a:[a_class ["os-page-demo-transition"]]
myid_o
(Demo_pagetransition.make_detail_page page ())
in
Daegsrv_base.App.register
~service:Demo_pagetransition.detail_page_service
(Daegsrv_page.Opt.connected_page detail_page_handler)

31
daegsrv/demo_cache.eliom Normal file
View file

@ -0,0 +1,31 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Eliom_cscache demo *)
open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-cache"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.cache]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-cache"
(* Page for this demo *)
let%shared page () =
Lwt.return
[ h1 [%i18n Demo.cache_1]
; p
[%i18n
Demo.cache_2
~eliom_cscache:[code [txt "Eliom_cscache"]]
~os_user_proxy:[code [txt "Os_user_proxy"]]]
; p [%i18n Demo.cache_3 ~eliom_cscache:[code [txt "Eliom_cscache"]]]
; p [%i18n Demo.cache_4 ~eliom_cscache:[code [txt "Eliom_cscache"]]] ]

View file

@ -0,0 +1,64 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Calendar demo *)
open Eliom_content.Html.D]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-calendar"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* A reactive value containing the currently selected date *)
(* NOTE: in this example, we define a shared signal on the server side. Its
original value can only be read when the server generates the first page
(declaring it `%client`-only would obviously not work) and injected to be
read-/writable on the (possibly disconnected) client side since any
*shared value* is injectable; subsequent updates won't be sent to the server.
Declaring this signal as `%shared` wouldn't work either, as you'd end up with
two different signals (one for each side): a Reactive `map` in `page` would
use the server's signal when it's first generated on the server, while the
client-side click event would use its own `f`, so nothing would actually
happen. You can observe this duplication by replacing `%server` below with
`%shared`: the compiler will emit an error because the type of one of those
signals can't be inferred (it remains unknown at the end of the typing pass)
since it's never used throughout the program. *)
let%server s, f = Eliom_shared.React.S.create None
let%client action y m d =
~%f (Some (y, m, d));
Lwt.return_unit
let%shared string_of_date = function
| Some (y, m, d) ->
[%i18n
Demo.S.you_click_on_date ~y:(string_of_int y) ~m:(string_of_int m)
~d:(string_of_int d)]
| None -> ""
let%server date_as_string () : string Eliom_shared.React.S.t =
Eliom_shared.React.S.map [%shared string_of_date] s
let%rpc date_reactive () : string Eliom_shared.React.S.t Lwt.t =
Lwt.return @@ date_as_string ()
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.calendar]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-calendar"
(* Page for this demo *)
let%shared page () =
let calendar =
Ot_calendar.make ~click_non_highlighted:true ~action:[%client action] ()
in
let%lwt dr = date_reactive () in
Lwt.return
[ h1 [%i18n Demo.calendar]
; p [%i18n Demo.this_page_show_calendar]
; div ~a:[a_class ["os-calendar"]] [calendar]
; p [Eliom_content.Html.R.txt dr] ]

View file

@ -0,0 +1,73 @@
[%%client
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Carousel demo *)
open Eliom_content.Html]
[%%shared open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-carousel1"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.carousel_1]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-carousel1"
(* Bind arrow keys *)
let%shared bind_keys change carousel =
ignore
[%client
(let arrow_thread =
(* Wait for the carousel to be in the page
(in the case the page is generated client side): *)
let%lwt () = Ot_nodeready.nodeready (To_dom.of_element ~%carousel) in
Ot_carousel.bind_arrow_keys ~change:~%change
Js_of_ocaml.Dom_html.document##.body
in
(* Do not forget to cancel the thread when we remove the carousel
(here, when we go to another page): *)
Eliom_client.onunload (fun () -> Lwt.cancel arrow_thread)
: unit)]
(* Page for this demo *)
let%shared page () =
let make_page name =
div
~a:[a_class ["demo-carousel1-page"; "demo-carousel1-page-" ^ name]]
[txt "Page "; txt name]
in
let carousel_change_signal =
[%client
(React.E.create ()
: ([`Goto of int | `Next | `Prev] as 'a) React.E.t
* (?step:React.step -> 'a -> unit))]
in
let update = [%client fst ~%carousel_change_signal] in
let change = [%client fun a -> snd ~%carousel_change_signal ?step:None a] in
let carousel_pages = ["1"; "2"; "3"; "4"] in
let length = List.length carousel_pages in
let carousel_content = List.map make_page carousel_pages in
let {Ot_carousel.elt = carousel; pos; vis_elts} =
Ot_carousel.make ~update carousel_content
in
let bullets = Ot_carousel.bullets ~change ~pos ~length ~size:vis_elts () in
let prev = Ot_carousel.previous ~change ~pos [] in
let next = Ot_carousel.next ~change ~pos ~vis_elts ~length [] in
bind_keys change carousel;
Lwt.return
[ h1 [%i18n Demo.carousel_1]
; p [%i18n Demo.ot_carousel_first_example_1]
; p [%i18n Demo.ot_carousel_first_example_2]
; p [%i18n Demo.ot_carousel_first_example_3]
; p [%i18n Demo.ot_carousel_first_example_4]
; div
~a:[a_class ["demo-carousel1"]]
[div ~a:[a_class ["demo-carousel1-box"]] [carousel; prev; next; bullets]]
]

View file

@ -0,0 +1,95 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Page with several tabs *)
open Eliom_content.Html]
[%%shared open Eliom_content.Html.F]
let%shared lorem_ipsum =
[ p
[ txt
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Hanc ergo intuens debet institutum illud quasi signum absolvere. Animi enim quoque dolores percipiet omnibus partibus maiores quam corporis. Atque haec ita iustitiae propria sunt, ut sint virtutum reliquarum communia. Sed ad bona praeterita redeamus. Duarum enim vitarum nobis erunt instituta capienda. Nunc ita separantur, ut disiuncta sint, quo nihil potest esse perversius. Hoc est non dividere, sed frangere. Duo Reges: constructio interrete. Satis est ad hoc responsum."
]
; p
[ txt
"Traditur, inquit, ab Epicuro ratio neglegendi doloris. Quod quidem iam fit etiam in Academia. Quodcumque in mentem incideret, et quodcumque tamquam occurreret. Immo vero, inquit, ad beatissime vivendum parum est, ad beate vero satis. Re mihi non aeque satisfacit, et quidem locis pluribus."
]
; p
[ txt
"Amicitiam autem adhibendam esse censent, quia sit ex eo genere, quae prosunt. Hoc loco tenere se Triarius non potuit. Facile est hoc cernere in primis puerorum aetatulis. Sed in rebus apertissimis nimium longi sumus. Utrum igitur tibi litteram videor an totas paginas commovere? Quid de Platone aut de Democrito loquar?"
] ]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-carousel2"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.carousel_2]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-carousel2"
(* Page for this demo *)
let%shared page () =
let make_page name =
let c = if name = "1" then lorem_ipsum else [] in
div
~a:[a_class ["demo-carousel2-page"; "demo-carousel2-page-" ^ name]]
(p [txt "Page "; txt name] :: c)
in
let make_tab name = [txt "Page "; txt name] in
let carousel_change_signal =
[%client
(React.E.create ()
: ([`Goto of int | `Next | `Prev] as 'a) React.E.t
* (?step:React.step -> 'a -> unit))]
in
let update = [%client fst ~%carousel_change_signal] in
let change = [%client fun a -> snd ~%carousel_change_signal ?step:None a] in
let carousel_pages = ["1"; "2"; "3"; "4"] in
let carousel_content = List.map make_page carousel_pages in
let tab_content = List.map make_tab carousel_pages in
let tabs_r = ref (div []) in
let get_header_height =
[%client
fun () ->
let t = To_dom.of_element !(~%tabs_r) in
int_of_float (Ot_size.client_top t) + t##.offsetHeight]
in
(* We want a "full-height" carousel. See Ot_carousel documentation. *)
let {Ot_carousel.elt = carousel; pos; swipe_pos} =
Ot_carousel.make ~update ~full_height:(`Header get_header_height)
carousel_content
in
let ribbon = Ot_carousel.ribbon ~change ~pos ~cursor:swipe_pos tab_content in
let tabs =
(* ribbon container is necessary for shadow,
because position:sticky is not interpreted as relative
on browsers that do not support sticky. *)
D.div ~a:[a_class ["demo-carousel2-tabs"]] [ribbon]
in
tabs_r := ribbon;
(* We want the tabs to be always visible on top of the page.
To do that, we use position: sticky;
As this is not available in all browsers, we use a polyfill to
simulate this behaviour when not supported:
*)
ignore
[%client
(Lwt.async (fun () ->
Lwt.map ignore
(Ot_sticky.make_sticky ~ios_html_scroll_hack:true ~dir:`Top ~%tabs))
: unit)];
Lwt.return
[ h1 [%i18n Demo.carousel_2]
; p [%i18n Demo.ot_carousel_second_example_1]
; p [%i18n Demo.ot_carousel_second_example_2]
; p [%i18n Demo.ot_carousel_second_example_3]
; div
~a:[a_class ["demo-carousel2"]]
[div ~a:[a_class ["demo-carousel2-box"]] [tabs; carousel]] ]

View file

@ -0,0 +1,88 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Wheel demo *)
open Eliom_content.Html]
[%%shared open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-carousel3"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.carousel_wheel]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-carousel3"
(* Page for this demo *)
let%shared page () =
let carousel_pages =
[ [%i18n Demo.S.monday] ^ " 1"
; [%i18n Demo.S.tuesday] ^ " 1"
; [%i18n Demo.S.wednesday] ^ " 1"
; [%i18n Demo.S.thursday] ^ " 1"
; [%i18n Demo.S.friday] ^ " 1"
; [%i18n Demo.S.saturday] ^ " 1"
; [%i18n Demo.S.sunday] ^ " 1"
; [%i18n Demo.S.monday] ^ " 2"
; [%i18n Demo.S.tuesday] ^ " 2"
; [%i18n Demo.S.wednesday] ^ " 2"
; [%i18n Demo.S.thursday] ^ " 2"
; [%i18n Demo.S.friday] ^ " 2"
; [%i18n Demo.S.saturday] ^ " 2"
; [%i18n Demo.S.sunday] ^ " 2"
; [%i18n Demo.S.monday] ^ " 3"
; [%i18n Demo.S.tuesday] ^ " 3"
; [%i18n Demo.S.wednesday] ^ " 3"
; [%i18n Demo.S.thursday] ^ " 3"
; [%i18n Demo.S.friday] ^ " 3"
; [%i18n Demo.S.saturday] ^ " 3"
; [%i18n Demo.S.sunday] ^ " 3"
; [%i18n Demo.S.monday] ^ " 4"
; [%i18n Demo.S.tuesday] ^ " 4"
; [%i18n Demo.S.wednesday] ^ " 4"
; [%i18n Demo.S.thursday] ^ " 4"
; [%i18n Demo.S.friday] ^ " 4"
; [%i18n Demo.S.saturday] ^ " 4"
; [%i18n Demo.S.sunday] ^ " 4"
; [%i18n Demo.S.monday] ^ " 5"
; [%i18n Demo.S.tuesday] ^ " 5"
; [%i18n Demo.S.wednesday] ^ " 5"
; [%i18n Demo.S.thursday] ^ " 5"
; [%i18n Demo.S.friday] ^ " 5"
; [%i18n Demo.S.saturday] ^ " 5"
; [%i18n Demo.S.sunday] ^ " 5" ]
in
let length = List.length carousel_pages in
let carousel_content = List.map (fun p -> D.div [txt p]) carousel_pages in
let carousel_change_signal =
[%client
(React.E.create ()
: ([`Goto of int | `Next | `Prev] as 'a) React.E.t
* (?step:React.step -> 'a -> unit))]
in
let update = [%client fst ~%carousel_change_signal] in
let change = [%client fun a -> snd ~%carousel_change_signal ?step:None a] in
let carousel, pos, _swipe_pos =
Ot_carousel.wheel
~a:[a_class ["demo-carousel3"]]
~update ~vertical:true ~inertia:1. ~position:10 ~transition_duration:3.
~face_size:25 carousel_content
in
Lwt.return
[ h1 [%i18n Demo.carousel_wheel]
; p [%i18n Demo.carousel_third_example_1]
; carousel
; div
[ Ot_carousel.previous ~a:[a_class ["demo-prev"]] ~change ~pos []
; Ot_carousel.next
~a:[a_class ["demo-next"]]
~change ~pos
~vis_elts:(Eliom_shared.React.S.const 1)
~length [] ] ]

45
daegsrv/demo_i18n.eliom Normal file
View file

@ -0,0 +1,45 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Ocsigen_i18n demo *)
open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-i18n"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.internationalization ~capitalize:true]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-i18n"
(* Page for this demo *)
let%shared page () =
(* Syntax [%i18n key] or [%i18n Module.key] inserts
the text corresponding to the key, in the language chosen by the user,
as a list of HTML elements.
Syntax [%i18n S.key] or [%i18n Module.S.key] inserts the text as a string.
It is possible to give parameters (here a boolean ~capitalize, or
a piece of HTML text ~f1 or ~f2). Have a look at file
assets/daegsrv_Demo_i18n.tsv
to see how to write the corresponding translations.
*)
Lwt.return
[ h1 [%i18n Demo.internationalization ~capitalize:true]
; p [%i18n Demo.internationalization_1]
; p
[%i18n
Demo.internationalization_2
~f1:[code [txt "assets/daegsrv_i18n.tsv"]]
~f2:[code [txt "daegsrv_i18n.eliom"]]]
; p [txt [%i18n Demo.S.internationalization_3]]
; p
[%i18n
Demo.internationalization_4
~f:[code [txt "assets/daegsrv_Demo_i18n.tsv"]]
~demo_prefix:[code [txt "demo_"]]] ]

72
daegsrv/demo_links.eliom Normal file
View file

@ -0,0 +1,72 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Static files demo *)
open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-static-files"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.links_and_static_files]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-links"
(* An example of external service: *)
let%server ocsigen_service =
Eliom_service.extern ~prefix:"http://ocsigen.org" ~path:[]
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client ocsigen_service = ~%ocsigen_service
(* Page for this demo *)
let%shared page () =
Lwt.return
[ h1 [%i18n Demo.links_and_static_files]
; h2 [%i18n Demo.services]
; p
[%i18n
Demo.services_1
~f1:[code [txt "daegsrv_services.eliom"]]
~f2:[code [txt "daegsrv_handlers.eliom"]]
~f3:[code [txt "daegsrv.eliom"]]]
; h2 [%i18n Demo.links_and_forms]
; p
[%i18n
Demo.links_and_forms_1
~t1:
[a ~service:Os_services.main_service [%i18n Demo.internal_link] ()]
~t2:[a ~service:ocsigen_service [%i18n Demo.external_service] ()]]
; h2 [%i18n Demo.static_files]
; p
[%i18n
Demo.static_files_1
~static:[code [txt "static"]]
~static_dir:[code [txt "static_dir"]]]
; img
~a:[a_class ["demo-static-img"]]
~alt:"local_img"
~src:
(Eliom_content.Html.F.make_uri
~absolute:false (* We want local file on mobile app *)
~service:(Eliom_service.static_dir ())
["images"; "ocsigen.png"])
()
; p [%i18n Demo.static_files_2]
; img
~a:[a_class ["demo-static-img"]]
~alt:"distant_img"
~src:
(Eliom_content.Html.F.make_uri
(* We want a distant file:
keep the default value of ~absolute *)
~service:(Eliom_service.static_dir ())
["images"; "ocsigen.png"])
() ]

106
daegsrv/demo_notif.eliom Normal file
View file

@ -0,0 +1,106 @@
[%%client
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Notification demo *)
open Js_of_ocaml_lwt]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-notif"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.notification]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-notif"
(* Instantiate function Os_notif.Simple for each kind of notification
you need.
The key is the resource ID. For example, if you are implementing a
messaging application, it can be the chatroom ID
(for example type key = int64).
*)
module Notif = Os_notif.Make_Simple (struct
type key = unit
(* The resources identifiers.
Here unit because we have only one resource. *)
type notification = string
end)
(* Broadcast message [v] *)
let%rpc notify (v : string) : unit Lwt.t =
(* Notify all client processes listening on this resource
(identified by its key, given as first parameter)
by sending them message v. *)
Notif.notify (* ~notfor:`Me *) (() : Notif.key) v;
(* Use ~notfor:`Me to avoid receiving the message in this tab,
or ~notfor:(`User myid) to avoid sending to the current user.
(Where myid is Os_current_user.get_current_userid ())
*)
Lwt.return_unit
let%rpc listen () : unit Lwt.t = Notif.listen (); Lwt.return_unit
(* Display a message every time the React event [e = Notif.client_ev ()]
happens. *)
let%server () =
Os_session.on_start_process (fun _ ->
let e : (unit * string) Eliom_react.Down.t = Notif.client_ev () in
ignore
[%client
(ignore
@@ React.E.map
(fun (_, msg) ->
(* Eliom_lib.alert "%s" msg *)
Os_msg.msg ~level:`Msg (Printf.sprintf "%s" msg))
~%e
: unit)];
Lwt.return_unit)
(* Make a text input field that calls [f s] for each [s] submitted *)
let%shared make_form msg f =
let inp = Eliom_content.Html.D.Raw.input ()
and btn =
Eliom_content.Html.(D.button ~a:[D.a_class ["button"]] [D.txt msg])
in
ignore
[%client
(Lwt.async @@ fun () ->
let btn = Eliom_content.Html.To_dom.of_element ~%btn
and inp = Eliom_content.Html.To_dom.of_input ~%inp in
Lwt_js_events.clicks btn @@ fun _ _ ->
let v = Js_of_ocaml.Js.to_string inp##.value in
let%lwt () = ~%f v in
inp##.value := Js_of_ocaml.Js.string "";
Lwt.return_unit
: unit)];
Eliom_content.Html.D.div [inp; btn]
let%rpc unlisten () : unit Lwt.t = Notif.unlisten (); Lwt.return_unit
(* Page for this demo *)
let%shared page () =
(* Subscribe to notifications when entering this page: *)
let%lwt () = listen () in
(* Unsubscribe from notifications when user leaves this page *)
let (_ : unit Eliom_client_value.t) =
[%client Eliom_client.Page_status.ondead (fun () -> Lwt.async unlisten)]
in
Lwt.return
Eliom_content.Html.F.
[ h1 [%i18n Demo.notification]
; p
([%i18n
Demo.exchange_msg_between_users ~os_notif:[code [txt "Os_notif"]]]
@ [ br ()
; txt [%i18n Demo.S.open_multiple_tabs_browsers]
; br ()
; txt [%i18n Demo.S.fill_input_form_send_message] ])
; make_form [%i18n Demo.S.send_message]
[%client (notify : string -> unit Lwt.t)] ]

View file

@ -0,0 +1,93 @@
[%%shared
(* This demo illustrates Eliom's DOM caching feature.
By running [Eliom_client.onload Eliom_client.push_history_dom] one
can push the DOM of the current page into Eliom's cache. Every page
which is cached in this manner will be immediately served from the
cache instead of being charged from the server or regenerated by
the client. Also the scroll position is restored that the page had
at the end of the last visit. *)
open Eliom_content]
[%%shared open Html]
[%%shared open Html.D]
[%%client open Js_of_ocaml_lwt]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-page-transition"; ""])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
let%server detail_page_service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-page-transition"; "detail"; ""])
~meth:(Eliom_service.Get (Eliom_parameter.int "page"))
()
(* Make service available on the client *)
let%client service = ~%service
let%client detail_page_service = ~%detail_page_service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.pagetransition]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-transition"
let%shared create_item index =
let open F in
li
~a:
[ a_class
["demo-list-item"; Printf.sprintf "demo-list-item-%d" (index mod 5)]
]
[a ~service:detail_page_service [txt (Printf.sprintf "list%d" index)] index]
let%shared page () =
let l =
(fun i -> create_item (i + 1))
|> Array.init 10 |> Array.to_list
|> ul ~a:[a_class ["demo-list"]]
in
let add_button =
div ~a:[a_class ["demo-button"]] [%i18n Demo.pagetransition_add_button]
in
ignore
[%client
((* It is the address of the dom that will be stored in cache, so
it doesn't matter when [push_history_dom] is called. However,
it is important that the dom is bound to the right state id.
So it is better to call [push_history_dom] in Eliom_client.onload,
when the state id has already been updated and the dom of
the current page is ready. *)
Eliom_client.onload Eliom_client.push_history_dom;
let counter =
let r = ref 10 in
fun () ->
r := !r + 1;
!r
in
Lwt_js_events.clicks (To_dom.of_element ~%add_button) (fun _ _ ->
Html.Manip.appendChild ~%l (create_item (counter ()));
Lwt.return_unit)
: unit Lwt.t)];
Lwt.return
[ h1 [%i18n Demo.pagetransition_list_page]
; p [%i18n Demo.pagetransition_intro]
; l
; add_button ]
let%shared make_detail_page page () =
let back_button =
div ~a:[a_class ["demo-button"]] [%i18n Demo.pagetransition_back_button]
in
ignore
[%client
(Lwt.async (fun () ->
Lwt_js_events.clicks (To_dom.of_element ~%back_button) (fun _ _ ->
Js_of_ocaml.Dom_html.window##.history##back;
Lwt.return_unit))
: unit)];
[ h1
([%i18n Demo.pagetransition_detail_page]
@ [txt (Printf.sprintf " %d" page)])
; back_button ]

View file

@ -0,0 +1,46 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* PGOcaml demo *)
open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-pgocaml"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.pgocaml]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-pgocaml"
(* Fetch users in database *)
let%rpc get_users () : string list Lwt.t =
(* For this demo, we add a delay to simulate a network or db latency: *)
let%lwt () = Lwt_unix.sleep 2. in
Demo_pgocaml_db.get ()
(* Generate page for this demo *)
let%shared page () =
let%lwt user_block =
Ot_spinner.with_spinner
(let%lwt users = get_users () in
let users =
List.map
(fun u -> if u = "" then li [em [txt "new user"]] else li [txt u])
users
in
if users = []
then Lwt.return [p [em [%i18n Demo.no_user_create_accounts]]]
else Lwt.return [p [%i18n Demo.pgocaml_users]; ul users])
in
Lwt.return
[ h1 [%i18n Demo.pgocaml]
; p [%i18n Demo.pgocaml_description_1]
; p [%i18n Demo.pgocaml_description_2]
; p [%i18n Demo.pgocaml_description_3]
; user_block ]

View file

@ -0,0 +1,13 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
open Os_db
(* We are using PGOCaml to make type safe DB requests to Postgresql.
The Makefile automatically compiles
all files *_db.ml with PGOCaml's ppx syntax extension.
*)
let get () =
full_transaction_block (fun dbh ->
[%pgsql dbh "SELECT lastname FROM ocsigen_start.users"])

68
daegsrv/demo_popup.eliom Normal file
View file

@ -0,0 +1,68 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Popup button demo *)
open Eliom_content.Html]
[%%shared open Eliom_content.Html.F]
[%%client open Js_of_ocaml_lwt]
(* Service for this demo, defined in the server-side app *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-popup"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu. This value is defined both server and client-side. *)
let%shared name () = [%i18n Demo.S.popup]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-popup"
(* The function generating the page can be called either from the server or
the client (shared section). *)
let%shared page () =
let button =
(* As we are using ~%button (in a client section below)
to refer to this precise occurrence of the button in the page,
button must be a D node
(from module Eliom_content.Html.D,
which will add an unique identifier in its attributes),
and not a functional node (Eliom_content.Html.F). *)
D.Form.input
~a:[a_class ["button"]]
~input_type:`Submit ~value:[%i18n Demo.S.popup_click] Form.string
in
(* Every time this page is generated,
we want to execute the following piece of client-side code.
Lwt_js_events.clicks means "For each click on ... do ...".
It creates an Lwt thread that never returns.
We run it asynchronously using Lwt.async.
Lwt_js_events.clicks is expecting a DOM node
(i.e. an actual part of the current page).
To_dom.of_element will return the DOM node corresponding to the
OCaml value ~%button.
~%button refers to the value button, defined outside [%client ] section
(possibly on server or client).
*)
ignore
[%client
(* This client section will be executed after the page is
displayed by the browser. *)
(Lwt.async (fun () ->
(* Lwt_js_events.clicks returns a Lwt thread, which never terminates.
We run it asynchronously. *)
Lwt_js_events.clicks (To_dom.of_element ~%button) (fun _ _ ->
let%lwt _ =
Ot_popup.popup ~close_button:[Os_icons.F.close ()] (fun _ ->
Lwt.return @@ p [%i18n Demo.popup_message])
in
Lwt.return_unit))
: unit)];
(* Page elements, using module Eliom_content.Html.F
(as we don't want to add a unique identifier).
See internationalization demo for i18n syntax.
*)
Lwt.return [h1 [%i18n Demo.popup]; p [%i18n Demo.popup_content]; p [button]]

View file

@ -0,0 +1,47 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(** Demo for refreshable content *)
[%%shared open Eliom_content.Html]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-pull-to-refresh"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.pull_to_refresh]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-pull"
let%shared page () =
let counter_sig, set_counter = Eliom_shared.React.S.create 0 in
let reload =
[%client
fun () ->
let%lwt _ = Js_of_ocaml_lwt.Lwt_js.sleep 1. in
let n = Eliom_shared.React.S.value ~%counter_sig in
~%set_counter (n + 1);
Lwt.return_true]
in
let counter_node_sig =
Eliom_shared.React.S.map
[%shared
fun n ->
let n = [F.txt @@ string_of_int n] in
F.p [%i18n Demo.pull_to_refresh_counter ~n]]
counter_sig
in
let content =
F.div
~a:[F.a_class ["demo-pull-to-refresh-content"]]
[ F.h1 [%i18n Demo.pull_to_refresh]
; F.p [%i18n Demo.pull_to_refresh_1]
; F.p [%i18n Demo.pull_to_refresh_2]
; R.node counter_node_sig ]
in
Lwt.return @@ [Ot_pulltorefresh.make ~dragThreshold:15. ~content reload]

72
daegsrv/demo_react.eliom Normal file
View file

@ -0,0 +1,72 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(** Demo for shared reactive content *)
[%%client open Js_of_ocaml_lwt]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-react"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.reactive_programming]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-react"
(* Make a text input field that calls [f s] for each [s] submitted *)
let%shared make_form msg f =
let inp = Eliom_content.Html.D.Raw.input ()
and btn =
Eliom_content.Html.(D.button ~a:[D.a_class ["button"]] [D.txt msg])
in
ignore
[%client
(Lwt.async @@ fun () ->
let btn = Eliom_content.Html.To_dom.of_element ~%btn
and inp = Eliom_content.Html.To_dom.of_input ~%inp in
Lwt_js_events.clicks btn @@ fun _ _ ->
let v = Js_of_ocaml.Js.to_string inp##.value in
let%lwt () = ~%f v in
inp##.value := Js_of_ocaml.Js.string "";
Lwt.return_unit
: unit)];
Eliom_content.Html.D.div [inp; btn]
(* Page for this demo *)
let%shared page () =
(* Client reactive list, initially empty.
It can be defined either from client or server side,
(depending on whether this code is executed client or server-side).
Use Eliom_shared.ReactiveData.RList for lists or
Eliom_shared.React.S for other data types.
*)
let l, h = Eliom_shared.ReactiveData.RList.create [] in
let inp =
(* Form that performs a cons (client-side). *)
make_form [%i18n Demo.S.reactive_programming_button]
[%client
(fun v -> Lwt.return (Eliom_shared.ReactiveData.RList.cons v ~%h)
: string -> unit Lwt.t)]
and l =
(* Produce <li> items from l contents.
The shared function will first be called once server or client-side
to compute the initial page. It will then be called client-side
every time the reactive list changes to update the
page automatically. *)
Eliom_shared.ReactiveData.RList.map
[%shared (fun s -> Eliom_content.Html.(D.li [D.txt s]) : _ -> _)]
l
in
Lwt.return
Eliom_content.Html.
[ F.h1 [%i18n Demo.reactive_programming]
; F.p [F.txt [%i18n Demo.S.reactive_programming_1]]
; F.p [F.txt [%i18n Demo.S.reactive_programming_2]]
; F.p [F.txt [%i18n Demo.S.reactive_programming_3]]
; inp
; F.div [R.ul l] ]

55
daegsrv/demo_ref.eliom Normal file
View file

@ -0,0 +1,55 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Demo for Eliom references and Os_date *)
open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-ref"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.eliom_ref]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-ref"
(* An Eliom reference storing the last time the user visited the current
page. It has scope Eliom_common.default_group_scope, which means that
the value will be different for each user of the Web site, but the same
for all the sessions of a same user.
Ocsigen Start is creating a session group for each user.
*)
let%server last_visit =
Eliom_reference.eref ~persistent:"demo_last_visit"
~scope:Eliom_common.default_group_scope None
(* Read & reset last_visit *)
let%rpc get_reset_last_visit () : Os_date.local_calendar option Lwt.t =
let%lwt v = Eliom_reference.get last_visit in
let%lwt () = Eliom_reference.set last_visit (Some (Os_date.now ())) in
Lwt.return v
(* Call get_reset_last_visit and produce pretty message *)
let%shared get_reset_last_visit_message () =
let%lwt last_visit = get_reset_last_visit () in
match last_visit with
| None -> Lwt.return [%i18n Demo.eliom_ref_first_visit]
| Some last_visit ->
Lwt.return
([%i18n Demo.eliom_ref_last_visit]
@ [txt " "; txt (Os_date.smart_time last_visit)])
(* Generate page for this demo *)
let%shared page () =
let%lwt last_visit_message = get_reset_last_visit_message () in
Lwt.return
[ h1 [%i18n Demo.eliom_ref]
; p [txt [%i18n Demo.S.eliom_ref_1]]
; p [txt [%i18n Demo.S.eliom_ref_2]]
; p last_visit_message
; p [txt [%i18n Demo.S.eliom_ref_3]] ]

63
daegsrv/demo_rpc.eliom Normal file
View file

@ -0,0 +1,63 @@
[%%client
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* RPC button demo *)
open Js_of_ocaml_lwt]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-rpc"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.rpc_button]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-rpc"
(* A server-side reference that stores data for the current browser
(scope = session).
It's also possible to define Eliom references with other scopes,
like client-process (a tab of a browser) or session-group (a user).
*)
let%server my_ref =
Eliom_reference.eref ~scope:Eliom_common.default_session_scope 0
(* Server-side function that increments my_ref and returns new val *)
let%rpc incr_my_ref () : int Lwt.t =
let%lwt v = Eliom_reference.get my_ref in
let v = v + 1 in
let%lwt () = Eliom_reference.set my_ref v in
Lwt.return v
let%shared button msg f =
let btn =
Eliom_content.Html.(D.button ~a:[D.a_class ["button"]] [D.txt msg])
in
ignore
[%client
(Lwt.async @@ fun () ->
Lwt_js_events.clicks (Eliom_content.Html.To_dom.of_element ~%btn)
(fun _ _ -> ~%f ())
: unit)];
btn
(* Page for this demo *)
let%shared page () =
let btn =
button [%i18n Demo.S.rpc_button_click_increase]
[%client
(fun () ->
let%lwt v = incr_my_ref () in
Eliom_lib.alert "Update: %d" v;
Lwt.return_unit
: unit -> unit Lwt.t)]
in
Lwt.return
Eliom_content.Html.
[ F.h1 [%i18n Demo.rpc_button]
; F.p [F.txt [%i18n Demo.S.rpc_button_description]]
; F.p [btn] ]

View file

@ -0,0 +1,44 @@
[%%client
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Spinner demo *)
open Js_of_ocaml_lwt]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-spinner"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.spinner]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-spinner"
(* Build the spinner *)
let%client make_spinner () =
(* [Ot_spinner.with_spinner_no_lwt] accepts an Lwt thread "slowly"
producing HTML content *)
Ot_spinner.with_spinner_no_lwt
(* sleep for 5 seconds to simulate a delay, then return content *)
(let%lwt () = Lwt_js.sleep 5. in
Lwt.return
Eliom_content.Html.D.
[ txt [%i18n Demo.S.spinner_content_ready]
; txt " "
; txt [%i18n Demo.S.spinner_message_replace_spinner] ])
(* Page for this demo *)
let%shared page () =
Lwt.return
Eliom_content.Html.
[ F.h1 [%i18n Demo.spinner]
; F.p [F.txt [%i18n Demo.S.spinner_description_ot]]
; F.p [F.txt [%i18n Demo.S.spinner_description_1]]
; F.p [F.txt [%i18n Demo.S.spinner_description_2]]
; F.p [F.txt [%i18n Demo.S.spinner_description_3]]
; F.p [F.txt [%i18n Demo.S.spinner_generated_client_side]]
; C.node [%client (make_spinner () : [> `Div] Eliom_content.Html.elt)] ]

View file

@ -0,0 +1,60 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
open Eliom_content.Html.D]
[%%client open Js_of_ocaml_lwt]
(* Timepicker demo *)
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-timepicker"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
let%server s, f = Eliom_shared.React.S.create None
let%client action (h, m) =
~%f (Some (h, m));
Lwt.return_unit
let%shared string_of_time = function
| Some (h, m) ->
[%i18n Demo.S.you_click_on_time ~h:(string_of_int h) ~m:(string_of_int m)]
| None -> ""
let%server time_as_string () : string Eliom_shared.React.S.t =
Eliom_shared.React.S.map [%shared string_of_time] s
let%rpc time_reactive () : string Eliom_shared.React.S.t Lwt.t =
Lwt.return @@ time_as_string ()
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.timepicker]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-timepicker"
(* Page for this demo *)
let%shared page () =
let time_picker, _, back_f =
Ot_time_picker.make ~h24:true ~action:[%client action] ()
in
let button =
Eliom_content.Html.D.button [%i18n Demo.timepicker_back_to_hours]
in
ignore
[%client
(Lwt.async (fun () ->
Lwt_js_events.clicks (Eliom_content.Html.To_dom.of_element ~%button)
(fun _ _ -> ~%back_f (); Lwt.return_unit))
: _)];
let%lwt tr = time_reactive () in
Lwt.return
[ h1 [%i18n Demo.timepicker]
; p [%i18n Demo.timepicker_description]
; div [time_picker]
; p [Eliom_content.Html.R.txt tr]
; div [button] ]

48
daegsrv/demo_tips.eliom Normal file
View file

@ -0,0 +1,48 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Os_tips demo *)
open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-tips"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.tips]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-tips"
(* Here is an example of tip. Call this function while generating the
widget concerned by the explanation it contains. *)
let%shared example_tip () =
(* Have a look at the API documentation of module Os_tips for
more options. *)
Os_tips.bubble () ~top:[%client 40] ~right:[%client 0] ~width:[%client 300]
~height:[%client 180] ~arrow:[%client `top 250] ~name:"example"
~content:
[%client
fun _ ->
Lwt.return
Eliom_content.Html.F.
[p [%i18n Demo.example_tip]; p [%i18n Demo.look_module_tip]]]
(* Page for this demo *)
let%shared page () =
(* Call the function defining the tip from the server or the client: *)
let%lwt () = example_tip () in
Lwt.return
[ h1 [%i18n Demo.tips1]
; p [%i18n Demo.tips2 ~os_tips:[code [txt "Os_tips"]]]
; p [%i18n Demo.tips3]
; p
[%i18n
Demo.tips4
~set_page:
[ a ~service:Daegsrv_services.settings_service
[%i18n Demo.tips5] () ]] ]

39
daegsrv/demo_tongue.eliom Normal file
View file

@ -0,0 +1,39 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Tongue demo *)
open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-tongue"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.tongue_1]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-tongue"
(* Page for this demo *)
let%shared page () =
let content =
[ div ~a:[a_class ["demo-tongue-1"]] []
; div ~a:[a_class ["demo-tongue-2"]] []
; div ~a:[a_class ["demo-tongue-3"]] []
; div ~a:[a_class ["demo-tongue-4"]] []
; div ~a:[a_class ["demo-tongue-5"]] []
; div ~a:[a_class ["demo-tongue-6"]] [] ]
in
let tongue =
Ot_tongue.tongue ~side:`Bottom
~stops:[`Px 70; `Interval (`Percent 100, `Full_content)]
~init:(`Px 70) content
in
Lwt.return
[ h1 [%i18n Demo.tongue_1]
; p [%i18n Demo.ot_tongue_1]
; div ~a:[a_class ["demo-tongue"]] [tongue.Ot_tongue.elt] ]

61
daegsrv/demo_tools.eliom Normal file
View file

@ -0,0 +1,61 @@
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
[%%shared.start]
module type Page = sig
val name : unit -> string
val page_class : string
val service
: ( unit
, unit
, Eliom_service.get
, Eliom_service.att
, Eliom_service.non_co
, Eliom_service.non_ext
, Eliom_service.reg
, [`WithoutSuffix]
, unit
, unit
, Eliom_service.non_ocaml )
Eliom_service.t
val page : unit -> Html_types.div_content Eliom_content.Html.D.elt list Lwt.t
end
let demos =
[ (module Demo_popup : Page)
; (module Demo_rpc)
; (module Demo_ref)
; (module Demo_spinner)
; (module Demo_pgocaml)
; (module Demo_users)
; (module Demo_links)
; (module Demo_i18n)
; (module Demo_tips)
; (module Demo_carousel1)
; (module Demo_carousel2)
; (module Demo_carousel3)
; (module Demo_tongue)
; (module Demo_calendar)
; (module Demo_timepicker)
; (module Demo_notif)
; (module Demo_react)
; (module Demo_pulltorefresh)
; (module Demo_cache)
; (module Demo_pagetransition) ]
let drawer_contents () =
let open Eliom_content.Html.F in
let make_link (module D : Page) =
li [a ~service:D.service [txt @@ D.name ()] ()]
in
let submenu =
ul ~a:[a_class ["os-drawer-submenu"]] (List.map make_link demos)
in
li
[ a
~a:[a_class ["os-drawer-item"]]
~service:Daegsrv_services.demo_service [%i18n Demo.intro] ()
; submenu ]

57
daegsrv/demo_users.eliom Normal file
View file

@ -0,0 +1,57 @@
[%%shared
(* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Os_current_user demo *)
open Eliom_content.Html.F]
(* Service for this demo *)
let%server service =
Eliom_service.create
~path:(Eliom_service.Path ["demo-users"])
~meth:(Eliom_service.Get Eliom_parameter.unit) ()
(* Make service available on the client *)
let%client service = ~%service
(* Name for demo menu *)
let%shared name () = [%i18n Demo.S.users]
(* Class for the page containing this demo (for internal use) *)
let%shared page_class = "os-page-demo-users"
let%shared display_user_name = function
| None -> p [%i18n Demo.you_are_not_connected]
| Some user ->
p
[ txt ([%i18n Demo.S.you_are] ^ " ")
; em [txt (Os_user.fullname_of_user user)] ]
let%shared display_user_id = function
| None -> p [%i18n Demo.log_in_to_see_demo]
| Some userid ->
p
[ txt ([%i18n Demo.S.your_user_id] ^ " ")
; em [txt (Int64.to_string userid)] ]
(* Page for this demo *)
let%shared page () =
(* We use the convention to use "myid" for the user id of currently
connected user, and "userid" for all other user id.
We recommend to follow this convention, to reduce the risk
of mistaking an user for another.
We use prefix "_o" for optional value.
*)
let myid_o = Os_current_user.Opt.get_current_userid () in
let me_o = Os_current_user.Opt.get_current_user () in
Lwt.return
[ h1 [%i18n Demo.users]
; p
[ txt [%i18n Demo.S.the_module]
; code [txt " Os_current_user "]
; txt [%i18n Demo.S.allows_get_information_currently_connected_user] ]
; display_user_name me_o
; display_user_id myid_o
; p [txt [%i18n Demo.S.these_functions_called_server_or_client_side]]
; p
[ txt [%i18n Demo.S.always_get_current_user_using_module]
; code [txt " Os_current_user. "]
; txt [%i18n Demo.S.never_trust_client_pending_user_id] ] ]

View file

View file

@ -0,0 +1,5 @@
{
"release": "%%DATE%%",
"content_url": "%%APPSERVER%%%%APPPATH%%/update/%%DATE%%",
"update": "now"
}

View file

@ -0,0 +1,165 @@
<?xml version='1.0' encoding='utf-8'?>
<widget id="%%APPID%%" version="%%MOBILE_APP_VERSION%%" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
%%MOBILE_USE_CLEARTEXT_TRAFFIC%%
<name>%%MOBILE_APP_NAME%%</name>
<description>
%%MOBILE_DESCRIPTION%%
</description>
<author email="%%MOBILE_AUTHOR_EMAIL%%" href="%%MOBILE_AUTHOR_HREF%%">
%%MOBILE_AUTHOR_DESCRIPTION%%
</author>
<content src="index.html" />
<plugin name="cordova-plugin-whitelist" spec="1" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<!-- Android settings -->
<platform name="android">
<allow-intent href="market:*" />
<preference name="android-minSdkVersion" value="%%MOBILE_ANDROID_SDK_VERSION%%" />
<icon src="res/android/icon-36.png" density="ldpi" />
<icon src="res/android/icon-48.png" density="mdpi" />
<icon src="res/android/icon-72.png" density="hdpi" />
<icon src="res/android/icon-96.png" density="xhdpi" />
<icon src="res/android/icon-144.png" density="xxhdpi" />
<icon src="res/android/icon-192.png" density="xxxhdpi" />
</platform>
<!-- iOS settings -->
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
<!-- Workaround for a bug in Cordova that prevents iframes with external URLs -->
<allow-navigation href="*" />
<!-- iOS 8.0+ -->
<!-- iPhone 6 Plus -->
<icon src="res/ios/icon-60@3x.png" width="180" height="180" />
<!-- iOS 7.0+ -->
<!-- iPhone / iPod Touch -->
<icon src="res/ios/icon-60.png" width="60" height="60" />
<icon src="res/ios/icon-60@2x.png" width="120" height="120" />
<!-- iPad -->
<icon src="res/ios/icon-76.png" width="76" height="76" />
<icon src="res/ios/icon-76@2x.png" width="152" height="152" />
<!-- iOS 6.1 -->
<!-- Spotlight Icon -->
<icon src="res/ios/icon-40.png" width="40" height="40" />
<icon src="res/ios/icon-40@2x.png" width="80" height="80" />
<!-- iPhone / iPod Touch -->
<icon src="res/ios/icon.png" width="57" height="57" />
<icon src="res/ios/icon@2x.png" width="114" height="114" />
<!-- iPad -->
<icon src="res/ios/icon-72.png" width="72" height="72" />
<icon src="res/ios/icon-72@2x.png" width="144" height="144" />
<icon src="res/ios/icon-83.5@2x.png" width="167" height="167" />
<!-- iPhone Spotlight and Settings Icon -->
<icon src="res/ios/icon-small.png" width="29" height="29" />
<icon src="res/ios/icon-small@2x.png" width="58" height="58" />
<icon src="res/ios/icon-small@3x.png" width="87" height="87" />
<!-- iPad Spotlight and Settings Icon -->
<icon src="res/ios/icon-50.png" width="50" height="50" />
<icon src="res/ios/icon-50@2x.png" width="100" height="100" />
<!-- <preference name="DisallowOverscroll" value="true" /> -->
<!-- <preference name="WKWebViewDecelerationSpeed" value="normal" /> -->
</platform>
<!-- Plugins -->
<plugin name="cordova-plugin-whitelist" spec="1" />
<!-- <allow-navigation href="*" /> --><!-- this seems to be necessary for outside resources, e.g., outside videos in iframes -->
<!-- The CrossWalk plugin bundles a browser runtime. Enable this
if you are facing trouble with old versions of Android (4.x
and older). You may also want to enable the CrossWalk
settings as they appear later in this file. -->
<!-- <plugin name="cordova-plugin-crosswalk-webview" spec="~1.6.0" /> -->
<plugin name="cordova-hot-code-push-plugin" spec="~1.5.3" />
<!-- <plugin name="cordova-plugin-facebook4" spec="~1.7.0"> -->
<!-- <variable name="APP_ID" value="%%FACEBOOK_ID%%" /> -->
<!-- <variable name="APP_NAME" value="%%MOBILE_APP_NAME%%" /> -->
<!-- </plugin> -->
<plugin name="cordova-plugin-network-information" spec="~1.2.0" />
<plugin name="cordova-plugin-globalization" spec="~1.0.3" />
<!-- BEWARE OF ALL PLUGINS IN DEV VERSION: THEY ARE, BY DEFINITION, "UNSTABLE"!
ALSO, IF YOU START A NEW PROJECT, YOU MIGHT WANT TO CHECK FOR THE LATEST VERSIONS OF NON-DEV VERSIONS! -->
<plugin name="cordova-plugin-wkwebview-engine" spec="https://github.com/apache/cordova-plugin-wkwebview-engine.git" />
<!-- Status bar colors and other adjustments -->
<plugin name="cordova-plugin-statusbar" spec="https://github.com/apache/cordova-plugin-statusbar" />
<!-- use this alternative if you need tap on status bar to scroll up the page:
<plugin name="cordova-plugin-statusbar" spec="https://github.com/besport/cordova-plugin-statusbar" /> -->
<platform name="ios">
<preference name="StatusBarBackgroundColor" value="#2255aa" />
</platform>
<platform name="android">
<preference name="StatusBarBackgroundColor" value="#194899" />
</platform>
<preference name="StatusBarStyle" value="default" />
<preference name="StatusBarOverlaysWebView" value="false" />
<!-- To be able to reduce the webview instead of having the keyboard push it up -->
<plugin name="cordova-plugin-keyboard" spec="~1.1.4" />
<preference name="KeyboardDisplayRequiresUserAction" value="false" />
<preference name="KeyboardShrinksView" value="false" />
<!-- Let app handle URLs corresponding to our host -->
<!-- Temporarily depend on non official version of cordova-universal-links-plugin because it fixes a bug ... cf https://github.com/nordnet/cordova-universal-links-plugin/issues/133 -->
<plugin name="cordova-universal-links-plugin" spec="https://github.com/besport/cordova-universal-links-plugin.git" />
<universal-links>
<host name="%%APPHOST%%" scheme="%%APPSCHEME%%" %%APPPORTARG%% />
</universal-links>
<preference name="AndroidLaunchMode" value="singleInstance" />
<!-- For geolocation: -->
<!-- This plugin (or another one if you have one) is necessary if you need geolocation in your app: -->
<!-- <plugin name="cordova-plugin-geolocation" spec="https://github.com/apache/cordova-plugin-geolocation.git" /> -->
<!-- It might be helpful to know there are bindings for OCaml: https://github.com/dannywillems/ocaml-cordova-plugin-geolocation -->
<!-- If you have trouble with annoying popups on iOS regarding the usage of geolocation features,
do consider calling the function directly:
```
cordova##exec
(Js.wrap_callback f_success_cordova)
(Js.wrap_callback f_error)
(Js.string "Geolocation")
(Js.string "getLocation")
```
because webkit's version might take over otherwise.
-->
<!-- CrossWalk settings -->
<!-- <preference name="xwalkVersion" value="15+" /> -->
<!-- <preference name="xwalkCommandLine" value="&#45;&#45;disable&#45;pull&#45;to&#45;refresh&#45;effect" /> -->
<!-- <preference name="xwalkMode" value="embedded" /> -->
<!-- <preference name="xwalkMultipleApk" value="false" /> -->
<!-- BackgroundColor of the app (rarely visible).
However if it doesn't do what you expect, make sure you're not simply having trouble with ARGB vs RGBA. -->
<!-- <preference name="BackgroundColor" value="0x00000000" /> -->
<!-- Hot Code Push settings -->
<chcp>
<config-file url="%%APPSERVER%%%%APPPATH%%/update/conf/chcp.json"/>
</chcp>
<!-- Phonegap plugin push notification settings -->
<!-- Uncomment the following lines if you want to use push notifications -->
<!--
<plugin name="phonegap-plugin-push" spec="1.8.2" source="npm">
<variable name="SENDER_ID" value="%%MOBILE_NOTIFICATIONS_SENDER_ID%%" />
</plugin>
-->
<!-- If you want to allow videos to play inline:
<preference name="AllowInlineMediaPlayback" value="true" />
-->
</widget>

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html class="os-client-app">
<head>
<title>%%PROJECTNAME%%</title>
<meta http-equiv="Content-Security-Policy"
content="default-src 'self' data: gap: https://ssl.gstatic.com
%%APPSERVER%%;
script-src 'self'
https://ssl.gstatic.com
'unsafe-eval'
'unsafe-inline';
style-src 'self' https://fonts.googleapis.com
'unsafe-inline';
font-src 'self' https://fonts.gstatic.com;
connect-src *;
img-src * data:; frame-src *;">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1,
maximum-scale=1, minimum-scale=1,
width=device-width">
<link rel="stylesheet" type="text/css" href="css/%%PROJECTNAME%%.css">
<script>
//<![CDATA[
var __eliom_server = '%%APPSERVER%%%%APPPATH%%';
var __eliom_path = '%%APPPATH%%';
var __eliom_app_name = '%%APPNAME%%';
// var __eliom_debug_mode = true;
var __css_name = '%%PROJECTNAME%%.css';
// var __eliom_use_cookie_substitutes = true; // activate if you need cookies and you're using iOS WkWebView
//]]>
</script>
<script src="cordova.js"></script>
<script defer src="%%PROJECTNAME%%.js"></script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,200 @@
(* This file was generated by Ocsigen-start.
Feel free to use it, modify it, and redistribute it as you wish. *)
(* Load Eliom client-side program after storing global data in
localStorage. Compile as follos:
ocamlfind ocamlc \
-package js_of_ocaml,js_of_ocaml.ppx,lwt_ppx \
-linkpkg -o eliom_loader.byte \
eliom_loader.ml
js_of_ocaml eliom_loader.byte
*)
module XmlHttpRequest = Js_of_ocaml_lwt.XmlHttpRequest
(* Debug mode. Set to true if you want to use the debug mode. Used by "log".
*)
let debug = false
(* If debug mode is activated, a paragraph is created and a message is printed
* in the console.
*)
let log =
if debug
then (fun s ->
Js_of_ocaml.Firebug.console##log (Js_of_ocaml.Js.string s);
let p = Js_of_ocaml.Dom_html.createP Js_of_ocaml.Dom_html.document in
p##.style##.color := Js_of_ocaml.Js.string "#64b5f6";
Js_of_ocaml.Dom.appendChild p
(Js_of_ocaml.Dom_html.document##createTextNode (Js_of_ocaml.Js.string s));
let container = Js_of_ocaml.Dom_html.getElementById "app-container" in
Js_of_ocaml.Dom.appendChild container p)
else fun s -> ()
(* Reference used by the binding to fetchUpdate to know if update has been done
* or if it failed.
*)
let update_failed = ref false
let data_upload_failed = ref false
(* Get the Eliom server URL where updates must be fetched. *)
let url =
Js_of_ocaml.Js.Optdef.case
Js_of_ocaml.Js.Unsafe.global##.___eliom_server_
(fun () -> "127.0.0.1:8080/__global_data__")
(fun server -> Js_of_ocaml.Js.to_string server ^ "/__global_data__")
(* Get the local storage object. Fail if local storage is not supported. *)
let storage () =
Js_of_ocaml.Js.Optdef.case
Js_of_ocaml.Dom_html.window##.localStorage
(fun () -> failwith "Browser storage not supported")
(fun v -> v)
(* This function is called when updating the files. It changes the class of the
* main container in index.html and add a button with an error message if
* something went wrong.
*)
let rec add_retry_button wake msg =
let container = Js_of_ocaml.Dom_html.getElementById "app-container" in
let p = Js_of_ocaml.Dom_html.createP Js_of_ocaml.Dom_html.document in
let btn = Js_of_ocaml.Dom_html.createButton Js_of_ocaml.Dom_html.document in
(* Set error class *)
container##.className := Js_of_ocaml.Js.string "app-error";
(* Error message paragraph *)
Js_of_ocaml.Dom.appendChild p
(Js_of_ocaml.Dom_html.document##createTextNode (Js_of_ocaml.Js.string msg));
p##.id := Js_of_ocaml.Js.string "retry-message";
(* Retry button *)
Js_of_ocaml.Dom.appendChild btn
(Js_of_ocaml.Dom_html.document##createTextNode
(Js_of_ocaml.Js.string "Retry"));
btn##.onclick :=
Js_of_ocaml.Dom_html.handler (fun _ ->
Js_of_ocaml.Dom.removeChild container p;
container##.className := Js_of_ocaml.Js.string "app blink";
if !update_failed
then (
update_failed := false;
ignore Js_of_ocaml.Js.Unsafe.global##.chcp##fetchUpdate);
if !data_upload_failed
then (
data_upload_failed := false;
Lwt.async (fun () -> get_data wake));
Js_of_ocaml.Js._false);
btn##.id := Js_of_ocaml.Js.string "retry-button";
Js_of_ocaml.Dom.appendChild p btn;
Js_of_ocaml.Dom.appendChild container p
and get_data wake =
let%lwt {XmlHttpRequest.content; code} = XmlHttpRequest.get url in
if code = 200
then (
log "Got global data";
(storage ())##setItem
(Js_of_ocaml.Js.string "__global_data")
(Js_of_ocaml.Js.string content);
Lwt.wakeup wake ())
else (
log "Could not get global data";
if not (!update_failed || !data_upload_failed)
then (
data_upload_failed := true;
add_retry_button wake
"Cannot connect to the server. Please make sure that this app has access to a data connection."));
Lwt.return_unit
(* Get the URL saved in the JavaScript variables "___eliom_html_url_" defined in
* index.html and go this location.
*)
let redirect () =
Js_of_ocaml.Js.Optdef.iter Js_of_ocaml.Js.Unsafe.global##.___eliom_html_url_
(fun url -> Js_of_ocaml.Dom_html.window##.location##replace url)
let _ =
(* CHCP does not run in the background, so we check for updates on resume *)
ignore
@@ Js_of_ocaml.Dom.addEventListener Js_of_ocaml.Dom_html.document
(Js_of_ocaml.Dom_html.Event.make "resume")
(Js_of_ocaml.Dom.handler (fun _ ->
log "Resume";
ignore Js_of_ocaml.Js.Unsafe.global##.chcp##fetchUpdate;
Js_of_ocaml.Js._true))
Js_of_ocaml.Js._false;
(* Create two threads for success callbacks and error callbacks. *)
let wait_success, wake_success = Lwt.wait () in
let wait_error, wake_error = Lwt.wait () in
(* Callback when success.
* [callback ev] will print the event if debug mode is activated.
* Calls by the event chcp_nothingToUpdate.
*)
let callback ev =
Js_of_ocaml.Dom.handler (fun _ ->
log ev;
update_failed := false;
Lwt.wakeup wake_success ();
Js_of_ocaml.Js._true)
in
(* Callback when errors.
* Calls by the event chcp_nothingToUpdate.
*)
let error_callback name =
Js_of_ocaml.Dom.handler (fun ev ->
log
(name ^ ": "
^ Js_of_ocaml.Js.to_string ev##.detail##.error##.description);
update_failed := true;
if not !data_upload_failed
then
add_retry_button wake_error
(Js_of_ocaml.Js.to_string ev##.detail##.error##.description
^ ". Please try again later.");
Js_of_ocaml.Js.bool true)
in
(* Callback to print a message *)
let status_callback name =
Js_of_ocaml.Dom.handler (fun ev -> log name; Js_of_ocaml.Js.bool true)
in
(* Binding to chcp_nothingToUpdate. Calls [callback ev]. *)
List.iter
(fun ev ->
ignore
@@ Js_of_ocaml.Dom.addEventListener Js_of_ocaml.Dom_html.document
(Js_of_ocaml.Dom_html.Event.make ev)
(callback ev) Js_of_ocaml.Js._false)
["chcp_nothingToUpdate"];
(* Binding to chcp_updateLoadFailed, chcp_updateInstallFailed and
* chcp_assetsInstallationError. It calls [error_callback ev].
*)
List.iter
(fun ev ->
ignore
@@ Js_of_ocaml.Dom.addEventListener Js_of_ocaml.Dom_html.document
(Js_of_ocaml.Dom_html.Event.make ev)
(error_callback ev) Js_of_ocaml.Js._false)
[ "chcp_updateLoadFailed"
; "chcp_updateInstallFailed"
; "chcp_assetsInstallationError" ];
(* Binding to other chcp events. It calls [status_callback ev] which will only
* print the event.
*)
List.iter
(fun ev ->
ignore
@@ Js_of_ocaml.Dom.addEventListener Js_of_ocaml.Dom_html.document
(Js_of_ocaml.Dom_html.Event.make ev)
(status_callback ev) Js_of_ocaml.Js._false)
[ "chcp_updateIsReadyToInstall"
; "chcp_beforeInstall"
; "chcp_nothingToInstall"
; "chcp_updateInstalled"
; "chcp_beforeAssetsInstalledOnExternalStorage"
; "chcp_assetsInstalledOnExternalStorage" ];
Lwt.async @@ fun () ->
let%lwt _ = Js_of_ocaml_lwt.Lwt_js_events.onload () in
let%lwt _ = get_data wake_error in
let%lwt _ = wait_error in
let%lwt _ = wait_success in
Lwt.return (redirect ())

View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<!--
Customize this policy to fit your own app's needs. For more guidance, see:
https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
Some notes:
* gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
* https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
-->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self' data: gap: https://ssl.gstatic.com
%%APPSERVER%%
'unsafe-eval' 'unsafe-inline'">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>%%MOBILE_APP_NAME%%</title>
<script>
//<![CDATA[
var __eliom_server = '%%APPSERVER%%%%APPPATH%%';
var __eliom_html_url = 'eliom.html';
var __eliom_app_name = '%%APPNAME%%';
// var __eliom_use_cookie_substitutes = true; // activate if you need cookies and you're using iOS WkWebView
//]]>
</script>
</head>
<body>
<div class="app blink" id="app-container">
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="eliom_loader.js"></script>
</body>
</html>

1
daegsrv/mobile/res/.gitignore vendored Normal file
View file

@ -0,0 +1 @@

View file

@ -0,0 +1,96 @@
/**
* This file is used in index.html when the mobile application starts.
* - *app* and *blink* classes defines the style for the app loader image.
* - *retry-button* and *app-error* is used when an error occurs while updating
* the application. If an error occurs, *app-error* will replace *app*.
* - the logo (saved in img/logo.png) is set as background of the DOM elements
* which contains *app* or *app-error* class.
*
* See eliom_loader.ml and index.html for more information.
*/
/* ------------------------------------------------------------- */
/* Defines rules for the animation. It is used by *blink* class. */
@keyframes fade {
from { opacity: 1.0; }
50% { opacity: 0.4; }
to { opacity: 1.0; }
}
@-webkit-keyframes fade {
from { opacity: 1.0; }
50% { opacity: 0.4; }
to { opacity: 1.0; }
}
* {
-webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
}
body {
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
background-color:#FFFFFF;
font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
height:100%;
margin:0px;
padding:0px;
width:100%;
}
/* --------------------- */
/* app and error classes */
/* Portrait layout (default) */
.app, /* text area height */
.app-error{
background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
position:absolute; /* position in the center of the screen */
left:50%;
top:50%;
height:50px; /* text area height */
width:225px; /* text area width */
text-align:center;
padding:180px 0px 0px 0px; /* image height is 200px (bottom 20px are overlapped with text) */
margin:-115px 0px 0px -112px; /* offset vertical: half of image height and text area height */
/* offset horizontal: half of text area width */
font-size: 16px;
}
/* Landscape layout (with min-width) */
@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
.app {
padding:75px 0px 75px 170px; /* padding-top + padding-bottom + text area = image height */
margin:-90px 0px 0px -198px; /* offset vertical: half of image height */
}
.app-error {
padding:40px 0px 75px 420px; /* padding-top + padding-bottom + text area = image height */
margin:-90px 0px 0px -450px; /* offset vertical: half of image height */
/* offset horizontal: half of image width and text area width */
}
}
/* -------------------------------- */
/* This class is the fade animation */
.blink {
animation:fade 3000ms infinite;
-webkit-animation:fade 3000ms infinite;
}
/* --------------------------------------------------------------- */
/* Style for the retry button when an error occurs while updating */
#retry-button {
width: 100%;
display: block;
margin-top: 1.8rem;
margin-bottom: 1.8rem;
background-color: #64b5f6;
color: white;
height: 35px;
border: none;
font-size: 16px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

14
daegsrv/sass/daegsrv.scss Normal file
View file

@ -0,0 +1,14 @@
@import "font-awesome.min.css";
@import "ot_buttons.css";
@import "ot_carousel.css";
@import "ot_sticky.css";
@import "ot_datetime.css";
@import "ot_drawer.css";
@import "ot_icons.css";
@import "ot_picture_uploader.css";
@import "ot_popup.css";
@import "ot_spinner.css";
@import "ot_page_transition.css";
@import "ot_tongue.css";
@import "os";
@import "demo";

178
daegsrv/sass/demo.scss Normal file
View file

@ -0,0 +1,178 @@
/* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish.
*/
.demo-carousel1 {
display: flex;
justify-content: center;
.demo-carousel1-box {
position: relative;
margin: auto;
}
.ot-carousel {
width: 300px;
height: 200px;
}
.demo-carousel1-page {
width: 100%;
height: 100%;
padding: 16px;
color: white;
}
.demo-carousel1-page-1 {
background-color: #49b2cc;
}
.demo-carousel1-page-2 {
background-color: #dddd55;
}
.demo-carousel1-page-3 {
background-color: #c14768;
}
.demo-carousel1-page-4 {
background-color: #45bf7d;
}
.ot-bullet-nav {
position: absolute;
bottom: 16px;
right: 16px;
margin: 0;
}
.ot-car-prev,
.ot-car-next {
position: absolute;
top: 75px;
width: 50px;
height: 50px;
color: white;
outline: none;
}
.ot-car-prev {
left: 0;
}
.ot-car-next {
right: 0;
}
}
.os-page-demo-carousel2 {
.demo-carousel2 {
margin: 0 -16px;
}
.ot-carousel {
width: 100%;
height: auto;
}
.demo-carousel2-page {
padding: 16px;
p {
text-align: justify;
}
}
.demo-carousel2-page-1 {
background-color: white;
}
.demo-carousel2-page-2 {
background-color: #ffffee;
}
.demo-carousel2-page-3 {
background-color: #ffddee;
}
.demo-carousel2-page-4 {
background-color: #ddffee;
}
.demo-carousel2-tabs {
position: sticky;
background-color: white;
z-index: 1;
top: 50px;
}
}
.os-page-demo-carousel3 {
.demo-prev,
.demo-next {
width: 20px;
height: 20px;
background-color: #6ae;
color: white;
}
}
.os-page-demo-notif,
.os-page-demo-react {
input:not([type]) {
background-color: #eee;
}
}
.os-page-demo-links .demo-static-img {
width: 300px;
}
.os-page-demo-transition {
.demo-list {
padding-left: 0;
list-style-type: none;
}
.demo-list-item {
width: 100%;
height: 150px;
font-size: 20px;
padding-top: 65px;
text-align: center;
}
.demo-list-item > a:visited,
.demo-list-item > a {
color: white;
}
.demo-list-item.demo-list-item-0 {
background-color: #b1eb00;
}
.demo-list-item.demo-list-item-1 {
background-color: #53baf3;
}
.demo-list-item.demo-list-item-2 {
background-color: #ff85cb;
}
.demo-list-item.demo-list-item-3 {
background-color: #f4402c;
}
.demo-list-item.demo-list-item-4 {
background-color: #ffac00;
}
.demo-button {
margin: 0 auto;
padding: 20px;
width: 200px;
font-size: 20px;
text-align: center;
background-color: #ffffee;
}
}
.demo-tongue {
display: flex;
flex-direction: column;
.demo-tongue-1 {
background-color: #ffffee;
height: 100px;
}
.demo-tongue-2 {
background-color: #ffddee;
height: 150px;
}
.demo-tongue-3 {
background-color: #ddffee;
height: 80px;
}
.demo-tongue-4 {
background-color: #ddeeff;
height: 200px;
}
.demo-tongue-5 {
background-color: #eeccff;
height: 130px;
}
.demo-tongue-6 {
background-color: #ffeeaa;
height: 300px;
}
}

335
daegsrv/sass/os.scss Normal file
View file

@ -0,0 +1,335 @@
/* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish.
*/
/* Default CSS for Ocsigen Start */
@import url('https://fonts.googleapis.com/css?family=Quicksand');
$main-color: #25a;
$dark-color: #445;
$breakpoint1: 720px;
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
body {
margin: 0;
font-size: 14px;
background-color: white;
}
body, input, textarea, keygen, select, button {
font-family: 'Quicksand', sans-serif;
}
a, a:visited {
text-decoration: none;
color: #666;
cursor: pointer;
}
button, input {
border: none;
}
button, input[type="submit"], input[type="button"] {
cursor: pointer;
}
.button {
border-radius: 2px;
box-shadow: 0 2px 2px rgba(0,0,0,.28);
background-color: white;
color: #666;
text-transform: uppercase;
font-size: 14px;
padding: 5px 16px;
margin: 5px;
}
input {
padding: 5px;
border-radius: 2px;
}
.connected-user-box {
display: flex;
align-items: center;
.fa-user {
height: 50px;
width: 50px;
font-size: 22px;
&::before {
line-height: 50px;
}
}
}
.os-page-header {
position: fixed;
top: 0;
z-index: 1;
height: 50px;
width: 100%;
background-color: $main-color;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
box-shadow: 0 4px 8px rgba(0,0,0,.28);
padding: 0 20px 0 50px;
.connected-user-box {
color: white;
}
}
a.os-page-header-app-name {
text-decoration: none;
color: white;
font-size: 30px;
}
.ot-dr-toggle-button {
color: white;
}
.os-drawer-menu {
padding: 0;
margin: 0;
overflow-y: auto;
height: 100%;
touch-action: pan-y;
&, ul {
list-style-type: none;
}
.os-drawer-item {
display: block;
padding: 16px;
}
a:hover {
background-color: $dark-color;
color: white;
}
.connected-user-box {
border-bottom: 1px solid #ddd;
}
}
.os-drawer-submenu {
padding: 0;
> li > a {
display: block;
padding: 5px 5px 5px 50px;
}
}
.os-body {
padding: 66px 16px;
}
.os-page-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ddd;
padding: 16px;
font-size: 10px;
text-align: center;
}
.os-welcome-box, .os-settings, .os-sign-in, .os-sign-up, .os-forgot-pwd {
h2 {
font-size: 14px;
margin: 16px 0 4px 0;
}
form, .form-like {
display: flex;
margin: 0 auto;
flex-direction: column;
background-color: #eee;
padding: 16px;
width: 350px;
}
input[type="checkbox"] {
vertical-align: middle;
}
input[type="submit"], button {
margin: 5px auto;
}
input[type="text"], input[type="password"],
input[type="email"], input[type="tel"] {
width: 150px;
display: block;
margin: 5px auto;
}
label {
font-size: 12px;
}
}
.os-sign-up-btn {
background-color: #fb8282;
color: white;
}
.os-forgot-pwd-link {
font-size: 12px;
}
#os-msg {
position: fixed;
top: 100px;
left: 0;
right: 0;
z-index: 100;
p {
padding: 50px 0;
text-align: center;
background-color: rgba(200,220,240,0.9);
&.os-err {
background-color: rgba(200,0,0,0.7);
color: white;
}
}
}
.os-page-main .os-page-header .os-connection-box {
position: absolute;
top: 15px;
right: 15px;
}
.os-page-main:not(.os-connected) {
.os-page-header {
display: block;
height: 40vh;
.os-page-header-app-name {
display: block;
position: absolute;
top: 30vh;
}
}
.os-body {
margin-top: 40vh;
}
}
@media (max-width: $breakpoint1) {
.os-page-footer { display: none; }
.os-page-header {
.os-connection-box, .os-page-header-app-name, .connected-user-box {
display: none;
}
}
.os-page-main {
.os-page-header .os-connection-box {
display: flex;
flex-direction: column;
position: absolute;
top: 45vh;
left: 0;
width: 100%;
.button {
width: 150px;
margin: 16px auto;
}
}
&.os-not-connected .os-body {
display: none;
}
}
.os-page-header { display: block; }
}
.os-settings p {
text-align: center;
}
.os-emails {
text-align: left;
display: flex;
justify-content: center;
ul {
list-style: none;
li {
padding: 5px 10px;
border-bottom: 1px solid #e5e5e5;
display: flex;
justify-content: space-between;
align-items: center;
.os-settings-label {
font-weight: bold;
padding: 4px 10px;
margin-left: 4px;
border-radius: 3px;
}
.os-settings-email {
flex-grow: 2;
padding-right: 20px;
}
.os-main-email {
color: #fff;
background-color: #6cc664;
}
.os-validated-email {
color: #fff;
background-color: #25a;
}
.os-remove-email-button {
box-shadow: none;
font-size: 18px;
margin: 0px;
color: #bd2c00;
}
}
}
}
.os-avatar {
margin-right: 15px;
width: 35px;
}
.ot-drawer {
display: flex;
flex-direction: column;
.connected-user-box {
flex-shrink: 0;
}
}
.ot-dr-left .os-avatar {
margin-left: 16px;
}
/* Tips */
div.os-tip {
position: absolute;
margin: 20px;
box-shadow: 0px 0px 2px 2px rgba(0, 0, 0, 0.2);
z-index: 1111;
transition: opacity 0.5s;
div.os-tip-bec {
background-color: white;
width: 20px;
height: 20px;
position: absolute;
transform: rotate(45deg);
box-shadow: 0px 0px 2px 2px rgba(0, 0, 0, 0.2);
z-index: -1;
}
div.os-tip-content {
padding: 30px 20px 30px 20px;
background-color: white;
height: 100%;
width: 100%;
}
.os-tip-close {
position: absolute;
top: 5px;
right: 5px;
}
li,
p {
padding-bottom: 10px;
}
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,122 @@
/* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish.
*/
.demo-carousel1 {
display: flex;
justify-content: center; }
.demo-carousel1 .demo-carousel1-box {
position: relative;
margin: auto; }
.demo-carousel1 .ot-carousel {
width: 300px;
height: 200px; }
.demo-carousel1 .demo-carousel1-page {
width: 100%;
height: 100%;
padding: 16px;
color: white; }
.demo-carousel1 .demo-carousel1-page-1 {
background-color: #49b2cc; }
.demo-carousel1 .demo-carousel1-page-2 {
background-color: #dddd55; }
.demo-carousel1 .demo-carousel1-page-3 {
background-color: #c14768; }
.demo-carousel1 .demo-carousel1-page-4 {
background-color: #45bf7d; }
.demo-carousel1 .ot-bullet-nav {
position: absolute;
bottom: 16px;
right: 16px;
margin: 0; }
.demo-carousel1 .ot-car-prev, .demo-carousel1 .ot-car-next {
position: absolute;
top: 75px;
width: 50px;
height: 50px;
color: white;
outline: none; }
.demo-carousel1 .ot-car-prev {
left: 0; }
.demo-carousel1 .ot-car-next {
right: 0; }
.os-page-demo-carousel2 .demo-carousel2 {
margin: 0 -16px; }
.os-page-demo-carousel2 .ot-carousel {
width: 100%;
height: auto; }
.os-page-demo-carousel2 .demo-carousel2-page {
padding: 16px; }
.os-page-demo-carousel2 .demo-carousel2-page p {
text-align: justify; }
.os-page-demo-carousel2 .demo-carousel2-page-1 {
background-color: white; }
.os-page-demo-carousel2 .demo-carousel2-page-2 {
background-color: #ffffee; }
.os-page-demo-carousel2 .demo-carousel2-page-3 {
background-color: #ffddee; }
.os-page-demo-carousel2 .demo-carousel2-page-4 {
background-color: #ddffee; }
.os-page-demo-carousel2 .demo-carousel2-tabs {
position: sticky;
background-color: white;
z-index: 1;
top: 50px; }
.os-page-demo-carousel3 .demo-prev, .os-page-demo-carousel3 .demo-next {
width: 20px;
height: 20px;
background-color: #6ae;
color: white; }
.os-page-demo-notif input:not([type]),
.os-page-demo-react input:not([type]) {
background-color: #eee; }
.os-page-demo-links .demo-static-img {
width: 300px; }
.os-page-demo-transition .demo-list{
padding-left: 0;
list-style-type: none;
}
.os-page-demo-transition .demo-list-item {
width: 100%;
height: 150px;
font-size: 20px;
padding-top: 65px;
text-align: center;
}
.os-page-demo-transition .demo-list-item>a:visited,
.os-page-demo-transition .demo-list-item>a {
color: white;
}
.os-page-demo-transition .demo-list-item.demo-list-item-0 {
background-color: #B1EB00
}
.os-page-demo-transition .demo-list-item.demo-list-item-1 {
background-color: #53BAF3
}
.os-page-demo-transition .demo-list-item.demo-list-item-2 {
background-color: #FF85CB
}
.os-page-demo-transition .demo-list-item.demo-list-item-3 {
background-color: #F4402C
}
.os-page-demo-transition .demo-list-item.demo-list-item-4 {
background-color: #FFAC00
}
.os-page-demo-transition .demo-button {
margin: 0 auto;
padding: 20px;
width: 200px;
font-size: 20px;
text-align: center;
background-color: #ffffee;
}
/*# sourceMappingURL=.os_temporary_filename.css.map */

View file

@ -0,0 +1,269 @@
/* This file was generated by Ocsigen Start.
Feel free to use it, modify it, and redistribute it as you wish.
*/
/* Default CSS for Ocsigen Start */
@import url("https://fonts.googleapis.com/css?family=Quicksand");
html {
box-sizing: border-box; }
*, *::before, *::after {
box-sizing: inherit; }
body {
margin: 0;
font-size: 14px;
background-color: white; }
body, input, textarea, keygen, select, button {
font-family: 'Quicksand', sans-serif; }
a, a:visited {
text-decoration: none;
color: #666;
cursor: pointer; }
button, input {
border: none; }
button, input[type="submit"], input[type="button"] {
cursor: pointer; }
.button {
border-radius: 2px;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.28);
background-color: white;
color: #666;
text-transform: uppercase;
font-size: 14px;
padding: 5px 16px;
margin: 5px; }
input {
padding: 5px;
border-radius: 2px; }
.connected-user-box {
display: flex;
align-items: center; }
.connected-user-box .fa-user {
height: 50px;
width: 50px;
font-size: 22px; }
.connected-user-box .fa-user::before {
line-height: 50px; }
.os-page-header {
position: fixed;
top: 0;
z-index: 1;
height: 50px;
width: 100%;
background-color: #25a;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.28);
padding: 0 20px 0 50px; }
.os-page-header .connected-user-box {
color: white; }
a.os-page-header-app-name {
text-decoration: none;
color: white;
font-size: 30px; }
.ot-dr-toggle-button {
color: white; }
.os-drawer-menu {
padding: 0;
margin: 0;
overflow-y: auto;
height: 100%;
touch-action: pan-y; }
.os-drawer-menu, .os-drawer-menu ul {
list-style-type: none; }
.os-drawer-menu .os-drawer-item {
display: block;
padding: 16px; }
.os-drawer-menu a:hover {
background-color: #445;
color: white; }
.os-drawer-menu .connected-user-box {
border-bottom: 1px solid #ddd; }
.os-drawer-submenu {
padding: 0; }
.os-drawer-submenu > li > a {
display: block;
padding: 5px 5px 5px 50px; }
.os-body {
padding: 66px 16px; }
.os-page-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ddd;
padding: 16px;
font-size: 10px;
text-align: center; }
.os-welcome-box h2, .os-settings h2, .os-sign-in h2, .os-sign-up h2, .os-forgot-pwd h2 {
font-size: 14px;
margin: 16px 0 4px 0; }
.os-welcome-box form, .os-settings form, .os-sign-in form, .os-sign-up form, .os-forgot-pwd form {
display: flex;
margin: 0 auto;
flex-direction: column;
background-color: #eee;
padding: 16px;
width: 350px; }
.os-welcome-box input[type="checkbox"], .os-settings input[type="checkbox"], .os-sign-in input[type="checkbox"], .os-sign-up input[type="checkbox"], .os-forgot-pwd input[type="checkbox"] {
vertical-align: middle; }
.os-welcome-box input[type="submit"], .os-settings input[type="submit"], .os-sign-in input[type="submit"], .os-sign-up input[type="submit"], .os-forgot-pwd input[type="submit"] {
margin: 5px auto; }
.os-welcome-box input[type="text"], .os-welcome-box input[type="password"], .os-welcome-box input[type="email"], .os-settings input[type="text"], .os-settings input[type="password"], .os-settings input[type="email"], .os-sign-in input[type="text"], .os-sign-in input[type="password"], .os-sign-in input[type="email"], .os-sign-up input[type="text"], .os-sign-up input[type="password"], .os-sign-up input[type="email"], .os-forgot-pwd input[type="text"], .os-forgot-pwd input[type="password"], .os-forgot-pwd input[type="email"] {
width: 150px;
display: block;
margin: 5px auto; }
.os-welcome-box label, .os-settings label, .os-sign-in label, .os-sign-up label, .os-forgot-pwd label {
font-size: 12px; }
.os-sign-up-btn {
background-color: #fb8282;
color: white; }
.os-forgot-pwd-link {
font-size: 12px; }
#os-msg {
position: fixed;
top: 100px;
left: 0;
right: 0;
z-index: 100; }
#os-msg p {
padding: 50px 0;
text-align: center;
background-color: rgba(200, 220, 240, 0.9); }
#os-msg p.os-err {
background-color: rgba(200, 0, 0, 0.7);
color: white; }
.os-page-main .os-page-header .os-connection-box {
position: absolute;
top: 15px;
right: 15px; }
.os-page-main:not(.os-connected) .os-page-header {
display: block;
height: 40vh; }
.os-page-main:not(.os-connected) .os-page-header .os-page-header-app-name {
display: block;
position: absolute;
top: 30vh; }
.os-page-main:not(.os-connected) .os-body {
margin-top: 40vh; }
@media (max-width: 720px) {
.os-page-footer {
display: none; }
.os-page-header .os-connection-box, .os-page-header .os-page-header-app-name, .os-page-header .connected-user-box {
display: none; }
.os-page-main .os-page-header .os-connection-box {
display: flex;
flex-direction: column;
position: absolute;
top: 45vh;
left: 0;
width: 100%; }
.os-page-main .os-page-header .os-connection-box .button {
width: 150px;
margin: 16px auto; }
.os-page-main.os-not-connected .os-body {
display: none; }
.os-page-header {
display: block; } }
.os-settings p {
text-align: center; }
.os-emails {
text-align: left;
display: flex;
justify-content: center; }
.os-emails ul {
list-style: none; }
.os-emails ul li {
padding: 5px 10px;
border-bottom: 1px solid #e5e5e5;
display: flex;
justify-content: space-between;
align-items: center; }
.os-emails ul li .os-settings-label {
font-weight: bold;
padding: 4px 10px;
margin-left: 4px;
border-radius: 3px; }
.os-emails ul li .os-settings-email {
flex-grow: 2;
padding-right: 20px; }
.os-emails ul li .os-main-email {
color: #fff;
background-color: #6cc664; }
.os-emails ul li .os-validated-email {
color: #fff;
background-color: #25a; }
.os-emails ul li .os-remove-email-button {
box-shadow: none;
font-size: 18px;
margin: 0px;
color: #bd2c00; }
.os-avatar {
margin-right: 15px;
width: 35px; }
.ot-drawer {
display: flex;
flex-direction: column; }
.ot-drawer .connected-user-box {
flex-shrink: 0; }
.ot-dr-left .os-avatar {
margin-left: 16px; }
/* Tips */
div.os-tip {
position: absolute;
margin: 20px;
box-shadow: 0px 0px 2px 2px rgba(0, 0, 0, 0.2);
z-index: 1111; }
div.os-tip div.os-tip-bec {
background-color: white;
width: 20px;
height: 20px;
position: absolute;
transform: rotate(45deg);
box-shadow: 0px 0px 2px 2px rgba(0, 0, 0, 0.2);
z-index: -1; }
div.os-tip div.os-tip-content {
padding: 30px 20px 30px 20px;
background-color: white;
height: 100%;
width: 100%; }
div.os-tip .os-tip-close {
position: absolute;
top: 5px;
right: 5px; }
div.os-tip li,
div.os-tip p {
padding-bottom: 10px; }
/*# sourceMappingURL=.os_temporary_filename.css.map */

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View file

@ -0,0 +1,73 @@
#load "str.cma"
let space_re = Str.regexp " +"
let edges = Hashtbl.create 128
let edge_count = Hashtbl.create 128
let chop s =
try
let i = String.rindex s '.' in
String.sub s 0 i
with Not_found -> s
let add_edge target dep =
if target <> dep
then (
Hashtbl.replace edges dep
(target :: (try Hashtbl.find edges dep with Not_found -> []));
Hashtbl.replace edge_count target
(1 + try Hashtbl.find edge_count target with Not_found -> 0);
if not (Hashtbl.mem edge_count dep) then Hashtbl.add edge_count dep 0)
let sort l =
let res = ref [] in
List.iter
(fun (target, deps) ->
let target = chop target in
if not (Hashtbl.mem edge_count target)
then Hashtbl.add edge_count target 0;
List.iter (fun dep -> add_edge target (chop dep)) deps)
l;
let q = Queue.create () in
Hashtbl.iter
(fun target count -> if count = 0 then Queue.add target q)
edge_count;
while not (Queue.is_empty q) do
let n = Queue.take q in
res := n :: !res;
let l = try Hashtbl.find edges n with Not_found -> [] in
Hashtbl.remove edges n;
List.iter
(fun target ->
let c = Hashtbl.find edge_count target - 1 in
Hashtbl.replace edge_count target c;
if c = 0 then Queue.add target q)
l
done;
if Hashtbl.length edges <> 0
then (
Format.eprintf "Dependency loop!@.";
exit 1);
List.rev !res
let _ =
let ch = open_in Sys.argv.(1) in
let lst = ref [] in
(try
while true do
let l = input_line ch in
let l = Str.split space_re l in
match l with
| target :: ":" :: deps -> lst := (target, deps) :: !lst
| _ -> assert false
done
with End_of_file -> ());
let lst = sort !lst in
let files = Hashtbl.create 128 in
for i = 2 to Array.length Sys.argv - 1 do
Hashtbl.add files (chop Sys.argv.(i)) Sys.argv.(i)
done;
List.iter
(fun f ->
try Format.printf "%s@." (Hashtbl.find files f) with Not_found -> ())
lst