From 930763e71e20c4423dc698d948de9b1ca1369e81 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 15 Sep 2014 14:46:34 -0300 Subject: [PATCH 001/386] Initial Import Rest on CMS. --- .gitignore | 2 + Readme.txt | 42 + api/launcher/any/application_launcher.e | 24 + api/launcher/any/application_launcher_i.e | 102 + api/launcher/default/application_launcher.e | 54 + api/launcher/default/application_launcher_i.e | 26 + api/roc.ini | 2 + api/roc_api.ecf | 82 + .../config/application_configuration.json | 32 + api/site/www/static/css/bootstrap.css | 5849 +++++++++++++++++ api/site/www/static/css/dashboard.css | 357 + api/site/www/static/images/ajax-loader.gif | Bin 0 -> 1456 bytes api/site/www/static/images/favicon.ico | Bin 0 -> 16446 bytes api/site/www/static/js/roc.js | 108 + api/site/www/template/html/layout2.tpl | 40 + .../www/template/html/master2/content.tpl | 11 + api/site/www/template/html/master2/error.tpl | 18 + api/site/www/template/html/master2/footer.tpl | 7 + api/site/www/template/html/master2/head.tpl | 10 + api/site/www/template/html/master2/header.tpl | 2 + api/site/www/template/html/master2/logoff.tpl | 5 + .../template/html/master2/main_navigation.tpl | 5 + .../html/master2/optional_enhancement_js.tpl | 5 + .../html/master2/optional_styling_css.tpl | 9 + .../template/html/master2/site_navigation.tpl | 25 + .../www/template/html/modules/navigation.tpl | 8 + api/site/www/template/html/modules/node.tpl | 174 + .../template/html/modules/node_content.tpl | 70 + .../template/html/modules/node_summary.tpl | 71 + .../www/template/html/modules/node_title.tpl | 70 + api/site/www/template/html/modules/nodes.tpl | 52 + .../www/template/html/modules/register.tpl | 100 + api/src/configuration/application_constants.e | 19 + api/src/configuration/configuration_factory.e | 70 + .../configuration/roc_json_configuration.e | 51 + .../representation/common/template_shared.e | 29 + api/src/representation/roc_response.e | 143 + api/src/representation/roc_template_page.e | 76 + api/src/roc_abstract_api.e | 119 + api/src/roc_config.e | 87 + api/src/roc_server.e | 216 + .../service/filter/authentication_filter.e | 56 + api/src/service/filter/cors_filter.e | 27 + api/src/service/filter/error_filter.e | 42 + api/src/service/filter/logger_filter.e | 54 + .../service/handler/app_abstract_handler.e | 48 + api/src/service/handler/app_handler.e | 98 + api/src/service/handler/navigation_handler.e | 57 + .../service/handler/node_content_handler.e | 189 + api/src/service/handler/node_handler.e | 221 + .../service/handler/node_summary_handler.e | 198 + api/src/service/handler/node_title_handler.e | 198 + api/src/service/handler/nodes_handler.e | 62 + api/src/service/handler/roc_login_handler.e | 63 + api/src/service/handler/roc_logoff_handler.e | 59 + api/src/service/handler/roc_root_handler.e | 63 + api/src/service/handler/user_handler.e | 163 + api/src/service/roc_api_service.e | 168 + api/src/service/roc_email_service.e | 127 + api/src/service/roc_rest_api.e | 185 + api/src/service/shared_conneg_helper.e | 48 + api/src/validator/roc_input_validator.e | 58 + layout/Readme.md | 13 + layout/layout.ecf | 21 + layout/src/application_layout.e | 80 + .../configuration/database_configuration.e | 39 + layout/src/configuration/json_configuration.e | 100 + .../src/configuration/logger_configuration.e | 80 + layout/src/error/error_handler.e | 51 + layout/src/error/shared_error.e | 105 + layout/src/logger/logging_facility.e | 137 + layout/src/logger/shared_logger.e | 109 + persistence/persistence.ecf | 27 + persistence/scripts/Readme.txt | 1 + persistence/scripts/create_database.sql | 105 + persistence/scripts/tables.sql | 14 + persistence/src/cms_storage.e | 231 + persistence/src/cms_storage_mysql.e | 217 + .../src/database/data_parameters_names.e | 132 + persistence/src/database/database_config.e | 29 + .../src/database/database_connection.e | 103 + .../src/database/database_connection_mysql.e | 137 + .../src/database/database_connection_null.e | 67 + persistence/src/database/database_handler.e | 177 + .../src/database/database_handler_impl.e | 284 + .../src/database/database_iteration_cursor.e | 91 + persistence/src/database/database_null.e | 538 ++ persistence/src/database/database_query.e | 137 + .../database/database_sql_server_encoder.e | 33 + .../src/database/database_store_procedure.e | 193 + .../src/database/parameter_name_helper.e | 23 + persistence/src/model/cms_node.e | 114 + persistence/src/model/cms_user.e | 68 + persistence/src/provider/node_data_provider.e | 272 + persistence/src/provider/user_data_provider.e | 182 + persistence/src/security/security_provider.e | 153 + persistence/src/string_helper.e | 53 + persistence/tests/application.e | 55 + persistence/tests/tests.ecf | 22 + 99 files changed, 14549 insertions(+) create mode 100644 .gitignore create mode 100644 Readme.txt create mode 100644 api/launcher/any/application_launcher.e create mode 100644 api/launcher/any/application_launcher_i.e create mode 100644 api/launcher/default/application_launcher.e create mode 100644 api/launcher/default/application_launcher_i.e create mode 100644 api/roc.ini create mode 100644 api/roc_api.ecf create mode 100644 api/site/config/application_configuration.json create mode 100644 api/site/www/static/css/bootstrap.css create mode 100644 api/site/www/static/css/dashboard.css create mode 100644 api/site/www/static/images/ajax-loader.gif create mode 100644 api/site/www/static/images/favicon.ico create mode 100644 api/site/www/static/js/roc.js create mode 100644 api/site/www/template/html/layout2.tpl create mode 100644 api/site/www/template/html/master2/content.tpl create mode 100644 api/site/www/template/html/master2/error.tpl create mode 100644 api/site/www/template/html/master2/footer.tpl create mode 100644 api/site/www/template/html/master2/head.tpl create mode 100644 api/site/www/template/html/master2/header.tpl create mode 100644 api/site/www/template/html/master2/logoff.tpl create mode 100644 api/site/www/template/html/master2/main_navigation.tpl create mode 100644 api/site/www/template/html/master2/optional_enhancement_js.tpl create mode 100644 api/site/www/template/html/master2/optional_styling_css.tpl create mode 100644 api/site/www/template/html/master2/site_navigation.tpl create mode 100644 api/site/www/template/html/modules/navigation.tpl create mode 100644 api/site/www/template/html/modules/node.tpl create mode 100644 api/site/www/template/html/modules/node_content.tpl create mode 100644 api/site/www/template/html/modules/node_summary.tpl create mode 100644 api/site/www/template/html/modules/node_title.tpl create mode 100644 api/site/www/template/html/modules/nodes.tpl create mode 100644 api/site/www/template/html/modules/register.tpl create mode 100644 api/src/configuration/application_constants.e create mode 100644 api/src/configuration/configuration_factory.e create mode 100644 api/src/configuration/roc_json_configuration.e create mode 100644 api/src/representation/common/template_shared.e create mode 100644 api/src/representation/roc_response.e create mode 100644 api/src/representation/roc_template_page.e create mode 100644 api/src/roc_abstract_api.e create mode 100644 api/src/roc_config.e create mode 100644 api/src/roc_server.e create mode 100644 api/src/service/filter/authentication_filter.e create mode 100644 api/src/service/filter/cors_filter.e create mode 100644 api/src/service/filter/error_filter.e create mode 100644 api/src/service/filter/logger_filter.e create mode 100644 api/src/service/handler/app_abstract_handler.e create mode 100644 api/src/service/handler/app_handler.e create mode 100644 api/src/service/handler/navigation_handler.e create mode 100644 api/src/service/handler/node_content_handler.e create mode 100644 api/src/service/handler/node_handler.e create mode 100644 api/src/service/handler/node_summary_handler.e create mode 100644 api/src/service/handler/node_title_handler.e create mode 100644 api/src/service/handler/nodes_handler.e create mode 100644 api/src/service/handler/roc_login_handler.e create mode 100644 api/src/service/handler/roc_logoff_handler.e create mode 100644 api/src/service/handler/roc_root_handler.e create mode 100644 api/src/service/handler/user_handler.e create mode 100644 api/src/service/roc_api_service.e create mode 100644 api/src/service/roc_email_service.e create mode 100644 api/src/service/roc_rest_api.e create mode 100644 api/src/service/shared_conneg_helper.e create mode 100644 api/src/validator/roc_input_validator.e create mode 100644 layout/Readme.md create mode 100644 layout/layout.ecf create mode 100644 layout/src/application_layout.e create mode 100644 layout/src/configuration/database_configuration.e create mode 100644 layout/src/configuration/json_configuration.e create mode 100644 layout/src/configuration/logger_configuration.e create mode 100644 layout/src/error/error_handler.e create mode 100644 layout/src/error/shared_error.e create mode 100644 layout/src/logger/logging_facility.e create mode 100644 layout/src/logger/shared_logger.e create mode 100644 persistence/persistence.ecf create mode 100644 persistence/scripts/Readme.txt create mode 100644 persistence/scripts/create_database.sql create mode 100644 persistence/scripts/tables.sql create mode 100644 persistence/src/cms_storage.e create mode 100644 persistence/src/cms_storage_mysql.e create mode 100644 persistence/src/database/data_parameters_names.e create mode 100644 persistence/src/database/database_config.e create mode 100644 persistence/src/database/database_connection.e create mode 100644 persistence/src/database/database_connection_mysql.e create mode 100644 persistence/src/database/database_connection_null.e create mode 100644 persistence/src/database/database_handler.e create mode 100644 persistence/src/database/database_handler_impl.e create mode 100644 persistence/src/database/database_iteration_cursor.e create mode 100644 persistence/src/database/database_null.e create mode 100644 persistence/src/database/database_query.e create mode 100644 persistence/src/database/database_sql_server_encoder.e create mode 100644 persistence/src/database/database_store_procedure.e create mode 100644 persistence/src/database/parameter_name_helper.e create mode 100644 persistence/src/model/cms_node.e create mode 100644 persistence/src/model/cms_user.e create mode 100644 persistence/src/provider/node_data_provider.e create mode 100644 persistence/src/provider/user_data_provider.e create mode 100644 persistence/src/security/security_provider.e create mode 100644 persistence/src/string_helper.e create mode 100644 persistence/tests/application.e create mode 100644 persistence/tests/tests.ecf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ca11d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +EIFGENs +*.swp diff --git a/Readme.txt b/Readme.txt new file mode 100644 index 0000000..f1e0c4f --- /dev/null +++ b/Readme.txt @@ -0,0 +1,42 @@ +CMS Hyperemedia API and Adaptive Web Design +============================================ + + +A content management system is not a simple domain. +This example shows how to build a basic Hypermedia API for a CMS using HTML5 and progressive enhacement. +The idea is based on an existing Eiffel CMS, the goal is learn the domain and create a new modular CMS. + +Persistence +============ +The current solution uses MySQL and only handle the nodes concept. + + +Authentication/Authorization +============================ +Basic Auth. + + +API features +============ + +There is no session. + +The root uri: + shows Navigation and the possiblity to add a New Node (only for loggedin users). + shows a predefined number of nodes the `n' most recent nodes. + +Guest users will be able to list all the nodes and view a particular node. +Logged in users (There is only one user: admin) +Logged users are able to +Add a new node +Edit an existing node +Edit a node title +Edit a node summary +Edit a node content +Delete a node + + +References + +1: http://codeartisan.blogspot.se/2012/07/using-html-as-media-type-for-your-api.html +2: https://github.com/gustafnk/combining-html-hypermedia-apis-and-adaptive-web-design \ No newline at end of file diff --git a/api/launcher/any/application_launcher.e b/api/launcher/any/application_launcher.e new file mode 100644 index 0000000..6146937 --- /dev/null +++ b/api/launcher/any/application_launcher.e @@ -0,0 +1,24 @@ +note + description: "[ + Effective class for APPLICATION_LAUNCHER_I + + You can put modification in this class + ]" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + APPLICATION_LAUNCHER + +inherit + APPLICATION_LAUNCHER_I + +feature -- Custom + + is_console_output_supported: BOOLEAN + do + Result := False + end + +end + diff --git a/api/launcher/any/application_launcher_i.e b/api/launcher/any/application_launcher_i.e new file mode 100644 index 0000000..82f65af --- /dev/null +++ b/api/launcher/any/application_launcher_i.e @@ -0,0 +1,102 @@ +note + description: "[ + Specific application launcher + + DO NOT EDIT THIS CLASS + + you can customize APPLICATION_LAUNCHER + ]" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +deferred class + APPLICATION_LAUNCHER_I + +inherit + + SHARED_EXECUTION_ENVIRONMENT + +feature -- Execution + + launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local +-- nature: like launcher_nature + do +-- nature := launcher_nature +-- if nature = Void or else nature = nature_nino then +-- launch_nino (a_service, opts) +-- elseif nature = nature_cgi then +-- launch_cgi (a_service, opts) +-- elseif nature = nature_libfcgi then + launch_libfcgi (a_service, opts) +-- else +-- -- bye bye +-- (create {EXCEPTIONS}).die (-1) +-- end + end + +feature {NONE} -- Access + + launcher_nature: detachable READABLE_STRING_8 + -- Initialize the launcher nature + -- either cgi, libfcgi, or nino. + --| We could extend with more connector if needed. + --| and we could use WSF_DEFAULT_SERVICE_LAUNCHER to configure this at compilation time. + local + p: PATH + ext: detachable READABLE_STRING_32 + do + create p.make_from_string (execution_environment.arguments.command_name) + if attached p.entry as l_entry then + ext := l_entry.extension + end + if ext /= Void then + if ext.same_string (nature_nino) then + Result := nature_nino + end + if ext.same_string (nature_cgi) then + Result := nature_cgi + end + if ext.same_string (nature_libfcgi) or else ext.same_string ("fcgi") then + Result := nature_libfcgi + end + end + end + +feature {NONE} -- nino + + nature_nino: STRING = "nino" + + launch_nino (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + launcher: WSF_NINO_SERVICE_LAUNCHER + do + create launcher.make_and_launch (a_service, opts) + end + +feature {NONE} -- cgi + + nature_cgi: STRING = "cgi" + + launch_cgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + launcher: WSF_CGI_SERVICE_LAUNCHER + do + create launcher.make_and_launch (a_service, opts) + end + +feature {NONE} -- libfcgi + + nature_libfcgi: STRING = "libfcgi" + + launch_libfcgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + launcher: WSF_LIBFCGI_SERVICE_LAUNCHER + do + create launcher.make_and_launch (a_service, opts) + end + + +end + + diff --git a/api/launcher/default/application_launcher.e b/api/launcher/default/application_launcher.e new file mode 100644 index 0000000..9e8dd6b --- /dev/null +++ b/api/launcher/default/application_launcher.e @@ -0,0 +1,54 @@ +note + description: "[ + Effective class for APPLICATION_LAUNCHER_I + + You can put modification in this class + ]" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + APPLICATION_LAUNCHER + +inherit + APPLICATION_LAUNCHER_I + + SHARED_EXECUTION_ENVIRONMENT + + +feature -- Status Report + + is_console_output_supported: BOOLEAN + do + Result := launcher_nature = nature_nino + end + + feature {NONE} -- Initialization + + launcher_nature: detachable READABLE_STRING_8 + -- Initialize the launcher nature + -- either cgi, libfcgi, or nino. + --| We could extend with more connector if needed. + --| and we could use WSF_DEFAULT_SERVICE_LAUNCHER to configure this at compilation time. + local + p: PATH + ext: detachable READABLE_STRING_32 + do + create p.make_from_string (execution_environment.arguments.command_name) + if attached p.entry as l_entry then + ext := l_entry.extension + end + if ext /= Void then + if ext.same_string (nature_nino) then + Result := nature_nino + end + end + end + +feature {NONE} -- nino + + nature_nino: STRING = "nino" + + +end + diff --git a/api/launcher/default/application_launcher_i.e b/api/launcher/default/application_launcher_i.e new file mode 100644 index 0000000..4d0cb05 --- /dev/null +++ b/api/launcher/default/application_launcher_i.e @@ -0,0 +1,26 @@ +note + description: "[ + Specific application launcher + + DO NOT EDIT THIS CLASS + + you can customize APPLICATION_LAUNCHER + ]" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +deferred class + APPLICATION_LAUNCHER_I + +feature -- Execution + + launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + launcher: WSF_SERVICE_LAUNCHER + do + create {WSF_DEFAULT_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts) + end + +end + + diff --git a/api/roc.ini b/api/roc.ini new file mode 100644 index 0000000..70c3255 --- /dev/null +++ b/api/roc.ini @@ -0,0 +1,2 @@ +port=7070 +#verbose=true \ No newline at end of file diff --git a/api/roc_api.ecf b/api/roc_api.ecf new file mode 100644 index 0000000..abec5e6 --- /dev/null +++ b/api/roc_api.ecf @@ -0,0 +1,82 @@ + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/site/config/application_configuration.json b/api/site/config/application_configuration.json new file mode 100644 index 0000000..bfda1a7 --- /dev/null +++ b/api/site/config/application_configuration.json @@ -0,0 +1,32 @@ +{ + "database": { + "datasource": { + "driver": "SQL Server", + "environment": "test" + }, + "environments": { + "test": { + "connection_string":"Server=JVELILLA\\SQLEXPRESS;Database=example;Uid=sa;Pwd=Eiffel1405;TrustedConnection=Yes" + }, + "development": { + "connection_string":"Server=sql.ise;Database=ESA_DB_TEST;Uid=eiffel2;Pwd=eiffel2;" + }, + "production": { + "connection_string":"Server=SQLSERVER;Database=LIVE_DATABASE;Uid=XXX;Pwd=YYY;" + } + } + }, + "smtp": { + "server": "localhost" + }, + "logger": { + "level":"debug", + "backup_count":"4" + }, + "server": { + "mode":"web" + } + +} + + \ No newline at end of file diff --git a/api/site/www/static/css/bootstrap.css b/api/site/www/static/css/bootstrap.css new file mode 100644 index 0000000..dede210 --- /dev/null +++ b/api/site/www/static/css/bootstrap.css @@ -0,0 +1,5849 @@ +/*! + * Bootstrap v3.1.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! normalize.css v3.0.0 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + select { + background: #fff !important; + } + .navbar { + display: none; + } + .table td, + .table th { + background-color: #fff !important; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 62.5%; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.428571429; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #428bca; + text-decoration: none; +} +a:hover, +a:focus { + color: #2a6496; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.428571429; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #999; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 200; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +cite { + font-style: normal; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-muted { + color: #999; +} +.text-primary { + color: #428bca; +} +a.text-primary:hover { + color: #3071a9; +} +.text-success { + color: #3c763d; +} +a.text-success:hover { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #428bca; +} +a.bg-primary:hover { + background-color: #3071a9; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +.list-inline > li:first-child { + padding-left: 0; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.428571429; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.428571429; + color: #999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +blockquote:before, +blockquote:after { + content: ""; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.428571429; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + white-space: nowrap; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.428571429; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + min-width: 100%; +} +.col-xs-11 { + min-width: 91.66666666666666%; +} +.col-xs-10 { + min-width: 83.33333333333334%; +} +.col-xs-9 { + min-width: 75%; +} +.col-xs-8 { + min-width: 66.66666666666666%; +} +.col-xs-7 { + min-width: 58.333333333333336%; +} +.col-xs-6 { + min-width: 50%; +} +.col-xs-5 { + min-width: 41.66666666666667%; +} +.col-xs-4 { + min-width: 33.33333333333333%; +} +.col-xs-3 { + min-width: 25%; +} +.col-xs-2 { + min-width: 16.666666666666664%; +} +.col-xs-1 { + min-width: 8.333333333333332%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666666666666%; +} +.col-xs-pull-10 { + right: 83.33333333333334%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666666666666%; +} +.col-xs-pull-7 { + right: 58.333333333333336%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666666666667%; +} +.col-xs-pull-4 { + right: 33.33333333333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.666666666666664%; +} +.col-xs-pull-1 { + right: 8.333333333333332%; +} +.col-xs-pull-0 { + right: 0; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666666666666%; +} +.col-xs-push-10 { + left: 83.33333333333334%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666666666666%; +} +.col-xs-push-7 { + left: 58.333333333333336%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666666666667%; +} +.col-xs-push-4 { + left: 33.33333333333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.666666666666664%; +} +.col-xs-push-1 { + left: 8.333333333333332%; +} +.col-xs-push-0 { + left: 0; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666666666666%; +} +.col-xs-offset-10 { + margin-left: 83.33333333333334%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666666666666%; +} +.col-xs-offset-7 { + margin-left: 58.333333333333336%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666666666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.666666666666664%; +} +.col-xs-offset-1 { + margin-left: 8.333333333333332%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + min-width: 100%; + } + .col-sm-11 { + min-width: 91.66666666666666%; + } + .col-sm-10 { + min-width: 83.33333333333334%; + } + .col-sm-9 { + min-width: 75%; + } + .col-sm-8 { + min-width: 66.66666666666666%; + } + .col-sm-7 { + min-width: 58.333333333333336%; + } + .col-sm-6 { + min-width: 50%; + } + .col-sm-5 { + min-width: 41.66666666666667%; + } + .col-sm-4 { + min-width: 33.33333333333333%; + } + .col-sm-3 { + min-width: 25%; + } + .col-sm-2 { + min-width: 16.666666666666664%; + } + .col-sm-1 { + min-width: 8.333333333333332%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666666666666%; + } + .col-sm-pull-10 { + right: 83.33333333333334%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666666666666%; + } + .col-sm-pull-7 { + right: 58.333333333333336%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666666666667%; + } + .col-sm-pull-4 { + right: 33.33333333333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.666666666666664%; + } + .col-sm-pull-1 { + right: 8.333333333333332%; + } + .col-sm-pull-0 { + right: 0; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666666666666%; + } + .col-sm-push-10 { + left: 83.33333333333334%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666666666666%; + } + .col-sm-push-7 { + left: 58.333333333333336%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666666666667%; + } + .col-sm-push-4 { + left: 33.33333333333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.666666666666664%; + } + .col-sm-push-1 { + left: 8.333333333333332%; + } + .col-sm-push-0 { + left: 0; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666666666666%; + } + .col-sm-offset-10 { + margin-left: 83.33333333333334%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666666666666%; + } + .col-sm-offset-7 { + margin-left: 58.333333333333336%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666666666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.666666666666664%; + } + .col-sm-offset-1 { + margin-left: 8.333333333333332%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + min-width: 100%; + } + .col-md-11 { + min-width: 91.66666666666666%; + } + .col-md-10 { + min-width: 83.33333333333334%; + } + .col-md-9 { + min-width: 75%; + } + .col-md-8 { + min-width: 66.66666666666666%; + } + .col-md-7 { + min-width: 58.333333333333336%; + } + .col-md-6 { + min-width: 50%; + } + .col-md-5 { + min-width: 41.66666666666667%; + } + .col-md-4 { + min-width: 33.33333333333333%; + } + .col-md-3 { + min-width: 25%; + } + .col-md-2 { + min-width: 16.666666666666664%; + } + .col-md-1 { + min-width: 8.333333333333332%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666666666666%; + } + .col-md-pull-10 { + right: 83.33333333333334%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666666666666%; + } + .col-md-pull-7 { + right: 58.333333333333336%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666666666667%; + } + .col-md-pull-4 { + right: 33.33333333333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.666666666666664%; + } + .col-md-pull-1 { + right: 8.333333333333332%; + } + .col-md-pull-0 { + right: 0; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666666666666%; + } + .col-md-push-10 { + left: 83.33333333333334%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666666666666%; + } + .col-md-push-7 { + left: 58.333333333333336%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666666666667%; + } + .col-md-push-4 { + left: 33.33333333333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.666666666666664%; + } + .col-md-push-1 { + left: 8.333333333333332%; + } + .col-md-push-0 { + left: 0; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666666666666%; + } + .col-md-offset-10 { + margin-left: 83.33333333333334%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666666666666%; + } + .col-md-offset-7 { + margin-left: 58.333333333333336%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666666666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.666666666666664%; + } + .col-md-offset-1 { + margin-left: 8.333333333333332%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + min-width: 100%; + } + .col-lg-11 { + min-width: 91.66666666666666%; + } + .col-lg-10 { + min-width: 83.33333333333334%; + } + .col-lg-9 { + min-width: 75%; + } + .col-lg-8 { + min-width: 66.66666666666666%; + } + .col-lg-7 { + min-width: 58.333333333333336%; + } + .col-lg-6 { + min-width: 50%; + } + .col-lg-5 { + min-width: 41.66666666666667%; + } + .col-lg-4 { + min-width: 33.33333333333333%; + } + .col-lg-3 { + min-width: 25%; + } + .col-lg-2 { + min-width: 16.666666666666664%; + } + .col-lg-1 { + min-width: 8.333333333333332%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666666666666%; + } + .col-lg-pull-10 { + right: 83.33333333333334%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666666666666%; + } + .col-lg-pull-7 { + right: 58.333333333333336%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666666666667%; + } + .col-lg-pull-4 { + right: 33.33333333333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.666666666666664%; + } + .col-lg-pull-1 { + right: 8.333333333333332%; + } + .col-lg-pull-0 { + right: 0; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666666666666%; + } + .col-lg-push-10 { + left: 83.33333333333334%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666666666666%; + } + .col-lg-push-7 { + left: 58.333333333333336%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666666666667%; + } + .col-lg-push-4 { + left: 33.33333333333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.666666666666664%; + } + .col-lg-push-1 { + left: 8.333333333333332%; + } + .col-lg-push-0 { + left: 0; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666666666666%; + } + .col-lg-offset-10 { + margin-left: 83.33333333333334%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666666666666%; + } + .col-lg-offset-7 { + margin-left: 58.333333333333336%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666666666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.666666666666664%; + } + .col-lg-offset-1 { + margin-left: 8.333333333333332%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + max-width: 100%; + background-color: transparent; +} +th { + text-align: left; +} +.table { + width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.428571429; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover > td, +.table-hover > tbody > tr:hover > th { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +@media (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-x: scroll; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + /* IE8-9 */ + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.428571429; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.428571429; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control:-moz-placeholder { + color: #999; +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eee; + opacity: 1; +} +textarea.form-control { + height: auto; +} +input[type="date"] { + line-height: 34px; +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + display: block; + min-height: 20px; + padding-left: 20px; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + display: inline; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + float: left; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +.radio[disabled], +.radio-inline[disabled], +.checkbox[disabled], +.checkbox-inline[disabled], +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"], +fieldset[disabled] .radio, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.has-feedback .form-control-feedback { + position: absolute; + top: 25px; + right: 0; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.form-control-static { + margin-bottom: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .control-label, +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +.form-horizontal .form-control-static { + padding-top: 7px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + top: 0; + right: 15px; +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.428571429; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + color: #333; + background-color: #ebebeb; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #428bca; + border-color: #357ebd; +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + color: #fff; + background-color: #3276b1; + border-color: #285e8e; +} +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #428bca; + border-color: #357ebd; +} +.btn-primary .badge { + color: #428bca; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + color: #fff; + background-color: #47a447; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + color: #fff; + background-color: #39b3d7; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ed9c28; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + color: #fff; + background-color: #d2322d; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #428bca; + cursor: pointer; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #2a6496; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #999; + text-decoration: none; +} +.btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height .35s ease; + transition: height .35s ease; +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\2a"; +} +.glyphicon-plus:before { + content: "\2b"; +} +.glyphicon-euro:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.428571429; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #428bca; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.428571429; + color: #999; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px solid; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus { + outline: none; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child > .btn:last-child, +.btn-group > .btn-group:first-child > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +[data-toggle="buttons"] > .btn > input[type="radio"], +[data-toggle="buttons"] > .btn > input[type="checkbox"] { + display: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #428bca; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.428571429; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #428bca; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + max-height: 340px; + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 20px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: none; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #999; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #999; +} +.navbar-inverse .navbar-nav > li > a { + color: #999; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #999; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #999; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #999; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.428571429; + color: #428bca; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: #2a6496; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #428bca; + border-color: #428bca; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +.label[href]:hover, +.label[href]:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #999; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #808080; +} +.label-primary { + background-color: #428bca; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #3071a9; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #999; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #428bca; + background-color: #fff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.container .jumbotron { + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.428571429; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + display: block; + max-width: 100%; + height: auto; + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #428bca; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable { + padding-right: 35px; +} +.alert-dismissable .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #428bca; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-size: 40px 40px; +} +.progress.active .progress-bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media-object { + display: block; +} +.media-heading { + margin: 0 0 5px; +} +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +a.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +a.list-group-item:focus { + text-decoration: none; + background-color: #f5f5f5; +} +a.list-group-item.active, +a.list-group-item.active:hover, +a.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #428bca; + border-color: #428bca; +} +a.list-group-item.active .list-group-item-heading, +a.list-group-item.active:hover .list-group-item-heading, +a.list-group-item.active:focus .list-group-item-heading { + color: inherit; +} +a.list-group-item.active .list-group-item-text, +a.list-group-item.active:hover .list-group-item-text, +a.list-group-item.active:focus .list-group-item-text { + color: #e1edf7; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +a.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +a.list-group-item-success.active:hover, +a.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +a.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +a.list-group-item-info.active:hover, +a.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +a.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +a.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group .list-group-item:first-child { + border-top: 0; +} +.panel > .list-group .list-group-item:last-child { + border-bottom: 0; +} +.panel > .list-group:first-child .list-group-item:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table { + margin-bottom: 0; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th, +.panel > .table-bordered > tfoot > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:first-child > th, +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > tfoot > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:first-child > td { + border-top: 0; +} +.panel > .table-bordered > thead > tr:last-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:last-child > th, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-bordered > thead > tr:last-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + overflow: hidden; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse .panel-body { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #428bca; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #428bca; + border-color: #428bca; +} +.panel-primary > .panel-heading + .panel-collapse .panel-body { + border-top-color: #428bca; +} +.panel-primary > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #428bca; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #ebccd1; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: auto; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -moz-transition: -moz-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + min-height: 16.428571429px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.428571429; +} +.modal-body { + position: relative; + padding: 20px; +} +.modal-footer { + padding: 19px 20px 20px; + margin-top: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1030; + display: block; + font-size: 12px; + line-height: 1.4; + visibility: visible; + filter: alpha(opacity=0); + opacity: 0; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + text-decoration: none; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + left: 5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + right: 5px; + bottom: 0; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + left: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + right: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover .arrow { + border-width: 11px; +} +.popover .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; + line-height: 1; +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .5) 0%), color-stop(rgba(0, 0, 0, .0001) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .0001) 0%), color-stop(rgba(0, 0, 0, .5) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: none; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + margin-left: -10px; + font-family: serif; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicons-chevron-left, + .carousel-control .glyphicons-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + margin-left: -15px; + font-size: 30px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +tr.visible-xs, +th.visible-xs, +td.visible-xs { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +.visible-sm, +tr.visible-sm, +th.visible-sm, +td.visible-sm { + display: none !important; +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +.visible-md, +tr.visible-md, +th.visible-md, +td.visible-md { + display: none !important; +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +.visible-lg, +tr.visible-lg, +th.visible-lg, +td.visible-lg { + display: none !important; +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .hidden-xs, + tr.hidden-xs, + th.hidden-xs, + td.hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm, + tr.hidden-sm, + th.hidden-sm, + td.hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md, + tr.hidden-md, + th.hidden-md, + td.hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg, + tr.hidden-lg, + th.hidden-lg, + td.hidden-lg { + display: none !important; + } +} +.visible-print, +tr.visible-print, +th.visible-print, +td.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +@media print { + .hidden-print, + tr.hidden-print, + th.hidden-print, + td.hidden-print { + display: none !important; + } +} + +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.glyphicon-home:before { + content: "\e021"; +} + +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/api/site/www/static/css/dashboard.css b/api/site/www/static/css/dashboard.css new file mode 100644 index 0000000..a126fc3 --- /dev/null +++ b/api/site/www/static/css/dashboard.css @@ -0,0 +1,357 @@ +/* + * Base structure + */ + +/* Move down content because we have a fixed navbar that is 36px tall on small screen */ +body { + padding-top: 40px; +} +/* On large screen, we give it more space and the navbar is 30px tall. */ +@media (min-width: 768px) { + body { + padding-top: 45px; + } +} + + +/* + * Global add-ons + */ + +h1 { + margin-top: initial; + margin-bottom: 5px; +} + +h2.sub-header{ + margin-top: 1px; + margin-bottom: 1px; + border-bottom: 1px solid #eee; +} + + +.container .jumbotron { + padding: 10px; + text-align: center; +} + + +/* + * Sidebar + */ + +/* Hide for mobile, show later */ +.sidebar { + display: none; +} +@media (min-width: 768px) { + .sidebar { + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: 1000; + display: block; + padding: 70px 20px 20px; + background-color: #f5f5f5; + border-right: 1px solid #eee; + } +} + +/* Sidebar navigation */ +.nav-sidebar { + margin-left: -20px; + margin-right: -21px; /* 20px padding + 1px border */ + margin-bottom: 20px; +} +.nav-sidebar > li > a { + padding-left: 20px; + padding-right: 20px; +} +.nav-sidebar > .active > a { + color: #fff; + background-color: #428bca; +} + + +/* + * Main content + */ + +.main { + padding: 3px; +} +@media (min-width: 768px) { + .main { + padding-left: 15px; + padding-right: 15px; + } +} +.main .page-header { + margin-top: 0; +} + + +/* + * Placeholder dashboard ideas + */ + +.placeholders { + margin-bottom: 30px; + text-align: center; +} +.placeholders h4 { + margin-bottom: 0; +} +.placeholder { + margin-bottom: 20px; +} +.placeholder img { + border-radius: 50%; +} + +.navbar-default { + background-color:#194573; + border-color: #400040; +} +.navbar-default .navbar-brand { + color: #ffffff; +} +.navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { + color: #ffffff; +} +.navbar-default .navbar-text { + color: #ffffff; +} +.navbar-default .navbar-nav > li > a { + color: #ffffff; +} +.navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { + color: #ffffff; +} +.navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #400040; +} +.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { + color: #ffffff; + background-color: #400040; +} +.navbar-default .navbar-toggle { + border-color: #400040; +} +.navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { + background-color: #400040; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #ffffff; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #ffffff; +} +.navbar-default .navbar-link { + color: #ffffff; +} +.navbar-default .navbar-link:hover { + color: #ffffff; +} + +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #ffffff; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #400040; + } +} + +.navbar-nav > li > a {padding-top:5px !important; padding-bottom:5px !important;} +.navbar {min-height:30px !important} + +.navbar-brand { + float: left; + padding: 15px; + padding-top: 5px; + padding-right: 15px; + padding-bottom: 5px; + padding-left: 15px; + font-size: 18px; + line-height: 18px; + height: 30px; +} + + +/* Tooltips */ +.blue-tooltip + .tooltip > .tooltip-inner {background-color: #FF;} +.blue-tooltip + .tooltip > .tooltip-arrow { border-bottom-color:#FF; } + + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} +.tooltip-inner { + text-align: left; + color: #000; + background: #fff; + border: solid 1px #000000; + max-width: 450px +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +/* pre */ +pre { + word-wrap: code; + white-space: pre-wrap; + background-color:white; +} + + +/* Container -Fluid */ +.container-fluid { + padding: 0 2px; +} +@media (min-width: 768px) { + .container-fluid { + padding: 0 5px; + } +} + +.container-fluid .row { + margin: 0px; +} + +.row-padding { + margin-top: 25px; + margin-bottom: 25px; +} + +/* Width for the text field to enter a bug report number in the reports page. + * We put a maximum width to override the width value coming from `form-control'. */ +.form-bug-number-entry { + max-width: 100px; +} + +/* Default width for the entries in a table like layout. */ +.form-inline .form-control { + width: 95%; +} + +.form-inline .checkbox { + font-weight: initial; + vertical-align: top; +} + +/* Note that there is also a class called label. */ +label { + padding-right: 5px; +} + +.label { + padding: 0px; + padding-right: 5px; +} + +.label-primary-api-default { + display: inline-block; + width: 105px; + text-align: left; + background: #fff; + color: #000; + font-size: 100%; + text-align: right; +} + + +.label-primary-api-interactions { + display: inline-block; + padding-right: 5px; + text-align: left; + color: #000; + font-size: 100%; +} + + +pre { + padding: 1.5px; + display: block; + margin: 0 0 10px; + font-size: 12px; + font-family: monospace; + line-height: 1.428571429; + word-break: break-word; + word-wrap: break-word; + color: #333; + border: 0px; + border-radius: 4px; +} + + +/* No padding, so that nested columns are always properly aligned. */ +.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12 { + padding-left: 0px; + padding-right: 0px; +} + +.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td { + padding:2px; + vertical-align: middle; +} + +.form-control{ + height:inherit; + padding: 1px 2px; + margin: 1px; +} + +.btn { + padding: 1px 12px; + margin: 1px; + min-width: 100px; +} + +.dropdown-toggle, .login { + cursor: pointer; +} + +.pager { + margin:10px 0; +} + +.pager li>a,.pager li>span { + padding:1px 12px; + border-radius:8px; +} + +.well { + padding: 9px; + margin-bottom: 10px; + min-height: 44px; +} + +.panel-heading { + background-color: #ddeaf2 !important; +} + +.private-panel-border { + border: solid 1px #DBA458 !important; +} + +.private-panel { + background-color: #f2eadd !important; +} diff --git a/api/site/www/static/images/ajax-loader.gif b/api/site/www/static/images/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..cd1a9983479910cc31f9a99c7a228084945ba12e GIT binary patch literal 1456 zcmb`GYfw~W9EV?FS2%*gVywGFvTJ9_6=K&#ZB0bExTqnbn0dj=nwnaZjpIyxXqtS_ zIeT>x6TB4{%_PvulG(|0RFc%xoX|i;xr&-*h^Q!tKRSn$*{M&>^nQH5Jn!?o|9{#P zi+QXyl!THFLV9|7NOe!w6MS+Tj$3GbjFS&=@gWLZA>QZwf#dgKs>94?+p#Bkl@4}&b7;zb6>R@TaZx2y< z9|`ryXhLEGCO6{O4xD;`c~?>1j$2)L?*{g^;rn*{a1RSxkbM(aQD?=D|Ww4E{%C?d5zK^c=~Wu+u# zPBO(A{ribmvC>PVPb2{=J|>jY42#xrP7)LrL9`4r+(r8bGtUp*!sw~ODccyS40`dU z5k#dfq5%U3zYvwDat(S;p6Bul3^|bJzcKtUVI4ad<*}cLd_d@sV^!x$50^U?>KbR+ zUapR1>#KB*^HuAfzNeor{A~#yFlzKGoYNK0Fovybm|)`KjQd^22^!ar$B2%tq^yn}KS@rVtvbi4Dyv2M1*b}_ za5emVf;Ls^PM<&djPK{m@0AxdIERz?GfX(qg^h43sLZ3Bn8ZX+;uLCCuah~^V{zhpYPJ z{e9tYN%Fj4qdJ|PQ*bOxQiC#P@*-0RnZnEvwh5D`PS0`+QAYW0L5f6lxo)Y_xBKk| zuXzs@vy|W|o-iOYQ1z2d&?^@7N? zsni}m-~Oq)u!xFP9J%hlqwKcJN|nXEC5pva5(>DO*>h$eqMP!i3a=-YTKH|+(&1@u z%uSwe4=&Y{w0Ti(X=eK1a$oqr#QzDm>v9WO7ui|#MX_6%8Dd+YS-@EaGg>vDniU(t zWiu6esfSo;F{e&p5=`+GhNy*$g7|4OOvF%O-Xjf6AG`m5VT(epLMknR_=4aPeVI%0 z*u}?W%=D7}Ns-g7oB1&%kxDTS-Lj2Xv**SS@-GReztH*fCKFkS@}RT>ee3{V_S5Lp zZ^`(n;-}lF$241q<$Hx~Kyljr4zgKJqc7x~4^>t*NwBNnwFUfB%NB{r; literal 0 HcmV?d00001 diff --git a/api/site/www/static/images/favicon.ico b/api/site/www/static/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..676d74466d01b2c9b3e0d2c9300c68952145a9c8 GIT binary patch literal 16446 zcmeI330PBC7Jy^NX*-?q``S_Kg32l`D7dwV;{rN}Q=QJ}H`dmr>;XkoWIzR%Xt7pt z1Cp=?0)#DsY=RpFtRNtYjvxvug2+}7M7Br*XU=^gF(9#D(UMG<@V&g7``&%${P&!D z?|JuLXtZJYXKYNvZw=a1Jby}~(Wc^oNg4wWlK=nD|4t16!0^Q58%^U5{@pIx&D4gs zXOV5N(;Rwwh>Mj-)M<&=tpx&smZFF<_(ycIVh)?+#+{g-lW^MbMgr3?FZq;F@sAPe z??R`h>N0gcV`WkNo_P@MG8dx$JqyCO>Oy%gAJG9sB2gutroRhW19?W?Sx=+5BWtp` z+vn8>I_lSOw$7{$ahzElxp&#E`&o%o2J%eVGG%!e48mNNJPNRx2);I&z_d^Up6fzr zdG?gBcXxGl9by>MK0InQWyC29jgLd^b=6oFlV^Et9K9m(___^Qu|McBXteiapZPA+ z85xY>DFL>+(G0WH$Q^nRMAxK_C}#t>!zK6iJ#!rO4A{0> zWgL4Q@Uzth?+v5jTFhZA0?^v}to00Il=SNZBYPS2iTUAfU{K}s_y4d{12`tgRi9;1legp5Y`Vl z{B6`B*kL+6xpkhpr@6K?uP{AqjlAc-%lzB~@9AMK3-594wa`}LIzXRIJGS?_WPr_3FAPM>uJ(zPIft_^|cmtN~fo%~N)$RhNCyAo*08SS5k>5;N}F&MP> zSeDv8q}`KxWSWl$-tNWlyzVhoOwif{+?}6^`E9h2eGDWX`x*xX0W{TB)ZR#D&XIlI zV5j2`|Lgnk9r~zyPaR-gFfGRA6=g*JB;oFJc9iJIU#s`Nua)K@UplsXv^{A{gg@2C zT3V!2&kj*+cc4L1)`f%)@h%H05 z&jCK;3rO-@4;NYXkaBVp`t!V6KWohPbE0^+<1dbip9?C(` z`kcyteEV{+qYmwZzIrI~_mwD*1rfWJNXKu&L+XO`TRz{8r4FXK8Zf^d2Z3}AY78Uy z$R6b0M9;55)G6r2FZ-kNUS4Lz?vx zOn}_jqr~8{$kKFUWy!t#xKmp4hRLW)fif{r#( zNlwzqw7~89za&>#u2)yz_$cPU^3YI+$=D8F`8cTu!s?H)V)n76P{dvZAr4c4jWI2W zZ?1&9LpR3zvY*(}U!;$`(!sVj?)sNo!PHpLyDwm&y)PeH2ze(=A<%9zu(3W) zuOA6_GsN#al1gZ)FK?^3o}{M?JOj}o%H7l_YNx?#V}XyQ23%kmLAK{oajb}OFB|>j z;f)jtI(2TSD&?^l#(x@!Y-K2u#oNCw*l|v40L~T3_JK?*O^Dw;2U7PNVt*ixdxN*l zf~UpVQht%(1(e^uY^4kw1J&X2+D@(bglbig59i}1Y>*IhV{_(-FFAm54mWEf;Mvc{_DRaluK2jEr zx6vcWVOm?@8{#4Fji17~(-!!i+$Oevw-CzjWS)E*naWc38sF&;(eBG1kvU7*K0r22 z`u?=Q9a7(Ke_q#_7w2iGEDUd>2f%|55B~>nI>@|7K?+-C`H4+Joj>pHZ(e}B_XXNdh5OlOdic9Q8EyEsI>j6` ztqkbreTdBY1aF@W6?f7p{w`q$T#fNK`nK|vt$gG@v(OOx-f<9V3*@~w)Y%YfAKs++ z1?{aJ$w98mludtGeO{biJD~N>(4O@j**<{G2}a<2e^d1Xiod0KY+lU1IdyJH^iT@lX-lX?iSp8}=12^7CPpHZd!aHn~|lO&*O{$$lhUY)5@e(a7} z;3MJ3ScuGfw>H+mOOa?u@F%$%0TahOYQF=l&a-TTLbuJJ_C%4jpBqVD6hH3wxZ$c#o1iGY21{w;?XzXD_}P^6rM%5XsRjC= 0) { + var bracket_position = selector.indexOf('<'); + if (bracket_position > hash_position) { + throw 'Syntax error, unrecognized expression: ' + selector; + } + } + } + return jquery_init.call(this, selector, context, rootjQuery); + }; + jQuery.fn.init.prototype = jquery_init.prototype; +})(); + + +var ROC = ROC || { }; + +$('body').on('click',"a[rel='node']",function(e){ + + e.preventDefault(); + /* + if uncomment the above line, html5 nonsupported browers won't change the url but will display the ajax content; + if commented, html5 nonsupported browers will reload the page to the specified link. + */ + + //get the link location that was clicked + pageurl = $(this).attr('href'); + + spinner = "

Loading content..

loading...
"; + //to get the ajax content and display in div with class 'main' + $.ajax({url:pageurl+'?rel=node',success: function(data){ + $('.main').html(data); + }}); + + //to change the browser URL to the given link location + //if(pageurl!=window.location){ + //window.history.pushState({path:pageurl},'',pageurl); + //} + //stop refreshing to the page given in + return false; +}); + +$('body').on('click',"a[rel='register']",function(e){ + + e.preventDefault(); + /* + if uncomment the above line, html5 nonsupported browers won't change the url but will display the ajax content; + if commented, html5 nonsupported browers will reload the page to the specified link. + */ + + //get the link location that was clicked + pageurl = $(this).attr('href'); + + spinner = "

Loading content..

loading...
"; + //to get the ajax content and display in div with class 'main' + $.ajax({url:pageurl+'?rel=node',success: function(data){ + $('.main').html(data); + }}); + + //to change the browser URL to the given link location + //if(pageurl!=window.location){ + //window.history.pushState({path:pageurl},'',pageurl); + //} + //stop refreshing to the page given in + return false; +}); + + + +$("a[rel='node']").click(function(e){ + e.preventDefault(); + /* + if uncomment the above line, html5 nonsupported browers won't change the url but will display the ajax content; + if commented, html5 nonsupported browers will reload the page to the specified link. + */ + + //get the link location that was clicked + pageurl = $(this).attr('href'); + + spinner = "

Loading content..

loading...
"; + //to get the ajax content and display in div with class 'main' + $.ajax({url:pageurl+'?rel=node',success: function(data){ + $('.main').html(data); + }}); + + //to change the browser URL to the given link location + //if(pageurl!=window.location){ + //window.history.pushState({path:pageurl},'',pageurl); + //} + //stop refreshing to the page given in + return false; +}); + + diff --git a/api/site/www/template/html/layout2.tpl b/api/site/www/template/html/layout2.tpl new file mode 100644 index 0000000..34a5659 --- /dev/null +++ b/api/site/www/template/html/layout2.tpl @@ -0,0 +1,40 @@ + + + + {include file="master2/head.tpl"/} + + + + + {include file="master2/site_navigation.tpl"/} + + + +
+
+
+
+ {include file="master2/content.tpl"/} +
+
+
+ + + + {if condition="$web"} + {include file="master2/optional_enhancement_js.tpl"/} + {/if} + + {if condition="$html"} + {include file="master2/optional_enhancement_js.tpl"/} + {/if} + + + + \ No newline at end of file diff --git a/api/site/www/template/html/master2/content.tpl b/api/site/www/template/html/master2/content.tpl new file mode 100644 index 0000000..b18c5dd --- /dev/null +++ b/api/site/www/template/html/master2/content.tpl @@ -0,0 +1,11 @@ + +
+

Top most recent nodes


+ + + {foreach from="$nodes" item="item"} + + {/foreach} +
diff --git a/api/site/www/template/html/master2/error.tpl b/api/site/www/template/html/master2/error.tpl new file mode 100644 index 0000000..0ca7ad0 --- /dev/null +++ b/api/site/www/template/html/master2/error.tpl @@ -0,0 +1,18 @@ +

Error: {$code/}

+ +{assign name="status400" value="400"/} +{assign name="status404" value="404"/} +{assign name="status500" value="500"/} + +{if condition="$code ~ $status500"} +

Internal server error, for the request {$request/}

+{/if} + + +{if condition="$code ~ $status404"} +

Resourse not found, for the request {$request/}

+{/if} + +{if condition="$code ~ $status400"} +

Bad request, the request {$request/} is not valid

+{/if} \ No newline at end of file diff --git a/api/site/www/template/html/master2/footer.tpl b/api/site/www/template/html/master2/footer.tpl new file mode 100644 index 0000000..11fe183 --- /dev/null +++ b/api/site/www/template/html/master2/footer.tpl @@ -0,0 +1,7 @@ + +
+

API Documentation     + Questions? Comments? Let us know!

+

© Copyright 2014 Eiffel Software -- Privacy Policy +

+
\ No newline at end of file diff --git a/api/site/www/template/html/master2/head.tpl b/api/site/www/template/html/master2/head.tpl new file mode 100644 index 0000000..4ba4616 --- /dev/null +++ b/api/site/www/template/html/master2/head.tpl @@ -0,0 +1,10 @@ + + +Eiffel RESTonCMS +{if condition="$web"} + {include file="master2/optional_styling_css.tpl"/} +{/if} +{if condition="$html"} + {include file="master2/optional_styling_css.tpl"/} +{/if} + diff --git a/api/site/www/template/html/master2/header.tpl b/api/site/www/template/html/master2/header.tpl new file mode 100644 index 0000000..ce8ec71 --- /dev/null +++ b/api/site/www/template/html/master2/header.tpl @@ -0,0 +1,2 @@ +

RESTonCMS

+

Tagline

\ No newline at end of file diff --git a/api/site/www/template/html/master2/logoff.tpl b/api/site/www/template/html/master2/logoff.tpl new file mode 100644 index 0000000..8139dc7 --- /dev/null +++ b/api/site/www/template/html/master2/logoff.tpl @@ -0,0 +1,5 @@ +

You have successfully signed out

+ +You may want to return + +Press this neat little button:Take Me Home diff --git a/api/site/www/template/html/master2/main_navigation.tpl b/api/site/www/template/html/master2/main_navigation.tpl new file mode 100644 index 0000000..88365d7 --- /dev/null +++ b/api/site/www/template/html/master2/main_navigation.tpl @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/api/site/www/template/html/master2/optional_enhancement_js.tpl b/api/site/www/template/html/master2/optional_enhancement_js.tpl new file mode 100644 index 0000000..1353b83 --- /dev/null +++ b/api/site/www/template/html/master2/optional_enhancement_js.tpl @@ -0,0 +1,5 @@ + + +{if condition="$web"} + +{/if} \ No newline at end of file diff --git a/api/site/www/template/html/master2/optional_styling_css.tpl b/api/site/www/template/html/master2/optional_styling_css.tpl new file mode 100644 index 0000000..f56d770 --- /dev/null +++ b/api/site/www/template/html/master2/optional_styling_css.tpl @@ -0,0 +1,9 @@ +{if condition="$html"} + + + +{/if} +{if condition="$web"} + + +{/if} diff --git a/api/site/www/template/html/master2/site_navigation.tpl b/api/site/www/template/html/master2/site_navigation.tpl new file mode 100644 index 0000000..caa4f0f --- /dev/null +++ b/api/site/www/template/html/master2/site_navigation.tpl @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/api/site/www/template/html/modules/navigation.tpl b/api/site/www/template/html/modules/navigation.tpl new file mode 100644 index 0000000..ee2d15e --- /dev/null +++ b/api/site/www/template/html/modules/navigation.tpl @@ -0,0 +1,8 @@ +{if isset="$user"} + Logoff +{/if} +{unless isset="$user"} + Login + Register +{/unless} +List of Nodes diff --git a/api/site/www/template/html/modules/node.tpl b/api/site/www/template/html/modules/node.tpl new file mode 100644 index 0000000..0f6173f --- /dev/null +++ b/api/site/www/template/html/modules/node.tpl @@ -0,0 +1,174 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + + +{if condition="html"} + +
+
+
+
+{/if} + + +
+ {if condition="$web"} + + {/if} + {if condition="$html"} + + {/if} +
+
+ {if isset="$node"} +
+
+
+
+

{$node.title/}

+
+
+
{$node.content/}
+
+
+
+ {/if} +
+ +
+ {if isset="$user"} +
+
+
+ {if isset="$node"} + +
+ {/if} +
+
+
+ {if isset="$user"} +
+
+ + {if isset="$node"} +
+ +
+ Delete Node + +
+ +
+
+
+ {/if} +
+ {/if} +
+
+
+ +{if condition="html"} +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/api/site/www/template/html/modules/node_content.tpl b/api/site/www/template/html/modules/node_content.tpl new file mode 100644 index 0000000..4825500 --- /dev/null +++ b/api/site/www/template/html/modules/node_content.tpl @@ -0,0 +1,70 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + +{if condition="html"} + +
+
+
+
+{/if} + + +
+
+
+ +
+ Edit Node Content + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+{if condition="html"} + +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/api/site/www/template/html/modules/node_summary.tpl b/api/site/www/template/html/modules/node_summary.tpl new file mode 100644 index 0000000..24f69e6 --- /dev/null +++ b/api/site/www/template/html/modules/node_summary.tpl @@ -0,0 +1,71 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + + + {unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} + {/unless} + +{if condition="html"} + +
+
+
+
+{/if} + +
+
+
+ +
+ Edit Node Summary + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+ +{if condition="html"} + +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/api/site/www/template/html/modules/node_title.tpl b/api/site/www/template/html/modules/node_title.tpl new file mode 100644 index 0000000..2e02d02 --- /dev/null +++ b/api/site/www/template/html/modules/node_title.tpl @@ -0,0 +1,70 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + + {unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} + {/unless} + + +{if condition="html"} + +
+
+
+
+{/if} + +
+
+
+ +
+ Edit Node Title + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+{if condition="html"} + +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/api/site/www/template/html/modules/nodes.tpl b/api/site/www/template/html/modules/nodes.tpl new file mode 100644 index 0000000..23b24c7 --- /dev/null +++ b/api/site/www/template/html/modules/nodes.tpl @@ -0,0 +1,52 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + +{if condition="html"} + +
+
+
+
+{/if} +
+

List nodes


+ + + {foreach from="$nodes" item="item"} + + {/foreach} +
+ +{if condition="html"} +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/api/site/www/template/html/modules/register.tpl b/api/site/www/template/html/modules/register.tpl new file mode 100644 index 0000000..3a14667 --- /dev/null +++ b/api/site/www/template/html/modules/register.tpl @@ -0,0 +1,100 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + +{if condition="html"} + +
+
+
+
+{/if} + + +
+
+
+
+ Register +
+

Register new user

+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ s
+ +
+ +
+
+
+
+
+ +{if condition="html"} +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/api/src/configuration/application_constants.e b/api/src/configuration/application_constants.e new file mode 100644 index 0000000..dd4f7d9 --- /dev/null +++ b/api/src/configuration/application_constants.e @@ -0,0 +1,19 @@ +note + description: "Summary description for {ESA_APPLICATION_CONSTANTS}." + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + APPLICATION_CONSTANTS + +feature -- Access + + major: INTEGER = 0 + minor: INTEGER = 1 + built: STRING = "0001" + + version: STRING + do + Result := major.out + "." + minor.out + "." + built + end +end diff --git a/api/src/configuration/configuration_factory.e b/api/src/configuration/configuration_factory.e new file mode 100644 index 0000000..6542bff --- /dev/null +++ b/api/src/configuration/configuration_factory.e @@ -0,0 +1,70 @@ +note + description: "API configuration factory" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + CONFIGURATION_FACTORY + +inherit + + SHARED_EXECUTION_ENVIRONMENT + + SHARED_ERROR + +feature -- Factory + + roc_config (a_dir: detachable STRING): ROC_CONFIG + local + l_layout: APPLICATION_LAYOUT + l_email_service: ROC_EMAIL_SERVICE + l_database: DATABASE_CONNECTION + l_api_service: ROC_API_SERVICE + l_retried: BOOLEAN + do + if not l_retried then + if attached a_dir then + create l_layout.make_with_path (create {PATH}.make_from_string (a_dir)) + else + create l_layout.make_default + end + log.write_information (generator + ".roc_config " + l_layout.path.name.out) + + create l_email_service.make ((create {JSON_CONFIGURATION}).new_smtp_configuration(l_layout.application_config_path)) + + if attached (create {JSON_CONFIGURATION}).new_database_configuration (l_layout.application_config_path) as l_database_config then + create {DATABASE_CONNECTION_MYSQL} l_database.login_with_schema ("cms", "root", "") + create l_api_service.make_with_database (l_database) + create Result.make (l_database, l_api_service, l_email_service, l_layout) + if (create {ROC_JSON_CONFIGURATION}).is_web_mode(l_layout.application_config_path) then + Result.mark_web + elseif (create {ROC_JSON_CONFIGURATION}).is_html_mode(l_layout.application_config_path) then + Result.mark_html + end + set_successful + else + create {DATABASE_CONNECTION_NULL} l_database.make_common + create l_api_service.make_with_database (l_database) + create Result.make (l_database, l_api_service, l_email_service, l_layout) + set_last_error ("Database Connections", generator + ".roc_config") + log.write_error (generator + ".roc_config Error database connection" ) + end + else + if attached a_dir then + create l_layout.make_with_path (create {PATH}.make_from_string (a_dir)) + else + create l_layout.make_default + end + create l_email_service.make ((create {JSON_CONFIGURATION}).new_smtp_configuration(l_layout.application_config_path)) + + create {DATABASE_CONNECTION_NULL} l_database.make_common + create l_api_service.make_with_database (l_database) + create Result.make (l_database, l_api_service, l_email_service, l_layout) + end + rescue + set_last_error_from_exception ("Database Connection execution") + log.write_critical (generator + ".roc_config Database Connection execution exceptions") + l_retried := True + retry + end +end diff --git a/api/src/configuration/roc_json_configuration.e b/api/src/configuration/roc_json_configuration.e new file mode 100644 index 0000000..3fdb368 --- /dev/null +++ b/api/src/configuration/roc_json_configuration.e @@ -0,0 +1,51 @@ +note + description: "Summary description for {ROC_JSON_CONFIGURATION}." + date: "$Date$" + revision: "$Revision$" + +class + ROC_JSON_CONFIGURATION + +inherit + + JSON_CONFIGURATION + +feature -- Access + + is_html_mode (a_path: PATH): BOOLEAN + -- Is the server running on web mode? + local + l_parser: JSON_PARSER + do + if attached json_file_from (a_path) as json_file then + l_parser := new_json_parser (json_file) + if attached {JSON_OBJECT} l_parser.parse as jv and then l_parser.is_parsed and then + attached {JSON_OBJECT} jv.item ("server") as l_server and then + attached {JSON_STRING} l_server.item ("mode") as l_mode then + Result := l_mode.item.is_case_insensitive_equal_general ("html") + end + end + end + + + + is_web_mode (a_path: PATH): BOOLEAN + -- Is the server running on web mode? + local + l_parser: JSON_PARSER + do + if attached json_file_from (a_path) as json_file then + l_parser := new_json_parser (json_file) + if attached {JSON_OBJECT} l_parser.parse as jv and then l_parser.is_parsed and then + attached {JSON_OBJECT} jv.item ("server") as l_server and then + attached {JSON_STRING} l_server.item ("mode") as l_mode then + Result := l_mode.item.is_case_insensitive_equal_general ("web") + end + end + end + + + + + +end diff --git a/api/src/representation/common/template_shared.e b/api/src/representation/common/template_shared.e new file mode 100644 index 0000000..ed6eec7 --- /dev/null +++ b/api/src/representation/common/template_shared.e @@ -0,0 +1,29 @@ +note + description: "Template shared common features to all the templates" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +deferred class + TEMPLATE_SHARED + +inherit + + ROC_TEMPLATE_PAGE + +feature -- + + add_host (a_host: READABLE_STRING_GENERAL) + -- Add value `a_host' to `host' + do + template.add_value (a_host, "host") + end + + + add_user (a_user: detachable ANY) + -- Add value `a_host' to `host' + do + if attached a_user then + template.add_value (a_user,"user") + end + end +end diff --git a/api/src/representation/roc_response.e b/api/src/representation/roc_response.e new file mode 100644 index 0000000..b2d659c --- /dev/null +++ b/api/src/representation/roc_response.e @@ -0,0 +1,143 @@ +note + description: "Summary description for {ROC_RESPONSE}." + date: "$Date$" + revision: "$Revision$" + +class + ROC_RESPONSE + +inherit + + APP_HANDLER + + TEMPLATE_SHARED + +create + make + +feature {NONE} -- Initialization + + make (a_request: WSF_REQUEST; a_template: READABLE_STRING_32) + do + request := a_request + -- Set template to HTML + set_template_folder (html_path) + -- Build Common Template + set_template_file_name (a_template) + -- Process the current tempate. + set_value (a_request.absolute_script_url (""), "host") + if attached current_user_name (request) as l_user then + set_value (l_user, "user") + end + end + +feature -- Access + + request: WSF_REQUEST + +feature -- Access + + values: STRING_TABLE [detachable ANY] + do + Result := template.values + end + + value (a_key: READABLE_STRING_GENERAL): detachable ANY + do + Result := template.values.item (a_key) + end + +feature -- Element change + + set_value (a_value: detachable ANY; a_key: READABLE_STRING_GENERAL) + do + template.add_value (a_value, a_key) + end + + +feature -- Output + + send_to (res: WSF_RESPONSE) + do + process + if attached representation as l_output then + new_response (res, l_output, {HTTP_STATUS_CODE}.ok) + end + end + + new_response_redirect (req: WSF_REQUEST; res: WSF_RESPONSE; a_location: READABLE_STRING_32) + -- Redirect to `a_location' + local + h: HTTP_HEADER + do + create h.make + h.put_content_type_text_html + h.put_current_date + h.put_location (a_location) + res.set_status_code ({HTTP_STATUS_CODE}.see_other) + res.put_header_text (h.string) + end + + new_response_authenticate (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Handle forbidden. + local + h: HTTP_HEADER + do + create h.make + h.put_content_type_text_html + h.put_current_date + h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"CMS-User%"") + res.set_status_code ({HTTP_STATUS_CODE}.unauthorized) + res.put_header_text (h.string) + end + + new_response_denied (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Handle forbidden. + local + h: HTTP_HEADER + do + process + create h.make + if attached representation as l_output then + h.put_content_length (l_output.count) + end + + h.put_content_type_text_html + h.put_current_date + res.set_status_code ({HTTP_STATUS_CODE}.unauthorized) + res.put_header_text (h.string) + if attached representation as l_output then + res.put_string (l_output) + end + end + + + new_response_unauthorized (req: WSF_REQUEST; res: WSF_RESPONSE) + local + h: HTTP_HEADER + output: STRING + do + create h.make + h.put_content_type_text_html + h.put_current_date + res.set_status_code ({HTTP_STATUS_CODE}.forbidden) + res.put_header_text (h.string) + end + + +feature {NONE} -- Implemenation + + new_response (res: WSF_RESPONSE; output: STRING; status_code: INTEGER) + local + h: HTTP_HEADER + do + create h.make + h.put_content_type_text_html + h.put_content_length (output.count) + h.put_current_date + res.set_status_code (status_code) + res.put_header_text (h.string) + res.put_string (output) + end + +end diff --git a/api/src/representation/roc_template_page.e b/api/src/representation/roc_template_page.e new file mode 100644 index 0000000..008a544 --- /dev/null +++ b/api/src/representation/roc_template_page.e @@ -0,0 +1,76 @@ +note + description: "Abstract template class" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +deferred class + ROC_TEMPLATE_PAGE + +inherit + + SHARED_TEMPLATE_CONTEXT + + SHARED_LOGGER + + ARGUMENTS + +feature -- Status + + representation: detachable STRING + -- String representation, if any. + + set_template_folder (v: PATH) + -- Set template folder to `v'. + do + template_context.set_template_folder (v) + end + + set_template_file_name (v: STRING) + -- Set `template' to `v'. + do + create template.make_from_file (v) + end + + set_template (v: like template) + -- Set `template' to `v'. + do + template := v + end + + template: TEMPLATE_FILE + + layout: APPLICATION_LAYOUT + local + l_env: EXECUTION_ENVIRONMENT + once + create l_env + if attached separate_character_option_value ('d') as l_dir then + create Result.make_with_path (create {PATH}.make_from_string (l_dir)) + else + create Result.make_default + end + end + + html_path: PATH + -- Html template paths. + do + Result := layout.template_path.extended ("html") + end + +feature -- Process + + process + -- Process the current template. + do + template_context.enable_verbose + template.analyze + template.get_output + + if attached template.output as l_output then + representation := l_output + debug + log.write_debug (generator + ".make " + l_output) + end + end + end +end diff --git a/api/src/roc_abstract_api.e b/api/src/roc_abstract_api.e new file mode 100644 index 0000000..36c5d56 --- /dev/null +++ b/api/src/roc_abstract_api.e @@ -0,0 +1,119 @@ +note + description: "Abstract API service" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +deferred class + ROC_ABSTRACT_API + +inherit + WSF_ROUTED_SKELETON_SERVICE + undefine + requires_proxy + redefine + execute_default + end + + WSF_NO_PROXY_POLICY + + WSF_URI_HELPER_FOR_ROUTED_SERVICE + + WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_SERVICE + + SHARED_CONNEG_HELPER + + SHARED_LOGGER + +feature {NONE} -- Initialization + + make (a_esa_config: ROC_CONFIG; a_server: ROC_SERVER) + do + roc_config := a_esa_config + server := a_server + initialize_router + end + +feature -- ESA + + roc_config: ROC_CONFIG + -- Configuration + + server: ROC_SERVER + -- Server + +feature -- Router setup + + setup_router + -- Setup `router' + deferred + end + + layout: APPLICATION_LAYOUT + do + Result := roc_config.layout + end + +feature -- Access + + handle_debug (req: WSF_REQUEST; res: WSF_RESPONSE) + local + s: STRING_8 + h: HTTP_HEADER + do + if req.is_get_request_method then + s := "debug" + create h.make_with_count (1) + h.put_content_type_text_html + h.put_content_length (s.count) + res.put_header_lines (h) + res.put_string (s) + else + create s.make (30_000) + across + req.form_parameters as c + loop + s.append (c.item.url_encoded_name) + s.append ("=") + s.append (c.item.string_representation) + s.append ("
") + end + if s.is_empty then + req.read_input_data_into (s) + end + create h.make_with_count (1) + h.put_content_type_text_html + h.put_content_length (s.count) + res.put_header_lines (h) + res.put_string (s) + end + end + +feature -- Handler + + not_yet_implemented_uri_template_handler (msg: READABLE_STRING_8): WSF_URI_TEMPLATE_HANDLER + do + create {WSF_URI_TEMPLATE_AGENT_HANDLER} Result.make (agent not_yet_implemented(?, ?, msg)) + end + + not_yet_implemented (req: WSF_REQUEST; res: WSF_RESPONSE; msg: detachable READABLE_STRING_8) + local + m: WSF_NOT_IMPLEMENTED_RESPONSE + do + create m.make (req) + if msg /= Void then + m.set_body (msg) + end + res.send (m) + end + + +feature -- Default Execution + + execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Dispatch requests without a matching handler. + local + do + + end + +end diff --git a/api/src/roc_config.e b/api/src/roc_config.e new file mode 100644 index 0000000..8ab46d0 --- /dev/null +++ b/api/src/roc_config.e @@ -0,0 +1,87 @@ +note + description: "Eiffel Suppor API configuration" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + ROC_CONFIG + +inherit + + SHARED_ERROR + + +create + make + +feature -- Initialization + + make (a_database: DATABASE_CONNECTION; a_api_service: ROC_API_SERVICE; a_email_service: ROC_EMAIL_SERVICE; a_layout: APPLICATION_LAYOUT ) + -- Create an object with defaults. + do + database := a_database + api_service := a_api_service + email_service := a_email_service + layout := a_layout + mark_api + ensure + database_set: database = a_database + api_service_set: api_service = a_api_service + email_service_set: email_service = a_email_service + layout_set: layout = a_layout + end + +feature -- Access + + is_successful: BOOLEAN + -- Is the configuration successful? + do + Result := successful + end + + is_api: BOOLEAN + -- Is the server running on server mode API + + is_web: BOOLEAN + -- Is the server running on server mode API + + is_html: BOOLEAN + -- Is the server running on html mode API + + database: DATABASE_CONNECTION + -- Database connection. + + api_service: ROC_API_SERVICE + -- Support API. + + email_service: ROC_EMAIL_SERVICE + -- Email service. + + layout: APPLICATION_LAYOUT + -- Api layout. + + mark_api + -- Set server mode to api. + do + is_api := True + is_html := False + is_web := False + end + + mark_web + -- Set server mode to web. + do + is_web := True + is_api := False + is_html := False + end + + mark_html + -- Set server mode to web. + do + is_html := True + is_api := False + is_web := False + end + +end diff --git a/api/src/roc_server.e b/api/src/roc_server.e new file mode 100644 index 0000000..9175669 --- /dev/null +++ b/api/src/roc_server.e @@ -0,0 +1,216 @@ +note + description: "[ + application service + ]" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + ROC_SERVER + +inherit + + WSF_LAUNCHABLE_SERVICE + rename + make_and_launch as make_and_launch_service + redefine + initialize + end + + WSF_FILTERED_SERVICE + + WSF_FILTER + rename + execute as execute_filter + end + + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + + APPLICATION_CONSTANTS + + REFACTORING_HELPER + + ARGUMENTS + +create + make_and_launch + +feature {NONE} -- Initialization + + make_and_launch + do + setup_config + create launcher + make_and_launch_service + end + + initialize + -- Initialize current service. + do + Precursor + service_options := create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("roc.ini") + initialize_filter + end + +feature {NONE} -- Launch operation + + launcher: APPLICATION_LAUNCHER + + launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + l_retry: BOOLEAN + l_message: STRING + do + if not l_retry then + launcher.launch (a_service, opts) + else + -- error hanling. + create l_message.make (1024) + if attached ((create {EXCEPTION_MANAGER}).last_exception) as l_exception then + if attached l_exception.description as l_description then + l_message.append (l_description.as_string_32) + l_message.append ("%N%N") + elseif attached l_exception.trace as l_trace then + l_message.append (l_trace) + l_message.append ("%N%N") + else + l_message.append (l_exception.out) + l_message.append ("%N%N") + end + else + l_message.append ("The application crash without available information") + l_message.append ("%N%N") + end + roc_config.email_service.send_shutdown_email (l_message) + roc_config.log.write_emergency (generator + ".launch %N" + l_message) + end + rescue + l_retry := True + retry + end + +feature {ROC_ABSTRACT_API} -- Services + + api_service: ROC_REST_API + -- rest api. + local + s: like internal_api_service + do + s := internal_api_service + if s = Void then + create s.make (roc_config, Current) + internal_api_service := s + end + Result := s + end + +feature {NONE} -- Internal + + internal_api_service: detachable like api_service + +feature -- ESA Configuraion + + roc_config: ROC_CONFIG + -- Configuration. + + setup_config + -- Configure API. + local + l_configuration_factory: CONFIGURATION_FACTORY + do + create l_configuration_factory + roc_config := l_configuration_factory.roc_config (separate_character_option_value ('d')) + if attached l_configuration_factory.last_error as l_error then + roc_config.set_last_error_from_handler (l_error) + else + roc_config.set_successful + end + end + +feature -- Execute Filter + + execute_filter (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + do + + res.put_header_line ("Date: " + (create {HTTP_DATE}.make_now_utc).string) + + res.put_header_line ("ROCServer: " + version) + api_service.execute (req, res) + end + +feature -- Filters + + create_filter + -- Create `filter'. + local + f, l_filter: detachable WSF_FILTER + fh: WSF_CUSTOM_HEADER_FILTER + do + l_filter := Void + + -- Header + create fh.make (1) + fh.set_next (l_filter) + fh.custom_header.put_header ("X-ROCServer: " + version) + l_filter := fh + + -- Cors + create {WSF_CORS_FILTER}f + f.set_next (l_filter) + l_filter := f + + + -- Maintenance + create {WSF_MAINTENANCE_FILTER} f + f.set_next (l_filter) + l_filter := f + + if launcher.is_console_output_supported then + -- Logging for nino + create {WSF_LOGGING_FILTER} f.make_with_output (io.output) + f.set_next (l_filter) + l_filter := f + end + -- CORS Authentication + create {CORS_FILTER} f + f.set_next (l_filter) + l_filter := f + + -- Logger Filter + create {LOGGER_FILTER} f.make (roc_config) + f.set_next (l_filter) + l_filter := f + + -- Error Filter + create {ERROR_FILTER} f.make (roc_config) + f.set_next (l_filter) + l_filter := f + + -- Authentication + create {AUTHENTICATION_FILTER} f.make (roc_config) + f.set_next (l_filter) + l_filter := f + + filter := l_filter + end + + setup_filter + -- Setup `filter'. + local + f: WSF_FILTER + do + from + f := filter + until + not attached f.next as l_next + loop + f := l_next + end + f.set_next (Current) + end + +end diff --git a/api/src/service/filter/authentication_filter.e b/api/src/service/filter/authentication_filter.e new file mode 100644 index 0000000..296c7ac --- /dev/null +++ b/api/src/service/filter/authentication_filter.e @@ -0,0 +1,56 @@ +note + description: "Authentication filter." + author: "Olivier Ligot" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + AUTHENTICATION_FILTER + +inherit + WSF_URI_TEMPLATE_HANDLER + + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + WSF_FILTER + + + +create + make + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter + local + l_auth: HTTP_AUTHORIZATION + do + log.write_debug (generator + ".execute " ) + create l_auth.make (req.http_authorization) + if attached req.raw_header_data as l_raw_data then + log.write_debug (generator + ".execute " + l_raw_data ) + end + -- A valid user + if (attached l_auth.type as l_auth_type and then l_auth_type.is_case_insensitive_equal ("basic")) and then + attached l_auth.login as l_auth_login and then attached l_auth.password as l_auth_password then + if api_service.login_valid (l_auth_login, l_auth_password) then + req.set_execution_variable ("user", create {CMS_USER}.make (l_auth_login)) + execute_next (req, res) + else + log.write_error (generator + ".execute login_valid failed for: " + l_auth_login ) + execute_next (req, res) + end + else + log.write_error (generator + ".execute Not valid") + execute_next (req, res) + end + end + +note + copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + +end diff --git a/api/src/service/filter/cors_filter.e b/api/src/service/filter/cors_filter.e new file mode 100644 index 0000000..640fed4 --- /dev/null +++ b/api/src/service/filter/cors_filter.e @@ -0,0 +1,27 @@ +note + description: "CORS filter" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + CORS_FILTER + +inherit + WSF_FILTER + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + local + l_header: HTTP_HEADER + do + create l_header.make +-- l_header.add_header_key_value ("Access-Control-Allow-Origin", "localhost") + l_header.add_header_key_value ("Access-Control-Allow-Headers", "*") + l_header.add_header_key_value ("Access-Control-Allow-Methods", "*") + l_header.add_header_key_value ("Access-Control-Allow-Credentials", "true") + res.put_header_lines (l_header) + execute_next (req, res) + end +end diff --git a/api/src/service/filter/error_filter.e b/api/src/service/filter/error_filter.e new file mode 100644 index 0000000..4bdd7d3 --- /dev/null +++ b/api/src/service/filter/error_filter.e @@ -0,0 +1,42 @@ +note + description: "Error filter" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + ERROR_FILTER + +inherit + WSF_URI_TEMPLATE_HANDLER + + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + WSF_FILTER + + + +create + make + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter + + do + if roc_config.is_successful and then roc_config.api_service.successful then + log.write_information (generator + ".execute") + execute_next (req, res) + else + -- send internal server error. + end + end + + +note + copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + +end diff --git a/api/src/service/filter/logger_filter.e b/api/src/service/filter/logger_filter.e new file mode 100644 index 0000000..a8132c1 --- /dev/null +++ b/api/src/service/filter/logger_filter.e @@ -0,0 +1,54 @@ +note + description: "Logger filter" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + LOGGER_FILTER + + +inherit + WSF_URI_TEMPLATE_HANDLER + + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + WSF_FILTER + +create + make + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + local + s: STRING_8 + do + log.write_debug (generator + ".execute") + create s.make (2048) + if attached req.content_type as l_type then + s.append ("[length=") + s.append_natural_64 (req.content_length_value) + s.append_character (']') + s.append_character (' ') + s.append (l_type.debug_output) + s.append_character ('%N') + end + + append_iterable_to ("Path parameters", req.path_parameters, s) + append_iterable_to ("Query parameters", req.query_parameters, s) + append_iterable_to ("Form parameters", req.form_parameters, s) + + if not s.is_empty then + log.write_debug (generator + ".execute" + s) + end + execute_next (req, res) + end + + +note + copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/api/src/service/handler/app_abstract_handler.e b/api/src/service/handler/app_abstract_handler.e new file mode 100644 index 0000000..62825a2 --- /dev/null +++ b/api/src/service/handler/app_abstract_handler.e @@ -0,0 +1,48 @@ +note + description: "Abstrat Eiffel Support API Handler." + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +deferred class + APP_ABSTRACT_HANDLER + +inherit + WSF_HANDLER + + APP_HANDLER + + SHARED_CONNEG_HELPER + +feature -- Change + + set_esa_config (a_esa_config: like roc_config) + -- Set `roc_config' to `a_esa_condig'. + do + roc_config := a_esa_config + ensure + esa_config_set: roc_config = a_esa_config + end + +feature -- Access + + roc_config: ROC_CONFIG + -- Configuration. + + api_service: ROC_API_SERVICE + -- api Service. + do + Result := roc_config.api_service + end + + email_service: ROC_EMAIL_SERVICE + -- Email Service. + do + Result := roc_config.email_service + end + + is_web: BOOLEAN + do + Result := roc_config.is_web + end + +end diff --git a/api/src/service/handler/app_handler.e b/api/src/service/handler/app_handler.e new file mode 100644 index 0000000..1ce943f --- /dev/null +++ b/api/src/service/handler/app_handler.e @@ -0,0 +1,98 @@ +note + description: "Summary description for {ESA_HANDLER}." + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + APP_HANDLER + +inherit + + SHARED_LOGGER + +feature -- User + + current_user_name (req: WSF_REQUEST): detachable READABLE_STRING_32 + -- Current user name or Void in case of Guest users. + note + EIS: "src=eiffel:?class=AUTHENTICATION_FILTER&feature=execute" + do + if attached {CMS_USER} current_user (req) as l_user then + Result := l_user.name + end + end + + current_user (req: WSF_REQUEST): detachable CMS_USER + -- Current user or Void in case of Guest user. + note + EIS: "eiffel:?class=AUTHENTICATION_FILTER&feature=execute" + do + if attached {CMS_USER} req.execution_variable ("user") as l_user then + Result := l_user + end + end + +feature -- Media Type + + current_media_type (req: WSF_REQUEST): detachable READABLE_STRING_32 + -- Current media type or Void if it's not acceptable. + do + if attached {STRING} req.execution_variable ("media_type") as l_type then + Result := l_type + end + end + +feature -- Absolute Host + + absolute_host (req: WSF_REQUEST; a_path:STRING): STRING + do + Result := req.absolute_script_url (a_path) + if Result.last_index_of ('/', Result.count) = Result.count then + Result.remove_tail (1) + end + log.write_debug (generator + ".absolute_host " + Result ) + end + +feature -- Compression + + current_compression (req: WSF_REQUEST): detachable READABLE_STRING_32 + -- Current compression encoding or Void if it's not acceptable. + do + if attached {STRING} req.execution_variable ("compression") as l_encoding then + Result := l_encoding + end + end + +feature {NONE} -- Implementations + + append_iterable_to (a_title: READABLE_STRING_8; it: detachable ITERABLE [WSF_VALUE]; s: STRING_8) + local + n: INTEGER + do + if it /= Void then + across it as c loop + n := n + 1 + end + if n > 0 then + s.append (a_title) + s.append_character (':') + s.append_character ('%N') + across + it as c + loop + s.append (" - ") + s.append (c.item.url_encoded_name) + s.append_character (' ') + s.append_character ('{') + s.append (c.item.generating_type) + s.append_character ('}') + s.append_character ('=') + s.append (c.item.debug_output.as_string_8) + s.append_character ('%N') + end + end + end + end + + +end diff --git a/api/src/service/handler/navigation_handler.e b/api/src/service/handler/navigation_handler.e new file mode 100644 index 0000000..fa93802 --- /dev/null +++ b/api/src/service/handler/navigation_handler.e @@ -0,0 +1,57 @@ +note + description: "Summary description for {NAVIGATION_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NAVIGATION_HANDLER +inherit + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: ROC_RESPONSE + do + create l_page.make (req, "modules/navigation.tpl") + l_page.send_to (res) + end +end diff --git a/api/src/service/handler/node_content_handler.e b/api/src/service/handler/node_content_handler.e new file mode 100644 index 0000000..9614ed1 --- /dev/null +++ b/api/src/service/handler/node_content_handler.e @@ -0,0 +1,189 @@ +note + description: "Summary description for {NEW_CONTENT_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NODE_CONTENT_HANDLER + +inherit + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post, + do_put + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + -- Existing node + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + create l_page.make (req, "modules/node_content.tpl") + l_page.set_value (l_node.content, "content") + l_page.set_value (l_id.value, "id") + l_page.set_value (roc_config.is_web, "web") + l_page.set_value (roc_config.is_html, "html") + l_page.send_to (res) + else + do_error (req, res, l_id) + end + else + -- Todo extract method + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + if attached {WSF_STRING} req.form_parameter ("method") as l_method then + if l_method.is_case_insensitive_equal ("PUT") then + do_put (req, res) + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + end + else + do_error (req, res, l_id) + end + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + + do_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + u_node := extract_data_form (req) + u_node.set_id (l_id.integer_value) + api_service.update_node_content (u_node.id, u_node.content) + (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end +feature -- Error + + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + -- Handling error. + local + l_page: ROC_RESPONSE + do + create l_page.make (req, "master2/error.tpl") + if a_id.is_integer then + -- resource not found + l_page.set_value ("404", "code") + else + -- bad request + l_page.set_value ("400", "code") + end + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to(res) + end + + +feature -- {NONE} Form data + + + extract_data_form (req: WSF_REQUEST): CMS_NODE + -- Extract request form data and build a object + -- Node + do + create Result.make ("", "", "") + if attached {WSF_STRING}req.form_parameter ("content") as l_content then + Result.set_content (l_content.value) + end + end + +end diff --git a/api/src/service/handler/node_handler.e b/api/src/service/handler/node_handler.e new file mode 100644 index 0000000..4c9efaa --- /dev/null +++ b/api/src/service/handler/node_handler.e @@ -0,0 +1,221 @@ +note + description: "Summary description for {NODE_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NODE_HANDLER + +inherit + + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post, + do_put, + do_delete + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: ROC_RESPONSE + do + -- Existing node + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + create l_page.make (req, "modules/node.tpl") + l_page.set_value (l_node, "node") + l_page.set_value (roc_config.is_web, "web") + l_page.set_value (roc_config.is_html, "html") + l_page.send_to (res) + else + do_error (req, res, l_id) + end + else + -- Factory + new_node (req, res) + end + end + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + if attached {WSF_STRING} req.form_parameter ("method") as l_method then + if l_method.is_case_insensitive_equal ("DELETE") then + do_delete (req, res) + elseif l_method.is_case_insensitive_equal ("PUT") then + do_put (req, res) + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + end + else + do_error (req, res, l_id) + end + else + -- New node + api_service.new_node (extract_data_form (req)) + (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + + do_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + u_node := extract_data_form (req) + u_node.set_id (l_id.integer_value) + api_service.update_node (u_node) + (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + -- Internal server error + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + + end + + do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + api_service.delete_node (l_id.integer_value) + (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + -- Internal server error + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + +feature -- Error + + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + -- Handling error. + local + l_page: ROC_RESPONSE + do + create l_page.make (req, "master2/error.tpl") + if a_id.is_integer then + -- resource not found + l_page.set_value ("404", "code") + else + -- bad request + l_page.set_value ("400", "code") + end + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + +feature {NONE} -- Node + + new_node (req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + create l_page.make (req, "modules/node.tpl") + l_page.set_value (roc_config.is_web, "web") + l_page.set_value (roc_config.is_html, "html") + + l_page.send_to (res) + else + (create {ROC_RESPONSE}.make (req, "")).new_response_unauthorized (req, res) + end + end + +feature -- {NONE} Form data + + extract_data_form (req: WSF_REQUEST): CMS_NODE + -- Extract request form data and build a object + -- Node + do + create Result.make ("", "", "") + if attached {WSF_STRING} req.form_parameter ("title") as l_title then + Result.set_title (l_title.value) + end + if attached {WSF_STRING} req.form_parameter ("summary") as l_summary then + Result.set_summary (l_summary.value) + end + if attached {WSF_STRING} req.form_parameter ("content") as l_content then + Result.set_content (l_content.value) + end + end + +end diff --git a/api/src/service/handler/node_summary_handler.e b/api/src/service/handler/node_summary_handler.e new file mode 100644 index 0000000..3d7caa3 --- /dev/null +++ b/api/src/service/handler/node_summary_handler.e @@ -0,0 +1,198 @@ +note + description: "Summary description for {NODE_SUMMARY_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NODE_SUMMARY_HANDLER + +inherit + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post, + do_put + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + -- Existing node + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + create l_page.make (req, "modules/node_summary.tpl") + l_page.set_value (l_node.summary, "summary") + l_page.set_value (roc_config.is_web, "web") + l_page.set_value (roc_config.is_html, "html") + l_page.set_value (l_id.value, "id") + l_page.send_to (res) + else + do_error (req, res, l_id) + end + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + if attached {WSF_STRING} req.form_parameter ("method") as l_method then + if l_method.is_case_insensitive_equal ("PUT") then + do_put (req, res) + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + end + else + do_error (req, res, l_id) + end + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + + do_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + u_node := extract_data_form (req) + u_node.set_id (l_id.integer_value) + api_service.update_node_summary (u_node.id, u_node.summary) + (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + +feature -- Error + + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + -- Handling error. + local + l_page: ROC_RESPONSE + do + create l_page.make (req, "master2/error.tpl") + if a_id.is_integer then + -- resource not found + l_page.set_value ("404", "code") + else + -- bad request + l_page.set_value ("400", "code") + end + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to(res) + end + +feature {NONE} -- Node + + new_node (req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_page: ROC_RESPONSE + do + create l_page.make (req, "modules/node.tpl") + l_page.send_to (res) + end + +feature -- {NONE} Form data + + + extract_data_form (req: WSF_REQUEST): CMS_NODE + -- Extract request form data and build a object + -- Node + do + create Result.make ("", "", "") + if attached {WSF_STRING}req.form_parameter ("summary") as l_summary then + Result.set_summary (l_summary.value) + end + end + +end diff --git a/api/src/service/handler/node_title_handler.e b/api/src/service/handler/node_title_handler.e new file mode 100644 index 0000000..958a6d0 --- /dev/null +++ b/api/src/service/handler/node_title_handler.e @@ -0,0 +1,198 @@ +note + description: "Summary description for {NODE_TITLE_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + NODE_TITLE_HANDLER + +inherit + + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post, + do_put + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: ROC_RESPONSE + do + if attached current_user_name (req) as l_user then + -- Existing node + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + create l_page.make (req, "modules/node_title.tpl") + l_page.set_value (l_node.title, "title") + l_page.set_value (l_id.value, "id") + l_page.set_value (roc_config.is_web, "web") + l_page.set_value (roc_config.is_html, "html") + l_page.send_to (res) + else + do_error (req, res, l_id) + end + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + l_page: ROC_RESPONSE + do + if attached current_user_name (req) as l_user then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + if attached {WSF_STRING} req.form_parameter ("method") as l_method then + if l_method.is_case_insensitive_equal ("PUT") then + do_put (req, res) + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + end + else + do_error (req, res, l_id) + end + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + + do_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + u_node := extract_data_form (req) + u_node.set_id (l_id.integer_value) + api_service.update_node_title (u_node.id, u_node.title) + (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + create l_page.make (req, "master2/error.tpl") + l_page.set_value ("500", "code") + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + else + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + end + end + +feature -- Error + + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + -- Handling error. + local + l_page: ROC_RESPONSE + do + create l_page.make (req, "master2/error.tpl") + if a_id.is_integer then + -- resource not found + l_page.set_value ("404", "code") + else + -- bad request + l_page.set_value ("400", "code") + end + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + +feature {NONE} -- Node + + new_node (req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_page: ROC_RESPONSE + do + create l_page.make (req, "modules/node.tpl") + l_page.send_to (res) + end + +feature -- {NONE} Form data + + extract_data_form (req: WSF_REQUEST): CMS_NODE + -- Extract request form data and build a object + -- Node + do + create Result.make ("", "", "") + if attached {WSF_STRING} req.form_parameter ("title") as l_title then + Result.set_title (l_title.value) + end + end + +end diff --git a/api/src/service/handler/nodes_handler.e b/api/src/service/handler/nodes_handler.e new file mode 100644 index 0000000..b98354e --- /dev/null +++ b/api/src/service/handler/nodes_handler.e @@ -0,0 +1,62 @@ +note + description: "Summary description for {NODES_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NODES_HANDLER + +inherit + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: ROC_RESPONSE + do + + create l_page.make (req, "modules/nodes.tpl") + l_page.set_value (api_service.nodes, "nodes") + l_page.set_value (roc_config.is_web, "web") + l_page.set_value (roc_config.is_html, "html") + l_page.send_to (res) + end +end diff --git a/api/src/service/handler/roc_login_handler.e b/api/src/service/handler/roc_login_handler.e new file mode 100644 index 0000000..ff39ced --- /dev/null +++ b/api/src/service/handler/roc_login_handler.e @@ -0,0 +1,63 @@ +note + description: "Handle Login using Basic Authentication" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + ROC_LOGIN_HANDLER + +inherit + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_FILTER + + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + do + if attached {STRING_32} current_user_name (req) as l_user then + (create {ROC_RESPONSE}.make(req,"")).new_response_redirect (req, res, req.absolute_script_url("")) + else + (create {ROC_RESPONSE}.make(req,"")).new_response_authenticate (req, res) + end + end + + + +end diff --git a/api/src/service/handler/roc_logoff_handler.e b/api/src/service/handler/roc_logoff_handler.e new file mode 100644 index 0000000..dcc0071 --- /dev/null +++ b/api/src/service/handler/roc_logoff_handler.e @@ -0,0 +1,59 @@ +note + description: "Handle Logoff for Basic Authentication" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + ROC_LOGOFF_HANDLER + +inherit + + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + do + log.write_information(generator + ".do_get Processing logoff") + if attached req.query_parameter ("prompt") as l_prompt then + (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) + else + (create {ROC_RESPONSE}.make(req,"master2/logoff.tpl")).new_response_denied (req, res) + end + end + +end diff --git a/api/src/service/handler/roc_root_handler.e b/api/src/service/handler/roc_root_handler.e new file mode 100644 index 0000000..5fecea3 --- /dev/null +++ b/api/src/service/handler/roc_root_handler.e @@ -0,0 +1,63 @@ +note + description: "ROOT_HANDLER." + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + ROC_ROOT_HANDLER + +inherit + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: ROC_RESPONSE + do + + create l_page.make (req, "layout2.tpl") + l_page.set_value (api_service.recent_nodes (5), "nodes") + l_page.set_value (is_web, "web") + l_page.set_value (roc_config.is_html, "html") + l_page.send_to (res) + end + +end diff --git a/api/src/service/handler/user_handler.e b/api/src/service/handler/user_handler.e new file mode 100644 index 0000000..ab4f4c7 --- /dev/null +++ b/api/src/service/handler/user_handler.e @@ -0,0 +1,163 @@ +note + description: "Summary description for {USER_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + USER_HANDLER + +inherit + + APP_ABSTRACT_HANDLER + rename + set_esa_config as make + end + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post, + do_put, + do_delete + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: ROC_RESPONSE + do + -- Existing node + create l_page.make (req, "modules/register.tpl") + l_page.set_value (roc_config.is_web, "web") + l_page.set_value (roc_config.is_html, "html") + l_page.send_to (res) + end + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + l_page: ROC_RESPONSE + do + -- New user + api_service.new_user (extract_data_form (req)) + if api_service.successful then + (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) + else + + end + end + + do_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + do + end + + do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + do + end + +feature -- Error + + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + -- Handling error. + local + l_page: ROC_RESPONSE + do + create l_page.make (req, "master2/error.tpl") + if a_id.is_integer then + -- resource not found + l_page.set_value ("404", "code") + else + -- bad request + l_page.set_value ("400", "code") + end + l_page.set_value (req.absolute_script_url (req.path_info), "request") + l_page.send_to (res) + end + +feature {NONE} -- Node + + new_node (req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_page: ROC_RESPONSE + do + if attached current_user_name (req) then + create l_page.make (req, "modules/node.tpl") + l_page.send_to (res) + else + (create {ROC_RESPONSE}.make (req, "")).new_response_unauthorized (req, res) + end + end + +feature -- {NONE} Form data + + extract_data_form (req: WSF_REQUEST): CMS_USER + -- Extract request form data and build a object + -- user + do + create Result.make ("") + if attached {WSF_STRING} req.form_parameter ("username") as l_username then + Result.set_name (l_username) + end + if + attached {WSF_STRING} req.form_parameter ("password") as l_password and then + attached {WSF_STRING} req.form_parameter ("check_password") as l_check_password and then + l_password.value.is_case_insensitive_equal (l_check_password.value) + then + Result.set_password (l_password) + end + if attached {WSF_STRING} req.form_parameter ("email") as l_email then + Result.set_email (l_email) + end + end + +end diff --git a/api/src/service/roc_api_service.e b/api/src/service/roc_api_service.e new file mode 100644 index 0000000..60f877c --- /dev/null +++ b/api/src/service/roc_api_service.e @@ -0,0 +1,168 @@ +note + description: "API Service facade to the underlying business logic" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + ROC_API_SERVICE + +inherit + + SHARED_ERROR + + +create make_with_database + + +feature -- Initialize + + make_with_database (a_connection: DATABASE_CONNECTION) + -- Create the API service + require + is_connected: a_connection.is_connected + do + log.write_information (generator+".make_with_database is database connected? "+ a_connection.is_connected.out ) + create node_provider.make (a_connection) + create user_provider.make (a_connection) + post_node_provider_execution + post_user_provider_execution + end + +feature -- Access + + login_valid (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN + local + l_security: SECURITY_PROVIDER + do + if attached user_provider.user_salt (l_auth_login) as l_hash then + if attached user_provider.user_by_name (l_auth_login) as l_user then + create l_security + if + attached l_user.password as l_password and then + l_security.password_hash (l_auth_password, l_hash).is_case_insensitive_equal (l_password) + then + Result := True + else + log.write_information (generator + ".login_valid User: wrong username or password" ) + end + else + log.write_information (generator + ".login_valid User:" + l_auth_login + "does not exist" ) + end + end + post_user_provider_execution + end + + nodes: LIST[CMS_NODE] + -- List of nodes. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + across node_provider.nodes as c loop + Result.force (c.item) + end + post_node_provider_execution + end + + recent_nodes (a_rows: INTEGER): LIST[CMS_NODE] + -- List of the `a_rows' most recent nodes. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + across node_provider.recent_nodes (0,a_rows) as c loop + Result.force (c.item) + end + post_node_provider_execution + end + + node (a_id: INTEGER_64): detachable CMS_NODE + -- + do + Result := node_provider.node (a_id) + post_node_provider_execution + end + + +feature -- Node + + new_node (a_node: CMS_NODE) + -- Add a new node + do + node_provider.new_node (a_node) + post_node_provider_execution + end + + delete_node (a_id: INTEGER_64) + do + node_provider.delete_node (a_id) + post_node_provider_execution + end + + update_node (a_node: CMS_NODE) + do + node_provider.update_node (a_node) + post_node_provider_execution + end + + update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + do + node_provider.update_node_title (a_id, a_title) + post_node_provider_execution + end + + update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + do + node_provider.update_node_summary (a_id, a_summary) + post_node_provider_execution + end + + update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + do + node_provider.update_node_content (a_id, a_content) + post_node_provider_execution + end + + +feature -- User + + new_user (a_user: CMS_USER) + -- Add a new user `a_user'. + do + if + attached a_user.password as l_password and then + attached a_user.email as l_email + then + user_provider.new_user (a_user.name, l_password, l_email) + else + -- set error + end + end + +feature {NONE} -- Post process + + post_node_provider_execution + do + if node_provider.successful then + set_successful + else + if attached node_provider.last_error then + set_last_error_from_handler (node_provider.last_error) + end + end + end + + post_user_provider_execution + do + if user_provider.successful then + set_successful + else + if attached user_provider.last_error then + set_last_error_from_handler (user_provider.last_error) + end + end + end + + node_provider: NODE_DATA_PROVIDER + -- Node Data provider. + + user_provider: USER_DATA_PROVIDER + -- User Data provider. + +end diff --git a/api/src/service/roc_email_service.e b/api/src/service/roc_email_service.e new file mode 100644 index 0000000..fdf8f0c --- /dev/null +++ b/api/src/service/roc_email_service.e @@ -0,0 +1,127 @@ +note + description: "Provides email access" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + ROC_EMAIL_SERVICE + +inherit + + SHARED_ERROR + +create + make + +feature {NONE} -- Initialization + + make (a_smtp_server: READABLE_STRING_32) + -- Create an instance of {ESA_EMAIL_SERVICE} with an smtp_server `a_smtp_server'. + -- Using "noreplies@eiffel.com" as admin email. + local + l_address_factory: INET_ADDRESS_FACTORY + do + -- Get local host name needed in creation of SMTP_PROTOCOL. + create l_address_factory + create smtp_protocol.make (a_smtp_server, l_address_factory.create_localhost.host_name) + set_successful + end + + admin_email: IMMUTABLE_STRING_8 + -- Administrator email. + once + Result := "noreplies@eiffel.com" + end + + webmaster_email: IMMUTABLE_STRING_8 + -- Webmaster email. + once + Result := "webmaster@eiffel.com" + end + + smtp_protocol: SMTP_PROTOCOL + -- SMTP protocol. + +feature -- Basic Operations + + send_template_email (a_to, a_token, a_host: STRING) + -- Send successful registration message containing activation code `a_token' to `a_to'. + require + attached_to: a_to /= Void + attached_token: a_token /= Void + attached_host: a_host /= Void + local + l_content: STRING + l_url: URL_ENCODER + l_path: PATH + l_html: HTML_ENCODER + l_email: EMAIL + do + if successful then + log.write_information (generator + ".send_post_registration_email to [" + a_to + "]" ) + create l_path.make_current + create l_url + create l_html + create l_content.make (1024) + l_content.append ("Thank you for registering at CMS.%N%NTo complete your registration, please click on this link to activate your account:%N%N") + l_content.append (a_host) + l_content.append ("/activation?code=") + l_content.append (l_url.encoded_string (a_token)) + l_content.append ("&email=") + l_content.append (l_url.encoded_string (a_to)) + l_content.append ("%N%NOnce there, please enter the following information and then click the Activate Account, button.%N%N") + l_content.append ("Your e-mail: ") + l_content.append (l_html.encoded_string (a_to)) + l_content.append ("%N%NYour activation code: ") + l_content.append (l_html.encoded_string(a_token)) + l_content.append ("%N%NThank you for joining us.%N%N CMS team.") + l_content.append (Disclaimer) + -- Create our message. + create l_email.make_with_entry (admin_email, a_to) + l_email.set_message (l_content) + l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, "CMS Site: Account Activation") + send_email (l_email) + end + end + + send_shutdown_email (a_message: READABLE_STRING_GENERAL) + -- Send email shutdown cause by an unexpected condition. + local + l_email: EMAIL + l_content: STRING + do + create l_email.make_with_entry (admin_email, webmaster_email) + create l_content.make (2048) + l_content.append (a_message.as_string_32) + l_email.set_message (l_content) + l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, "ROC API exception") + send_email (l_email) + end + +feature {NONE} -- Implementation + + send_email (a_email: EMAIL) + -- Send the email represented by `a_email'. + local + l_retried: BOOLEAN + do + if not l_retried then + log.write_information (generator + ".send_email Process send email.") + smtp_protocol.initiate_protocol + smtp_protocol.transfer (a_email) + smtp_protocol.close_protocol + log.write_information (generator + ".send_email Email sent.") + set_successful + else + log.write_error (generator + ".send_email Email not send" + last_error_message ) + end + rescue + set_last_error_from_exception (generator + ".send_email") + l_retried := True + retry + end + + Disclaimer: STRING = "This email is generated automatically, and the address is not monitored for responses. If you try contacting us by using %"reply%", you will not receive an answer." + -- Email not monitored disclaimer. + +end diff --git a/api/src/service/roc_rest_api.e b/api/src/service/roc_rest_api.e new file mode 100644 index 0000000..65aa451 --- /dev/null +++ b/api/src/service/roc_rest_api.e @@ -0,0 +1,185 @@ +note + description: "[ + REST API configuration + We manage URI and Uri templates using Routers. They are used to delegate calls (to the corresponing handlers) based on a URI template. + We define a Rooter and attach handlers to it. + ]" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + ROC_REST_API + +inherit + + ROC_ABSTRACT_API + +create + make + +feature -- Initialization + + setup_router + -- Setup `router'. + local + fhdl: WSF_FILE_SYSTEM_HANDLER + do + configure_api_root + configure_api_node + configure_api_navigation + configure_api_login + configure_api_nodes + configure_api_node_title + configure_api_node_summary + configure_api_node_content + configure_api_logoff + configure_api_register + + + create fhdl.make_hidden_with_path (layout.www_path) + fhdl.disable_index + fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE) + do + execute_default (ia_req, ia_res) + end) + router.handle_with_request_methods ("/", fhdl, router.methods_GET) + end + +feature -- Configure Resources Routes + + configure_api_root + local + l_root_handler:ROC_ROOT_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_root_handler.make (roc_config) + create l_methods + l_methods.enable_get + router.handle_with_request_methods ("/", l_root_handler, l_methods) + end + + configure_api_node + local + l_report_handler: NODE_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + router.handle_with_request_methods ("/node", l_report_handler, l_methods) + + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + l_methods.enable_delete + router.handle_with_request_methods ("/node/{id}", l_report_handler, l_methods) + end + + configure_api_navigation + local + l_report_handler: NAVIGATION_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + router.handle_with_request_methods ("/navigation", l_report_handler, l_methods) + end + + configure_api_login + local + l_report_handler: ROC_LOGIN_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + router.handle_with_request_methods ("/login", l_report_handler, l_methods) + end + + configure_api_logoff + local + l_report_handler: ROC_LOGOFF_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + router.handle_with_request_methods ("/logoff", l_report_handler, l_methods) + end + + + configure_api_nodes + local + l_report_handler: NODES_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + router.handle_with_request_methods ("/nodes", l_report_handler, l_methods) + end + + + configure_api_node_summary + local + l_report_handler: NODE_SUMMARY_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + router.handle_with_request_methods ("/node/{id}/summary", l_report_handler, l_methods) + end + + + configure_api_node_title + local + l_report_handler: NODE_TITLE_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + router.handle_with_request_methods ("/node/{id}/title", l_report_handler, l_methods) + end + + + configure_api_node_content + local + l_report_handler: NODE_CONTENT_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + router.handle_with_request_methods ("/node/{id}/content", l_report_handler, l_methods) + end + + + configure_api_register + local + l_report_handler: USER_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (roc_config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + router.handle_with_request_methods ("/user", l_report_handler, l_methods) + end + + + +end diff --git a/api/src/service/shared_conneg_helper.e b/api/src/service/shared_conneg_helper.e new file mode 100644 index 0000000..6748707 --- /dev/null +++ b/api/src/service/shared_conneg_helper.e @@ -0,0 +1,48 @@ +note + description: "Conneg Helper" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + SHARED_CONNEG_HELPER + + +feature -- Access + + conneg (req: WSF_REQUEST): SERVER_CONTENT_NEGOTIATION + -- Content negotiatior for all requests. + once + create Result.make ({HTTP_MIME_TYPES}.text_html, "en", "UTF-8", "identity") + end + + mime_types_supported (req: WSF_REQUEST): LIST [STRING] + -- All values for Accept header that `Current' can serve. + do + create {ARRAYED_LIST [STRING]} Result.make_from_array (<<{HTTP_MIME_TYPES}.text_html, "application/vnd.collection+json">>) + Result.compare_objects + ensure + mime_types_supported_includes_default: Result.has (conneg (req).default_media_type) + end + + media_type_variants (req: WSF_REQUEST): HTTP_ACCEPT_MEDIA_TYPE_VARIANTS + -- Media type negotiation. + do + Result := conneg (req).media_type_preference (mime_types_supported (req), req.http_accept) + end + + compression_supported (req: WSF_REQUEST): LIST [STRING] + -- All values for Accept-Encofing header that `Current' can serve. + do + create {ARRAYED_LIST [STRING]} Result.make_from_array (<<"identity","deflate">>) + Result.compare_objects + ensure + compression_supported_includes_default: Result.has (conneg (req).default_encoding) + end + + compression_variants (req: WSF_REQUEST): HTTP_ACCEPT_ENCODING_VARIANTS + -- Compression negotiation. + do + Result := conneg (req).encoding_preference (compression_supported (req), req.http_accept_encoding) + end + +end diff --git a/api/src/validator/roc_input_validator.e b/api/src/validator/roc_input_validator.e new file mode 100644 index 0000000..817eac0 --- /dev/null +++ b/api/src/validator/roc_input_validator.e @@ -0,0 +1,58 @@ +note + description: "Provide input validation" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +deferred class + ROC_INPUT_VALIDATOR + +feature -- Basic Operations + + input_from (a_request: ITERABLE [WSF_VALUE]) + -- Update current object using parameters extracted from QUERY_STRING. + -- If there are errors they are set to the errors parameter. + do + if attached {STRING_TABLE[WSF_VALUE]} a_request as l_table_request then + validate (l_table_request) + end + end + +feature -- Validation + + validate (a_request: STRING_TABLE [WSF_VALUE]) + -- Validate input for control `a_request'. + deferred + end + + +feature -- Access + + acceptable_query_parameters: ARRAY[STRING] + -- The parameters are optionals, more parameters is a bad request, the order is not important. + deferred + end + +feature -- Errors + + errors: STRING_TABLE[READABLE_STRING_32] + -- Hash table with errors and descriptions. + + has_error: BOOLEAN + -- Has errors the last request? + do + Result := not errors.is_empty + end + + error_message: STRING + -- String representation. + do + create Result.make_empty + across errors as c loop + Result.append (c.item) + Result.append ("%N") + end + end + + + +end -- class EFA_INPUT_VALIDATOR diff --git a/layout/Readme.md b/layout/Readme.md new file mode 100644 index 0000000..3db66b3 --- /dev/null +++ b/layout/Readme.md @@ -0,0 +1,13 @@ +Layout Library +============== + +Define a generic layout to be re-used by different applications. + +site/ + doc/ + logs/ + www/ + assets + template + theme + config/ \ No newline at end of file diff --git a/layout/layout.ecf b/layout/layout.ecf new file mode 100644 index 0000000..441ff4b --- /dev/null +++ b/layout/layout.ecf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + diff --git a/layout/src/application_layout.e b/layout/src/application_layout.e new file mode 100644 index 0000000..6108602 --- /dev/null +++ b/layout/src/application_layout.e @@ -0,0 +1,80 @@ +note + description: "[API Layout, to provide paths(config, application, log, documentation, www, html cj)]" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + APPLICATION_LAYOUT + +create + make_default, + make_with_path + +feature {NONE} -- Initialization + + make_default + -- Create a default layout based on current working directory. + local + p: PATH + do + create p.make_current + p := p.extended ("site") + make_with_path (p) + end + + make_with_path (p: PATH) + -- Create a layour based on a path `p'. + do + path := p.absolute_path.canonical_path + end + +feature -- Access + + path: PATH + +feature -- Access: internal + + config_path: PATH + -- Configuration file path. + once + Result := path.extended ("config") + end + + application_config_path: PATH + -- Database Configuration file path. + once + Result := config_path.extended ("application_configuration.json") + end + + logs_path: PATH + -- Directory for logs. + once + Result := path.extended ("logs") + end + + documentation_path: PATH + -- Directory for API documentation. + once + Result := path.extended ("doc") + end + + www_path: PATH + -- Directory for www. + once + Result := path.extended ("www") + end + + assets_path: PATH + -- Directory for public assets. + -- css, images, js. + once + Result := path.extended ("www").extended ("assets") + end + + template_path: PATH + -- Directory for templates (HTML, etc). + once + Result := www_path.extended ("template") + end + +end diff --git a/layout/src/configuration/database_configuration.e b/layout/src/configuration/database_configuration.e new file mode 100644 index 0000000..c24629b --- /dev/null +++ b/layout/src/configuration/database_configuration.e @@ -0,0 +1,39 @@ +note + description: "Object that represent Database configuration settings" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_CONFIGURATION + +create + make + +feature {NONE} -- Initialization + + make (a_driver: READABLE_STRING_32; a_connection: READABLE_STRING_32) + -- Create a database configuration setting: `driver' with `a_driver', + -- `database_string' with `a_connection'. + do + driver := a_driver + database_string := a_connection + ensure + driver_set: driver = a_driver + server_set: database_string = a_connection + end + +feature -- Access + + driver: READABLE_STRING_32 + --Database driver. + + database_string: READABLE_STRING_32 + -- Database connection. + + connection_string: READABLE_STRING_32 + -- Connection string + do + Result := "Driver={"+driver+"};" + database_string; + end + +end diff --git a/layout/src/configuration/json_configuration.e b/layout/src/configuration/json_configuration.e new file mode 100644 index 0000000..087e233 --- /dev/null +++ b/layout/src/configuration/json_configuration.e @@ -0,0 +1,100 @@ +note + description: "Provide access to json configuration" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + JSON_CONFIGURATION + +feature -- Application Configuration + + new_smtp_configuration (a_path: PATH): READABLE_STRING_32 + -- Build a new database configuration. + local + l_parser: JSON_PARSER + do + Result := "" + if attached json_file_from (a_path) as json_file then + l_parser := new_json_parser (json_file) + if attached {JSON_OBJECT} l_parser.parse as jv and then l_parser.is_parsed and then + attached {JSON_OBJECT} jv.item ("smtp") as l_smtp and then + attached {JSON_STRING} l_smtp.item ("server") as l_server then + Result := l_server.item + end + end + end + + new_database_configuration (a_path: PATH): detachable DATABASE_CONFIGURATION + -- Build a new database configuration. + local + l_parser: JSON_PARSER + do + if attached json_file_from (a_path) as json_file then + l_parser := new_json_parser (json_file) + if attached {JSON_OBJECT} l_parser.parse as jv and then l_parser.is_parsed and then + attached {JSON_OBJECT} jv.item ("database") as l_database and then + attached {JSON_OBJECT} l_database.item ("datasource") as l_datasource and then + attached {JSON_STRING} l_datasource.item ("driver") as l_driver and then + attached {JSON_STRING} l_datasource.item ("environment") as l_envrionment and then + attached {JSON_OBJECT} l_database.item ("environments") as l_environments and then + attached {JSON_OBJECT} l_environments.item (l_envrionment.item) as l_environment_selected and then + attached {JSON_STRING} l_environment_selected.item ("connection_string") as l_connection_string then + create Result.make (l_driver.item, l_connection_string.unescaped_string_8) + end + end + end + + + new_logger_level_configuration (a_path: PATH): READABLE_STRING_32 + -- Retrieve a new logger level configuration. + -- By default, level is set to `DEBUG'. + local + l_parser: JSON_PARSER + do + Result := "DEBUG" + if attached json_file_from (a_path) as json_file then + l_parser := new_json_parser (json_file) + if attached {JSON_OBJECT} l_parser.parse as jv and then l_parser.is_parsed and then + attached {JSON_OBJECT} jv.item ("logger") as l_logger and then + attached {JSON_STRING} l_logger.item ("level") as l_level then + Result := l_level.item + end + end + end + + new_database_configuration_test (a_path: PATH): detachable DATABASE_CONFIGURATION + -- Build a new database configuration for testing purposes. + local + l_parser: JSON_PARSER + do + if attached json_file_from (a_path) as json_file then + l_parser := new_json_parser (json_file) + if attached {JSON_OBJECT} l_parser.parse as jv and then l_parser.is_parsed and then + attached {JSON_OBJECT} jv.item ("database") as l_database and then + attached {JSON_OBJECT} l_database.item ("datasource") as l_datasource and then + attached {JSON_STRING} l_datasource.item ("driver") as l_driver and then + attached {JSON_STRING} l_datasource.item ("environment") as l_envrionment and then + attached {JSON_STRING} l_datasource.item ("trusted") as l_trusted and then + attached {JSON_OBJECT} l_database.item ("environments") as l_environments and then + attached {JSON_OBJECT} l_environments.item ("test") as l_environment_selected and then + attached {JSON_STRING} l_environment_selected.item ("connection_string") as l_connection_string and then + attached {JSON_STRING} l_environment_selected.item ("name") as l_name then + create Result.make (l_driver.item, l_connection_string.unescaped_string_8) + end + end + end + + +feature {NONE} -- JSON + + json_file_from (a_fn: PATH): detachable STRING + do + Result := (create {JSON_FILE_READER}).read_json_from (a_fn.name.out) + end + + new_json_parser (a_string: STRING): JSON_PARSER + do + create Result.make_parser (a_string) + end + +end diff --git a/layout/src/configuration/logger_configuration.e b/layout/src/configuration/logger_configuration.e new file mode 100644 index 0000000..25650a6 --- /dev/null +++ b/layout/src/configuration/logger_configuration.e @@ -0,0 +1,80 @@ +note + description: "Object that represent Logger configuration settings" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + LOGGER_CONFIGURATION + +inherit + LOG_PRIORITY_CONSTANTS + rename + default_create as log_default_create + end + ANY + redefine + default_create + select + default_create + end + +create + default_create + +feature -- Initialization + + default_create + -- Initialize a Logger Instance configuration with default values + -- backups = `4' and level = `DEBUG'. + do + backup_count := 4 + level := Log_debug + ensure then + backup_count_set: backup_count = 4 + level_set: level = Log_debug + end + +feature -- Access + + backup_count: NATURAL + -- Max number of backup files. + -- When 0, no backup files are created, and the log file is simply truncated when it becomes larger than `max_file_size'. + -- When non-zero, the value specifies the maximum number of backup files. + + level: INTEGER + -- Logger level. + +feature -- Element Change + + set_backup_count (a_backup: NATURAL) + -- Set backup_count to `a_backup'. + do + backup_count := a_backup + ensure + backup_count_set: backup_count = a_backup + end + + set_level (a_level: READABLE_STRING_GENERAL) + -- Set a level based on `a_level'. + do + if a_level.is_case_insensitive_equal (emerg_str) then + level := log_emergency + elseif a_level.is_case_insensitive_equal (alert_str) then + level := log_alert + elseif a_level.is_case_insensitive_equal (crit_str) then + level := log_critical + elseif a_level.is_case_insensitive_equal (error_str) then + level := log_error + elseif a_level.is_case_insensitive_equal (warn_str) then + level := log_warning + elseif a_level.is_case_insensitive_equal (notic_str) then + level := log_notice + elseif a_level.is_case_insensitive_equal (info_str) then + level := log_information + elseif a_level.is_case_insensitive_equal (debug_str) then + level := log_debug + else + level := 0 + end + end +end diff --git a/layout/src/error/error_handler.e b/layout/src/error/error_handler.e new file mode 100644 index 0000000..2977f0a --- /dev/null +++ b/layout/src/error/error_handler.e @@ -0,0 +1,51 @@ +note + description: "Object handling error information" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + ERROR_HANDLER + +create + make + +feature -- Initialization + + make (a_error_message: READABLE_STRING_32; a_error_location: READABLE_STRING_32) + -- Create an object error, set `error_message' to `a_error_message' + -- set `error_location' to `a_error_location'. + do + set_error_message (a_error_message) + set_error_location (a_error_location) + ensure + error_message_set: error_message = a_error_message + error_location_set: error_location = a_error_location + end + +feature -- Access + + error_message: READABLE_STRING_32 + -- Message. + + error_location: READABLE_STRING_32 + -- Code to represent an error. + +feature -- Change Element + + set_error_message (a_message: like error_message) + -- Set error_message with `a_message'. + do + error_message := a_message + ensure + message_set: error_message = a_message + end + + set_error_location (a_location: READABLE_STRING_32) + -- Set error_location with `a_location'. + do + error_location := a_location + ensure + error_location_set: error_location = a_location + end + +end diff --git a/layout/src/error/shared_error.e b/layout/src/error/shared_error.e new file mode 100644 index 0000000..3b0cfb9 --- /dev/null +++ b/layout/src/error/shared_error.e @@ -0,0 +1,105 @@ +note + description: "Provides error information" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + SHARED_ERROR + +inherit + + SHARED_LOGGER + +feature -- Access + + last_error: detachable ERROR_HANDLER + -- Object represent last error. + + last_error_message: READABLE_STRING_32 + -- Last error string representation. + do + if attached last_error as ll_error then + Result := ll_error.error_message + else + Result := "" + end + end + +feature -- Status Report + + successful: BOOLEAN + -- Was last operation successful? + -- If not, `last_error' must be set. + +feature -- Element Settings + + set_last_error_from_exception (a_location: STRING) + -- Initialize instance from last exception. + -- Don't show too much internal details (e.g. stack trace). + -- We really don't want this to fail since it is called from rescue clauses. + require + attached_location: a_location /= Void + local + l_exceptions: EXCEPTIONS + l_message: STRING + l_tag: detachable STRING + l_retried: BOOLEAN + do + if not l_retried then + create l_exceptions + create l_message.make (256) + l_tag := l_exceptions.tag_name + if l_tag /= Void then + l_message.append ("The following exception was raised: ") + l_message.append (l_tag) + else + l_message.append ("An unknown exception was raised.") + end + set_last_error (l_message, a_location) + log.write_critical (generator + ".set_last_error_from_exception " + l_message) + else + set_last_error ("Generic error", "") + log.write_critical (generator + ".set_last_error_from_exception Generic Error") + end + rescue + l_retried := True + retry + end + + set_last_error (a_message, a_location: STRING) + -- Set `last_error_message' with `a_message', + -- `last_error_location' with `a_location' and + -- `successful' to `False'. + require + attached_message: a_message /= Void + attached_location: a_location /= Void + do + create last_error.make (a_message, a_location) + log.write_critical (generator + ".set_last_error " + a_message) + successful := False + ensure + last_error_set: attached last_error + failed: not successful + end + + set_last_error_from_handler (a_error: detachable ERROR_HANDLER) + -- Set `last_error' with `a_error'. + do + last_error := a_error + successful := False + ensure + last_error_set: attached last_error + failed: not successful + end + + set_successful + -- Reset `last_error_message' and `last_error_location' and + -- set `successful' to `True'. + do + last_error := Void + successful := True + ensure + last_error__reset: last_error = Void + successful: successful + end +end diff --git a/layout/src/logger/logging_facility.e b/layout/src/logger/logging_facility.e new file mode 100644 index 0000000..e2cbb94 --- /dev/null +++ b/layout/src/logger/logging_facility.e @@ -0,0 +1,137 @@ +note + description: "Wrapper class providing synchronize logging access." + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + LOGGING_FACILITY + +create + make + +feature -- Initialization + + make + do + create logging.make + end + +feature -- Access + + logging: LOG_LOGGING_FACILITY + + + register_log_writer (a_log_writer: LOG_WRITER) + -- -- Register the non-default log writer `a_log_writer'. + do + logging.register_log_writer (a_log_writer) + end + +feature -- Output + + + write_alert (msg: STRING) + -- Write `msg' to the log writers as an alert. + local + l_retry: BOOLEAN + do + if not l_retry then + logging.write_alert (msg) + end + rescue + l_retry := True + retry + end + + write_critical (msg: STRING) + -- Write `msg' to the log writers as an critical + local + l_retry: BOOLEAN + do + if not l_retry then + logging.write_critical (msg) + end + rescue + l_retry := True + retry + end + + write_debug (msg: STRING) + -- Write `msg' to the log writers as an debug. + local + l_retry: BOOLEAN + do + if not l_retry then + logging.write_debug (msg) + end + rescue + l_retry := True + retry + + end + + write_emergency (msg: STRING) + -- Write `msg' to the log writers as an emergency. + local + l_retry: BOOLEAN + do + if not l_retry then + logging.write_emergency (msg) + end + rescue + l_retry := True + retry + end + + write_error (msg: STRING) + -- Write `msg' to the log writers as an error. + local + l_retry: BOOLEAN + do + if not l_retry then + logging.write_error (msg) + end + rescue + l_retry := True + retry + end + + write_information (msg: STRING) + -- Write `msg' to the log writers as an information. + local + l_retry: BOOLEAN + do + if not l_retry then + logging.write_information (msg) + end + rescue + l_retry := True + retry + end + + write_notice (msg: STRING) + -- Write `msg' to the log writers as an notice. + local + l_retry: BOOLEAN + do + if not l_retry then + logging.write_notice (msg) + end + rescue + l_retry := True + retry + end + + write_warning (msg: STRING) + -- Write `msg' to the log writers as an warning. + local + l_retry: BOOLEAN + do + if not l_retry then + logging.write_warning (msg) + end + rescue + l_retry := True + retry + end +end diff --git a/layout/src/logger/shared_logger.e b/layout/src/logger/shared_logger.e new file mode 100644 index 0000000..f8160a8 --- /dev/null +++ b/layout/src/logger/shared_logger.e @@ -0,0 +1,109 @@ +note + description: "Provides logger information" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + SHARED_LOGGER + +inherit + LOG_PRIORITY_CONSTANTS + + ARGUMENTS + +feature -- Logger + + + log: LOGGING_FACILITY + -- New `log' (once per process) + -- that could be shared between threads + -- without reinitializing it. + local + l_log_writer: LOG_ROLLING_WRITER_FILE + l_environment: EXECUTION_ENVIRONMENT + l_path: PATH + l_logger_config: LOGGER_CONFIGURATION + once ("PROCESS") + --| Initialize the logging facility + create Result.make + create l_environment + if attached separate_character_option_value ('d') as l_dir then + l_path := create {PATH}.make_from_string (l_dir) + create l_log_writer.make_at_location (l_path.extended ("..").appended ("\api.log")) + else + l_path := create {PATH}.make_current + create l_log_writer.make_at_location (l_path.extended("api.log")) + end + l_log_writer.set_max_file_size ({NATURAL_64}1024*1204) + if attached separate_character_option_value ('d') as l_dir then + l_logger_config := new_logger_level_configuration (l_path.extended("config").extended ("application_configuration.json")) + else + l_logger_config := new_logger_level_configuration (l_path.extended ("site").extended("config").extended ("application_configuration.json")) + end + l_log_writer.set_max_backup_count (l_logger_config.backup_count) + set_logger_level (l_log_writer, l_logger_config.level) + log.register_log_writer (l_log_writer) + + --| Write an informational message + Result.write_information ("The application is starting up...") + end + +feature {NONE} -- JSON + + set_logger_level (a_log_writer: LOG_ROLLING_WRITER_FILE; a_priority: INTEGER) + -- Setup the logger level based on `a_priority' + do + if a_priority = log_debug then + a_log_writer.enable_debug_log_level + elseif a_priority = Log_emergency then + a_log_writer.enable_emergency_log_level + elseif a_priority = Log_alert then + a_log_writer.enable_alert_log_level + elseif a_priority = Log_critical then + a_log_writer.enable_critical_log_level + elseif a_priority = Log_error then + a_log_writer.enable_error_log_level + elseif a_priority = Log_warning then + a_log_writer.enable_warning_log_level + elseif a_priority = Log_notice then + a_log_writer.enable_notice_log_level + elseif a_priority = Log_information then + a_log_writer.enable_information_log_level + else + a_log_writer.enable_unkno_log_level + end + end + + new_logger_level_configuration (a_path: PATH): LOGGER_CONFIGURATION + -- Retrieve a new logger level configuration. + -- By default, level is set to `DEBUG'. + local + l_parser: JSON_PARSER + do + create Result + if attached json_file_from (a_path) as json_file then + l_parser := new_json_parser (json_file) + if attached {JSON_OBJECT} l_parser.parse as jv and then l_parser.is_parsed and then + attached {JSON_OBJECT} jv.item ("logger") as l_logger and then + attached {JSON_STRING} l_logger.item ("backup_count") as l_count and then + attached {JSON_STRING} l_logger.item ("level") as l_level then + Result.set_level (l_level.item) + if l_count.item.is_natural then + Result.set_backup_count (l_count.item.to_natural) + end + end + end + end + + + json_file_from (a_fn: PATH): detachable STRING + do + Result := (create {JSON_FILE_READER}).read_json_from (a_fn.name.out) + end + + new_json_parser (a_string: STRING): JSON_PARSER + do + create Result.make_parser (a_string) + end + +end diff --git a/persistence/persistence.ecf b/persistence/persistence.ecf new file mode 100644 index 0000000..e3b2f16 --- /dev/null +++ b/persistence/persistence.ecf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + diff --git a/persistence/scripts/Readme.txt b/persistence/scripts/Readme.txt new file mode 100644 index 0000000..04064dc --- /dev/null +++ b/persistence/scripts/Readme.txt @@ -0,0 +1 @@ +Added Stored Procedures \ No newline at end of file diff --git a/persistence/scripts/create_database.sql b/persistence/scripts/create_database.sql new file mode 100644 index 0000000..06d4fd0 --- /dev/null +++ b/persistence/scripts/create_database.sql @@ -0,0 +1,105 @@ +SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; +SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; +SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; + +-- ----------------------------------------------------- +-- Schema cms +-- ----------------------------------------------------- +CREATE SCHEMA IF NOT EXISTS `cms` DEFAULT CHARACTER SET latin1 ; +USE `cms` ; + +-- ----------------------------------------------------- +-- Table `cms`.`nodes` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `cms`.`nodes` ( + `id` SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT, + `publication_date` DATE NOT NULL, + `creation_date` DATE NOT NULL, + `modification_date` DATE NOT NULL, + `title` VARCHAR(255) NOT NULL, + `summary` TEXT NOT NULL, + `content` MEDIUMTEXT NOT NULL, + PRIMARY KEY (`id`)) +ENGINE = InnoDB +AUTO_INCREMENT = 1 +DEFAULT CHARACTER SET = latin1; + + +-- ----------------------------------------------------- +-- Table `cms`.`roles` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `cms`.`roles` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `role` VARCHAR(100) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `role` (`role` ASC)) +ENGINE = InnoDB +AUTO_INCREMENT = 1 +DEFAULT CHARACTER SET = latin1; + + +-- ----------------------------------------------------- +-- Table `cms`.`users` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `cms`.`users` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `username` VARCHAR(100) NOT NULL, + `password` VARCHAR(100) NOT NULL, + `salt` VARCHAR(100) NOT NULL, + `email` VARCHAR(250) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `username` (`username` ASC)) +ENGINE = InnoDB +AUTO_INCREMENT = 1 +DEFAULT CHARACTER SET = latin1; + + +-- ----------------------------------------------------- +-- Table `cms`.`users_nodes` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `cms`.`users_nodes` ( + `users_id` INT(10) UNSIGNED NOT NULL, + `nodes_id` SMALLINT(5) UNSIGNED NOT NULL, + PRIMARY KEY (`users_id`, `nodes_id`), + INDEX `fk_users_has_nodes_nodes1_idx` (`nodes_id` ASC), + INDEX `fk_users_has_nodes_users_idx` (`users_id` ASC), + CONSTRAINT `fk_users_has_nodes_nodes1` + FOREIGN KEY (`nodes_id`) + REFERENCES `cms`.`nodes` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `fk_users_has_nodes_users` + FOREIGN KEY (`users_id`) + REFERENCES `cms`.`users` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB +DEFAULT CHARACTER SET = latin1; + + +-- ----------------------------------------------------- +-- Table `cms`.`users_roles` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `cms`.`users_roles` ( + `users_id` INT(10) UNSIGNED NOT NULL, + `roles_id` INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (`users_id`, `roles_id`), + INDEX `fk_users_has_roles_roles1_idx` (`roles_id` ASC), + INDEX `fk_users_has_roles_users1_idx` (`users_id` ASC), + CONSTRAINT `fk_users_has_roles_roles1` + FOREIGN KEY (`roles_id`) + REFERENCES `cms`.`roles` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `fk_users_has_roles_users1` + FOREIGN KEY (`users_id`) + REFERENCES `cms`.`users` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB +DEFAULT CHARACTER SET = latin1; + + +SET SQL_MODE=@OLD_SQL_MODE; +SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/persistence/scripts/tables.sql b/persistence/scripts/tables.sql new file mode 100644 index 0000000..4b1a4ba --- /dev/null +++ b/persistence/scripts/tables.sql @@ -0,0 +1,14 @@ +DROP TABLE IF EXISTS nodes; + +CREATE TABLE nodes +( + id smallint unsigned NOT NULL auto_increment, + publication_date date NOT NULL, #When the article was published + creation_date date NOT NULL, #When the article was created + modification_date date NOT NULL, #When the article was updated + title varchar(255) NOT NULL, #Full title of the article + summary text NOT NULL, #A short summary of the articule + content mediumtext NOT NULL, #The HTML content of the article + + PRIMARY KEY (ID) +); \ No newline at end of file diff --git a/persistence/src/cms_storage.e b/persistence/src/cms_storage.e new file mode 100644 index 0000000..5085328 --- /dev/null +++ b/persistence/src/cms_storage.e @@ -0,0 +1,231 @@ + +note + description : "[ + CMS interface to storage + ]" + date : "$Date$" + revision : "$Revision$" + +deferred class + CMS_STORAGE + +inherit + + SHARED_ERROR + +feature {NONE} -- Initialization + + initialize + do + end + +feature -- Access: user + + has_user: BOOLEAN + -- Has any user? + deferred + end + + all_users: LIST [CMS_USER] + deferred + end + + user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER + require + a_id > 0 + deferred + ensure + same_id: Result /= Void implies Result.id = a_id + no_password: Result /= Void implies Result.password = Void + end + + user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + require + a_name /= Void and then not a_name.is_empty + deferred + ensure + no_password: Result /= Void implies Result.password = Void + end + + user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + deferred + ensure + no_password: Result /= Void implies Result.password = Void + end + + is_valid_credential (u, p: READABLE_STRING_32): BOOLEAN + deferred + end + +feature -- Change: user + + save_user (a_user: CMS_USER) + deferred + ensure + a_user_password_is_encoded: a_user.password = Void + end + +feature -- Access: roles and permissions + +-- user_has_permission (u: detachable CMS_USER; s: detachable READABLE_STRING_8): BOOLEAN +-- -- Anonymous or user `u' has permission for `s' ? +-- --| `s' could be "create page", +-- do +-- if s = Void then +-- Result := True +-- elseif u = Void then +-- Result := user_role_has_permission (anonymous_user_role, s) +-- else +-- Result := user_role_has_permission (authenticated_user_role, s) +-- if not Result and attached u.roles as l_roles then +-- across +-- l_roles as r +-- until +-- Result +-- loop +-- if attached user_role_by_id (r.item) as ur then +-- Result := user_role_has_permission (ur, s) +-- end +-- end +-- end +-- end +-- end + +-- anonymous_user_role: CMS_USER_ROLE +-- do +-- if attached user_role_by_id (1) as l_anonymous then +-- Result := l_anonymous +-- else +-- create Result.make ("anonymous") +-- end +-- end + +-- authenticated_user_role: CMS_USER_ROLE +-- do +-- if attached user_role_by_id (2) as l_authenticated then +-- Result := l_authenticated +-- else +-- create Result.make ("authenticated") +-- end +-- end + +-- user_role_has_permission (a_role: CMS_USER_ROLE; s: READABLE_STRING_8): BOOLEAN +-- do +-- Result := a_role.has_permission (s) +-- end + +-- user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE +-- deferred +-- end + +-- user_roles: LIST [CMS_USER_ROLE] +-- deferred +-- end + +feature -- Change: roles and permissions + +-- save_user_role (a_user_role: CMS_USER_ROLE) +-- deferred +-- end + +feature -- Email + +-- save_email (a_email: NOTIFICATION_EMAIL) +-- deferred +-- end + +--feature -- Log + +-- recent_logs (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_LOG] +-- deferred +-- end + +-- log (a_id: like {CMS_LOG}.id): detachable CMS_LOG +-- require +-- a_id > 0 +-- deferred +-- end + +-- save_log (a_log: CMS_LOG) +-- deferred +-- end + +feature -- Access: Node + + recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] + deferred + end + + node (a_id: INTEGER_64): detachable CMS_NODE + -- Retrieve node by id `a_id', if any. + require + a_id > 0 + deferred + end + + +feature -- Change: Node + + save_node (a_node: CMS_NODE) + -- Save node `a_node'. + deferred + end + + + delete_node (a_id: INTEGER_64) + -- Remove node by id `a_id'. + require + valid_id: a_id > 0 + deferred + end + + update_node (a_node: CMS_NODE) + -- Update node content `a_node'. + require + valid_id: a_node.id > 0 + deferred + end + + update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + -- Update node title to `a_title', node identified by id `a_id'. + require + valid_id: a_id > 0 + deferred + end + + update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + -- Update node summary to `a_summary', node identified by id `a_id'. + require + valid_id: a_id > 0 + deferred + end + + update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + -- Update node content to `a_content', node identified by id `a_id'. + require + valid_id: a_id > 0 + deferred + end + + + + + +--feature -- Misc + +-- set_custom_value (a_name: READABLE_STRING_8; a_value: attached like custom_value; a_type: READABLE_STRING_8) +-- -- Save data `a_name:a_value' for type `a_type' +-- deferred +-- end + +-- custom_value (a_name: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable TABLE_ITERABLE [READABLE_STRING_8, STRING_8] +-- -- Data for name `a_name' and type `a_type'. +-- deferred +-- end + +-- custom_value_names_where (a_where_key, a_where_value: READABLE_STRING_8; a_type: READABLE_STRING_8): detachable LIST [READABLE_STRING_8] +-- -- Names where custom value has item `a_where_key' same as `a_where_value' for type `a_type'. +-- deferred +-- end + +end diff --git a/persistence/src/cms_storage_mysql.e b/persistence/src/cms_storage_mysql.e new file mode 100644 index 0000000..c6279fe --- /dev/null +++ b/persistence/src/cms_storage_mysql.e @@ -0,0 +1,217 @@ +note + description: "Summary description for {CMS_STORAGE_MYSQL}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_STORAGE_MYSQL + +inherit + + CMS_STORAGE + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_connection: DATABASE_CONNECTION) + -- + require + is_connected: a_connection.is_connected + do + log.write_information (generator+".make_with_database is database connected? "+ a_connection.is_connected.out ) + create node_provider.make (a_connection) + create user_provider.make (a_connection) + post_node_provider_execution + post_user_provider_execution + end + + +feature -- Access: user + + has_user: BOOLEAN + -- Has any user? + do + to_implement("Not implemented!!!") + end + + + all_users: LIST [CMS_USER] + do + to_implement("Not implemented!!!") + create {ARRAYED_LIST[CMS_USER]} Result.make (0) + end + + user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER + do + Result := user_provider.user (a_id) + post_user_provider_execution + end + + user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + do + Result := user_provider.user_by_name (a_name) + post_user_provider_execution + end + + user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + do + Result := user_provider.user_by_email (a_email) + post_user_provider_execution + end + + is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN + local + l_security: SECURITY_PROVIDER + do + if attached user_provider.user_salt (l_auth_login) as l_hash then + if attached user_provider.user_by_name (l_auth_login) as l_user then + create l_security + if + attached l_user.password as l_password and then + l_security.password_hash (l_auth_password, l_hash).is_case_insensitive_equal (l_password) + then + Result := True + else + log.write_information (generator + ".login_valid User: wrong username or password" ) + end + else + log.write_information (generator + ".login_valid User:" + l_auth_login + "does not exist" ) + end + end + post_user_provider_execution + end + +feature -- Change: user + + save_user (a_user: CMS_USER) + -- Add a new user `a_user'. + do + if + attached a_user.password as l_password and then + attached a_user.email as l_email + then + user_provider.new_user (a_user.name, l_password, l_email) + else + -- set error + end + end + +feature -- Access: node + + nodes: LIST[CMS_NODE] + -- List of nodes. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + across node_provider.nodes as c loop + Result.force (c.item) + end + post_node_provider_execution + end + + recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] + -- List of the `a_count' most recent nodes, starting from `a_lower'. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + across node_provider.recent_nodes (a_lower,a_count) as c loop + Result.force (c.item) + end + post_node_provider_execution + end + + node (a_id: INTEGER_64): detachable CMS_NODE + -- + do + Result := node_provider.node (a_id) + post_node_provider_execution + end + + +feature -- Node + + save_node (a_node: CMS_NODE) + -- Add a new node + do + node_provider.new_node (a_node) + post_node_provider_execution + end + + delete_node (a_id: INTEGER_64) + do + node_provider.delete_node (a_id) + post_node_provider_execution + end + + update_node (a_node: CMS_NODE) + do + node_provider.update_node (a_node) + post_node_provider_execution + end + + update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + do + node_provider.update_node_title (a_id, a_title) + post_node_provider_execution + end + + update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + do + node_provider.update_node_summary (a_id, a_summary) + post_node_provider_execution + end + + update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + do + node_provider.update_node_content (a_id, a_content) + post_node_provider_execution + end + + +feature -- User + + new_user (a_user: CMS_USER) + -- Add a new user `a_user'. + do + if + attached a_user.password as l_password and then + attached a_user.email as l_email + then + user_provider.new_user (a_user.name, l_password, l_email) + else + -- set error + end + end + +feature {NONE} -- Post process + + post_node_provider_execution + do + if node_provider.successful then + set_successful + else + if attached node_provider.last_error then + set_last_error_from_handler (node_provider.last_error) + end + end + end + + post_user_provider_execution + do + if user_provider.successful then + set_successful + else + if attached user_provider.last_error then + set_last_error_from_handler (user_provider.last_error) + end + end + end + + node_provider: NODE_DATA_PROVIDER + -- Node Data provider. + + user_provider: USER_DATA_PROVIDER + -- User Data provider. + +end diff --git a/persistence/src/database/data_parameters_names.e b/persistence/src/database/data_parameters_names.e new file mode 100644 index 0000000..da170ba --- /dev/null +++ b/persistence/src/database/data_parameters_names.e @@ -0,0 +1,132 @@ +note + description: "Stored procedures paramters names" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATA_PARAMETERS_NAMES + +feature -- Access + + Contactid_param: STRING = "ContactID" + + Username_param: STRING = "Username" + + Submitter_username_param: STRING = "SubmitterUsername" + + Responsibleid_param: STRING = "ResponsibleID" + + Email_param: STRING = "Email" + + Synopsis_param: STRING = "Synopsis" + + Openonly_param: STRING = "OpenOnly" + + Number_param: STRING = "Number" + + Categoryid_param: STRING = "CategoryID" + + Category_synopsis_param: STRING = "CategorySynopsis" + + Statusid_param: STRING = "StatusID" + + Private_param: STRING = "Private" + + Priorityid_param: STRING = "PriorityID" + + Severityid_param: STRING = "SeverityID" + + Searchtext_param: STRING = "SearchText" + + Searchsynopsis_param: STRING = "SearchSynopsis" + + Searchdescription_param: STRING = "SearchDescription" + + Reportid_param: STRING = "ReportID" + + Interactionid_param: STRING = "InteractionID" + + Attachmentid_param: STRING = "AttachmentID" + + Fieldtitle_param: STRING = "FieldTitle" + + Passwordhash_param: STRING = "PasswordHash" + + Registrationtoken_param: STRING = "RegistrationToken" + + Answerhash_param: STRING = "AnswerHash" + + Classid_param: STRING = "Classid" + + Confidential_param: STRING = "Confidential" + + Release_param: STRING = "Release" + + Environment_param: STRING = "Environment" + + Description_param: STRING = "Description" + + Toreproduce_param: STRING = "ToReproduce" + + Content_param: STRING = "Content" + + Alreadyexists_param: STRING = "AlreadyExists" + + Firstname_param: STRING = "FirstName" + + Lastname_param: STRING = "LastName" + + Responsible_param: STRING = "Responsible" + + Responsible_firstname_param: STRING = "ResponsibleFirstName" + + Responsible_lastname_param: STRING = "ResponsibleLastName" + + Length_param: STRING = "Length" + + Filename_param: STRING = "Filename" + + Position_param: STRING = "Position" + + Address_param: STRING = "Address" + + City_param: STRING = "City" + + Country_param: STRING = "Country" + + Region_param: STRING = "Region" + + Code_param: STRING = "Code" + + Tel_param: STRING = "Tel" + + Fax_param: STRING = "Fax" + + Name_param: STRING = "Name" + + Url_param: STRING = "URL" + + Token_param: STRING = "Token" + + Filter_param: STRING = "Filter" + +feature -- Login + + Passwordsalt_param: STRING = "PasswordSalt" + + Answersalt_param: STRING = "AnswerSalt" + + Questionid_param: STRING = "QuestionID" + + +feature -- Download Parameter Names + + Subject_param: STRING = "Subject" + + Notes_param: STRING = "Notes" + + Platform_param: STRING = "Platform" + + Newsletter_param: STRING = "Newsletter" + +end diff --git a/persistence/src/database/database_config.e b/persistence/src/database/database_config.e new file mode 100644 index 0000000..0409013 --- /dev/null +++ b/persistence/src/database/database_config.e @@ -0,0 +1,29 @@ +note + description: "Database configuration" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +deferred class + DATABASE_CONFIG + +feature -- Database access + + hostname: STRING = "" + -- Database hostname. + + username: STRING = "" + -- Database username. + + password: STRING = "" + -- Database password. + + database_name: STRING = "EiffelDB" + -- Database name. + + is_keep_connection: BOOLEAN + -- Keep Connection to database? + do + Result := True + end + +end diff --git a/persistence/src/database/database_connection.e b/persistence/src/database/database_connection.e new file mode 100644 index 0000000..a2b995e --- /dev/null +++ b/persistence/src/database/database_connection.e @@ -0,0 +1,103 @@ +note + description: "Abstract class to handle a database connection" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +deferred class + DATABASE_CONNECTION + +inherit + + DATABASE_CONFIG + + SHARED_ERROR + +feature {NONE} -- Initialization + + make_common + -- Create a database handler with common settings. + deferred + ensure + db_application_not_void: db_application /= Void + db_control_not_void: db_control /= Void + end + + make_basic ( a_database_name: STRING) + -- Create a database handler with common settings and + -- set database_name with `a_database_name'. + require + database_name_not_void: a_database_name /= Void + database_name_not_empty: not a_database_name.is_empty + deferred + ensure + db_application_not_void: db_application /= Void + db_control_not_void: db_control /= Void + end + + make (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN) + + -- Create a database handler with user `a_username', password `a_password', + -- host `a_hostname', database_name `a_database_name', and keep_connection `connection'. + require + username_not_void: a_username /= Void + username_not_empty: not a_username.is_empty + password_not_void: a_password /= Void + hostname_not_void: a_hostname /= Void + hotname_not_empty: not a_hostname.is_empty + database_name_not_void: a_database_name /= Void + database_name_not_empty: not a_database_name.is_empty + deferred + ensure + db_application_not_void: db_application /= Void + db_control_not_void: db_control /= Void + end + + login_with_connection_string (a_connection_string: STRING) + -- Login with `a_connection_string' + -- and immediately connect to database. + deferred + ensure + db_application_not_void: db_application /= Void + db_control_not_void: db_control /= Void + end + +feature -- Database Setup + + db_application: DATABASE_APPL [DATABASE] + -- Database application. + + db_control: DB_CONTROL + -- Database control. + + keep_connection: BOOLEAN + -- Keep connection alive? + +feature -- Conection + + connect + -- Connect to the database. + require else + db_control_not_void: db_control /= Void + do + if not is_connected then + db_control.connect + end + end + + disconnect + -- Disconnect from the database. + require else + db_control_not_void: db_control /= Void + do + db_control.disconnect + end + + is_connected: BOOLEAN + -- True if connected to the database. + require else + db_control_not_void: db_control /= Void + do + Result := db_control.is_connected + end + +end diff --git a/persistence/src/database/database_connection_mysql.e b/persistence/src/database/database_connection_mysql.e new file mode 100644 index 0000000..1f08893 --- /dev/null +++ b/persistence/src/database/database_connection_mysql.e @@ -0,0 +1,137 @@ +note + description: "Object that handle a database connection for ODBC" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_CONNECTION_MYSQL + +inherit + + DATABASE_CONNECTION + redefine + db_application + end + +create + make, make_common, make_basic, login_with_connection_string, login_with_schema + +feature -- Initialization + + make_common + -- Create a database handler for MYSQL with common settings. + local + l_retried: BOOLEAN + do + create db_application.login (username, password) + + if not l_retried then + db_application.set_hostname (hostname) + db_application.set_data_source (database_name) + db_application.set_base + create db_control.make + keep_connection := is_keep_connection + if keep_connection then + connect + end + set_successful + else + create db_control.make + end + rescue + create db_control.make + set_last_error_from_exception ("Connection execution") + log.write_critical (generator + ".make_common:" + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + + make_basic (a_database_name: STRING) + -- Create a database handler and + -- set database_name to `a_database_name'. + local + l_retried: BOOLEAN + do + create db_application.login (username, password) + + if not l_retried then + db_application.set_hostname (hostname) + db_application.set_data_source (a_database_name) + db_application.set_base + create db_control.make + keep_connection := is_keep_connection + if keep_connection then + connect + end + set_successful + else + create db_control.make + end + rescue + create db_control.make + set_last_error_from_exception ("Connection execution") + log.write_critical (generator + ".make_common:" + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + + make (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN) + + -- Create a database handler for ODBC and set `username' to `a_username', + -- `password' to `a_password' + -- `database_name' to `a_database_name' + -- `connection' to `a_connection' + do + create db_application.login (a_username, a_password) + db_application.set_hostname (a_hostname) + db_application.set_data_source (a_database_name) + db_application.set_base + create db_control.make + keep_connection := connection + if keep_connection then + connect + end + end + + login_with_connection_string (a_string: STRING) + -- Login with `a_connection_string'and immediately connect to database. + do + log.write_debug (generator +".login_with_connection_string") + create db_application.login_with_connection_string (a_string) + db_application.set_base + create db_control.make + log.write_debug (generator +".login_with_connection_string, is_keep_connection? "+ is_keep_connection.out ) + keep_connection := is_keep_connection + if keep_connection then + connect + if not db_control.is_ok then + log.write_critical (generator +".login_with_connection_string:"+ db_control.error_code.out ) + log.write_critical (generator +".login_with_connection_string:"+ db_control.error_message_32 ) + end + log.write_debug (generator +".login_with_connection_string, After connect, is_connected? "+ is_connected.out) + end + end + + login_with_schema (a_schema: STRING; a_username: STRING; a_password: STRING) + -- Login with `a_connection_string'and immediately connect to database. + do + create db_application + db_application.set_application (a_schema) + db_application.login_and_connect (a_username, a_password) + db_application.set_base + create db_control.make + keep_connection := is_keep_connection + end + +feature -- Databse Connection + + db_application: DATABASE_APPL [MYSQL] + -- Database application. + +end diff --git a/persistence/src/database/database_connection_null.e b/persistence/src/database/database_connection_null.e new file mode 100644 index 0000000..d9257b3 --- /dev/null +++ b/persistence/src/database/database_connection_null.e @@ -0,0 +1,67 @@ +note + description: "Null object to meet Void Safe." + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_CONNECTION_NULL + +inherit + + DATABASE_CONNECTION + redefine + db_application, + is_connected + end + +create + make, make_common, make_basic + +feature -- Initialization + + make_common + -- Create a database handler for ODBC with common settings. + do + create db_application.login (username, password) + db_application.set_hostname (hostname) + db_application.set_data_source (database_name) + db_application.set_base + create db_control.make + end + + make (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN) + + -- Create a database handler for ODBC. + do + make_common + end + + make_basic (a_database_name: STRING) + + -- Create a database handler for ODBC. + do + make_common + end + + + login_with_connection_string (a_string: STRING) + -- Login with `a_connection_string' + -- and immediately connect to database. + do + make_common + end + + +feature -- Databse Connection + + db_application: DATABASE_APPL[DATABASE_NULL] + -- Database application. + + + is_connected: BOOLEAN + -- True if connected to the database. + do + Result := True + end + +end diff --git a/persistence/src/database/database_handler.e b/persistence/src/database/database_handler.e new file mode 100644 index 0000000..3551e4a --- /dev/null +++ b/persistence/src/database/database_handler.e @@ -0,0 +1,177 @@ +note + description: "Abstract Database Handler" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +deferred class + DATABASE_HANDLER + +inherit + + SHARED_ERROR + +feature -- Access + + store: detachable DATABASE_STORE_PROCEDURE + -- Database stored_procedure to handle. + + query: detachable DATABASE_QUERY + -- Database query. + +feature -- Modifiers + + set_store (a_store: DATABASE_STORE_PROCEDURE) + -- Set `store' to `a_store' to execute. + require + store_not_void: a_store /= Void + do + store := a_store + ensure + store_set: store = a_store + end + + set_query (a_query: DATABASE_QUERY) + -- Set `query' to `a_query' to execute. + require + query_not_void: a_query /= Void + do + query := a_query + ensure + query_set: query = a_query + end + +feature -- Functionality Store Procedures + + execute_reader + -- Execute store. + require + store_not_void: store /= void + deferred + end + + execute_writer + -- Execute store. + require + store_not_void: store /= void + deferred + end + +feature -- SQL Queries + + execute_query + -- Execute query. + require + query_not_void: query /= void + deferred + end + + execute_change + -- Execute sqlquery that update/add data. + require + query_not_void: query /= void + deferred + end + + +feature -- Iteration + + start + -- Set the cursor on first element. + deferred + end + + item: ANY + -- Item at current cursor position. + require + valid_position: not after + deferred + end + + after: BOOLEAN + -- Are there no more items to iterate over? + deferred + end + + forth + -- Move to next position. + require + valid_position: not after + deferred + end + +feature -- Access + + read_integer_32 (a_index: INTEGER): INTEGER_32 + -- Retrieved value at `a_index' position in `item'. + do + if attached {DB_TUPLE} item as l_item then + if attached {INTEGER_32_REF} l_item.item (a_index) as ll_item then + Result := ll_item.item + end + end + end + + read_string (a_index: INTEGER): detachable STRING + -- Retrieved value at `a_index' position in `item'. + do + if attached {DB_TUPLE} item as l_item then + if attached {STRING} l_item.item (a_index) as ll_item then + Result := ll_item + elseif attached {BOOLEAN_REF} l_item.item (a_index) as ll_item then + Result := ll_item.item.out + end + end + end + + read_date_time (a_index: INTEGER): detachable DATE_TIME + -- Retrieved value at `a_index' position in `item'. + do + if attached {DB_TUPLE} item as l_item then + if attached {DATE_TIME} l_item.item (a_index) as ll_item then + Result := ll_item + end + end + end + + read_boolean (a_index: INTEGER): detachable BOOLEAN + -- Retrieved value at `a_index' position in `item'. + do + if attached {DB_TUPLE} item as l_item then + if attached {BOOLEAN} l_item.item (a_index) as ll_item then + Result := ll_item + elseif attached {BOOLEAN_REF} l_item.item (a_index) as ll_item then + Result := ll_item.item + end + end + end + +feature -- Status Report + + has_error: BOOLEAN + -- Is there an error? + + count: INTEGER + -- Number of rows, last execution. + deferred + end + +feature {NODE_DATA_PROVIDER}-- Implementation + + connect + -- Connect to the database. + deferred + end + + disconnect + -- Disconnect from the database. + deferred + ensure + not_connected: not is_connected + end + + is_connected: BOOLEAN + -- True if connected to the database. + deferred + end + +end diff --git a/persistence/src/database/database_handler_impl.e b/persistence/src/database/database_handler_impl.e new file mode 100644 index 0000000..96078ee --- /dev/null +++ b/persistence/src/database/database_handler_impl.e @@ -0,0 +1,284 @@ +note + description: "Database handler Implementation" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_HANDLER_IMPL + +inherit + DATABASE_HANDLER + + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_connection: DATABASE_CONNECTION) + -- Create a database handler with connnection `connection'. + do + connection := a_connection + create last_query.make_now + set_successful + ensure + connection_not_void: connection /= Void + last_query_not_void: last_query /= Void + end + +feature -- Functionality + + execute_reader + -- Execute stored procedure that returns data. + local + l_db_selection: DB_SELECTION + l_retried: BOOLEAN + do + if not l_retried then + if not keep_connection then + connect + end + + if attached store as l_store then + create l_db_selection.make + db_selection := l_db_selection + items := l_store.execute_reader (l_db_selection) + end + + if not keep_connection then + disconnect + end + set_successful + log.write_debug ( generator+".execute_reader Successful") + end + rescue + set_last_error_from_exception ("Store procedure execution") + log.write_critical (generator+ ".execute_reader " + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + + execute_writer + -- Execute stored procedure that update/add data. + local + l_db_change: DB_CHANGE + l_retried : BOOLEAN + do + if not l_retried then + if not keep_connection and not is_connected then + connect + end + + if attached store as l_store then + create l_db_change.make + db_update := l_db_change + l_store.execute_writer (l_db_change) + if not l_store.has_error then + db_control.commit + end + end + if not keep_connection then + disconnect + end + set_successful + log.write_debug ( generator+".execute_writer Successful") + end + rescue + set_last_error_from_exception ("Store procedure execution") + log.write_critical (generator+ ".execute_writer " + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + +feature -- SQL Queries + + execute_query + -- Execute query. + local + l_db_selection: DB_SELECTION + l_retried: BOOLEAN + do + if not l_retried then + if not keep_connection then + connect + end + + if attached query as l_query then + create l_db_selection.make + db_selection := l_db_selection + items := l_query.execute_reader (l_db_selection) + end + if not keep_connection then + disconnect + end + set_successful + end + rescue + set_last_error_from_exception ("execute_query") + log.write_critical (generator+ ".execute_query " + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + + + execute_change + -- Execute sqlquery that update/add data. + local + l_db_change: DB_CHANGE + l_retried : BOOLEAN + do + if not l_retried then + if not keep_connection and not is_connected then + connect + end + + if attached query as l_query then + create l_db_change.make + db_update := l_db_change + l_query.execute_change (l_db_change) + db_control.commit + end + if not keep_connection then + disconnect + end + set_successful + end + rescue + set_last_error_from_exception ("Store procedure execution") + log.write_critical (generator+ ".execute_writer " + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + + +feature -- Iteration + + start + -- Set the cursor on first element. + do + if attached db_selection as l_db_selection and then l_db_selection.container /= Void then + l_db_selection.start + end + end + + forth + -- Move cursor to next element. + do + if attached db_selection as l_db_selection then + l_db_selection.forth + else + check False end + end + end + + after: BOOLEAN + -- True for the last element. + do + if attached db_selection as l_db_selection and then l_db_selection.container /= Void then + Result := l_db_selection.after or else l_db_selection.cursor = Void + else + Result := True + end + end + + item: DB_TUPLE + -- Current element. + do + if attached db_selection as l_db_selection and then attached l_db_selection.cursor as l_cursor then + create {DB_TUPLE} Result.copy (l_cursor) + else + check False then end + end + end + + +feature {NONE} -- Implementation + + connection: DATABASE_CONNECTION + -- Database connection. + + db_control: DB_CONTROL + -- Database control. + do + Result := connection.db_control + end + + db_result: detachable DB_RESULT + -- Database query result. + + db_selection: detachable DB_SELECTION + -- Database selection. + + db_update: detachable DB_CHANGE + -- Database modification. + + last_query: DATE_TIME + -- Last time when a query was executed. + + keep_connection: BOOLEAN + -- Keep connection alive? + do + Result := connection.keep_connection + end + + connect + -- Connect to the database. + require else + db_control_not_void: db_control /= Void + do + if not is_connected then + db_control.connect + end + end + + disconnect + -- Disconnect from the database. + require else + db_control_not_void: db_control /= Void + do + db_control.disconnect + end + + is_connected: BOOLEAN + -- True if connected to the database. + require else + db_control_not_void: db_control /= Void + do + Result := db_control.is_connected + end + + affected_row_count: INTEGER + -- The number of rows changed, deleted, or inserted by the last statement. + do + if attached db_update as l_update then + Result := l_update.affected_row_count + end + end + +feature -- Result + + items : detachable LIST[DB_RESULT] + -- Query result. + + count: INTEGER + -- + do + if attached items as l_items then + Result := l_items.count + end + end + +end diff --git a/persistence/src/database/database_iteration_cursor.e b/persistence/src/database/database_iteration_cursor.e new file mode 100644 index 0000000..7c0f073 --- /dev/null +++ b/persistence/src/database/database_iteration_cursor.e @@ -0,0 +1,91 @@ +note + description: "External iteration cursor for {ESA_DATABASE_HANDLER}" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_ITERATION_CURSOR [G] + +inherit + + ITERATION_CURSOR [G] + + ITERABLE [G] + +create + make + +feature -- Initialization + + make (a_handler: DATABASE_HANDLER; a_action: like action) + -- Create an iterator and set `db_handlet' to `a_handler' + -- `action' to `a_action' + do + db_handler := a_handler + action := a_action + ensure + db_handler_set: db_handler = a_handler + action_set: action = a_action + end + +feature -- Access + + item: G + -- Item at current cursor position. + do + Result := action.item ([db_item]) + end + + db_item: DB_TUPLE + -- Current element. + do + if attached {DB_TUPLE} db_handler.item as l_item then + Result := l_item + else + check False then + end + end + end + +feature -- Status report + + after: BOOLEAN + -- Are there no more items to iterate over? + do + Result := db_handler.after + end + +feature -- Cursor movement + + start + -- Set the cursor on first element. + do + db_handler.start + end + + forth + -- Move to next position. + do + db_handler.forth + end + +feature -- Cursor + + new_cursor: DATABASE_ITERATION_CURSOR [G] + -- + do + Result := twin + Result.start + end + +feature -- Action + + action: FUNCTION [ANY, detachable TUPLE [], G] + -- Agent to create a new item of type G. + +feature {NONE} -- Implementation + + db_handler: DATABASE_HANDLER + -- Associated handler used for iteration. + +end diff --git a/persistence/src/database/database_null.e b/persistence/src/database/database_null.e new file mode 100644 index 0000000..9d5dc15 --- /dev/null +++ b/persistence/src/database/database_null.e @@ -0,0 +1,538 @@ +note + description: "Null object to meet void safe" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_NULL + +inherit + DATABASE + + +feature -- Acccess + + database_handle_name: STRING = "NULL" + -- Handle name + +feature -- For DATABASE_STATUS + + is_error_updated: BOOLEAN + -- Has an NULL function been called since last update which may have + -- updated error code, error message? + + is_warning_updated: BOOLEAN + -- Has an ODBC function been called since last update which may have + -- updated warning message? + + found: BOOLEAN + -- Is there any record matching the last + -- selection condition used ? + + clear_error + -- Reset database error status. + do + end + + insert_auto_identity_column: BOOLEAN = False + -- For INSERTs and UPDATEs should table auto-increment identity columns be explicitly included in the statement? + +feature -- For DATABASE_CHANGE + + descriptor_is_available: BOOLEAN + do + end + + +feature -- For DATABASE_FORMAT + + date_to_str (object: DATE_TIME): STRING + -- String representation in SQL of `object' + -- For ODBC, ORACLE + do + create Result.make_empty + end + + string_format (object: detachable STRING): STRING + -- String representation in SQL of `object' + obsolete + "Use `string_format_32' instead." + do + Result := "" + end + + string_format_32 (object: detachable READABLE_STRING_GENERAL): STRING_32 + -- String representation in SQL of `object' + do + Result := "" + end + + True_representation: STRING + -- Database representation of the boolean True + do + Result := "" + end + + False_representation: STRING + -- Database representation of the boolean False + do + Result := "" + end + +feature -- For DATABASE_SELECTION, DATABASE_CHANGE + + normal_parse: BOOLEAN + -- Should the SQL string be normal parsed, + -- using SQL_SCAN? + do + end + +feature -- DATABASE_STRING + + sql_name_string: STRING + -- SQL type name of string + do + Result := "" + end + +feature -- DATABASE_REAL + + sql_name_real: STRING + -- SQL type name for real + do + Result := "" + end + +feature -- DATABASE_DATETIME + + sql_name_datetime: STRING + -- SQL type name for datetime + do + Result := "" + end + +feature -- DATABASE_DECIMAL + + sql_name_decimal: STRING + -- SQL type name for decimal + do + Result := "" + end + +feature -- DATABASE_DOUBLE + + sql_name_double: STRING + -- SQL type name for double + do + Result := "" + end + +feature -- DATABASE_CHARACTER + + sql_name_character: STRING + -- SQL type name for character + do + Result := "" + end +feature -- DATABASE_INTEGER + + sql_name_integer: STRING + -- SQL type name for integer + do + Result := "" + end + + sql_name_integer_16: STRING + -- SQL type name for integer + do + Result := "" + end + + sql_name_integer_64: STRING + -- SQL type name for integer + do + Result := "" + end +feature -- DATABASE_BOOLEAN + + sql_name_boolean: STRING + -- SQL type name for boolean + do + Result := "" + end + +feature -- LOGIN and DATABASE_APPL only for password_ok + + + password_ok (upasswd: STRING): BOOLEAN + -- Can the user password be Void? + do + end + + password_ensure (name, passwd, uname, upasswd: STRING): BOOLEAN + -- Is name equal to uname and passwd equal to upasswd? + do + end + +feature -- For DATABASE_PROC + + + support_sql_of_proc: BOOLEAN + -- Does the database support SQL attachment to the stored procedure? + do + end + + support_stored_proc: BOOLEAN + -- Does the database support creating a stored procedure? + do + end + + sql_as: STRING + -- Creating a stored procedure "as"... + do + Result := "" + end + + sql_end: STRING + -- End of the stored procedure creation string. + do + Result := "" + end + + sql_execution: STRING + -- Begining of the stored procedure execution string. + do + Result := "" + end + + sql_creation: STRING + -- Begining of the stored procedure creation string. + do + Result := "" + end + + sql_after_exec: STRING + -- End of the stored procedure execution string. + do + Result := "" + end + + support_drop_proc: BOOLEAN + -- Does the database support stored procedure dropping from server? + do + end + + name_proc_lower: BOOLEAN + -- Has the name of the stored procedure to be in lower case? + do + end + + map_var_between: STRING + -- @ symbol for ODBC and Sybase + do + Result := "" + end + + map_var_name_32 (par_name: READABLE_STRING_GENERAL): STRING_32 + -- Redefined for Sybase + do + Result := "" + end + + Select_text_32 (proc_name: READABLE_STRING_GENERAL): STRING_32 + -- SQL query to get stored procedure text + do + Result := "" + end + + Select_exists_32 (name: READABLE_STRING_GENERAL): STRING_32 + -- SQL query to test stored procedure existing + do + Result := "" + end + + + Selection_string (rep_qualifier, rep_owner, repository_name: STRING): STRING + -- String to select the table needed + do + Result := "" + end + + sql_string: STRING + -- Database type of a string + -- with a size less than Max_char_size + do + Result := "" + end + + sql_string2 (int: INTEGER): STRING + -- Database type of a string + -- with a size more than Max_char_size + do + Result := "" + end + + sql_wstring: STRING + -- Database type of a string + -- with a size less than Max_char_size + do + Result := "" + end + + sql_wstring2 (int: INTEGER): STRING + -- Database type of a string + -- with a size more than Max_char_size + do + Result := "" + end + +feature -- External features + + get_error_message: POINTER + -- Function related with the error processing + do + create Result + end + + get_error_message_string: STRING_32 + -- Function related with the error processing + do + Result := "" + end + + get_error_code: INTEGER + -- Function related with the error processing + do + end + + get_warn_message: POINTER + -- Function related with the error processing + do + create Result + end + + get_warn_message_string: STRING_32 + -- Function related with the error processing + do + Result := "" + end + + new_descriptor: INTEGER + -- A descriptor is used to store a row fetched by FETCH command + -- Whenever perform a SELECT statement, allocate a new descriptor + -- by int_new_descriptor(), the descriptor is freed + -- when the SELECT statement terminates. + do + end + + init_order (no_descriptor: INTEGER; command: READABLE_STRING_GENERAL) + -- In DYNAMICALLY EXECUTE mode perform the SQL statement + -- But this routine only get things ready for dynamic execution: + -- 1. get the SQL statement PREPAREd; and check if there are + -- warning message for the SQL statement; + -- 2. DESCRIBE the SQL statement and get enough information to + -- allocate enough memory space for the corresponding descriptor. + do + end + + start_order (no_descriptor: INTEGER) + -- Finish execution of a SQL statement in DYNAMICLLY EXECUTION mode: */ + -- 1. if the PREPAREd SQL statement is a NON_SELECT statement, + -- just EXECUTE it; otherwise, DEFINE a CURSOR for it and + -- OPEN the CURSOR. In the process, if error occurs, do some + -- clearence; + do + end + + next_row (no_descriptor: INTEGER) + -- A SELECT statement is now being executed in DYNAMIC EXECUTION mode, + -- the routine is to FETCH a new tuple from database + -- and if a new tuple is fetched, return 1 otherwise return 0. + do + end + + terminate_order (no_descriptor: INTEGER) + -- A SQL has been performed in DYNAMIC EXECUTION mode, + -- so the routine is to do some clearence: + -- 1. if the DYNAMICALLY EXECUTED SQL statement is a NON_SELECT + -- statement, just free the memory for ODBCSQLDA and clear + -- the cell in 'descriptor' to NULL; otherwise, CLOSE the CURSOR + -- and then do the same clearence. + -- 2. return error number. + do + end + + close_cursor (no_descriptor: INTEGER) + -- A SQL has been performed in DYNAMIC EXECUTION mode, + -- Then if the DYNAMICALLY EXECUTED SQL statement is a SELECT + -- statement, then the cursor is closed. + -- Then one can do an other selection on the previous cursor. + do + end + + exec_immediate (no_descriptor: INTEGER; command: READABLE_STRING_GENERAL) + -- In IMMEDIATE EXECUTE mode perform the SQL statement, + -- and then check if there is warning message for the execution, + do + end + + put_col_name (no_descriptor: INTEGER; index: INTEGER; ar: STRING; max_len:INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + put_data (no_descriptor: INTEGER; index: INTEGER; ar: STRING; max_len:INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + put_data_32 (no_descriptor: INTEGER; index: INTEGER; ar: STRING_32; max_len:INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + + conv_type (indicator: INTEGER; index: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + --| FIXME + --| This description really does not explain a thing... + do + end + + get_count (no_descriptor: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_data_len (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_col_len (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_col_type (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_integer_data (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_integer_16_data (no_descriptor: INTEGER; ind: INTEGER): INTEGER_16 + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_integer_64_data (no_descriptor: INTEGER; ind: INTEGER): INTEGER_64 + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_float_data (no_descriptor: INTEGER; ind: INTEGER): DOUBLE + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_real_data (no_descriptor: INTEGER; ind: INTEGER): REAL + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_boolean_data (no_descriptor: INTEGER; ind: INTEGER): BOOLEAN + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + is_null_data (no_descriptor: INTEGER; ind: INTEGER): BOOLEAN + -- Is last retrieved data null? + do + end + + get_date_data (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_hour (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_sec (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_min (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_year (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_day (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_month (no_descriptor: INTEGER; ind: INTEGER): INTEGER + -- Function used to get data from structure SQLDA filled by FETCH clause. + do + end + + get_decimal (no_descriptor: INTEGER; ind: INTEGER): detachable TUPLE [digits: STRING_8; sign, precision, scale: INTEGER] + -- Function used to get decimal info + do + end + + + database_make (i: INTEGER) + -- Initialize database c-module + do + end + + connect (user_name, user_passwd, data_source, application, hostname, role_id: STRING; role_passwd: detachable STRING; group_id: STRING) + -- Connect to database + do + end + + connect_by_connection_string (a_connect_string: STRING) + -- Connect to database by connection string + do + end + + disconnect + -- Disconnect the current connection with an database + do + end + + commit + -- Commit the current transaction + do + end + + rollback + -- Commit the current transaction + do + end + + trancount: INTEGER + -- Return the number of transactions now active + do + end + + begin + -- Begin a data base transaction + do + end + +end diff --git a/persistence/src/database/database_query.e b/persistence/src/database/database_query.e new file mode 100644 index 0000000..5dba725 --- /dev/null +++ b/persistence/src/database/database_query.e @@ -0,0 +1,137 @@ +note + description: "Abstract Database Query" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_QUERY + +inherit + + SHARED_LOGGER + + REFACTORING_HELPER + +create + data_reader + +feature -- Intialization + + data_reader (a_query: STRING; a_parameters: STRING_TABLE [detachable ANY]) + -- SQL data reader for the query `a_query' with arguments `a_parameters' + do + log.write_information (generator + ".data_reader" + " execute query: " + a_query) + log.write_debug (generator + ".data_reader" + " arguments:" + log_parameters (a_parameters)) + query := a_query + parameters := a_parameters + ensure + query_set: query = a_query + parameters_set: parameters = a_parameters + end + + execute_reader (a_base_selection: DB_SELECTION): detachable LIST [DB_RESULT] + -- Execute the Current sql query. + do + to_implement ("Check test dynamic sequel. to redesign.") + create {ARRAYED_LIST [DB_RESULT]} Result.make (100) + a_base_selection.set_container (Result) + set_map_name (a_base_selection) + a_base_selection.set_query (query) + a_base_selection.execute_query + if a_base_selection.is_ok then + a_base_selection.load_result + Result := a_base_selection.container + else + log.write_error (generator + "." + a_base_selection.error_message_32) + end + unset_map_name (a_base_selection) + a_base_selection.terminate + end + + execute_change (a_base_change: DB_CHANGE) + -- Execute the Current sql query . + do + to_implement ("Check test dynamic sequel. to redesign.") + set_map_name (a_base_change) + a_base_change.set_query (query) + a_base_change.execute_query + unset_map_name (a_base_change) + end + +feature -- Access + + query: STRING + -- SQL query to execute. + + parameters: STRING_TABLE [detachable ANY] + -- query parameters. + +feature -- Status Report + + has_error: BOOLEAN + -- is there an error? + + error_message: detachable STRING_32 + -- Error message if any. + + error_code: INTEGER + -- Error code. + +feature {NONE} -- Implementation + + set_map_name (a_base_selection: DB_EXPRESSION) + -- Store parameters `item' and their `key'. + do + from + parameters.start + until + parameters.after + loop + a_base_selection.set_map_name (parameters.item_for_iteration, parameters.key_for_iteration) + parameters.forth + end + end + + unset_map_name (a_base_selection: DB_EXPRESSION) + -- Remove parameters item associated with key `key'. + do + from + parameters.start + until + parameters.after + loop + a_base_selection.unset_map_name (parameters.key_for_iteration) + parameters.forth + end + end + + log_parameters (a_parameters: like parameters): STRING + -- Parameters to log with name and value + -- exclude sensitive information. + do + create Result.make_empty + from + a_parameters.start + until + a_parameters.after + loop + Result.append ("name:") + Result.append (a_parameters.key_for_iteration.as_string_32) + Result.append (", value:") + if + a_parameters.key_for_iteration.has_substring ("Password") or else + a_parameters.key_for_iteration.has_substring ("password") + then + -- Data to exclude + else + if attached a_parameters.item_for_iteration as l_item then + Result.append (l_item.out) + end + end + Result.append ("%N") + a_parameters.forth + end + end + + +end -- ESA_DATABASE_QUERY diff --git a/persistence/src/database/database_sql_server_encoder.e b/persistence/src/database/database_sql_server_encoder.e new file mode 100644 index 0000000..c576ef9 --- /dev/null +++ b/persistence/src/database/database_sql_server_encoder.e @@ -0,0 +1,33 @@ +note + description: "Help to encode sql queries, to prevent sql injections." + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + EIS: "SQL server injection", "src=http://blogs.msdn.com/b/raulga/archive/2007/01/04/dynamic-sql-sql-injection.aspx", "protocol=url" +expanded class + DATABASE_SQL_SERVER_ENCODER + +inherit + + SHARED_LOGGER + +feature -- Escape SQL input + + encode (a_string:READABLE_STRING_32): READABLE_STRING_32 + -- Escape single quote (') and braces ([,]). + local + l_string: STRING + do + l_string := a_string.twin + if not l_string.is_empty then + l_string.replace_substring_all ("[", "[[") + l_string.replace_substring_all ("]", "]]") + if l_string.index_of ('%'', 1) > 0 then + l_string.replace_substring ("[", 1, l_string.index_of ('%'', 1)) + end + if l_string.last_index_of ('%'', l_string.count) > 0 then + l_string.replace_substring ("]", l_string.last_index_of ('%'', l_string.count), l_string.count) + end + end + Result := l_string + end +end diff --git a/persistence/src/database/database_store_procedure.e b/persistence/src/database/database_store_procedure.e new file mode 100644 index 0000000..82c9fa8 --- /dev/null +++ b/persistence/src/database/database_store_procedure.e @@ -0,0 +1,193 @@ +note + description: "Database Store Procedure" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_STORE_PROCEDURE + +inherit + + SHARED_ERROR + +create + data_reader, data_writer + +feature -- Intialization + + data_reader (a_sp: STRING; a_parameters: HASH_TABLE [ANY, STRING_32]) + -- SQL data reader for the stored procedure `a_sp' with arguments `a_parameters'. + local + l_retried: BOOLEAN + do + log.write_information (generator + ".data_reader" + " execute store procedure: " + a_sp) + log.write_debug (generator + ".data_reader" + " arguments:" + log_parameters (a_parameters)) + if not l_retried then + stored_procedure := a_sp + parameters := a_parameters + create proc.make (stored_procedure) + proc.load + if not a_parameters.is_empty then + proc.set_arguments_32 (a_parameters.current_keys, a_parameters.linear_representation.to_array) + end + if proc.exists then + if proc.text_32 /= Void then + debug + log.write_debug ( generator + ".data_reader: " + proc.text_32) + end + end + else + has_error := True + error_message := proc.error_message_32 + error_code := proc.error_code + log.write_error (generator + ".data_witer message:" + proc.error_message_32 + " code:" + proc.error_code.out) + end + else + stored_procedure := a_sp + parameters := a_parameters + create proc.make (stored_procedure) + end + rescue + set_last_error_from_exception ("SQL execution") + log.write_critical (generator+ ".data_reader " + last_error_message) + l_retried := True + retry + end + + data_writer (a_sp: STRING; a_parameters: HASH_TABLE [ANY, STRING_32]) + -- SQL data reader for the stored procedure `a_sp' with arguments `a_parameters' + local + l_retried: BOOLEAN + do + log.write_information (generator + ".data_reader" + " execute store procedure: " + a_sp) + log.write_debug (generator + ".data_reader" + " arguments:" + log_parameters (a_parameters)) + if not l_retried then + stored_procedure := a_sp + parameters := a_parameters + create proc.make (stored_procedure) + proc.load + proc.set_arguments_32 (a_parameters.current_keys, a_parameters.linear_representation.to_array) + if proc.exists then + if proc.text_32 /= Void then + debug + log.write_debug ( generator + ".data_writer: " + proc.text_32) + end + end + else + has_error := True + error_message := proc.error_message_32 + error_code := proc.error_code + log.write_error (generator + ".data_witer message:" + proc.error_message_32 + " code:" + proc.error_code.out) + end + else + stored_procedure := a_sp + parameters := a_parameters + create proc.make (stored_procedure) + end + rescue + set_last_error_from_exception ("SQL execution") + log.write_critical (generator+ ".data_reader " + last_error_message) + l_retried := True + retry + end + + + execute_reader (a_base_selection: DB_SELECTION): detachable LIST [DB_RESULT] + -- Execute the Current store procedure. + do + create {ARRAYED_LIST [DB_RESULT]} Result.make (100) + a_base_selection.set_container (Result) + set_map_name (a_base_selection) + proc.execute (a_base_selection) + a_base_selection.load_result + Result := a_base_selection.container + unset_map_name (a_base_selection) + end + + execute_writer (a_base_change: DB_CHANGE) + -- Execute the Current store procedure. + do + set_map_name (a_base_change) + proc.execute (a_base_change) + unset_map_name (a_base_change) + end + +feature -- Access + + proc: DB_PROC + -- object to create and execute stored procedure. + + parameters: HASH_TABLE [detachable ANY, STRING_32] + -- Parameters to be used by the stored procedure. + + stored_procedure: STRING + -- Store procedure to execute + +feature -- Status Report + + has_error: BOOLEAN + -- Is there an error. + + error_message: detachable STRING_32 + -- Last error message. + + error_code: INTEGER + -- Last error code. + +feature {NONE} -- Implementation + + set_map_name (a_base_selection: DB_EXPRESSION) + -- Store parameters `item' and their `key'. + do + from + parameters.start + until + parameters.after + loop + a_base_selection.set_map_name (parameters.item_for_iteration, parameters.key_for_iteration) + parameters.forth + end + end + + unset_map_name (a_base_selection: DB_EXPRESSION) + -- Remove parameters item associated with key `key'. + do + from + parameters.start + until + parameters.after + loop + a_base_selection.unset_map_name (parameters.key_for_iteration) + parameters.forth + end + end + + log_parameters (a_parameters: like parameters): STRING + -- Parameters to log with name and value + -- exclude sensitive information. + do + create Result.make_empty + from + a_parameters.start + until + a_parameters.after + loop + Result.append ("name:") + Result.append (a_parameters.key_for_iteration) + Result.append (", value:") + if + a_parameters.key_for_iteration.has_substring ("Password") or else + a_parameters.key_for_iteration.has_substring ("password") + then + -- Data to exclude + else + if attached a_parameters.item_for_iteration as l_item then + Result.append (l_item.out) + end + end + Result.append ("%N") + a_parameters.forth + end + end + +end diff --git a/persistence/src/database/parameter_name_helper.e b/persistence/src/database/parameter_name_helper.e new file mode 100644 index 0000000..3c614ee --- /dev/null +++ b/persistence/src/database/parameter_name_helper.e @@ -0,0 +1,23 @@ +note + description: "Helper for paramenter Names" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +deferred class + PARAMETER_NAME_HELPER + +feature -- String + + string_parameter (a_value: STRING; a_length: INTEGER): STRING + -- Adjust a parameter `a_value' to the lenght `a_length'. + require + valid_length: a_length > 0 + do + if a_value.count <= a_length then + Result := a_value + else + create Result.make_from_string (a_value.substring (1, a_length)) + end + end + +end diff --git a/persistence/src/model/cms_node.e b/persistence/src/model/cms_node.e new file mode 100644 index 0000000..2ac72af --- /dev/null +++ b/persistence/src/model/cms_node.e @@ -0,0 +1,114 @@ +note + description: "Summary description for {NODE}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_NODE + +create + make + +feature{NONE} -- Initialization + make (a_content: READABLE_STRING_32; a_summary:READABLE_STRING_32; a_title: READABLE_STRING_32) + local + l_time: DATE_TIME + do + create l_time.make_now_utc + set_content (a_content) + set_summary (a_summary) + set_title (a_title) + set_creation_date (l_time) + set_modification_date (l_time) + set_publication_date (l_time) + ensure + content_set: content = a_content + summary_set: summary = a_summary + title_set: title = a_title + end + +feature -- Access + + content: READABLE_STRING_32 + -- Content of the node. + + summary: READABLE_STRING_32 + -- A short summary of the node. + + title: READABLE_STRING_32 + -- Full title of the node. + + modification_date: DATE_TIME + -- When the node was updated. + + creation_date: DATE_TIME + -- When the node was created. + + publication_date: DATE_TIME + -- When the node was published. + + publication_date_output: READABLE_STRING_32 + + id: INTEGER_64 assign set_id + -- Unique id. + +feature -- Element change + + set_content (a_content: like content) + -- Assign `content' with `a_content'. + do + content := a_content + ensure + content_assigned: content = a_content + end + + set_summary (a_summary: like summary) + -- Assign `summary' with `a_summary'. + do + summary := a_summary + ensure + summary_assigned: summary = a_summary + end + + set_title (a_title: like title) + -- Assign `title' with `a_title'. + do + title := a_title + ensure + title_assigned: title = a_title + end + + set_modification_date (a_modification_date: like modification_date) + -- Assign `modification_date' with `a_modification_date'. + do + modification_date := a_modification_date + ensure + modification_date_assigned: modification_date = a_modification_date + end + + set_creation_date (a_creation_date: like creation_date) + -- Assign `creation_date' with `a_creation_date'. + do + creation_date := a_creation_date + ensure + creation_date_assigned: creation_date = a_creation_date + end + + set_publication_date (a_publication_date: like publication_date) + -- Assign `publication_date' with `a_publication_date'. + do + publication_date := a_publication_date + publication_date_output := publication_date.formatted_out ("yyyy/[0]mm/[0]dd") + ensure + publication_date_assigned: publication_date = a_publication_date + end + + set_id (an_id: like id) + -- Assign `id' with `an_id'. + do + id := an_id + ensure + id_assigned: id = an_id + end + +end diff --git a/persistence/src/model/cms_user.e b/persistence/src/model/cms_user.e new file mode 100644 index 0000000..a0aa0cb --- /dev/null +++ b/persistence/src/model/cms_user.e @@ -0,0 +1,68 @@ +note + description: "Summary description for {USER}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_USER + +create + make + +feature {NONE} -- Initialization + + make (a_name: READABLE_STRING_32) + -- Create an object with name `a_name'. + do + name := a_name + end + +feature -- Access + + id: INTEGER_64 + -- Unique id. + + name: READABLE_STRING_32 + -- User name. + + password: detachable READABLE_STRING_32 + -- User password. + + email: detachable READABLE_STRING_32 + -- User email. + +feature -- Change element + + set_id (a_id: like id) + -- Set `id' with `a_id'. + do + id := a_id + ensure + id_set: id = a_id + end + + set_name (n: like name) + -- Set `name' with `n'. + do + name := n + ensure + name_set: name = n + end + + set_password (p: like password) + -- Set `password' with `p'. + do + password := p + ensure + password_set: password = p + end + + set_email (m: like email) + -- Set `email' with `m'. + do + email := m + ensure + email_set: email = m + end + +end diff --git a/persistence/src/provider/node_data_provider.e b/persistence/src/provider/node_data_provider.e new file mode 100644 index 0000000..09802ad --- /dev/null +++ b/persistence/src/provider/node_data_provider.e @@ -0,0 +1,272 @@ +note + description: "Database access for node uses cases." + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + NODE_DATA_PROVIDER + +inherit + + PARAMETER_NAME_HELPER + + SHARED_ERROR + + REFACTORING_HELPER + +create + make + +feature -- Initialization + + make (a_connection: DATABASE_CONNECTION) + -- Create a data provider. + do + create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) + post_execution + end + + db_handler: DATABASE_HANDLER + -- Db handler. + +feature -- Status Report + + is_successful: BOOLEAN + -- Is the last execution sucessful? + do + Result := db_handler.successful + end + +feature -- Access + + nodes: DATABASE_ITERATION_CURSOR [CMS_NODE] + -- List of nodes. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (0) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_nodes, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_node) + post_execution + end + + recent_nodes (a_lower, a_rows: INTEGER): DATABASE_ITERATION_CURSOR [CMS_NODE] + -- The most recent `a_rows'. + local + l_parameters: STRING_TABLE [ANY] + l_query: STRING + do + log.write_information (generator + ".recent_nodes") + create l_parameters.make (2) + l_parameters.put (a_rows, "rows") + create l_query.make_from_string (select_recent_nodes) + l_query.replace_substring_all ("$offset", a_lower.out) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (l_query, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_node) + post_execution + end + + node (a_id: INTEGER_64): detachable CMS_NODE + -- Node for the given id `a_id', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (1) + l_parameters.put (a_id,"id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_node_by_id, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_node + end + post_execution + end + +feature -- Basic operations + + new_node (a_node: CMS_NODE) + -- Create a new node. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (6) + l_parameters.put (a_node.title, "title") + l_parameters.put (a_node.summary, "summary") + l_parameters.put (a_node.content, "content") + l_parameters.put (a_node.publication_date, "publication_date") + l_parameters.put (a_node.creation_date, "creation_date") + l_parameters.put (a_node.modification_date, "modification_date") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_insert_node, l_parameters)) + db_handler.execute_change + post_execution + end + + update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + -- Update node title for the corresponding the report with id `a_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (3) + l_parameters.put (a_title, "title") + l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") + l_parameters.put (a_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_update_node_title, l_parameters)) + db_handler.execute_change + post_execution + end + + update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + -- Update node summary for the corresponding the report with id `a_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (3) + l_parameters.put (a_summary, "summary") + l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") + l_parameters.put (a_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_update_node_summary, l_parameters)) + db_handler.execute_change + post_execution + end + + update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + -- Update node content for the corresponding the report with id `a_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (3) + l_parameters.put (a_content, "content") + l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") + l_parameters.put (a_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_update_node_content, l_parameters)) + db_handler.execute_change + post_execution + end + + update_node (a_node: CMS_NODE) + -- Update node. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (7) + l_parameters.put (a_node.title, "title") + l_parameters.put (a_node.summary, "summary") + l_parameters.put (a_node.content, "content") + l_parameters.put (a_node.publication_date, "publication_date") + l_parameters.put (a_node.creation_date, "creation_date") + l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") + l_parameters.put (a_node.id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_update_node, l_parameters)) + db_handler.execute_change + post_execution + end + + delete_node (a_id: INTEGER_64;) + -- Delete node with id `a_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (1) + l_parameters.put (a_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_delete_node, l_parameters)) + db_handler.execute_change + post_execution + end + +feature -- Connection + + connect + -- Connect to the database. + do + if not db_handler.is_connected then + db_handler.connect + end + end + + disconnect + -- Disconnect to the database. + do + if db_handler.is_connected then + db_handler.disconnect + end + end + +feature {NONE} -- Queries + + Select_nodes: STRING = "select * from Nodes;" + -- SQL Query to retrieve all nodes. + + Select_node_by_id: STRING = "select * from Nodes where id =:id order by id desc, publication_date desc;" + + Select_recent_nodes: STRING = "select * from Nodes order by id desc, publication_date desc Limit $offset , :rows " + + SQL_Insert_node: STRING = "insert into nodes (title, summary, content, publication_date, creation_date, modification_date) values (:title, :summary, :content, :publication_date, :creation_date, :modification_date);" + -- SQL Insert to add a new node. + + SQL_Update_node_title: STRING ="update nodes SET title=:title, modification_date=:modification_date where id=:id;" + -- SQL update node title. + + SQL_Update_node_summary: STRING ="update nodes SET summary=:summary, modification_date=:modification_date where id=:id;" + -- SQL update node summary. + + SQL_Update_node_content: STRING ="update nodes SET content=:content, modification_date=:modification_date where id=:id;" + -- SQL node content. + + SQL_Update_node : STRING = "update nodes SET title=:title, summary=:summary, content=:content, publication_date=:publication_date, creation_date=:creation_date, modification_date=:modification_date where id=:id;" + -- SQL node. + + SQL_Delete_node: STRING = "delete from nodes where id=:id;" + + +feature -- New Object + + fetch_node: CMS_NODE + do + create Result.make ("", "", "") + if attached db_handler.read_integer_32 (1) as l_id then + Result.set_id (l_id) + end + if attached db_handler.read_date_time (2) as l_pd then + Result.set_publication_date (l_pd) + end + if attached db_handler.read_date_time (3) as l_cd then + Result.set_creation_date (l_cd) + end + if attached db_handler.read_date_time (4) as l_md then + Result.set_modification_date (l_md) + end + if attached db_handler.read_string (5) as l_t then + Result.set_title (l_t) + end + if attached db_handler.read_string (6) as l_s then + Result.set_summary (l_s) + end + if attached db_handler.read_string (7) as l_c then + Result.set_content (l_c) + end + end + +feature {NONE} -- Implementation + + post_execution + -- Post database execution. + do + if db_handler.successful then + set_successful + else + if attached db_handler.last_error then + set_last_error_from_handler (db_handler.last_error) + end + end + end + +end diff --git a/persistence/src/provider/user_data_provider.e b/persistence/src/provider/user_data_provider.e new file mode 100644 index 0000000..81ce325 --- /dev/null +++ b/persistence/src/provider/user_data_provider.e @@ -0,0 +1,182 @@ +note + description: "Summary description for {USER_DATA_PROVIDER}." + date: "$Date$" + revision: "$Revision$" + +class + USER_DATA_PROVIDER + +inherit + + PARAMETER_NAME_HELPER + + SHARED_ERROR + + REFACTORING_HELPER + +create + make + +feature -- Initialization + + make (a_connection: DATABASE_CONNECTION) + -- Create a data provider. + do + create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) + post_execution + end + + db_handler: DATABASE_HANDLER + -- Db handler. + +feature -- Status Report + + is_successful: BOOLEAN + -- Is the last execution sucessful? + do + Result := db_handler.successful + end + +feature -- Basic Operations + + new_user (a_user_name: READABLE_STRING_32; a_password: READABLE_STRING_32; a_email: READABLE_STRING_32) + -- Create a new node. + local + l_parameters: STRING_TABLE [detachable ANY] + l_password_salt, l_password_hash: STRING + l_security: SECURITY_PROVIDER + do + create l_security + l_password_salt := l_security.salt + l_password_hash := l_security.password_hash (a_password, l_password_salt) + + log.write_information (generator + ".new_user") + create l_parameters.make (4) + l_parameters.put (a_user_name,"username") + l_parameters.put (l_password_hash,"password") + l_parameters.put (l_password_salt,"salt") + l_parameters.put (a_email,"email") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_insert_user, l_parameters)) + db_handler.execute_change + post_execution + end + + user (a_id: INTEGER_64): detachable CMS_USER + -- User for the given id `a_id', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (1) + l_parameters.put (a_id,"id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_user_by_id, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_user + end + post_execution + end + + user_by_name (a_name: READABLE_STRING_32): detachable CMS_USER + -- User for the given name `a_name', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".user_by_name") + create l_parameters.make (1) + l_parameters.put (a_name,"name") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_user_by_name, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_user + end + post_execution + end + + + user_by_email (a_email: detachable READABLE_STRING_32): detachable CMS_USER + -- User for the given email `a_email', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + log.write_information (generator + ".user_by_name") + create l_parameters.make (1) + l_parameters.put (a_email,"email") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_user_by_email, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_user + end + post_execution + end + + user_salt (a_username: READABLE_STRING_32): detachable READABLE_STRING_32 + -- User salt for the given user `a_username', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (1) + l_parameters.put (a_username,"name") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_salt_by_username, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + if attached db_handler.read_string (1) as l_salt then + Result := l_salt.as_string_32 + end + end + post_execution + end + +feature -- New Object + + fetch_user: CMS_USER + do + create Result.make ("") + if attached db_handler.read_integer_32 (1) as l_id then + Result.set_id (l_id) + end + if attached db_handler.read_string (2) as l_u then + Result.set_name (l_u) + end + if attached db_handler.read_string (3) as l_p then + Result.set_password (l_p) + end + if attached db_handler.read_string (5) as l_e then + Result.set_email (l_e) + end + end + +feature -- Sql Queries + + Select_user_by_id: STRING = "select * from Users where id =:id;" + -- Retrieve user by id if exists. + + Select_user_by_name: STRING = "select * from Users where username =:name;" + -- Retrieve user by name if exists. + + Select_user_by_email: STRING = "select * from Users where email =:email;" + -- Retrieve user by email if exists. + + Select_salt_by_username: STRING = "select salt from Users where username =:name;" + -- Retrieve salt by username if exists. + + SQL_Insert_user: STRING = "insert into users (username, password, salt, email) values (:username, :password, :salt, :email);" + -- SQL Insert to add a new node. + + +feature {NONE} -- Implementation + + post_execution + -- Post database execution. + do + if db_handler.successful then + set_successful + else + if attached db_handler.last_error then + set_last_error_from_handler (db_handler.last_error) + end + end + end + +end diff --git a/persistence/src/security/security_provider.e b/persistence/src/security/security_provider.e new file mode 100644 index 0000000..ae40a8c --- /dev/null +++ b/persistence/src/security/security_provider.e @@ -0,0 +1,153 @@ +note + description: "Provides security routine helpers" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + SECURITY_PROVIDER + +inherit + + REFACTORING_HELPER + +feature -- Access + + token: STRING + -- Cryptographic random base 64 string. + do + Result := salt_with_size (5) + -- Remove trailing equal sign + Result.keep_head (Result.count - 1) + end + + salt: STRING + -- Cryptographic random number of 16 bytes. + do + Result := salt_with_size (16) + end + + password: STRING + -- Cryptographic random password of 10 bytes. + do + Result := salt_with_size (10) + -- Remove trailing equal signs + Result.keep_head (Result.count - 2) + end + + password_hash (a_password, a_salt: STRING): STRING + -- Password hash based on password `a_password' and salt value `a_salt'. + do + Result := sha1_string (a_password + a_salt ) + end + +feature {NONE} -- Implementation + + salt_with_size (a_val: INTEGER): STRING + -- Return a salt with size `a_val'. + local + l_salt: SALT_XOR_SHIFT_64_GENERATOR + l_array: ARRAY [INTEGER_8] + i: INTEGER + do + create l_salt.make (a_val) + create l_array.make_empty + i := 1 + across + l_salt.new_sequence as c + loop + l_array.force (c.item.as_integer_8, i) + i := i + 1 + end + Result := encode_base_64 (l_array) + end + + sha1_string (a_str: STRING): STRING + -- SHA1 diggest of `a_str'. + do + sha1.update_from_string (a_str) + Result := sha1.digest_as_string + sha1.reset + end + + sha1: SHA1 + -- Create a SHA1 object. + once + create Result.make + end + +feature -- Encoding + + + encode_base_64 (bytes: SPECIAL [INTEGER_8]): STRING_8 + -- Encodes a byte array into a STRING doing base64 encoding. + local + l_output: SPECIAL [INTEGER_8] + l_remaining: INTEGER + i, ptr: INTEGER + char: CHARACTER + do + to_implement ("Check existing code to do that!!!.") + create l_output.make_filled (0, ((bytes.count + 2) // 3) * 4) + l_remaining := bytes.count + from + i := 0 + ptr := 0 + until + l_remaining <= 3 + loop + l_output [ptr] := encode_value (bytes [i] |>> 2) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF)) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i + 1] & 0xF) |<< 2) | ((bytes [i + 2] |>> 6) & 0x3)) + ptr := ptr + 1 + l_output [ptr] := encode_value (bytes [i + 2] & 0x3F) + ptr := ptr + 1 + l_remaining := l_remaining - 3 + i := i + 3 + end + -- encode when exactly 1 element (left) to encode + char := '=' + if l_remaining = 1 then + l_output [ptr] := encode_value (bytes [i] |>> 2) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i]) & 0x3) |<< 4) + ptr := ptr + 1 + l_output [ptr] := char.code.as_integer_8 + ptr := ptr + 1 + l_output [ptr] := char.code.as_integer_8 + ptr := ptr + 1 + end + + -- encode when exactly 2 elements (left) to encode + if l_remaining = 2 then + l_output [ptr] := encode_value (bytes [i] |>> 2) + ptr := ptr + 1 + l_output [ptr] := encode_value (((bytes [i] & 0x3) |<< 4) | ((bytes [i + 1] |>> 4) & 0xF)); + ptr := ptr + 1 + l_output [ptr] := encode_value ((bytes [i + 1] & 0xF) |<< 2); + ptr := ptr + 1 + l_output [ptr] := char.code.as_integer_8 + ptr := ptr + 1 + end + Result := "" + across + l_output as elem + loop + Result.append_character (elem.item.to_character_8) + end + end + + base64_map: SPECIAL [CHARACTER_8] + -- Table for Base64 encoding. + once + Result := ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").area + end + + encode_value (i: INTEGER_8): INTEGER_8 + -- Encode `i'. + do + Result := base64_map [i & 0x3F].code.as_integer_8 + end + +end diff --git a/persistence/src/string_helper.e b/persistence/src/string_helper.e new file mode 100644 index 0000000..f280e82 --- /dev/null +++ b/persistence/src/string_helper.e @@ -0,0 +1,53 @@ +note + description: "Summary description for {STRING_HELPER}." + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + STRING_HELPER + +feature -- Access + + is_blank (s: detachable READABLE_STRING_32): BOOLEAN + local + i,n: INTEGER + do + Result := True + if s /= Void then + from + i := 1 + n := s.count + until + i > n or not Result + loop + Result := s[i].is_space + i := i + 1 + end + end + end + + indented_text (pre: READABLE_STRING_8; t: READABLE_STRING_8): READABLE_STRING_8 + -- Indendted text. + local + s8: STRING_8 + do + s8 := t.string + s8.prepend (pre) + s8.replace_substring_all ("%N", "%N" + pre) + Result := s8 + end + + + json_encode (a_string: STRING): STRING + -- json encode `a_string'. + local + encode: SHARED_JSON_ENCODER + do + create encode + Result := encode.json_encoder.encoded_string (a_string) + debug + print ("%NResult" + Result) + end + end + +end diff --git a/persistence/tests/application.e b/persistence/tests/application.e new file mode 100644 index 0000000..aade7b5 --- /dev/null +++ b/persistence/tests/application.e @@ -0,0 +1,55 @@ +note + description : "tests application root class" + date : "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision : "$Revision: 95678 $" + +class + APPLICATION + +inherit + ARGUMENTS + +create + make + +feature {NONE} -- Initialization + + make + -- Run application. + local + user: USER_DATA_PROVIDER + node: NODE_DATA_PROVIDER + l_security: SECURITY_PROVIDER + do + create connection.login_with_schema ("cms", "root", "") +-- create user.make (connection) +---- user.new_user ("jv", "test","test@test.com") + +-- if attached user.user (3) as l_user then +-- if attached user.user_salt ("jv") as l_salt then +-- create l_security +-- if attached l_user.password as l_password then +-- print (l_password) +-- io.put_new_line +-- print (l_security.password_hash ("test", l_salt)) + +-- check same_string: l_security.password_hash ("test", l_salt).is_case_insensitive_equal (l_password) end +-- end +-- end +-- end + +-- if attached user.user_by_name ("jv") as l_user then +-- check l_user.id = 3 end +-- end + + create node.make (connection) + across node.recent_nodes (0, 5) as c loop + print (c.item) + end + + + end + + connection: DATABASE_CONNECTION_MYSQL + +end diff --git a/persistence/tests/tests.ecf b/persistence/tests/tests.ecf new file mode 100644 index 0000000..30118d7 --- /dev/null +++ b/persistence/tests/tests.ecf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + From 46362731f0d996c801438fbd453d527b3f9ca266 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 15 Sep 2014 16:04:18 -0300 Subject: [PATCH 002/386] Fixed typo. Server modes. --- Readme.txt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Readme.txt b/Readme.txt index f1e0c4f..08f1c7f 100644 --- a/Readme.txt +++ b/Readme.txt @@ -1,14 +1,14 @@ -CMS Hyperemedia API and Adaptive Web Design +CMS Hypermedia API and Adaptive Web Design ============================================ A content management system is not a simple domain. This example shows how to build a basic Hypermedia API for a CMS using HTML5 and progressive enhacement. -The idea is based on an existing Eiffel CMS, the goal is learn the domain and create a new modular CMS. +The idea is based on an existing [Eiffel CMS] (https://github.com/EiffelWebFramework/cms), the goal is learn the domain and create a new modular CMS. Persistence ============ -The current solution uses MySQL and only handle the nodes concept. +The current solution uses MySQL and only handle users and nodes concept. Authentication/Authorization @@ -26,7 +26,7 @@ The root uri: shows a predefined number of nodes the `n' most recent nodes. Guest users will be able to list all the nodes and view a particular node. -Logged in users (There is only one user: admin) +Logged in users. Logged users are able to Add a new node Edit an existing node @@ -36,6 +36,13 @@ Edit a node content Delete a node +Server Modes +============ + +api: HTML5 API +html: api with progresive enhacements css and js, server side rendering. +web: api with progresive enhacements css and js and Ajax calls. + References 1: http://codeartisan.blogspot.se/2012/07/using-html-as-media-type-for-your-api.html From 3a699b23a04962cf8d383e62c43702c45e5f4286 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 15 Sep 2014 23:27:11 -0300 Subject: [PATCH 003/386] Refactor persistence library. Added a persistence speficication cluster and implementation cluser, Implementation MySQL. Added test cases for mysql implementation. --- api/roc_api.ecf | 2 +- .../mysql/persistence_mysql.ecf} | 9 ++- .../mysql}/scripts/Readme.txt | 0 .../mysql}/scripts/create_database.sql | 4 +- .../mysql}/scripts/tables.sql | 0 .../mysql}/src/cms_storage_mysql.e | 0 .../src/database/data_parameters_names.e | 0 .../mysql}/src/database/database_config.e | 0 .../mysql}/src/database/database_connection.e | 0 .../src/database/database_connection_mysql.e | 0 .../src/database/database_connection_null.e | 0 .../mysql}/src/database/database_handler.e | 0 .../src/database/database_handler_impl.e | 0 .../src/database/database_iteration_cursor.e | 0 .../mysql}/src/database/database_null.e | 0 .../mysql}/src/database/database_query.e | 0 .../database/database_sql_server_encoder.e | 0 .../src/database/database_store_procedure.e | 0 .../src/database/parameter_name_helper.e | 0 .../mysql}/src/model/cms_node.e | 0 .../mysql}/src/model/cms_user.e | 0 .../mysql}/src/provider/node_data_provider.e | 0 .../mysql}/src/provider/user_data_provider.e | 0 .../mysql}/src/security/security_provider.e | 0 .../mysql}/src/string_helper.e | 0 .../mysql}/tests/application.e | 0 .../mysql/tests/nodes/node_test_set.e | 26 +++++++ .../mysql}/tests/tests.ecf | 2 +- .../mysql/tests/users/user_test_set.e | 78 +++++++++++++++++++ .../mysql/tests/util/abstract_db_test.e | 17 ++++ .../mysql/tests/util/clean_db.e | 66 ++++++++++++++++ persistence/{src => interface}/cms_storage.e | 0 32 files changed, 196 insertions(+), 8 deletions(-) rename persistence/{persistence.ecf => implementation/mysql/persistence_mysql.ecf} (81%) rename persistence/{ => implementation/mysql}/scripts/Readme.txt (100%) rename persistence/{ => implementation/mysql}/scripts/create_database.sql (97%) rename persistence/{ => implementation/mysql}/scripts/tables.sql (100%) rename persistence/{ => implementation/mysql}/src/cms_storage_mysql.e (100%) rename persistence/{ => implementation/mysql}/src/database/data_parameters_names.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_config.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_connection.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_connection_mysql.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_connection_null.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_handler.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_handler_impl.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_iteration_cursor.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_null.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_query.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_sql_server_encoder.e (100%) rename persistence/{ => implementation/mysql}/src/database/database_store_procedure.e (100%) rename persistence/{ => implementation/mysql}/src/database/parameter_name_helper.e (100%) rename persistence/{ => implementation/mysql}/src/model/cms_node.e (100%) rename persistence/{ => implementation/mysql}/src/model/cms_user.e (100%) rename persistence/{ => implementation/mysql}/src/provider/node_data_provider.e (100%) rename persistence/{ => implementation/mysql}/src/provider/user_data_provider.e (100%) rename persistence/{ => implementation/mysql}/src/security/security_provider.e (100%) rename persistence/{ => implementation/mysql}/src/string_helper.e (100%) rename persistence/{ => implementation/mysql}/tests/application.e (100%) create mode 100644 persistence/implementation/mysql/tests/nodes/node_test_set.e rename persistence/{ => implementation/mysql}/tests/tests.ecf (93%) create mode 100644 persistence/implementation/mysql/tests/users/user_test_set.e create mode 100644 persistence/implementation/mysql/tests/util/abstract_db_test.e create mode 100644 persistence/implementation/mysql/tests/util/clean_db.e rename persistence/{src => interface}/cms_storage.e (100%) diff --git a/api/roc_api.ecf b/api/roc_api.ecf index abec5e6..ab56bd2 100644 --- a/api/roc_api.ecf +++ b/api/roc_api.ecf @@ -16,7 +16,7 @@ - + diff --git a/persistence/persistence.ecf b/persistence/implementation/mysql/persistence_mysql.ecf similarity index 81% rename from persistence/persistence.ecf rename to persistence/implementation/mysql/persistence_mysql.ecf index e3b2f16..c3190a8 100644 --- a/persistence/persistence.ecf +++ b/persistence/implementation/mysql/persistence_mysql.ecf @@ -1,6 +1,6 @@ - - + + diff --git a/persistence/scripts/Readme.txt b/persistence/implementation/mysql/scripts/Readme.txt similarity index 100% rename from persistence/scripts/Readme.txt rename to persistence/implementation/mysql/scripts/Readme.txt diff --git a/persistence/scripts/create_database.sql b/persistence/implementation/mysql/scripts/create_database.sql similarity index 97% rename from persistence/scripts/create_database.sql rename to persistence/implementation/mysql/scripts/create_database.sql index 06d4fd0..55dffb4 100644 --- a/persistence/scripts/create_database.sql +++ b/persistence/implementation/mysql/scripts/create_database.sql @@ -12,7 +12,7 @@ USE `cms` ; -- Table `cms`.`nodes` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `cms`.`nodes` ( - `id` SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT, + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `publication_date` DATE NOT NULL, `creation_date` DATE NOT NULL, `modification_date` DATE NOT NULL, @@ -59,7 +59,7 @@ DEFAULT CHARACTER SET = latin1; -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `cms`.`users_nodes` ( `users_id` INT(10) UNSIGNED NOT NULL, - `nodes_id` SMALLINT(5) UNSIGNED NOT NULL, + `nodes_id` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`users_id`, `nodes_id`), INDEX `fk_users_has_nodes_nodes1_idx` (`nodes_id` ASC), INDEX `fk_users_has_nodes_users_idx` (`users_id` ASC), diff --git a/persistence/scripts/tables.sql b/persistence/implementation/mysql/scripts/tables.sql similarity index 100% rename from persistence/scripts/tables.sql rename to persistence/implementation/mysql/scripts/tables.sql diff --git a/persistence/src/cms_storage_mysql.e b/persistence/implementation/mysql/src/cms_storage_mysql.e similarity index 100% rename from persistence/src/cms_storage_mysql.e rename to persistence/implementation/mysql/src/cms_storage_mysql.e diff --git a/persistence/src/database/data_parameters_names.e b/persistence/implementation/mysql/src/database/data_parameters_names.e similarity index 100% rename from persistence/src/database/data_parameters_names.e rename to persistence/implementation/mysql/src/database/data_parameters_names.e diff --git a/persistence/src/database/database_config.e b/persistence/implementation/mysql/src/database/database_config.e similarity index 100% rename from persistence/src/database/database_config.e rename to persistence/implementation/mysql/src/database/database_config.e diff --git a/persistence/src/database/database_connection.e b/persistence/implementation/mysql/src/database/database_connection.e similarity index 100% rename from persistence/src/database/database_connection.e rename to persistence/implementation/mysql/src/database/database_connection.e diff --git a/persistence/src/database/database_connection_mysql.e b/persistence/implementation/mysql/src/database/database_connection_mysql.e similarity index 100% rename from persistence/src/database/database_connection_mysql.e rename to persistence/implementation/mysql/src/database/database_connection_mysql.e diff --git a/persistence/src/database/database_connection_null.e b/persistence/implementation/mysql/src/database/database_connection_null.e similarity index 100% rename from persistence/src/database/database_connection_null.e rename to persistence/implementation/mysql/src/database/database_connection_null.e diff --git a/persistence/src/database/database_handler.e b/persistence/implementation/mysql/src/database/database_handler.e similarity index 100% rename from persistence/src/database/database_handler.e rename to persistence/implementation/mysql/src/database/database_handler.e diff --git a/persistence/src/database/database_handler_impl.e b/persistence/implementation/mysql/src/database/database_handler_impl.e similarity index 100% rename from persistence/src/database/database_handler_impl.e rename to persistence/implementation/mysql/src/database/database_handler_impl.e diff --git a/persistence/src/database/database_iteration_cursor.e b/persistence/implementation/mysql/src/database/database_iteration_cursor.e similarity index 100% rename from persistence/src/database/database_iteration_cursor.e rename to persistence/implementation/mysql/src/database/database_iteration_cursor.e diff --git a/persistence/src/database/database_null.e b/persistence/implementation/mysql/src/database/database_null.e similarity index 100% rename from persistence/src/database/database_null.e rename to persistence/implementation/mysql/src/database/database_null.e diff --git a/persistence/src/database/database_query.e b/persistence/implementation/mysql/src/database/database_query.e similarity index 100% rename from persistence/src/database/database_query.e rename to persistence/implementation/mysql/src/database/database_query.e diff --git a/persistence/src/database/database_sql_server_encoder.e b/persistence/implementation/mysql/src/database/database_sql_server_encoder.e similarity index 100% rename from persistence/src/database/database_sql_server_encoder.e rename to persistence/implementation/mysql/src/database/database_sql_server_encoder.e diff --git a/persistence/src/database/database_store_procedure.e b/persistence/implementation/mysql/src/database/database_store_procedure.e similarity index 100% rename from persistence/src/database/database_store_procedure.e rename to persistence/implementation/mysql/src/database/database_store_procedure.e diff --git a/persistence/src/database/parameter_name_helper.e b/persistence/implementation/mysql/src/database/parameter_name_helper.e similarity index 100% rename from persistence/src/database/parameter_name_helper.e rename to persistence/implementation/mysql/src/database/parameter_name_helper.e diff --git a/persistence/src/model/cms_node.e b/persistence/implementation/mysql/src/model/cms_node.e similarity index 100% rename from persistence/src/model/cms_node.e rename to persistence/implementation/mysql/src/model/cms_node.e diff --git a/persistence/src/model/cms_user.e b/persistence/implementation/mysql/src/model/cms_user.e similarity index 100% rename from persistence/src/model/cms_user.e rename to persistence/implementation/mysql/src/model/cms_user.e diff --git a/persistence/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e similarity index 100% rename from persistence/src/provider/node_data_provider.e rename to persistence/implementation/mysql/src/provider/node_data_provider.e diff --git a/persistence/src/provider/user_data_provider.e b/persistence/implementation/mysql/src/provider/user_data_provider.e similarity index 100% rename from persistence/src/provider/user_data_provider.e rename to persistence/implementation/mysql/src/provider/user_data_provider.e diff --git a/persistence/src/security/security_provider.e b/persistence/implementation/mysql/src/security/security_provider.e similarity index 100% rename from persistence/src/security/security_provider.e rename to persistence/implementation/mysql/src/security/security_provider.e diff --git a/persistence/src/string_helper.e b/persistence/implementation/mysql/src/string_helper.e similarity index 100% rename from persistence/src/string_helper.e rename to persistence/implementation/mysql/src/string_helper.e diff --git a/persistence/tests/application.e b/persistence/implementation/mysql/tests/application.e similarity index 100% rename from persistence/tests/application.e rename to persistence/implementation/mysql/tests/application.e diff --git a/persistence/implementation/mysql/tests/nodes/node_test_set.e b/persistence/implementation/mysql/tests/nodes/node_test_set.e new file mode 100644 index 0000000..b7146ee --- /dev/null +++ b/persistence/implementation/mysql/tests/nodes/node_test_set.e @@ -0,0 +1,26 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + NODE_TEST_SET + +inherit + EQA_TEST_SET + +feature -- Test routines + + new_test_routine + -- New test routine + do + assert ("not_implemented", False) + end + +end + + diff --git a/persistence/tests/tests.ecf b/persistence/implementation/mysql/tests/tests.ecf similarity index 93% rename from persistence/tests/tests.ecf rename to persistence/implementation/mysql/tests/tests.ecf index 30118d7..cf85bf2 100644 --- a/persistence/tests/tests.ecf +++ b/persistence/implementation/mysql/tests/tests.ecf @@ -8,7 +8,7 @@ - + diff --git a/persistence/implementation/mysql/tests/users/user_test_set.e b/persistence/implementation/mysql/tests/users/user_test_set.e new file mode 100644 index 0000000..6b06e9f --- /dev/null +++ b/persistence/implementation/mysql/tests/users/user_test_set.e @@ -0,0 +1,78 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + USER_TEST_SET + +inherit + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + user_provider.new_user ("admin", "admin","admin@admin.com") + end + + on_clean + -- + do + end + +feature -- Test routines + + test_user_exist + -- User admin exist + do + assert ("Not void", attached user_provider.user_by_email ("admin@admin.com")) + assert ("Not void", attached user_provider.user (1)) + assert ("Not void", attached user_provider.user_by_name ("admin")) + end + + test_user_not_exist + -- Uset test does not exist. + do + assert ("Void", user_provider.user_by_email ("test@admin.com") = Void) + assert ("Void", user_provider.user(2) = Void ) + assert ("Void", user_provider.user_by_name ("test") = Void) + end + + test_new_user + do + user_provider.new_user ("test", "test","test@admin.com") + assert ("Not void", attached user_provider.user_by_email ("test@admin.com")) + assert ("Not void", attached user_provider.user (2)) + assert ("Not void", attached user_provider.user (2) as l_user and then l_user.id = 2 and then l_user.name ~ "test") + assert ("Not void", attached user_provider.user_by_name ("test")) + end + + +feature {NONE} -- Implementation + + user_provider: USER_DATA_PROVIDER + -- user provider. + once + create Result.make (connection) + end +end + + diff --git a/persistence/implementation/mysql/tests/util/abstract_db_test.e b/persistence/implementation/mysql/tests/util/abstract_db_test.e new file mode 100644 index 0000000..0e23859 --- /dev/null +++ b/persistence/implementation/mysql/tests/util/abstract_db_test.e @@ -0,0 +1,17 @@ +note + description: "Summary description for {ABSTRACT_DB_TEST}." + date: "$Date$" + revision: "$Revision$" + +class + ABSTRACT_DB_TEST + + +feature -- Database connection + + connection: DATABASE_CONNECTION_MYSQL + -- MYSQL database connection + once + create Result.login_with_schema ("cms_dev", "root", "") + end +end diff --git a/persistence/implementation/mysql/tests/util/clean_db.e b/persistence/implementation/mysql/tests/util/clean_db.e new file mode 100644 index 0000000..ef00839 --- /dev/null +++ b/persistence/implementation/mysql/tests/util/clean_db.e @@ -0,0 +1,66 @@ +note + description: "[ + Setting up database tests + 1. Put the database in a known state before running your test suite. + 2. Data reinitialization. For testing in developer sandboxes, something that you should do every time you rebuild the system, you may want to forgo dropping and rebuilding the database in favor of simply reinitializing the source data. + You can do this either by erasing all existing data and then inserting the initial data vales back into the database, or you can simple run updates to reset the data values. + The first approach is less risky and may even be faster for large amounts of data. - See more at: http://www.agiledata.org/essays/databaseTesting.html#sthash.6yVp35g8.dpuf + ]" + + date: "$Date$" + revision: "$Revision$" + EIS: "name=Database Testing", "src=http://www.agiledata.org/essays/databaseTesting.html", "protocol=uri" + +class + CLEAN_DB + +feature + + clean_db (a_connection: DATABASE_CONNECTION) + -- Clean db test. + local + l_parameters: STRING_TABLE[STRING] + do + create l_parameters.make (0) + + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_nodes, l_parameters)) + db_handler(a_connection).execute_change + + -- Clean Users + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_users, l_parameters)) + db_handler(a_connection).execute_change + + -- Reset Autoincremente + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_users_autoincrement, l_parameters)) + db_handler(a_connection).execute_change + + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_nodes_autoincrement, l_parameters)) + db_handler(a_connection).execute_change + end + + + +feature -- Database Hanlder + + db_handler (a_connection: DATABASE_CONNECTION): DATABASE_HANDLER + -- Db handler + once + create {DATABASE_HANDLER_IMPL} Result.make (a_connection) + end + + +feature -- Sql delete queries + + Sql_delete_users: STRING = "delete from Users" + -- Clean Users. + + Sql_delete_nodes: STRING = "delete from Nodes" + -- Clean Nodes. + + Rest_users_autoincrement: STRING = "ALTER TABLE Users AUTO_INCREMENT = 1" + -- reset autoincrement + + Rest_nodes_autoincrement: STRING = "ALTER TABLE Nodes AUTO_INCREMENT = 1" + -- reset autoincrement. + +end diff --git a/persistence/src/cms_storage.e b/persistence/interface/cms_storage.e similarity index 100% rename from persistence/src/cms_storage.e rename to persistence/interface/cms_storage.e From 57e2a4a4849b6e5ec520fd7f588a915d21c4ac51 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Tue, 16 Sep 2014 11:29:01 -0300 Subject: [PATCH 004/386] Updating loging messages on USER and NODE provider. Added test cases to node provider. --- .../mysql/src/provider/node_data_provider.e | 31 ++- .../mysql/src/provider/user_data_provider.e | 6 +- .../mysql/tests/nodes/node_test_set.e | 205 +++++++++++++++++- 3 files changed, 229 insertions(+), 13 deletions(-) diff --git a/persistence/implementation/mysql/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e index 09802ad..eef4756 100644 --- a/persistence/implementation/mysql/src/provider/node_data_provider.e +++ b/persistence/implementation/mysql/src/provider/node_data_provider.e @@ -74,7 +74,7 @@ feature -- Access local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".nodes") + log.write_information (generator + ".node") create l_parameters.make (1) l_parameters.put (a_id,"id") db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_node_by_id, l_parameters)) @@ -85,6 +85,21 @@ feature -- Access post_execution end + count: INTEGER + -- Number of items nodes. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".count") + create l_parameters.make (0) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_count, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := db_handler.read_integer_32 (1) + end + post_execution + end + feature -- Basic operations new_node (a_node: CMS_NODE) @@ -92,7 +107,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".nodes") + log.write_information (generator + ".new_node") create l_parameters.make (6) l_parameters.put (a_node.title, "title") l_parameters.put (a_node.summary, "summary") @@ -110,7 +125,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".nodes") + log.write_information (generator + ".update_node_title") create l_parameters.make (3) l_parameters.put (a_title, "title") l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") @@ -125,7 +140,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".nodes") + log.write_information (generator + ".update_node_summary") create l_parameters.make (3) l_parameters.put (a_summary, "summary") l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") @@ -140,7 +155,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".nodes") + log.write_information (generator + ".update_node_content") create l_parameters.make (3) l_parameters.put (a_content, "content") l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") @@ -155,7 +170,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".nodes") + log.write_information (generator + ".update_node") create l_parameters.make (7) l_parameters.put (a_node.title, "title") l_parameters.put (a_node.summary, "summary") @@ -174,7 +189,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".nodes") + log.write_information (generator + ".delete_node") create l_parameters.make (1) l_parameters.put (a_id, "id") db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_delete_node, l_parameters)) @@ -202,6 +217,8 @@ feature -- Connection feature {NONE} -- Queries + Select_count: STRING = "select count(*) from Nodes;" + Select_nodes: STRING = "select * from Nodes;" -- SQL Query to retrieve all nodes. diff --git a/persistence/implementation/mysql/src/provider/user_data_provider.e b/persistence/implementation/mysql/src/provider/user_data_provider.e index 81ce325..f66df25 100644 --- a/persistence/implementation/mysql/src/provider/user_data_provider.e +++ b/persistence/implementation/mysql/src/provider/user_data_provider.e @@ -66,7 +66,7 @@ feature -- Basic Operations local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".nodes") + log.write_information (generator + ".user") create l_parameters.make (1) l_parameters.put (a_id,"id") db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_user_by_id, l_parameters)) @@ -99,7 +99,7 @@ feature -- Basic Operations local l_parameters: STRING_TABLE [detachable ANY] do - log.write_information (generator + ".user_by_name") + log.write_information (generator + ".user_by_email") create l_parameters.make (1) l_parameters.put (a_email,"email") db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_user_by_email, l_parameters)) @@ -115,7 +115,7 @@ feature -- Basic Operations local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".nodes") + log.write_information (generator + ".user_salt") create l_parameters.make (1) l_parameters.put (a_username,"name") db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_salt_by_username, l_parameters)) diff --git a/persistence/implementation/mysql/tests/nodes/node_test_set.e b/persistence/implementation/mysql/tests/nodes/node_test_set.e index b7146ee..3ae6665 100644 --- a/persistence/implementation/mysql/tests/nodes/node_test_set.e +++ b/persistence/implementation/mysql/tests/nodes/node_test_set.e @@ -12,15 +12,214 @@ class inherit EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + assert ("Empty Nodes", node_provider.nodes.after) + end + + on_clean + -- + do + end feature -- Test routines - new_test_routine - -- New test routine + test_new_node do - assert ("not_implemented", False) + assert ("Empty Nodes", node_provider.nodes.after) + node_provider.new_node (default_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached node_provider.node (1)) + -- Not exist node with id 2 + assert ("Not exist node with id 2", node_provider.node (2) = Void) + end + + + test_update_node + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Update node (content and summary) + + if attached {CMS_NODE} node_provider.node (1) as l_un then + l_un.set_content ("

Updating test node udpate

") + l_un.set_summary ("updating summary") + node_provider.update_node (l_un) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then not (ll_node.content ~ l_node.content) and then not (ll_node.summary ~ l_node.summary) and then ll_node.title ~ l_node.title ) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title ) + end + + -- Update node (content and summary and title) + if attached {CMS_NODE} node_provider.node (1) as l_un then + l_un.set_content ("

Updating test node udpate

") + l_un.set_summary ("updating summary") + l_un.set_title ("Updating Test case") + node_provider.update_node (l_un) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then not (ll_node.content ~ l_node.content) and then not (ll_node.summary ~ l_node.summary) and then not (ll_node.title ~ l_node.title) ) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title ) + end + end + + test_update_title + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Update node title + + if attached {CMS_NODE} node_provider.node (1) as l_un then + node_provider.update_node_title (l_un.id, "New Title") + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then not ( ll_node.title ~ l_un.title) and then ll_node.title ~ "New Title" ) + end + end + + test_update_summary + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Update node summary + + if attached {CMS_NODE} node_provider.node (1) as l_un then + node_provider.update_node_summary (l_un.id,"New Summary") + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then not (ll_node.summary ~ l_un.summary) and then ll_node.summary ~ "New Summary" and then ll_node.title ~ l_un.title) + end + end + + test_update_content + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Update node content + + if attached {CMS_NODE} node_provider.node (1) as l_un then + node_provider.update_node_content (l_un.id,"New Content") + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then not (ll_node.content ~ l_un.content) and then ll_node.content ~ "New Content" and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title) + end + end + + + test_delete_node + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Delte node 1 + + node_provider.delete_node (1) + assert ("Node does not exist", node_provider.node (1) = Void) + end + + test_recent_nodes + -- Content_10, Summary_10, Title_10 + -- Content_9, Summary_9, Title_9 + -- .. + -- Content_1, Summary_1, Title_1 + local + i : INTEGER + do + assert ("Empty Nodes", node_provider.nodes.after) + across 1 |..| 10 as c loop + node_provider.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + + -- Scenario (0,10) rows, recents (10 down to 1) + i := 10 + across node_provider.recent_nodes (0, 10) as c loop + assert ("Same id:" + i.out, c.item.id = i) + i := i - 1 + end + + -- Scenario (5, 10) rows, recent nodes (5 down to 1) + i := 5 + across node_provider.recent_nodes (5, 10) as c loop + assert ("Same id:" + i.out, c.item.id = i) + i := i - 1 + end + + -- Scenario (9,10) rows, recent node 1 + i := 1 + across node_provider.recent_nodes (9, 10) as c loop + assert ("Same id:" + i.out, c.item.id = i) + i := i - 1 + end + + -- Scenrario 10..10 empty + assert ("Empty", node_provider.recent_nodes (10, 10).after) + + end + +feature {NONE} -- Implementation + + node_provider: NODE_DATA_PROVIDER + -- node provider. + once + create Result.make (connection) + end + + +feature {NONE} -- Implementation Fixture Factories + + default_node: CMS_NODE + do + Result := custom_node ("Default content", "default summary", "Default") + end + + custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE + do + create Result.make (a_content, a_summary, a_title) end end + From fbb5c57b8feffe56e0c119e6dece6e1e13e2f06b Mon Sep 17 00:00:00 2001 From: jvelilla Date: Tue, 16 Sep 2014 17:16:19 -0300 Subject: [PATCH 005/386] Update CMS_STORAGE interface. Added test cases. --- .../mysql/src/cms_storage_mysql.e | 3 +- .../mysql/src/provider/user_data_provider.e | 24 ++ .../mysql/tests/storage/storage_test_set.e | 323 ++++++++++++++++++ persistence/interface/cms_storage.e | 10 +- 4 files changed, 354 insertions(+), 6 deletions(-) create mode 100644 persistence/implementation/mysql/tests/storage/storage_test_set.e diff --git a/persistence/implementation/mysql/src/cms_storage_mysql.e b/persistence/implementation/mysql/src/cms_storage_mysql.e index c6279fe..b134f72 100644 --- a/persistence/implementation/mysql/src/cms_storage_mysql.e +++ b/persistence/implementation/mysql/src/cms_storage_mysql.e @@ -34,7 +34,8 @@ feature -- Access: user has_user: BOOLEAN -- Has any user? do - to_implement("Not implemented!!!") + Result := user_provider.has_user + post_user_provider_execution end diff --git a/persistence/implementation/mysql/src/provider/user_data_provider.e b/persistence/implementation/mysql/src/provider/user_data_provider.e index f66df25..e603c05 100644 --- a/persistence/implementation/mysql/src/provider/user_data_provider.e +++ b/persistence/implementation/mysql/src/provider/user_data_provider.e @@ -37,6 +37,12 @@ feature -- Status Report Result := db_handler.successful end + has_user: BOOLEAN + -- Has any user? + do + Result := count > 0 + end + feature -- Basic Operations new_user (a_user_name: READABLE_STRING_32; a_password: READABLE_STRING_32; a_email: READABLE_STRING_32) @@ -128,6 +134,21 @@ feature -- Basic Operations post_execution end + count: INTEGER + -- Number of items users. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".count") + create l_parameters.make (0) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_count, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := db_handler.read_integer_32 (1) + end + post_execution + end + feature -- New Object fetch_user: CMS_USER @@ -149,6 +170,9 @@ feature -- New Object feature -- Sql Queries + Select_count: STRING = "select count(*) from Users;" + -- Number of users. + Select_user_by_id: STRING = "select * from Users where id =:id;" -- Retrieve user by id if exists. diff --git a/persistence/implementation/mysql/tests/storage/storage_test_set.e b/persistence/implementation/mysql/tests/storage/storage_test_set.e new file mode 100644 index 0000000..8a02131 --- /dev/null +++ b/persistence/implementation/mysql/tests/storage/storage_test_set.e @@ -0,0 +1,323 @@ +note + description: "Summary description for {STORAGE_TEST_SET}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + STORAGE_TEST_SET + +inherit + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + end + + on_clean + -- + do + end + +feature -- Test routines + + test_has_user + do + assert ("Not has user", not storage.has_user) + end + + test_all_users + do + assert ("to implement all_users", False) + end + + test_user_by_id_not_exist + do + assert ("User does not exist", storage.user_by_id (1) = Void) + end + + test_user_by_name_not_exist + do + assert ("User does not exist", storage.user_by_name ("test") = Void) + end + + test_user_by_email_not_exist + do + assert ("User does not exist", storage.user_by_name ("test@test.com") = Void) + end + + test_user_with_bad_id + local + l_retry: BOOLEAN + l_user: detachable CMS_USER + do + if not l_retry then + l_user := storage.user_by_id (0) + assert ("Precondition does not get the wrong value", False) + else + assert ("Expected precondition violation", True) + end + rescue + l_retry := True + retry + end + + test_user_with_bad_name_empty + local + l_retry: BOOLEAN + l_user: detachable CMS_USER + do + if not l_retry then + l_user := storage.user_by_name ("") + assert ("Precondition does not get the wrong value", False) + else + assert ("Expected precondition violation", True) + end + rescue + l_retry := True + retry + end + + test_user_with_bad_email_empty + local + l_retry: BOOLEAN + l_user: detachable CMS_USER + do + if not l_retry then + l_user := storage.user_by_email ("") + assert ("Precondition does not get the wrong value", False) + else + assert ("Expected precondition violation", True) + end + rescue + l_retry := True + retry + end + + test_save_user + do + storage.save_user (default_user) + assert ("Has user", storage.has_user) + end + + test_user_by_id + do + storage.save_user (default_user) + assert ("Has user", storage.has_user) + if attached {CMS_USER} storage.user_by_id (1) as l_user then + assert ("Exist", True) + assert ("User test", l_user.name ~ "test") + assert ("User id = 1", l_user.id = 1) + else + assert ("Wrong Implementation", False) + end + end + + test_user_by_name + do + storage.save_user (default_user) + assert ("Has user", storage.has_user) + if attached {CMS_USER} storage.user_by_name ("test") as l_user then + assert ("Exist", True) + assert ("User nane: test", l_user.name ~ "test") + else + assert ("Wrong Implementation", False) + end + end + + test_user_by_email + do + storage.save_user (default_user) + assert ("Has user", storage.has_user) + if attached {CMS_USER} storage.user_by_email ("test@test.com") as l_user then + assert ("Exist", True) + assert ("User email: test@test.com", l_user.email ~ "test@test.com") + else + assert ("Wrong Implementation", False) + end + end + + test_invalid_credential + do + storage.save_user (default_user) + assert ("Has user test", attached storage.user_by_name ("test")) + assert ("Wrong password", not storage.is_valid_credential ("test", "test")) + assert ("Wrong user", not storage.is_valid_credential ("test1", "test")) + end + + test_valid_credential + do + storage.save_user (default_user) + assert ("Has user test", attached storage.user_by_name ("test")) + assert ("Valid password", storage.is_valid_credential ("test", "password")) + end + + test_recent_nodes_empty + do + assert ("No recent nodes", storage.recent_nodes (0, 10).is_empty) + end + + test_recent_nodes + local + l_nodes: LIST[CMS_NODE] + do + across 1 |..| 10 as c loop + storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + l_nodes := storage.recent_nodes (0, 10) + assert ("10 recent nodes", l_nodes.count = 10) + assert ("First node id=10", l_nodes.first.id = 10) + assert ("Last node id=1", l_nodes.last.id = 1) + + + l_nodes := storage.recent_nodes (5, 10) + assert ("5 recent nodes", l_nodes.count = 5) + assert ("First node id=5", l_nodes.first.id = 5) + assert ("Last node id=1", l_nodes.last.id = 1) + + l_nodes := storage.recent_nodes (9, 10) + assert ("1 recent nodes", l_nodes.count = 1) + assert ("First node id=1", l_nodes.first.id = 1) + assert ("Last node id=1", l_nodes.last.id = 1) + + l_nodes := storage.recent_nodes (10, 10) + assert ("Is empty", l_nodes.is_empty) + end + + test_node_does_not_exist + local + l_nodes: LIST[CMS_NODE] + do + across 1 |..| 10 as c loop + storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + assert ("Not exist node id: 12", storage.node (12) = Void) + end + + test_node + local + l_nodes: LIST[CMS_NODE] + do + across 1 |..| 10 as c loop + storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + assert ("Node id: 10", attached storage.node (10) as l_node and then l_node.title ~ "Title_10" ) + end + + test_update_node + local + l_nodes: LIST[CMS_NODE] + l_node: CMS_NODE + do + storage.save_node (custom_node ("Content", "Summary", "Title")) + if attached {CMS_NODE} storage.node (1) as ll_node then + l_node := ll_node.twin + l_node.set_content ("New Content") + l_node.set_summary ("New Summary") + l_node.set_title("New Title") + + storage.update_node (l_node) + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary)) + end + end + + test_update_node_title + local + l_nodes: LIST[CMS_NODE] + l_node: CMS_NODE + do + storage.save_node (custom_node ("Content", "Summary", "Title")) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_title (ll_node.id, "New Title") + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary) + end + end + + test_update_node_summary + local + l_nodes: LIST[CMS_NODE] + l_node: CMS_NODE + do + storage.save_node (custom_node ("Content", "Summary", "Title")) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_summary (ll_node.id, "New Summary") + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary)) + end + end + + test_update_node_content + local + l_nodes: LIST[CMS_NODE] + l_node: CMS_NODE + do + storage.save_node (custom_node ("Content", "Summary", "Title")) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_content (ll_node.id, "New Content") + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary) + end + end + + test_delete_node + local + l_nodes: LIST[CMS_NODE] + do + across 1 |..| 10 as c loop + storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + assert ("Exist node id: 10", attached storage.node (10) as l_node and then l_node.title ~ "Title_10" ) + storage.delete_node (10) + assert ("Not exist node id: 10", storage.node (10) = Void) + end + + + +feature {NONE} -- Implementation + + storage: CMS_STORAGE + -- Storage + once + create {CMS_STORAGE_MYSQL}Result.make (connection) + end + +feature {NONE} -- Fixture Factory: Users + + default_user: CMS_USER + do + Result := custom_user ("test", "password", "test@test.com") + end + + custom_user (a_name, a_password, a_email: READABLE_STRING_32): CMS_USER + do + create Result.make (a_name) + Result.set_password (a_password) + Result.set_email (a_email) + end + +feature {NONE} -- Fixture Factories: Nodes + + default_node: CMS_NODE + do + Result := custom_node ("Default content", "default summary", "Default") + end + + custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE + do + create Result.make (a_content, a_summary, a_title) + end + +end diff --git a/persistence/interface/cms_storage.e b/persistence/interface/cms_storage.e index 5085328..22ea3a1 100644 --- a/persistence/interface/cms_storage.e +++ b/persistence/interface/cms_storage.e @@ -36,7 +36,7 @@ feature -- Access: user deferred ensure same_id: Result /= Void implies Result.id = a_id - no_password: Result /= Void implies Result.password = Void + password: Result /= Void implies Result.password /= Void end user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER @@ -44,13 +44,15 @@ feature -- Access: user a_name /= Void and then not a_name.is_empty deferred ensure - no_password: Result /= Void implies Result.password = Void + same_name: Result /= Void implies a_name ~ Result.name + password: Result /= Void implies Result.password /= Void end user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER deferred ensure - no_password: Result /= Void implies Result.password = Void + same_email: Result /= Void implies a_email ~ Result.email + password: Result /= Void implies Result.password /= Void end is_valid_credential (u, p: READABLE_STRING_32): BOOLEAN @@ -61,8 +63,6 @@ feature -- Change: user save_user (a_user: CMS_USER) deferred - ensure - a_user_password_is_encoded: a_user.password = Void end feature -- Access: roles and permissions From 4227b82c7e967b4b5d99f012b7017d0c249660b9 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 17 Sep 2014 10:33:37 -0300 Subject: [PATCH 006/386] Added a common database backend implementation. Added sqlite (proof of concept) --- .../config/application_configuration.json | 12 +- api/src/configuration/configuration_factory.e | 2 +- .../configuration/roc_json_configuration.e | 3 - .../src => common}/database/database_config.e | 0 .../database/database_connection.e | 7 + .../database/database_connection_null.e | 0 .../database/database_connection_odbc.e | 126 +++++++ .../database/database_handler.e | 0 .../database/database_handler_impl.e | 0 .../database/database_iteration_cursor.e | 0 .../src => common}/database/database_null.e | 0 .../src => common}/database/database_query.e | 0 .../database/database_sql_server_encoder.e | 0 .../database/database_store_procedure.e | 0 .../database/parameter_name_helper.e | 0 .../{mysql/src => common}/model/cms_node.e | 0 .../{mysql/src => common}/model/cms_user.e | 0 .../security/security_provider.e | 0 .../{mysql/src => common}/string_helper.e | 0 .../mysql/persistence_mysql.ecf | 2 + .../src/database/data_parameters_names.e | 132 ------- .../src/database/database_connection_mysql.e | 30 +- .../sqlite/persistence_sqlite.ecf | 30 ++ .../implementation/sqlite/scripts/Readme.txt | 1 + .../sqlite/scripts/create_database.sql | 61 ++++ .../implementation/sqlite/scripts/tables.sql | 14 + .../sqlite/src/cms_storage_sqlite.e | 218 ++++++++++++ .../src/database/database_connection_mysql.e | 145 ++++++++ .../sqlite/src/provider/node_data_provider.e | 289 ++++++++++++++++ .../sqlite/src/provider/user_data_provider.e | 206 +++++++++++ .../implementation/sqlite/tests/application.e | 31 ++ .../sqlite/tests/nodes/node_test_set.e | 225 ++++++++++++ .../sqlite/tests/storage/storage_test_set.e | 323 ++++++++++++++++++ .../implementation/sqlite/tests/tests.ecf | 22 ++ .../sqlite/tests/users/user_test_set.e | 78 +++++ .../sqlite/tests/util/abstract_db_test.e | 19 ++ .../sqlite/tests/util/clean_db.e | 66 ++++ 37 files changed, 1889 insertions(+), 153 deletions(-) rename persistence/implementation/{mysql/src => common}/database/database_config.e (100%) rename persistence/implementation/{mysql/src => common}/database/database_connection.e (96%) rename persistence/implementation/{mysql/src => common}/database/database_connection_null.e (100%) create mode 100644 persistence/implementation/common/database/database_connection_odbc.e rename persistence/implementation/{mysql/src => common}/database/database_handler.e (100%) rename persistence/implementation/{mysql/src => common}/database/database_handler_impl.e (100%) rename persistence/implementation/{mysql/src => common}/database/database_iteration_cursor.e (100%) rename persistence/implementation/{mysql/src => common}/database/database_null.e (100%) rename persistence/implementation/{mysql/src => common}/database/database_query.e (100%) rename persistence/implementation/{mysql/src => common}/database/database_sql_server_encoder.e (100%) rename persistence/implementation/{mysql/src => common}/database/database_store_procedure.e (100%) rename persistence/implementation/{mysql/src => common}/database/parameter_name_helper.e (100%) rename persistence/implementation/{mysql/src => common}/model/cms_node.e (100%) rename persistence/implementation/{mysql/src => common}/model/cms_user.e (100%) rename persistence/implementation/{mysql/src => common}/security/security_provider.e (100%) rename persistence/implementation/{mysql/src => common}/string_helper.e (100%) delete mode 100644 persistence/implementation/mysql/src/database/data_parameters_names.e create mode 100644 persistence/implementation/sqlite/persistence_sqlite.ecf create mode 100644 persistence/implementation/sqlite/scripts/Readme.txt create mode 100644 persistence/implementation/sqlite/scripts/create_database.sql create mode 100644 persistence/implementation/sqlite/scripts/tables.sql create mode 100644 persistence/implementation/sqlite/src/cms_storage_sqlite.e create mode 100644 persistence/implementation/sqlite/src/database/database_connection_mysql.e create mode 100644 persistence/implementation/sqlite/src/provider/node_data_provider.e create mode 100644 persistence/implementation/sqlite/src/provider/user_data_provider.e create mode 100644 persistence/implementation/sqlite/tests/application.e create mode 100644 persistence/implementation/sqlite/tests/nodes/node_test_set.e create mode 100644 persistence/implementation/sqlite/tests/storage/storage_test_set.e create mode 100644 persistence/implementation/sqlite/tests/tests.ecf create mode 100644 persistence/implementation/sqlite/tests/users/user_test_set.e create mode 100644 persistence/implementation/sqlite/tests/util/abstract_db_test.e create mode 100644 persistence/implementation/sqlite/tests/util/clean_db.e diff --git a/api/site/config/application_configuration.json b/api/site/config/application_configuration.json index bfda1a7..7a93a9a 100644 --- a/api/site/config/application_configuration.json +++ b/api/site/config/application_configuration.json @@ -1,18 +1,18 @@ { "database": { "datasource": { - "driver": "SQL Server", - "environment": "test" + "driver": "MySQL", + "environment": "development" }, "environments": { "test": { - "connection_string":"Server=JVELILLA\\SQLEXPRESS;Database=example;Uid=sa;Pwd=Eiffel1405;TrustedConnection=Yes" + "connection_string":"Server=localhost;Port=3306;Database=cms_dev;Uid=root;Pwd=;" }, "development": { - "connection_string":"Server=sql.ise;Database=ESA_DB_TEST;Uid=eiffel2;Pwd=eiffel2;" + "connection_string":"Server=localhost;Port=3306;Database=cms;Uid=root;Pwd=;" }, "production": { - "connection_string":"Server=SQLSERVER;Database=LIVE_DATABASE;Uid=XXX;Pwd=YYY;" + "connection_string":"" } } }, @@ -24,7 +24,7 @@ "backup_count":"4" }, "server": { - "mode":"web" + "mode":"api" } } diff --git a/api/src/configuration/configuration_factory.e b/api/src/configuration/configuration_factory.e index 6542bff..fa8127e 100644 --- a/api/src/configuration/configuration_factory.e +++ b/api/src/configuration/configuration_factory.e @@ -33,7 +33,7 @@ feature -- Factory create l_email_service.make ((create {JSON_CONFIGURATION}).new_smtp_configuration(l_layout.application_config_path)) if attached (create {JSON_CONFIGURATION}).new_database_configuration (l_layout.application_config_path) as l_database_config then - create {DATABASE_CONNECTION_MYSQL} l_database.login_with_schema ("cms", "root", "") + create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string) create l_api_service.make_with_database (l_database) create Result.make (l_database, l_api_service, l_email_service, l_layout) if (create {ROC_JSON_CONFIGURATION}).is_web_mode(l_layout.application_config_path) then diff --git a/api/src/configuration/roc_json_configuration.e b/api/src/configuration/roc_json_configuration.e index 3fdb368..8088f76 100644 --- a/api/src/configuration/roc_json_configuration.e +++ b/api/src/configuration/roc_json_configuration.e @@ -45,7 +45,4 @@ feature -- Access end - - - end diff --git a/persistence/implementation/mysql/src/database/database_config.e b/persistence/implementation/common/database/database_config.e similarity index 100% rename from persistence/implementation/mysql/src/database/database_config.e rename to persistence/implementation/common/database/database_config.e diff --git a/persistence/implementation/mysql/src/database/database_connection.e b/persistence/implementation/common/database/database_connection.e similarity index 96% rename from persistence/implementation/mysql/src/database/database_connection.e rename to persistence/implementation/common/database/database_connection.e index a2b995e..7591273 100644 --- a/persistence/implementation/mysql/src/database/database_connection.e +++ b/persistence/implementation/common/database/database_connection.e @@ -72,6 +72,13 @@ feature -- Database Setup keep_connection: BOOLEAN -- Keep connection alive? +feature -- Change Element + + not_keep_connection + do + keep_connection := False + end + feature -- Conection connect diff --git a/persistence/implementation/mysql/src/database/database_connection_null.e b/persistence/implementation/common/database/database_connection_null.e similarity index 100% rename from persistence/implementation/mysql/src/database/database_connection_null.e rename to persistence/implementation/common/database/database_connection_null.e diff --git a/persistence/implementation/common/database/database_connection_odbc.e b/persistence/implementation/common/database/database_connection_odbc.e new file mode 100644 index 0000000..3e7ea74 --- /dev/null +++ b/persistence/implementation/common/database/database_connection_odbc.e @@ -0,0 +1,126 @@ +note + description: "Object that handle a database connection for ODBC" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_CONNECTION_ODBC + +inherit + + DATABASE_CONNECTION + redefine + db_application + end + +create + make, make_common, make_basic, login_with_connection_string + +feature -- Initialization + + make_common + -- Create a database handler for ODBC with common settings. + local + l_retried: BOOLEAN + do + create db_application.login (username, password) + + if not l_retried then + db_application.set_hostname (hostname) + db_application.set_data_source (database_name) + db_application.set_base + create db_control.make + keep_connection := is_keep_connection + if keep_connection then + connect + end + set_successful + else + create db_control.make + end + rescue + create db_control.make + set_last_error_from_exception ("Connection execution") + log.write_critical (generator + ".make_common:" + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + + make_basic (a_database_name: STRING) + -- Create a database handler and + -- set database_name to `a_database_name'. + local + l_retried: BOOLEAN + do + create db_application.login (username, password) + + if not l_retried then + db_application.set_hostname (hostname) + db_application.set_data_source (a_database_name) + db_application.set_base + create db_control.make + keep_connection := is_keep_connection + if keep_connection then + connect + end + set_successful + else + create db_control.make + end + rescue + create db_control.make + set_last_error_from_exception ("Connection execution") + log.write_critical (generator + ".make_common:" + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + + make (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN) + + -- Create a database handler for ODBC and set `username' to `a_username', + -- `password' to `a_password' + -- `database_name' to `a_database_name' + -- `connection' to `a_connection' + do + create db_application.login (a_username, a_password) + db_application.set_hostname (a_hostname) + db_application.set_data_source (a_database_name) + db_application.set_base + create db_control.make + keep_connection := connection + if keep_connection then + connect + end + end + + login_with_connection_string (a_string: STRING) + -- Login with `a_connection_string'and immediately connect to database. + do + log.write_debug (generator +".login_with_connection_string") + create db_application.login_with_connection_string (a_string) + db_application.set_base + create db_control.make + log.write_debug (generator +".login_with_connection_string, is_keep_connection? "+ is_keep_connection.out ) + keep_connection := is_keep_connection + if keep_connection then + connect + if not db_control.is_ok then + log.write_critical (generator +".login_with_connection_string:"+ db_control.error_code.out ) + log.write_critical (generator +".login_with_connection_string:"+ db_control.error_message_32 ) + end + log.write_debug (generator +".login_with_connection_string, After connect, is_connected? "+ is_connected.out) + end + end + +feature -- Databse Connection + + db_application: DATABASE_APPL [ODBC] + -- Database application. + +end diff --git a/persistence/implementation/mysql/src/database/database_handler.e b/persistence/implementation/common/database/database_handler.e similarity index 100% rename from persistence/implementation/mysql/src/database/database_handler.e rename to persistence/implementation/common/database/database_handler.e diff --git a/persistence/implementation/mysql/src/database/database_handler_impl.e b/persistence/implementation/common/database/database_handler_impl.e similarity index 100% rename from persistence/implementation/mysql/src/database/database_handler_impl.e rename to persistence/implementation/common/database/database_handler_impl.e diff --git a/persistence/implementation/mysql/src/database/database_iteration_cursor.e b/persistence/implementation/common/database/database_iteration_cursor.e similarity index 100% rename from persistence/implementation/mysql/src/database/database_iteration_cursor.e rename to persistence/implementation/common/database/database_iteration_cursor.e diff --git a/persistence/implementation/mysql/src/database/database_null.e b/persistence/implementation/common/database/database_null.e similarity index 100% rename from persistence/implementation/mysql/src/database/database_null.e rename to persistence/implementation/common/database/database_null.e diff --git a/persistence/implementation/mysql/src/database/database_query.e b/persistence/implementation/common/database/database_query.e similarity index 100% rename from persistence/implementation/mysql/src/database/database_query.e rename to persistence/implementation/common/database/database_query.e diff --git a/persistence/implementation/mysql/src/database/database_sql_server_encoder.e b/persistence/implementation/common/database/database_sql_server_encoder.e similarity index 100% rename from persistence/implementation/mysql/src/database/database_sql_server_encoder.e rename to persistence/implementation/common/database/database_sql_server_encoder.e diff --git a/persistence/implementation/mysql/src/database/database_store_procedure.e b/persistence/implementation/common/database/database_store_procedure.e similarity index 100% rename from persistence/implementation/mysql/src/database/database_store_procedure.e rename to persistence/implementation/common/database/database_store_procedure.e diff --git a/persistence/implementation/mysql/src/database/parameter_name_helper.e b/persistence/implementation/common/database/parameter_name_helper.e similarity index 100% rename from persistence/implementation/mysql/src/database/parameter_name_helper.e rename to persistence/implementation/common/database/parameter_name_helper.e diff --git a/persistence/implementation/mysql/src/model/cms_node.e b/persistence/implementation/common/model/cms_node.e similarity index 100% rename from persistence/implementation/mysql/src/model/cms_node.e rename to persistence/implementation/common/model/cms_node.e diff --git a/persistence/implementation/mysql/src/model/cms_user.e b/persistence/implementation/common/model/cms_user.e similarity index 100% rename from persistence/implementation/mysql/src/model/cms_user.e rename to persistence/implementation/common/model/cms_user.e diff --git a/persistence/implementation/mysql/src/security/security_provider.e b/persistence/implementation/common/security/security_provider.e similarity index 100% rename from persistence/implementation/mysql/src/security/security_provider.e rename to persistence/implementation/common/security/security_provider.e diff --git a/persistence/implementation/mysql/src/string_helper.e b/persistence/implementation/common/string_helper.e similarity index 100% rename from persistence/implementation/mysql/src/string_helper.e rename to persistence/implementation/common/string_helper.e diff --git a/persistence/implementation/mysql/persistence_mysql.ecf b/persistence/implementation/mysql/persistence_mysql.ecf index c3190a8..4577af1 100644 --- a/persistence/implementation/mysql/persistence_mysql.ecf +++ b/persistence/implementation/mysql/persistence_mysql.ecf @@ -13,6 +13,7 @@ + @@ -24,5 +25,6 @@
+
diff --git a/persistence/implementation/mysql/src/database/data_parameters_names.e b/persistence/implementation/mysql/src/database/data_parameters_names.e deleted file mode 100644 index da170ba..0000000 --- a/persistence/implementation/mysql/src/database/data_parameters_names.e +++ /dev/null @@ -1,132 +0,0 @@ -note - description: "Stored procedures paramters names" - date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" - revision: "$Revision: 95678 $" - -class - DATA_PARAMETERS_NAMES - -feature -- Access - - Contactid_param: STRING = "ContactID" - - Username_param: STRING = "Username" - - Submitter_username_param: STRING = "SubmitterUsername" - - Responsibleid_param: STRING = "ResponsibleID" - - Email_param: STRING = "Email" - - Synopsis_param: STRING = "Synopsis" - - Openonly_param: STRING = "OpenOnly" - - Number_param: STRING = "Number" - - Categoryid_param: STRING = "CategoryID" - - Category_synopsis_param: STRING = "CategorySynopsis" - - Statusid_param: STRING = "StatusID" - - Private_param: STRING = "Private" - - Priorityid_param: STRING = "PriorityID" - - Severityid_param: STRING = "SeverityID" - - Searchtext_param: STRING = "SearchText" - - Searchsynopsis_param: STRING = "SearchSynopsis" - - Searchdescription_param: STRING = "SearchDescription" - - Reportid_param: STRING = "ReportID" - - Interactionid_param: STRING = "InteractionID" - - Attachmentid_param: STRING = "AttachmentID" - - Fieldtitle_param: STRING = "FieldTitle" - - Passwordhash_param: STRING = "PasswordHash" - - Registrationtoken_param: STRING = "RegistrationToken" - - Answerhash_param: STRING = "AnswerHash" - - Classid_param: STRING = "Classid" - - Confidential_param: STRING = "Confidential" - - Release_param: STRING = "Release" - - Environment_param: STRING = "Environment" - - Description_param: STRING = "Description" - - Toreproduce_param: STRING = "ToReproduce" - - Content_param: STRING = "Content" - - Alreadyexists_param: STRING = "AlreadyExists" - - Firstname_param: STRING = "FirstName" - - Lastname_param: STRING = "LastName" - - Responsible_param: STRING = "Responsible" - - Responsible_firstname_param: STRING = "ResponsibleFirstName" - - Responsible_lastname_param: STRING = "ResponsibleLastName" - - Length_param: STRING = "Length" - - Filename_param: STRING = "Filename" - - Position_param: STRING = "Position" - - Address_param: STRING = "Address" - - City_param: STRING = "City" - - Country_param: STRING = "Country" - - Region_param: STRING = "Region" - - Code_param: STRING = "Code" - - Tel_param: STRING = "Tel" - - Fax_param: STRING = "Fax" - - Name_param: STRING = "Name" - - Url_param: STRING = "URL" - - Token_param: STRING = "Token" - - Filter_param: STRING = "Filter" - -feature -- Login - - Passwordsalt_param: STRING = "PasswordSalt" - - Answersalt_param: STRING = "AnswerSalt" - - Questionid_param: STRING = "QuestionID" - - -feature -- Download Parameter Names - - Subject_param: STRING = "Subject" - - Notes_param: STRING = "Notes" - - Platform_param: STRING = "Platform" - - Newsletter_param: STRING = "Newsletter" - -end diff --git a/persistence/implementation/mysql/src/database/database_connection_mysql.e b/persistence/implementation/mysql/src/database/database_connection_mysql.e index 1f08893..bc5f890 100644 --- a/persistence/implementation/mysql/src/database/database_connection_mysql.e +++ b/persistence/implementation/mysql/src/database/database_connection_mysql.e @@ -101,21 +101,29 @@ feature -- Initialization login_with_connection_string (a_string: STRING) -- Login with `a_connection_string'and immediately connect to database. + local + l_string: LIST[STRING] + l_server: STRING + l_port: STRING + l_schema: STRING + l_user: STRING + l_password: STRING do - log.write_debug (generator +".login_with_connection_string") - create db_application.login_with_connection_string (a_string) + l_string := a_string.split (';') + l_server := l_string.at (2).split ('=').at (2) + l_port := l_string.at (3).split ('=').at (2) + l_schema := l_string.at (4).split ('=').at (2) + l_user := l_string.at (5).split ('=').at (2) + l_password := l_string.at (6).split ('=').at (2) + + create db_application + db_application.set_application (l_schema) + db_application.set_hostname (l_server + ":" + l_port) + db_application.login_and_connect (l_user, l_password) db_application.set_base create db_control.make - log.write_debug (generator +".login_with_connection_string, is_keep_connection? "+ is_keep_connection.out ) keep_connection := is_keep_connection - if keep_connection then - connect - if not db_control.is_ok then - log.write_critical (generator +".login_with_connection_string:"+ db_control.error_code.out ) - log.write_critical (generator +".login_with_connection_string:"+ db_control.error_message_32 ) - end - log.write_debug (generator +".login_with_connection_string, After connect, is_connected? "+ is_connected.out) - end + end login_with_schema (a_schema: STRING; a_username: STRING; a_password: STRING) diff --git a/persistence/implementation/sqlite/persistence_sqlite.ecf b/persistence/implementation/sqlite/persistence_sqlite.ecf new file mode 100644 index 0000000..d93e74c --- /dev/null +++ b/persistence/implementation/sqlite/persistence_sqlite.ecf @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + diff --git a/persistence/implementation/sqlite/scripts/Readme.txt b/persistence/implementation/sqlite/scripts/Readme.txt new file mode 100644 index 0000000..04064dc --- /dev/null +++ b/persistence/implementation/sqlite/scripts/Readme.txt @@ -0,0 +1 @@ +Added Stored Procedures \ No newline at end of file diff --git a/persistence/implementation/sqlite/scripts/create_database.sql b/persistence/implementation/sqlite/scripts/create_database.sql new file mode 100644 index 0000000..4b9a157 --- /dev/null +++ b/persistence/implementation/sqlite/scripts/create_database.sql @@ -0,0 +1,61 @@ +-- Creator: MySQL Workbench 6.1.7/ExportSQLite plugin 2009.12.02 +-- Author: javier +-- Caption: New Model +-- Project: Name of the project +-- Changed: 2014-09-16 23:12 +-- Created: 2014-09-16 23:12 +PRAGMA foreign_keys = OFF; + +-- Schema: cms_dev +BEGIN; +CREATE TABLE "nodes"( + "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0), + "publication_date" DATE NOT NULL, + "creation_date" DATE NOT NULL, + "modification_date" DATE NOT NULL, + "title" VARCHAR(255) NOT NULL, + "summary" TEXT NOT NULL, + "content" MEDIUMTEXT NOT NULL +); +CREATE TABLE "roles"( + "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0), + "role" VARCHAR(100) NOT NULL, + CONSTRAINT "role" + UNIQUE("role") +); +CREATE TABLE "users"( + "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id">=0), + "username" VARCHAR(100) NOT NULL, + "password" VARCHAR(100) NOT NULL, + "salt" VARCHAR(100) NOT NULL, + "email" VARCHAR(250) NOT NULL, + CONSTRAINT "username" + UNIQUE("username") +); +CREATE TABLE "users_nodes"( + "users_id" INTEGER NOT NULL CHECK("users_id">=0), + "nodes_id" INTEGER NOT NULL CHECK("nodes_id">=0), + PRIMARY KEY("users_id","nodes_id"), + CONSTRAINT "fk_users_has_nodes_nodes1" + FOREIGN KEY("nodes_id") + REFERENCES "nodes"("id"), + CONSTRAINT "fk_users_has_nodes_users" + FOREIGN KEY("users_id") + REFERENCES "users"("id") +); +CREATE INDEX "users_nodes.fk_users_has_nodes_nodes1_idx" ON "users_nodes"("nodes_id"); +CREATE INDEX "users_nodes.fk_users_has_nodes_users_idx" ON "users_nodes"("users_id"); +CREATE TABLE "users_roles"( + "users_id" INTEGER NOT NULL CHECK("users_id">=0), + "roles_id" INTEGER NOT NULL CHECK("roles_id">=0), + PRIMARY KEY("users_id","roles_id"), + CONSTRAINT "fk_users_has_roles_roles1" + FOREIGN KEY("roles_id") + REFERENCES "roles"("id"), + CONSTRAINT "fk_users_has_roles_users1" + FOREIGN KEY("users_id") + REFERENCES "users"("id") +); +CREATE INDEX "users_roles.fk_users_has_roles_roles1_idx" ON "users_roles"("roles_id"); +CREATE INDEX "users_roles.fk_users_has_roles_users1_idx" ON "users_roles"("users_id"); +COMMIT; diff --git a/persistence/implementation/sqlite/scripts/tables.sql b/persistence/implementation/sqlite/scripts/tables.sql new file mode 100644 index 0000000..4b1a4ba --- /dev/null +++ b/persistence/implementation/sqlite/scripts/tables.sql @@ -0,0 +1,14 @@ +DROP TABLE IF EXISTS nodes; + +CREATE TABLE nodes +( + id smallint unsigned NOT NULL auto_increment, + publication_date date NOT NULL, #When the article was published + creation_date date NOT NULL, #When the article was created + modification_date date NOT NULL, #When the article was updated + title varchar(255) NOT NULL, #Full title of the article + summary text NOT NULL, #A short summary of the articule + content mediumtext NOT NULL, #The HTML content of the article + + PRIMARY KEY (ID) +); \ No newline at end of file diff --git a/persistence/implementation/sqlite/src/cms_storage_sqlite.e b/persistence/implementation/sqlite/src/cms_storage_sqlite.e new file mode 100644 index 0000000..2a580af --- /dev/null +++ b/persistence/implementation/sqlite/src/cms_storage_sqlite.e @@ -0,0 +1,218 @@ +note + description: "Summary description for {CMS_STORAGE_MYSQL}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_STORAGE_SQLITE + +inherit + + CMS_STORAGE + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_connection: DATABASE_CONNECTION) + -- + require + is_connected: a_connection.is_connected + do + log.write_information (generator+".make_with_database is database connected? "+ a_connection.is_connected.out ) + create node_provider.make (a_connection) + create user_provider.make (a_connection) + post_node_provider_execution + post_user_provider_execution + end + + +feature -- Access: user + + has_user: BOOLEAN + -- Has any user? + do + Result := user_provider.has_user + post_user_provider_execution + end + + + all_users: LIST [CMS_USER] + do + to_implement("Not implemented!!!") + create {ARRAYED_LIST[CMS_USER]} Result.make (0) + end + + user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER + do + Result := user_provider.user (a_id) + post_user_provider_execution + end + + user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + do + Result := user_provider.user_by_name (a_name) + post_user_provider_execution + end + + user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + do + Result := user_provider.user_by_email (a_email) + post_user_provider_execution + end + + is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN + local + l_security: SECURITY_PROVIDER + do + if attached user_provider.user_salt (l_auth_login) as l_hash then + if attached user_provider.user_by_name (l_auth_login) as l_user then + create l_security + if + attached l_user.password as l_password and then + l_security.password_hash (l_auth_password, l_hash).is_case_insensitive_equal (l_password) + then + Result := True + else + log.write_information (generator + ".login_valid User: wrong username or password" ) + end + else + log.write_information (generator + ".login_valid User:" + l_auth_login + "does not exist" ) + end + end + post_user_provider_execution + end + +feature -- Change: user + + save_user (a_user: CMS_USER) + -- Add a new user `a_user'. + do + if + attached a_user.password as l_password and then + attached a_user.email as l_email + then + user_provider.new_user (a_user.name, l_password, l_email) + else + -- set error + end + end + +feature -- Access: node + + nodes: LIST[CMS_NODE] + -- List of nodes. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + across node_provider.nodes as c loop + Result.force (c.item) + end + post_node_provider_execution + end + + recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] + -- List of the `a_count' most recent nodes, starting from `a_lower'. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + across node_provider.recent_nodes (a_lower,a_count) as c loop + Result.force (c.item) + end + post_node_provider_execution + end + + node (a_id: INTEGER_64): detachable CMS_NODE + -- + do + Result := node_provider.node (a_id) + post_node_provider_execution + end + + +feature -- Node + + save_node (a_node: CMS_NODE) + -- Add a new node + do + node_provider.new_node (a_node) + post_node_provider_execution + end + + delete_node (a_id: INTEGER_64) + do + node_provider.delete_node (a_id) + post_node_provider_execution + end + + update_node (a_node: CMS_NODE) + do + node_provider.update_node (a_node) + post_node_provider_execution + end + + update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + do + node_provider.update_node_title (a_id, a_title) + post_node_provider_execution + end + + update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + do + node_provider.update_node_summary (a_id, a_summary) + post_node_provider_execution + end + + update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + do + node_provider.update_node_content (a_id, a_content) + post_node_provider_execution + end + + +feature -- User + + new_user (a_user: CMS_USER) + -- Add a new user `a_user'. + do + if + attached a_user.password as l_password and then + attached a_user.email as l_email + then + user_provider.new_user (a_user.name, l_password, l_email) + else + -- set error + end + end + +feature {NONE} -- Post process + + post_node_provider_execution + do + if node_provider.successful then + set_successful + else + if attached node_provider.last_error then + set_last_error_from_handler (node_provider.last_error) + end + end + end + + post_user_provider_execution + do + if user_provider.successful then + set_successful + else + if attached user_provider.last_error then + set_last_error_from_handler (user_provider.last_error) + end + end + end + + node_provider: NODE_DATA_PROVIDER + -- Node Data provider. + + user_provider: USER_DATA_PROVIDER + -- User Data provider. + +end diff --git a/persistence/implementation/sqlite/src/database/database_connection_mysql.e b/persistence/implementation/sqlite/src/database/database_connection_mysql.e new file mode 100644 index 0000000..bc5f890 --- /dev/null +++ b/persistence/implementation/sqlite/src/database/database_connection_mysql.e @@ -0,0 +1,145 @@ +note + description: "Object that handle a database connection for ODBC" + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + DATABASE_CONNECTION_MYSQL + +inherit + + DATABASE_CONNECTION + redefine + db_application + end + +create + make, make_common, make_basic, login_with_connection_string, login_with_schema + +feature -- Initialization + + make_common + -- Create a database handler for MYSQL with common settings. + local + l_retried: BOOLEAN + do + create db_application.login (username, password) + + if not l_retried then + db_application.set_hostname (hostname) + db_application.set_data_source (database_name) + db_application.set_base + create db_control.make + keep_connection := is_keep_connection + if keep_connection then + connect + end + set_successful + else + create db_control.make + end + rescue + create db_control.make + set_last_error_from_exception ("Connection execution") + log.write_critical (generator + ".make_common:" + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + + make_basic (a_database_name: STRING) + -- Create a database handler and + -- set database_name to `a_database_name'. + local + l_retried: BOOLEAN + do + create db_application.login (username, password) + + if not l_retried then + db_application.set_hostname (hostname) + db_application.set_data_source (a_database_name) + db_application.set_base + create db_control.make + keep_connection := is_keep_connection + if keep_connection then + connect + end + set_successful + else + create db_control.make + end + rescue + create db_control.make + set_last_error_from_exception ("Connection execution") + log.write_critical (generator + ".make_common:" + last_error_message) + if is_connected then + disconnect + end + l_retried := True + retry + end + + make (a_username: STRING; a_password: STRING; a_hostname: STRING; a_database_name: STRING; connection: BOOLEAN) + + -- Create a database handler for ODBC and set `username' to `a_username', + -- `password' to `a_password' + -- `database_name' to `a_database_name' + -- `connection' to `a_connection' + do + create db_application.login (a_username, a_password) + db_application.set_hostname (a_hostname) + db_application.set_data_source (a_database_name) + db_application.set_base + create db_control.make + keep_connection := connection + if keep_connection then + connect + end + end + + login_with_connection_string (a_string: STRING) + -- Login with `a_connection_string'and immediately connect to database. + local + l_string: LIST[STRING] + l_server: STRING + l_port: STRING + l_schema: STRING + l_user: STRING + l_password: STRING + do + l_string := a_string.split (';') + l_server := l_string.at (2).split ('=').at (2) + l_port := l_string.at (3).split ('=').at (2) + l_schema := l_string.at (4).split ('=').at (2) + l_user := l_string.at (5).split ('=').at (2) + l_password := l_string.at (6).split ('=').at (2) + + create db_application + db_application.set_application (l_schema) + db_application.set_hostname (l_server + ":" + l_port) + db_application.login_and_connect (l_user, l_password) + db_application.set_base + create db_control.make + keep_connection := is_keep_connection + + end + + login_with_schema (a_schema: STRING; a_username: STRING; a_password: STRING) + -- Login with `a_connection_string'and immediately connect to database. + do + create db_application + db_application.set_application (a_schema) + db_application.login_and_connect (a_username, a_password) + db_application.set_base + create db_control.make + keep_connection := is_keep_connection + end + +feature -- Databse Connection + + db_application: DATABASE_APPL [MYSQL] + -- Database application. + +end diff --git a/persistence/implementation/sqlite/src/provider/node_data_provider.e b/persistence/implementation/sqlite/src/provider/node_data_provider.e new file mode 100644 index 0000000..eef4756 --- /dev/null +++ b/persistence/implementation/sqlite/src/provider/node_data_provider.e @@ -0,0 +1,289 @@ +note + description: "Database access for node uses cases." + date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision: "$Revision: 95678 $" + +class + NODE_DATA_PROVIDER + +inherit + + PARAMETER_NAME_HELPER + + SHARED_ERROR + + REFACTORING_HELPER + +create + make + +feature -- Initialization + + make (a_connection: DATABASE_CONNECTION) + -- Create a data provider. + do + create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) + post_execution + end + + db_handler: DATABASE_HANDLER + -- Db handler. + +feature -- Status Report + + is_successful: BOOLEAN + -- Is the last execution sucessful? + do + Result := db_handler.successful + end + +feature -- Access + + nodes: DATABASE_ITERATION_CURSOR [CMS_NODE] + -- List of nodes. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".nodes") + create l_parameters.make (0) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_nodes, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_node) + post_execution + end + + recent_nodes (a_lower, a_rows: INTEGER): DATABASE_ITERATION_CURSOR [CMS_NODE] + -- The most recent `a_rows'. + local + l_parameters: STRING_TABLE [ANY] + l_query: STRING + do + log.write_information (generator + ".recent_nodes") + create l_parameters.make (2) + l_parameters.put (a_rows, "rows") + create l_query.make_from_string (select_recent_nodes) + l_query.replace_substring_all ("$offset", a_lower.out) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (l_query, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_node) + post_execution + end + + node (a_id: INTEGER_64): detachable CMS_NODE + -- Node for the given id `a_id', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".node") + create l_parameters.make (1) + l_parameters.put (a_id,"id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_node_by_id, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_node + end + post_execution + end + + count: INTEGER + -- Number of items nodes. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".count") + create l_parameters.make (0) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_count, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := db_handler.read_integer_32 (1) + end + post_execution + end + +feature -- Basic operations + + new_node (a_node: CMS_NODE) + -- Create a new node. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".new_node") + create l_parameters.make (6) + l_parameters.put (a_node.title, "title") + l_parameters.put (a_node.summary, "summary") + l_parameters.put (a_node.content, "content") + l_parameters.put (a_node.publication_date, "publication_date") + l_parameters.put (a_node.creation_date, "creation_date") + l_parameters.put (a_node.modification_date, "modification_date") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_insert_node, l_parameters)) + db_handler.execute_change + post_execution + end + + update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + -- Update node title for the corresponding the report with id `a_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".update_node_title") + create l_parameters.make (3) + l_parameters.put (a_title, "title") + l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") + l_parameters.put (a_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_update_node_title, l_parameters)) + db_handler.execute_change + post_execution + end + + update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + -- Update node summary for the corresponding the report with id `a_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".update_node_summary") + create l_parameters.make (3) + l_parameters.put (a_summary, "summary") + l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") + l_parameters.put (a_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_update_node_summary, l_parameters)) + db_handler.execute_change + post_execution + end + + update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + -- Update node content for the corresponding the report with id `a_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".update_node_content") + create l_parameters.make (3) + l_parameters.put (a_content, "content") + l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") + l_parameters.put (a_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_update_node_content, l_parameters)) + db_handler.execute_change + post_execution + end + + update_node (a_node: CMS_NODE) + -- Update node. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".update_node") + create l_parameters.make (7) + l_parameters.put (a_node.title, "title") + l_parameters.put (a_node.summary, "summary") + l_parameters.put (a_node.content, "content") + l_parameters.put (a_node.publication_date, "publication_date") + l_parameters.put (a_node.creation_date, "creation_date") + l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") + l_parameters.put (a_node.id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_update_node, l_parameters)) + db_handler.execute_change + post_execution + end + + delete_node (a_id: INTEGER_64;) + -- Delete node with id `a_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".delete_node") + create l_parameters.make (1) + l_parameters.put (a_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_delete_node, l_parameters)) + db_handler.execute_change + post_execution + end + +feature -- Connection + + connect + -- Connect to the database. + do + if not db_handler.is_connected then + db_handler.connect + end + end + + disconnect + -- Disconnect to the database. + do + if db_handler.is_connected then + db_handler.disconnect + end + end + +feature {NONE} -- Queries + + Select_count: STRING = "select count(*) from Nodes;" + + Select_nodes: STRING = "select * from Nodes;" + -- SQL Query to retrieve all nodes. + + Select_node_by_id: STRING = "select * from Nodes where id =:id order by id desc, publication_date desc;" + + Select_recent_nodes: STRING = "select * from Nodes order by id desc, publication_date desc Limit $offset , :rows " + + SQL_Insert_node: STRING = "insert into nodes (title, summary, content, publication_date, creation_date, modification_date) values (:title, :summary, :content, :publication_date, :creation_date, :modification_date);" + -- SQL Insert to add a new node. + + SQL_Update_node_title: STRING ="update nodes SET title=:title, modification_date=:modification_date where id=:id;" + -- SQL update node title. + + SQL_Update_node_summary: STRING ="update nodes SET summary=:summary, modification_date=:modification_date where id=:id;" + -- SQL update node summary. + + SQL_Update_node_content: STRING ="update nodes SET content=:content, modification_date=:modification_date where id=:id;" + -- SQL node content. + + SQL_Update_node : STRING = "update nodes SET title=:title, summary=:summary, content=:content, publication_date=:publication_date, creation_date=:creation_date, modification_date=:modification_date where id=:id;" + -- SQL node. + + SQL_Delete_node: STRING = "delete from nodes where id=:id;" + + +feature -- New Object + + fetch_node: CMS_NODE + do + create Result.make ("", "", "") + if attached db_handler.read_integer_32 (1) as l_id then + Result.set_id (l_id) + end + if attached db_handler.read_date_time (2) as l_pd then + Result.set_publication_date (l_pd) + end + if attached db_handler.read_date_time (3) as l_cd then + Result.set_creation_date (l_cd) + end + if attached db_handler.read_date_time (4) as l_md then + Result.set_modification_date (l_md) + end + if attached db_handler.read_string (5) as l_t then + Result.set_title (l_t) + end + if attached db_handler.read_string (6) as l_s then + Result.set_summary (l_s) + end + if attached db_handler.read_string (7) as l_c then + Result.set_content (l_c) + end + end + +feature {NONE} -- Implementation + + post_execution + -- Post database execution. + do + if db_handler.successful then + set_successful + else + if attached db_handler.last_error then + set_last_error_from_handler (db_handler.last_error) + end + end + end + +end diff --git a/persistence/implementation/sqlite/src/provider/user_data_provider.e b/persistence/implementation/sqlite/src/provider/user_data_provider.e new file mode 100644 index 0000000..e603c05 --- /dev/null +++ b/persistence/implementation/sqlite/src/provider/user_data_provider.e @@ -0,0 +1,206 @@ +note + description: "Summary description for {USER_DATA_PROVIDER}." + date: "$Date$" + revision: "$Revision$" + +class + USER_DATA_PROVIDER + +inherit + + PARAMETER_NAME_HELPER + + SHARED_ERROR + + REFACTORING_HELPER + +create + make + +feature -- Initialization + + make (a_connection: DATABASE_CONNECTION) + -- Create a data provider. + do + create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) + post_execution + end + + db_handler: DATABASE_HANDLER + -- Db handler. + +feature -- Status Report + + is_successful: BOOLEAN + -- Is the last execution sucessful? + do + Result := db_handler.successful + end + + has_user: BOOLEAN + -- Has any user? + do + Result := count > 0 + end + +feature -- Basic Operations + + new_user (a_user_name: READABLE_STRING_32; a_password: READABLE_STRING_32; a_email: READABLE_STRING_32) + -- Create a new node. + local + l_parameters: STRING_TABLE [detachable ANY] + l_password_salt, l_password_hash: STRING + l_security: SECURITY_PROVIDER + do + create l_security + l_password_salt := l_security.salt + l_password_hash := l_security.password_hash (a_password, l_password_salt) + + log.write_information (generator + ".new_user") + create l_parameters.make (4) + l_parameters.put (a_user_name,"username") + l_parameters.put (l_password_hash,"password") + l_parameters.put (l_password_salt,"salt") + l_parameters.put (a_email,"email") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_insert_user, l_parameters)) + db_handler.execute_change + post_execution + end + + user (a_id: INTEGER_64): detachable CMS_USER + -- User for the given id `a_id', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".user") + create l_parameters.make (1) + l_parameters.put (a_id,"id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_user_by_id, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_user + end + post_execution + end + + user_by_name (a_name: READABLE_STRING_32): detachable CMS_USER + -- User for the given name `a_name', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".user_by_name") + create l_parameters.make (1) + l_parameters.put (a_name,"name") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_user_by_name, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_user + end + post_execution + end + + + user_by_email (a_email: detachable READABLE_STRING_32): detachable CMS_USER + -- User for the given email `a_email', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + log.write_information (generator + ".user_by_email") + create l_parameters.make (1) + l_parameters.put (a_email,"email") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_user_by_email, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_user + end + post_execution + end + + user_salt (a_username: READABLE_STRING_32): detachable READABLE_STRING_32 + -- User salt for the given user `a_username', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".user_salt") + create l_parameters.make (1) + l_parameters.put (a_username,"name") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_salt_by_username, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + if attached db_handler.read_string (1) as l_salt then + Result := l_salt.as_string_32 + end + end + post_execution + end + + count: INTEGER + -- Number of items users. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".count") + create l_parameters.make (0) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_count, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := db_handler.read_integer_32 (1) + end + post_execution + end + +feature -- New Object + + fetch_user: CMS_USER + do + create Result.make ("") + if attached db_handler.read_integer_32 (1) as l_id then + Result.set_id (l_id) + end + if attached db_handler.read_string (2) as l_u then + Result.set_name (l_u) + end + if attached db_handler.read_string (3) as l_p then + Result.set_password (l_p) + end + if attached db_handler.read_string (5) as l_e then + Result.set_email (l_e) + end + end + +feature -- Sql Queries + + Select_count: STRING = "select count(*) from Users;" + -- Number of users. + + Select_user_by_id: STRING = "select * from Users where id =:id;" + -- Retrieve user by id if exists. + + Select_user_by_name: STRING = "select * from Users where username =:name;" + -- Retrieve user by name if exists. + + Select_user_by_email: STRING = "select * from Users where email =:email;" + -- Retrieve user by email if exists. + + Select_salt_by_username: STRING = "select salt from Users where username =:name;" + -- Retrieve salt by username if exists. + + SQL_Insert_user: STRING = "insert into users (username, password, salt, email) values (:username, :password, :salt, :email);" + -- SQL Insert to add a new node. + + +feature {NONE} -- Implementation + + post_execution + -- Post database execution. + do + if db_handler.successful then + set_successful + else + if attached db_handler.last_error then + set_last_error_from_handler (db_handler.last_error) + end + end + end + +end diff --git a/persistence/implementation/sqlite/tests/application.e b/persistence/implementation/sqlite/tests/application.e new file mode 100644 index 0000000..fc10bf5 --- /dev/null +++ b/persistence/implementation/sqlite/tests/application.e @@ -0,0 +1,31 @@ +note + description : "tests application root class" + date : "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" + revision : "$Revision: 95678 $" + +class + APPLICATION + +inherit + ARGUMENTS + +create + make + +feature {NONE} -- Initialization + + make + -- Run application. + local + user: USER_DATA_PROVIDER + node: NODE_DATA_PROVIDER + l_security: SECURITY_PROVIDER + do + create connection.make_basic ("cms_dev") + create user.make (connection) + user.new_user ("test", "test", "test") + end + + connection: DATABASE_CONNECTION_ODBC + +end diff --git a/persistence/implementation/sqlite/tests/nodes/node_test_set.e b/persistence/implementation/sqlite/tests/nodes/node_test_set.e new file mode 100644 index 0000000..3ae6665 --- /dev/null +++ b/persistence/implementation/sqlite/tests/nodes/node_test_set.e @@ -0,0 +1,225 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + NODE_TEST_SET + +inherit + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + assert ("Empty Nodes", node_provider.nodes.after) + end + + on_clean + -- + do + end + +feature -- Test routines + + test_new_node + do + assert ("Empty Nodes", node_provider.nodes.after) + node_provider.new_node (default_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached node_provider.node (1)) + -- Not exist node with id 2 + assert ("Not exist node with id 2", node_provider.node (2) = Void) + end + + + test_update_node + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Update node (content and summary) + + if attached {CMS_NODE} node_provider.node (1) as l_un then + l_un.set_content ("

Updating test node udpate

") + l_un.set_summary ("updating summary") + node_provider.update_node (l_un) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then not (ll_node.content ~ l_node.content) and then not (ll_node.summary ~ l_node.summary) and then ll_node.title ~ l_node.title ) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title ) + end + + -- Update node (content and summary and title) + if attached {CMS_NODE} node_provider.node (1) as l_un then + l_un.set_content ("

Updating test node udpate

") + l_un.set_summary ("updating summary") + l_un.set_title ("Updating Test case") + node_provider.update_node (l_un) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then not (ll_node.content ~ l_node.content) and then not (ll_node.summary ~ l_node.summary) and then not (ll_node.title ~ l_node.title) ) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title ) + end + end + + test_update_title + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Update node title + + if attached {CMS_NODE} node_provider.node (1) as l_un then + node_provider.update_node_title (l_un.id, "New Title") + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then not ( ll_node.title ~ l_un.title) and then ll_node.title ~ "New Title" ) + end + end + + test_update_summary + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Update node summary + + if attached {CMS_NODE} node_provider.node (1) as l_un then + node_provider.update_node_summary (l_un.id,"New Summary") + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then not (ll_node.summary ~ l_un.summary) and then ll_node.summary ~ "New Summary" and then ll_node.title ~ l_un.title) + end + end + + test_update_content + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Update node content + + if attached {CMS_NODE} node_provider.node (1) as l_un then + node_provider.update_node_content (l_un.id,"New Content") + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then not (ll_node.content ~ l_un.content) and then ll_node.content ~ "New Content" and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title) + end + end + + + test_delete_node + local + l_node: CMS_NODE + do + assert ("Empty Nodes", node_provider.nodes.after) + l_node := custom_node ("

test node udpate

", "Update node", "Test case update") + node_provider.new_node (l_node) + assert ("Not empty Nodes after new_node", not node_provider.nodes.after) + -- Exist node with id 1 + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_node.content and then ll_node.summary ~ l_node.summary and then ll_node.title ~ l_node.title ) + + -- Delte node 1 + + node_provider.delete_node (1) + assert ("Node does not exist", node_provider.node (1) = Void) + end + + test_recent_nodes + -- Content_10, Summary_10, Title_10 + -- Content_9, Summary_9, Title_9 + -- .. + -- Content_1, Summary_1, Title_1 + local + i : INTEGER + do + assert ("Empty Nodes", node_provider.nodes.after) + across 1 |..| 10 as c loop + node_provider.new_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + + -- Scenario (0,10) rows, recents (10 down to 1) + i := 10 + across node_provider.recent_nodes (0, 10) as c loop + assert ("Same id:" + i.out, c.item.id = i) + i := i - 1 + end + + -- Scenario (5, 10) rows, recent nodes (5 down to 1) + i := 5 + across node_provider.recent_nodes (5, 10) as c loop + assert ("Same id:" + i.out, c.item.id = i) + i := i - 1 + end + + -- Scenario (9,10) rows, recent node 1 + i := 1 + across node_provider.recent_nodes (9, 10) as c loop + assert ("Same id:" + i.out, c.item.id = i) + i := i - 1 + end + + -- Scenrario 10..10 empty + assert ("Empty", node_provider.recent_nodes (10, 10).after) + + end + +feature {NONE} -- Implementation + + node_provider: NODE_DATA_PROVIDER + -- node provider. + once + create Result.make (connection) + end + + +feature {NONE} -- Implementation Fixture Factories + + default_node: CMS_NODE + do + Result := custom_node ("Default content", "default summary", "Default") + end + + custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE + do + create Result.make (a_content, a_summary, a_title) + end + +end + + + diff --git a/persistence/implementation/sqlite/tests/storage/storage_test_set.e b/persistence/implementation/sqlite/tests/storage/storage_test_set.e new file mode 100644 index 0000000..521d78b --- /dev/null +++ b/persistence/implementation/sqlite/tests/storage/storage_test_set.e @@ -0,0 +1,323 @@ +note + description: "Summary description for {STORAGE_TEST_SET}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + STORAGE_TEST_SET + +inherit + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + end + + on_clean + -- + do + end + +feature -- Test routines + + test_has_user + do + assert ("Not has user", not storage.has_user) + end + + test_all_users + do + assert ("to implement all_users", False) + end + + test_user_by_id_not_exist + do + assert ("User does not exist", storage.user_by_id (1) = Void) + end + + test_user_by_name_not_exist + do + assert ("User does not exist", storage.user_by_name ("test") = Void) + end + + test_user_by_email_not_exist + do + assert ("User does not exist", storage.user_by_name ("test@test.com") = Void) + end + + test_user_with_bad_id + local + l_retry: BOOLEAN + l_user: detachable CMS_USER + do + if not l_retry then + l_user := storage.user_by_id (0) + assert ("Precondition does not get the wrong value", False) + else + assert ("Expected precondition violation", True) + end + rescue + l_retry := True + retry + end + + test_user_with_bad_name_empty + local + l_retry: BOOLEAN + l_user: detachable CMS_USER + do + if not l_retry then + l_user := storage.user_by_name ("") + assert ("Precondition does not get the wrong value", False) + else + assert ("Expected precondition violation", True) + end + rescue + l_retry := True + retry + end + + test_user_with_bad_email_empty + local + l_retry: BOOLEAN + l_user: detachable CMS_USER + do + if not l_retry then + l_user := storage.user_by_email ("") + assert ("Precondition does not get the wrong value", False) + else + assert ("Expected precondition violation", True) + end + rescue + l_retry := True + retry + end + + test_save_user + do + storage.save_user (default_user) + assert ("Has user", storage.has_user) + end + + test_user_by_id + do + storage.save_user (default_user) + assert ("Has user", storage.has_user) + if attached {CMS_USER} storage.user_by_id (1) as l_user then + assert ("Exist", True) + assert ("User test", l_user.name ~ "test") + assert ("User id = 1", l_user.id = 1) + else + assert ("Wrong Implementation", False) + end + end + + test_user_by_name + do + storage.save_user (default_user) + assert ("Has user", storage.has_user) + if attached {CMS_USER} storage.user_by_name ("test") as l_user then + assert ("Exist", True) + assert ("User nane: test", l_user.name ~ "test") + else + assert ("Wrong Implementation", False) + end + end + + test_user_by_email + do + storage.save_user (default_user) + assert ("Has user", storage.has_user) + if attached {CMS_USER} storage.user_by_email ("test@test.com") as l_user then + assert ("Exist", True) + assert ("User email: test@test.com", l_user.email ~ "test@test.com") + else + assert ("Wrong Implementation", False) + end + end + + test_invalid_credential + do + storage.save_user (default_user) + assert ("Has user test", attached storage.user_by_name ("test")) + assert ("Wrong password", not storage.is_valid_credential ("test", "test")) + assert ("Wrong user", not storage.is_valid_credential ("test1", "test")) + end + + test_valid_credential + do + storage.save_user (default_user) + assert ("Has user test", attached storage.user_by_name ("test")) + assert ("Valid password", storage.is_valid_credential ("test", "password")) + end + + test_recent_nodes_empty + do + assert ("No recent nodes", storage.recent_nodes (0, 10).is_empty) + end + + test_recent_nodes + local + l_nodes: LIST[CMS_NODE] + do + across 1 |..| 10 as c loop + storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + l_nodes := storage.recent_nodes (0, 10) + assert ("10 recent nodes", l_nodes.count = 10) + assert ("First node id=10", l_nodes.first.id = 10) + assert ("Last node id=1", l_nodes.last.id = 1) + + + l_nodes := storage.recent_nodes (5, 10) + assert ("5 recent nodes", l_nodes.count = 5) + assert ("First node id=5", l_nodes.first.id = 5) + assert ("Last node id=1", l_nodes.last.id = 1) + + l_nodes := storage.recent_nodes (9, 10) + assert ("1 recent nodes", l_nodes.count = 1) + assert ("First node id=1", l_nodes.first.id = 1) + assert ("Last node id=1", l_nodes.last.id = 1) + + l_nodes := storage.recent_nodes (10, 10) + assert ("Is empty", l_nodes.is_empty) + end + + test_node_does_not_exist + local + l_nodes: LIST[CMS_NODE] + do + across 1 |..| 10 as c loop + storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + assert ("Not exist node id: 12", storage.node (12) = Void) + end + + test_node + local + l_nodes: LIST[CMS_NODE] + do + across 1 |..| 10 as c loop + storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + assert ("Node id: 10", attached storage.node (10) as l_node and then l_node.title ~ "Title_10" ) + end + + test_update_node + local + l_nodes: LIST[CMS_NODE] + l_node: CMS_NODE + do + storage.save_node (custom_node ("Content", "Summary", "Title")) + if attached {CMS_NODE} storage.node (1) as ll_node then + l_node := ll_node.twin + l_node.set_content ("New Content") + l_node.set_summary ("New Summary") + l_node.set_title("New Title") + + storage.update_node (l_node) + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary)) + end + end + + test_update_node_title + local + l_nodes: LIST[CMS_NODE] + l_node: CMS_NODE + do + storage.save_node (custom_node ("Content", "Summary", "Title")) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_title (ll_node.id, "New Title") + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary) + end + end + + test_update_node_summary + local + l_nodes: LIST[CMS_NODE] + l_node: CMS_NODE + do + storage.save_node (custom_node ("Content", "Summary", "Title")) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_summary (ll_node.id, "New Summary") + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary)) + end + end + + test_update_node_content + local + l_nodes: LIST[CMS_NODE] + l_node: CMS_NODE + do + storage.save_node (custom_node ("Content", "Summary", "Title")) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_content (ll_node.id, "New Content") + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary) + end + end + + test_delete_node + local + l_nodes: LIST[CMS_NODE] + do + across 1 |..| 10 as c loop + storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + end + assert ("Exist node id: 10", attached storage.node (10) as l_node and then l_node.title ~ "Title_10" ) + storage.delete_node (10) + assert ("Not exist node id: 10", storage.node (10) = Void) + end + + + +feature {NONE} -- Implementation + + storage: CMS_STORAGE + -- Storage + once + create {CMS_STORAGE_SQLITE}Result.make (connection) + end + +feature {NONE} -- Fixture Factory: Users + + default_user: CMS_USER + do + Result := custom_user ("test", "password", "test@test.com") + end + + custom_user (a_name, a_password, a_email: READABLE_STRING_32): CMS_USER + do + create Result.make (a_name) + Result.set_password (a_password) + Result.set_email (a_email) + end + +feature {NONE} -- Fixture Factories: Nodes + + default_node: CMS_NODE + do + Result := custom_node ("Default content", "default summary", "Default") + end + + custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE + do + create Result.make (a_content, a_summary, a_title) + end + +end diff --git a/persistence/implementation/sqlite/tests/tests.ecf b/persistence/implementation/sqlite/tests/tests.ecf new file mode 100644 index 0000000..d401b33 --- /dev/null +++ b/persistence/implementation/sqlite/tests/tests.ecf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + diff --git a/persistence/implementation/sqlite/tests/users/user_test_set.e b/persistence/implementation/sqlite/tests/users/user_test_set.e new file mode 100644 index 0000000..6b06e9f --- /dev/null +++ b/persistence/implementation/sqlite/tests/users/user_test_set.e @@ -0,0 +1,78 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + USER_TEST_SET + +inherit + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + user_provider.new_user ("admin", "admin","admin@admin.com") + end + + on_clean + -- + do + end + +feature -- Test routines + + test_user_exist + -- User admin exist + do + assert ("Not void", attached user_provider.user_by_email ("admin@admin.com")) + assert ("Not void", attached user_provider.user (1)) + assert ("Not void", attached user_provider.user_by_name ("admin")) + end + + test_user_not_exist + -- Uset test does not exist. + do + assert ("Void", user_provider.user_by_email ("test@admin.com") = Void) + assert ("Void", user_provider.user(2) = Void ) + assert ("Void", user_provider.user_by_name ("test") = Void) + end + + test_new_user + do + user_provider.new_user ("test", "test","test@admin.com") + assert ("Not void", attached user_provider.user_by_email ("test@admin.com")) + assert ("Not void", attached user_provider.user (2)) + assert ("Not void", attached user_provider.user (2) as l_user and then l_user.id = 2 and then l_user.name ~ "test") + assert ("Not void", attached user_provider.user_by_name ("test")) + end + + +feature {NONE} -- Implementation + + user_provider: USER_DATA_PROVIDER + -- user provider. + once + create Result.make (connection) + end +end + + diff --git a/persistence/implementation/sqlite/tests/util/abstract_db_test.e b/persistence/implementation/sqlite/tests/util/abstract_db_test.e new file mode 100644 index 0000000..6ba5b1b --- /dev/null +++ b/persistence/implementation/sqlite/tests/util/abstract_db_test.e @@ -0,0 +1,19 @@ +note + description: "Summary description for {ABSTRACT_DB_TEST}." + date: "$Date$" + revision: "$Revision$" + +class + ABSTRACT_DB_TEST + + +feature -- Database connection + + connection: DATABASE_CONNECTION_ODBC + -- odbc database connection + once +-- create Result.login_with_connection_string ("Driver=SQLite3 ODBC Driver;Database=PATH/SQLITE.FILE;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;") + create Result.make_basic ("cms_dev") + end + +end diff --git a/persistence/implementation/sqlite/tests/util/clean_db.e b/persistence/implementation/sqlite/tests/util/clean_db.e new file mode 100644 index 0000000..d381540 --- /dev/null +++ b/persistence/implementation/sqlite/tests/util/clean_db.e @@ -0,0 +1,66 @@ +note + description: "[ + Setting up database tests + 1. Put the database in a known state before running your test suite. + 2. Data reinitialization. For testing in developer sandboxes, something that you should do every time you rebuild the system, you may want to forgo dropping and rebuilding the database in favor of simply reinitializing the source data. + You can do this either by erasing all existing data and then inserting the initial data vales back into the database, or you can simple run updates to reset the data values. + The first approach is less risky and may even be faster for large amounts of data. - See more at: http://www.agiledata.org/essays/databaseTesting.html#sthash.6yVp35g8.dpuf + ]" + + date: "$Date$" + revision: "$Revision$" + EIS: "name=Database Testing", "src=http://www.agiledata.org/essays/databaseTesting.html", "protocol=uri" + +class + CLEAN_DB + +feature + + clean_db (a_connection: DATABASE_CONNECTION) + -- Clean db test. + local + l_parameters: STRING_TABLE[STRING] + do + create l_parameters.make (0) + + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_nodes, l_parameters)) + db_handler(a_connection).execute_change + + -- Clean Users + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_users, l_parameters)) + db_handler(a_connection).execute_change + +-- -- Reset Autoincremente +-- db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_users_autoincrement, l_parameters)) +-- db_handler(a_connection).execute_change + +-- db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_nodes_autoincrement, l_parameters)) +-- db_handler(a_connection).execute_change + end + + + +feature -- Database Hanlder + + db_handler (a_connection: DATABASE_CONNECTION): DATABASE_HANDLER + -- Db handler + once + create {DATABASE_HANDLER_IMPL} Result.make (a_connection) + end + + +feature -- Sql delete queries + + Sql_delete_users: STRING = "delete from Users" + -- Clean Users. + + Sql_delete_nodes: STRING = "delete from Nodes" + -- Clean Nodes. + + Rest_users_autoincrement: STRING = "ALTER TABLE Users AUTO_INCREMENT = 1" + -- reset autoincrement + + Rest_nodes_autoincrement: STRING = "ALTER TABLE Nodes AUTO_INCREMENT = 1" + -- reset autoincrement. + +end From 1dce89329325af3df4369a77ef0aa1a5e9e17629 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 17 Sep 2014 14:43:08 -0300 Subject: [PATCH 007/386] Update API to use CMS_STORAGE --- api/src/configuration/configuration_factory.e | 6 +- api/src/roc_server.e | 6 - api/src/service/handler/roc_root_handler.e | 2 +- api/src/service/roc_api_service.e | 112 +++++------------- .../implementation/common/cms_storage_null.e | 105 ++++++++++++++++ 5 files changed, 140 insertions(+), 91 deletions(-) create mode 100644 persistence/implementation/common/cms_storage_null.e diff --git a/api/src/configuration/configuration_factory.e b/api/src/configuration/configuration_factory.e index fa8127e..2477bbf 100644 --- a/api/src/configuration/configuration_factory.e +++ b/api/src/configuration/configuration_factory.e @@ -34,7 +34,7 @@ feature -- Factory if attached (create {JSON_CONFIGURATION}).new_database_configuration (l_layout.application_config_path) as l_database_config then create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string) - create l_api_service.make_with_database (l_database) + create l_api_service.make (create {CMS_STORAGE_MYSQL}.make (l_database)) create Result.make (l_database, l_api_service, l_email_service, l_layout) if (create {ROC_JSON_CONFIGURATION}).is_web_mode(l_layout.application_config_path) then Result.mark_web @@ -44,7 +44,7 @@ feature -- Factory set_successful else create {DATABASE_CONNECTION_NULL} l_database.make_common - create l_api_service.make_with_database (l_database) + create l_api_service.make (create {CMS_STORAGE_NULL}) create Result.make (l_database, l_api_service, l_email_service, l_layout) set_last_error ("Database Connections", generator + ".roc_config") log.write_error (generator + ".roc_config Error database connection" ) @@ -58,7 +58,7 @@ feature -- Factory create l_email_service.make ((create {JSON_CONFIGURATION}).new_smtp_configuration(l_layout.application_config_path)) create {DATABASE_CONNECTION_NULL} l_database.make_common - create l_api_service.make_with_database (l_database) + create l_api_service.make (create {CMS_STORAGE_NULL}) create Result.make (l_database, l_api_service, l_email_service, l_layout) end rescue diff --git a/api/src/roc_server.e b/api/src/roc_server.e index 9175669..0a4c090 100644 --- a/api/src/roc_server.e +++ b/api/src/roc_server.e @@ -158,12 +158,6 @@ feature -- Filters fh.custom_header.put_header ("X-ROCServer: " + version) l_filter := fh - -- Cors - create {WSF_CORS_FILTER}f - f.set_next (l_filter) - l_filter := f - - -- Maintenance create {WSF_MAINTENANCE_FILTER} f f.set_next (l_filter) diff --git a/api/src/service/handler/roc_root_handler.e b/api/src/service/handler/roc_root_handler.e index 5fecea3..7574d08 100644 --- a/api/src/service/handler/roc_root_handler.e +++ b/api/src/service/handler/roc_root_handler.e @@ -54,7 +54,7 @@ feature -- HTTP Methods do create l_page.make (req, "layout2.tpl") - l_page.set_value (api_service.recent_nodes (5), "nodes") + l_page.set_value (api_service.recent_nodes (0,5), "nodes") l_page.set_value (is_web, "web") l_page.set_value (roc_config.is_html, "html") l_page.send_to (res) diff --git a/api/src/service/roc_api_service.e b/api/src/service/roc_api_service.e index 60f877c..300365a 100644 --- a/api/src/service/roc_api_service.e +++ b/api/src/service/roc_api_service.e @@ -9,23 +9,21 @@ class inherit SHARED_ERROR + REFACTORING_HELPER -create make_with_database +create make feature -- Initialize - make_with_database (a_connection: DATABASE_CONNECTION) - -- Create the API service - require - is_connected: a_connection.is_connected + make (a_storage: CMS_STORAGE) + -- Create the API service with an storege `a_storage'. do - log.write_information (generator+".make_with_database is database connected? "+ a_connection.is_connected.out ) - create node_provider.make (a_connection) - create user_provider.make (a_connection) - post_node_provider_execution - post_user_provider_execution + storage := a_storage + set_successful + ensure + storage_set: storage = a_storage end feature -- Access @@ -34,49 +32,27 @@ feature -- Access local l_security: SECURITY_PROVIDER do - if attached user_provider.user_salt (l_auth_login) as l_hash then - if attached user_provider.user_by_name (l_auth_login) as l_user then - create l_security - if - attached l_user.password as l_password and then - l_security.password_hash (l_auth_password, l_hash).is_case_insensitive_equal (l_password) - then - Result := True - else - log.write_information (generator + ".login_valid User: wrong username or password" ) - end - else - log.write_information (generator + ".login_valid User:" + l_auth_login + "does not exist" ) - end - end - post_user_provider_execution + Result := storage.is_valid_credential (l_auth_login, l_auth_password) end nodes: LIST[CMS_NODE] -- List of nodes. do - create {ARRAYED_LIST[CMS_NODE]} Result.make (0) - across node_provider.nodes as c loop - Result.force (c.item) - end - post_node_provider_execution + fixme ("Implementation") + Result := storage.recent_nodes (0, 10) end - recent_nodes (a_rows: INTEGER): LIST[CMS_NODE] - -- List of the `a_rows' most recent nodes. + recent_nodes (a_offset, a_rows: INTEGER): LIST[CMS_NODE] + -- List of the `a_rows' most recent nodes starting from `a_offset'. do - create {ARRAYED_LIST[CMS_NODE]} Result.make (0) - across node_provider.recent_nodes (0,a_rows) as c loop - Result.force (c.item) - end - post_node_provider_execution + Result := storage.recent_nodes (a_offset, a_rows) end node (a_id: INTEGER_64): detachable CMS_NODE - -- + -- Node by ID. do - Result := node_provider.node (a_id) - post_node_provider_execution + fixme ("Check preconditions") + Result := storage.node (a_id) end @@ -85,38 +61,35 @@ feature -- Node new_node (a_node: CMS_NODE) -- Add a new node do - node_provider.new_node (a_node) - post_node_provider_execution + storage.save_node (a_node) end delete_node (a_id: INTEGER_64) do - node_provider.delete_node (a_id) - post_node_provider_execution + storage.delete_node (a_id) end update_node (a_node: CMS_NODE) do - node_provider.update_node (a_node) - post_node_provider_execution + storage.update_node (a_node) end update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) do - node_provider.update_node_title (a_id, a_title) - post_node_provider_execution + fixme ("Check preconditions") + storage.update_node_title (a_id, a_title) end update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) do - node_provider.update_node_summary (a_id, a_summary) - post_node_provider_execution + fixme ("Check preconditions") + storage.update_node_summary (a_id, a_summary) end update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) do - node_provider.update_node_content (a_id, a_content) - post_node_provider_execution + fixme ("Check preconditions") + storage.update_node_content (a_id, a_content) end @@ -129,40 +102,17 @@ feature -- User attached a_user.password as l_password and then attached a_user.email as l_email then - user_provider.new_user (a_user.name, l_password, l_email) + storage.save_user (a_user) else - -- set error + fixme ("Add error") end end -feature {NONE} -- Post process - post_node_provider_execution - do - if node_provider.successful then - set_successful - else - if attached node_provider.last_error then - set_last_error_from_handler (node_provider.last_error) - end - end - end +feature {NONE} -- Implemenataion - post_user_provider_execution - do - if user_provider.successful then - set_successful - else - if attached user_provider.last_error then - set_last_error_from_handler (user_provider.last_error) - end - end - end - node_provider: NODE_DATA_PROVIDER - -- Node Data provider. - - user_provider: USER_DATA_PROVIDER - -- User Data provider. + storage: CMS_STORAGE + -- Persistence storage end diff --git a/persistence/implementation/common/cms_storage_null.e b/persistence/implementation/common/cms_storage_null.e new file mode 100644 index 0000000..0917b13 --- /dev/null +++ b/persistence/implementation/common/cms_storage_null.e @@ -0,0 +1,105 @@ +note + description: "Summary description for {CMS_STORAGE_NULL}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_STORAGE_NULL + +inherit + + CMS_STORAGE + REFACTORING_HELPER + + +feature -- Access: user + + has_user: BOOLEAN + -- Has any user? + do + end + + + all_users: LIST [CMS_USER] + do + create {ARRAYED_LIST[CMS_USER]} Result.make (0) + end + + user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER + do + end + + user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + do + end + + user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + do + end + + is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN + do + end + +feature -- Change: user + + save_user (a_user: CMS_USER) + -- Add a new user `a_user'. + do + end + +feature -- Access: node + + nodes: LIST[CMS_NODE] + -- List of nodes. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + end + + recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] + -- List of the `a_count' most recent nodes, starting from `a_lower'. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + end + + node (a_id: INTEGER_64): detachable CMS_NODE + -- + do + end + + +feature -- Node + + save_node (a_node: CMS_NODE) + -- Add a new node + do + end + + delete_node (a_id: INTEGER_64) + do + end + + update_node (a_node: CMS_NODE) + do + end + + update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + do + end + + update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + do + end + + update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + do + end + +feature -- User + + new_user (a_user: CMS_USER) + -- Add a new user `a_user'. + do + end + +end From 5d551f7fe1c412a3b63650cf7bc18389aea9dc05 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 18 Sep 2014 00:36:50 -0300 Subject: [PATCH 008/386] Updated navigation templte to include current user. Updated CMS_NODE, CMS_USER. Added USER_PROFILE and USER_ROLE Updated new USER_DATA_PROVIDER and NODE_DATA_PROVIDER to support new features. Added ROLE_DATA_PROVIDER. Updated test cases. Updated MySQL database schema. --- .../template/html/master2/site_navigation.tpl | 8 +- .../implementation/common/model/cms_node.e | 67 ++++++ .../implementation/common/model/cms_user.e | 98 ++++++++ .../common/model/cms_user_profile.e | 48 ++++ .../common/model/cms_user_role.e | 90 +++++++ .../mysql/scripts/create_database.sql | 79 +++++-- .../mysql/src/provider/role_data_provider.e | 222 ++++++++++++++++++ .../mysql/src/provider/user_data_provider.e | 43 +++- .../mysql/tests/roles/role_test_set.e | 100 ++++++++ .../mysql/tests/storage/storage_test_set.e | 16 -- .../mysql/tests/users/user_test_set.e | 15 ++ .../mysql/tests/util/clean_db.e | 37 +++ 12 files changed, 784 insertions(+), 39 deletions(-) create mode 100644 persistence/implementation/common/model/cms_user_profile.e create mode 100644 persistence/implementation/common/model/cms_user_role.e create mode 100644 persistence/implementation/mysql/src/provider/role_data_provider.e create mode 100644 persistence/implementation/mysql/tests/roles/role_test_set.e diff --git a/api/site/www/template/html/master2/site_navigation.tpl b/api/site/www/template/html/master2/site_navigation.tpl index caa4f0f..a75760c 100644 --- a/api/site/www/template/html/master2/site_navigation.tpl +++ b/api/site/www/template/html/master2/site_navigation.tpl @@ -7,8 +7,10 @@ diff --git a/persistence/implementation/common/model/cms_node.e b/persistence/implementation/common/model/cms_node.e index 2ac72af..3c63321 100644 --- a/persistence/implementation/common/model/cms_node.e +++ b/persistence/implementation/common/model/cms_node.e @@ -6,6 +6,10 @@ note class CMS_NODE +inherit + + REFACTORING_HELPER + create make @@ -21,6 +25,10 @@ feature{NONE} -- Initialization set_creation_date (l_time) set_modification_date (l_time) set_publication_date (l_time) + fixme ("Remove harcode format") + set_format ("HTML") + fixme ("Remove harcode content type") + set_content_type ("Page") ensure content_set: content = a_content summary_set: summary = a_summary @@ -48,10 +56,32 @@ feature -- Access -- When the node was published. publication_date_output: READABLE_STRING_32 + -- Formatted output. id: INTEGER_64 assign set_id -- Unique id. + format: READABLE_STRING_32 + -- Format associated with `body'. + -- For example: text, mediawiki, html, etc + + content_type: READABLE_STRING_32 + -- Associated content type name. + -- Page, Article, Blog, News, etc. + +feature -- status report + + has_id: BOOLEAN + do + Result := id > 0 + end + + author: detachable CMS_USER + -- Node's author. + + collaborators: detachable LIST[CMS_USER] + -- Node's collaborators. + feature -- Element change set_content (a_content: like content) @@ -103,6 +133,22 @@ feature -- Element change publication_date_assigned: publication_date = a_publication_date end + set_content_type (a_content_type: like content_type) + -- Assign `content_type' with `a_content_type'. + do + content_type := a_content_type + ensure + content_type_assigned: content_type = a_content_type + end + + set_format (a_format: like format) + -- Assign `format' with `a_format'. + do + format := a_format + ensure + format_assigned: format = a_format + end + set_id (an_id: like id) -- Assign `id' with `an_id'. do @@ -111,4 +157,25 @@ feature -- Element change id_assigned: id = an_id end + set_author (u: like author) + -- Assign 'author' with `u' + do + author := u + ensure + auther_set: author = u + end + + add_collaborator (a_user: CMS_USER) + -- Add collaborator `a_user' to the collaborators list. + local + lst: like collaborators + do + lst := collaborators + if lst = Void then + create {ARRAYED_SET [CMS_USER]} lst.make (1) + collaborators := lst + end + lst.force (a_user) + end + end diff --git a/persistence/implementation/common/model/cms_user.e b/persistence/implementation/common/model/cms_user.e index a0aa0cb..5bd836e 100644 --- a/persistence/implementation/common/model/cms_user.e +++ b/persistence/implementation/common/model/cms_user.e @@ -6,6 +6,10 @@ note class CMS_USER +inherit + + DEBUG_OUTPUT + create make @@ -15,6 +19,9 @@ feature {NONE} -- Initialization -- Create an object with name `a_name'. do name := a_name + create creation_date.make_now_utc + ensure + name_set: name = a_name end feature -- Access @@ -31,6 +38,48 @@ feature -- Access email: detachable READABLE_STRING_32 -- User email. + profile: detachable CMS_USER_PROFILE + -- User profile. + + creation_date: DATE_TIME + -- Creation date. + + last_login_date: detachable DATE_TIME + -- User last login. + + data: detachable STRING_TABLE [detachable ANY] + -- Additional user's data. + + data_item (k: READABLE_STRING_GENERAL): detachable ANY + -- Additional item data. + do + if attached data as l_data then + Result := l_data.item (k) + end + end + +feature -- Status report + + has_id: BOOLEAN + do + Result := id > 0 + end + + has_email: BOOLEAN + do + Result := attached email as e and then not e.is_empty + end + + debug_output: STRING + do + Result := name + end + + same_as (u: detachable CMS_USER): BOOLEAN + do + Result := u /= Void and then id = u.id + end + feature -- Change element set_id (a_id: like id) @@ -65,4 +114,53 @@ feature -- Change element email_set: email = m end + set_profile (prof: like profile) + -- Set `profile' with `prof'. + do + profile := prof + ensure + profile_set: profile = prof + end + + set_data_item (k: READABLE_STRING_GENERAL; d: like data_item) + local + l_data: like data + do + l_data := data + if l_data = Void then + create l_data.make (1) + data := l_data + end + l_data.force (d, k) + end + + remove_data_item (k: READABLE_STRING_GENERAL) + do + if attached data as l_data then + l_data.remove (k) + end + end + + set_profile_item (k: READABLE_STRING_8; v: READABLE_STRING_8) + local + prof: like profile + do + prof := profile + if prof = Void then + create prof.make + profile := prof + end + prof.force (v, k) + end + + set_last_login_date (dt: like last_login_date) + do + last_login_date := dt + end + + set_last_login_date_now + do + set_last_login_date (create {DATE_TIME}.make_now_utc) + end + end diff --git a/persistence/implementation/common/model/cms_user_profile.e b/persistence/implementation/common/model/cms_user_profile.e new file mode 100644 index 0000000..878ce38 --- /dev/null +++ b/persistence/implementation/common/model/cms_user_profile.e @@ -0,0 +1,48 @@ +note + description: "Summary description for {CMS_USER_PROFILE}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_USER_PROFILE + +inherit + TABLE_ITERABLE [READABLE_STRING_8, READABLE_STRING_8] + +create + make + +feature {NONE} -- Initialization + + make + do + create items.make (0) + end + +feature -- Access + + item (k: READABLE_STRING_8): detachable READABLE_STRING_8 + do + Result := items.item (k.as_string_8) + end + +feature -- Change + + force (v: READABLE_STRING_8; k: READABLE_STRING_8) + do + items.force (v, k.as_string_8) + end + +feature -- Access + + new_cursor: TABLE_ITERATION_CURSOR [READABLE_STRING_8, READABLE_STRING_8] + -- Fresh cursor associated with current structure + do + Result := items.new_cursor + end + +feature {NONE} -- Implementation + + items: HASH_TABLE [READABLE_STRING_8, STRING_8] + +end diff --git a/persistence/implementation/common/model/cms_user_role.e b/persistence/implementation/common/model/cms_user_role.e new file mode 100644 index 0000000..48f18ca --- /dev/null +++ b/persistence/implementation/common/model/cms_user_role.e @@ -0,0 +1,90 @@ +note + description: "Summary description for {CMS_USER_ROLE}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_USER_ROLE + +inherit + ANY + redefine + is_equal + end + +create + make, + make_with_id + +feature {NONE} -- Initialization + + make_with_id (a_id: like id; a_name: like name) + do + id := a_id + make (a_name) + end + + make (a_name: like name) + do + name := a_name + create {ARRAYED_LIST [READABLE_STRING_8]} permissions.make (0) + end + +feature -- Status report + + has_id: BOOLEAN + do + Result := id > 0 + end + + has_permission (p: READABLE_STRING_8): BOOLEAN + do + Result := across permissions as c some c.item.is_case_insensitive_equal (p) end + end + +feature -- Access + + id: INTEGER + + name: READABLE_STRING_8 + + permissions: LIST [READABLE_STRING_8] + +feature -- Comparison + + same_user_role (r: CMS_USER_ROLE): BOOLEAN + do + Result := r.id = id + end + + is_equal (other: like Current): BOOLEAN + -- Is `other' attached to an object considered + -- equal to current object? + do + Result := id = other.id + end + +feature -- Change + + set_id (a_id: like id) + -- Set `id' with `a_id'. + do + id := a_id + ensure + set_id: id = a_id + end + + set_name (a_name: like name) + -- Set `name' with `a_name'. + do + name := a_name + ensure + name_set: name = a_name + end + + add_permission (n: READABLE_STRING_8) + do + permissions.force (n) + end + +end diff --git a/persistence/implementation/mysql/scripts/create_database.sql b/persistence/implementation/mysql/scripts/create_database.sql index 55dffb4..431938b 100644 --- a/persistence/implementation/mysql/scripts/create_database.sql +++ b/persistence/implementation/mysql/scripts/create_database.sql @@ -3,15 +3,18 @@ SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; -- ----------------------------------------------------- --- Schema cms +-- Schema mydb -- ----------------------------------------------------- -CREATE SCHEMA IF NOT EXISTS `cms` DEFAULT CHARACTER SET latin1 ; -USE `cms` ; +-- ----------------------------------------------------- +-- Schema cms_dev +-- ----------------------------------------------------- +CREATE SCHEMA IF NOT EXISTS `cms_dev` DEFAULT CHARACTER SET latin1 ; +USE `cms_dev` ; -- ----------------------------------------------------- --- Table `cms`.`nodes` +-- Table `cms_dev`.`nodes` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms`.`nodes` ( +CREATE TABLE IF NOT EXISTS `cms_dev`.`nodes` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `publication_date` DATE NOT NULL, `creation_date` DATE NOT NULL, @@ -26,38 +29,39 @@ DEFAULT CHARACTER SET = latin1; -- ----------------------------------------------------- --- Table `cms`.`roles` +-- Table `cms_dev`.`roles` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms`.`roles` ( +CREATE TABLE IF NOT EXISTS `cms_dev`.`roles` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `role` VARCHAR(100) NOT NULL, PRIMARY KEY (`id`), UNIQUE INDEX `role` (`role` ASC)) ENGINE = InnoDB -AUTO_INCREMENT = 1 DEFAULT CHARACTER SET = latin1; -- ----------------------------------------------------- --- Table `cms`.`users` +-- Table `cms_dev`.`users` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms`.`users` ( +CREATE TABLE IF NOT EXISTS `cms_dev`.`users` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `username` VARCHAR(100) NOT NULL, `password` VARCHAR(100) NOT NULL, `salt` VARCHAR(100) NOT NULL, `email` VARCHAR(250) NOT NULL, + `creation_date` DATETIME NULL, + `last_login_date` DATETIME NULL, PRIMARY KEY (`id`), UNIQUE INDEX `username` (`username` ASC)) ENGINE = InnoDB -AUTO_INCREMENT = 1 +AUTO_INCREMENT = 2 DEFAULT CHARACTER SET = latin1; -- ----------------------------------------------------- --- Table `cms`.`users_nodes` +-- Table `cms_dev`.`users_nodes` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms`.`users_nodes` ( +CREATE TABLE IF NOT EXISTS `cms_dev`.`users_nodes` ( `users_id` INT(10) UNSIGNED NOT NULL, `nodes_id` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`users_id`, `nodes_id`), @@ -65,12 +69,12 @@ CREATE TABLE IF NOT EXISTS `cms`.`users_nodes` ( INDEX `fk_users_has_nodes_users_idx` (`users_id` ASC), CONSTRAINT `fk_users_has_nodes_nodes1` FOREIGN KEY (`nodes_id`) - REFERENCES `cms`.`nodes` (`id`) + REFERENCES `cms_dev`.`nodes` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `fk_users_has_nodes_users` FOREIGN KEY (`users_id`) - REFERENCES `cms`.`users` (`id`) + REFERENCES `cms_dev`.`users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB @@ -78,9 +82,9 @@ DEFAULT CHARACTER SET = latin1; -- ----------------------------------------------------- --- Table `cms`.`users_roles` +-- Table `cms_dev`.`users_roles` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms`.`users_roles` ( +CREATE TABLE IF NOT EXISTS `cms_dev`.`users_roles` ( `users_id` INT(10) UNSIGNED NOT NULL, `roles_id` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`users_id`, `roles_id`), @@ -88,18 +92,55 @@ CREATE TABLE IF NOT EXISTS `cms`.`users_roles` ( INDEX `fk_users_has_roles_users1_idx` (`users_id` ASC), CONSTRAINT `fk_users_has_roles_roles1` FOREIGN KEY (`roles_id`) - REFERENCES `cms`.`roles` (`id`) + REFERENCES `cms_dev`.`roles` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `fk_users_has_roles_users1` FOREIGN KEY (`users_id`) - REFERENCES `cms`.`users` (`id`) + REFERENCES `cms_dev`.`users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB DEFAULT CHARACTER SET = latin1; +-- ----------------------------------------------------- +-- Table `cms_dev`.`permissions` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `cms_dev`.`permissions` ( + `id` INT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(45) NOT NULL, + `roles_id` INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `name_UNIQUE` (`name` ASC), + INDEX `fk_permissions_roles1_idx` (`roles_id` ASC), + CONSTRAINT `fk_permissions_roles1` + FOREIGN KEY (`roles_id`) + REFERENCES `cms_dev`.`roles` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB; + + +-- ----------------------------------------------------- +-- Table `cms_dev`.`profiles` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `cms_dev`.`profiles` ( + `id` INT NOT NULL AUTO_INCREMENT, + `key` VARCHAR(45) NOT NULL, + `value` VARCHAR(100) NULL, + `users_id` INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `key_UNIQUE` (`key` ASC), + INDEX `fk_profiles_users1_idx` (`users_id` ASC), + CONSTRAINT `fk_profiles_users1` + FOREIGN KEY (`users_id`) + REFERENCES `cms_dev`.`users` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB; + + SET SQL_MODE=@OLD_SQL_MODE; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/persistence/implementation/mysql/src/provider/role_data_provider.e b/persistence/implementation/mysql/src/provider/role_data_provider.e new file mode 100644 index 0000000..61590a0 --- /dev/null +++ b/persistence/implementation/mysql/src/provider/role_data_provider.e @@ -0,0 +1,222 @@ +note + description: "Summary description for {ROLE_DATA_PROVIDER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + ROLE_DATA_PROVIDER + +inherit + + PARAMETER_NAME_HELPER + + SHARED_ERROR + + REFACTORING_HELPER + +create + make + +feature -- Initialization + + make (a_connection: DATABASE_CONNECTION) + -- Create a data provider. + do + create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) + post_execution + end + + db_handler: DATABASE_HANDLER + -- Db handler. + +feature -- Status Report + + is_successful: BOOLEAN + -- Is the last execution sucessful? + do + Result := db_handler.successful + end + + has_roles: BOOLEAN + -- Has any role? + do + Result := count > 0 + end + +feature -- Access + + roles: DATABASE_ITERATION_CURSOR [CMS_USER_ROLE] + -- List of roles. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".roles") + create l_parameters.make (0) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_roles, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_role) + post_execution + end + +feature -- Basic Operations + + new_role (a_role: READABLE_STRING_32) + -- Create a new node. + local + l_parameters: STRING_TABLE [detachable ANY] + do + log.write_information (generator + ".new_role") + create l_parameters.make (1) + l_parameters.put (a_role,"name") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_insert_role, l_parameters)) + db_handler.execute_change + post_execution + end + + role (a_id: INTEGER_64): detachable CMS_USER_ROLE + -- Role for the given id `a_id', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".role") + create l_parameters.make (1) + l_parameters.put (a_id,"id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_role_by_id, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_role + end + post_execution + end + + role_by_name (a_name: READABLE_STRING_32): detachable CMS_USER_ROLE + -- Role for the given name `a_name', if any. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".role_by_name") + create l_parameters.make (1) + l_parameters.put (a_name,"name") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_role_by_name, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := fetch_role + end + post_execution + end + + count: INTEGER + -- Number of items users. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".count") + create l_parameters.make (0) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_count, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := db_handler.read_integer_32 (1) + end + post_execution + end + + save_role_permission (a_role_id: INTEGER; a_permission: READABLE_STRING_32) + -- Add permission `a_permission' to the role id `a_role_id'. + require + valid_id: a_role_id > 0 + local + l_parameters: STRING_TABLE [detachable ANY] + do + log.write_information (generator + ".save_role_permission") + create l_parameters.make (1) + l_parameters.put (a_permission,"name") + l_parameters.put (a_role_id,"id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (SQL_Insert_permissions, l_parameters)) + db_handler.execute_change + post_execution + end + + permission_by_role (a_role_id: INTEGER_64): DATABASE_ITERATION_CURSOR [READABLE_STRING_32] + -- List of permission by role `a_role_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".permission_by_role") + create l_parameters.make (1) + l_parameters.put (a_role_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_permissions, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_permission) + post_execution + end + +feature -- New Object + + fetch_role: CMS_USER_ROLE + do + create Result.make_with_id (0,"") + if attached db_handler.read_integer_32 (1) as l_id then + Result.set_id (l_id) + end + if attached db_handler.read_string (2) as l_u then + Result.set_name (l_u) + end + end + + fetch_permission: STRING_32 + do + create Result.make_empty + if attached db_handler.read_string (1) as l_u then + Result := l_u + end + end + + +feature {NONE} -- Sql Queries: Roles + + Select_count: STRING = "select count(*) from Roles;" + -- Number of roles. + + Select_roles: STRING = "select * from Roles;" + -- roles. + + Select_role_by_id: STRING = "select * from Roles where id =:id;" + -- Retrieve role by id if exists. + + Select_role_by_name: STRING = "select * from Roles where role =:name;" + -- Retrieve user by name if exists. + + SQL_Insert_role: STRING = "insert into roles (role) values (:name);" + -- SQL Insert to add a new node. + + +feature {NONE} -- Sql Queries: Permissions + + Select_permissions_count: STRING = "select count(*) from permissions where roles_id=:id;" + -- Number of permissions for a given role. + + Select_permissions: STRING = "select * from permissions where roles_id=:id;" + -- List of permissions for a given role. + + Select_permissions_by_id: STRING = "select name from permissions where roles_id=:id and id=:permissionid;" + -- Permission for a given role and permission id + + SQL_Insert_permissions: STRING = "insert into permissions (name, roles_id) values (:name, :id);" + -- SQL Insert to add a new node. + +feature {NONE} -- Implementation + + post_execution + -- Post database execution. + do + if db_handler.successful then + set_successful + else + if attached db_handler.last_error then + set_last_error_from_handler (db_handler.last_error) + end + end + end + +end diff --git a/persistence/implementation/mysql/src/provider/user_data_provider.e b/persistence/implementation/mysql/src/provider/user_data_provider.e index e603c05..3a31df8 100644 --- a/persistence/implementation/mysql/src/provider/user_data_provider.e +++ b/persistence/implementation/mysql/src/provider/user_data_provider.e @@ -149,6 +149,34 @@ feature -- Basic Operations post_execution end + add_role (a_user_id: INTEGER; a_role_id: INTEGER) + -- Add Role `a_role_id' to user `a_user_id' + local + l_parameters: STRING_TABLE [detachable ANY] + do + log.write_information (generator + ".add_role") + create l_parameters.make (2) + l_parameters.put (a_user_id,"users_id") + l_parameters.put (a_role_id,"roles_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (slq_insert_users_roles, l_parameters)) + db_handler.execute_change + post_execution + end + + user_roles (a_id:INTEGER_64): DATABASE_ITERATION_CURSOR [INTEGER] + -- List of Roles id for the given user `a_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".user_roles") + create l_parameters.make (1) + l_parameters.put (a_id, "user_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_user_roles, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_role_id) + post_execution + end + feature -- New Object fetch_user: CMS_USER @@ -168,7 +196,14 @@ feature -- New Object end end -feature -- Sql Queries + fetch_role_id: INTEGER + do + if attached db_handler.read_integer_32 (1) as l_id then + Result := l_id + end + end + +feature {NONE} -- Sql Queries: USER Select_count: STRING = "select count(*) from Users;" -- Number of users. @@ -189,6 +224,12 @@ feature -- Sql Queries -- SQL Insert to add a new node. +feature {NONE} -- Sql Queries: USER_ROLES + + Slq_insert_users_roles: STRING = "insert into users_roles (users_id, roles_id) values (:users_id, :roles_id);" + + Select_user_roles: STRING = "Select roles_id from users_roles where users_id = :user_id" + feature {NONE} -- Implementation post_execution diff --git a/persistence/implementation/mysql/tests/roles/role_test_set.e b/persistence/implementation/mysql/tests/roles/role_test_set.e new file mode 100644 index 0000000..bac254a --- /dev/null +++ b/persistence/implementation/mysql/tests/roles/role_test_set.e @@ -0,0 +1,100 @@ +note + description: "Summary description for {ROLE_TEST_SET}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + ROLE_TEST_SET +inherit + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + end + + on_clean + -- + do + end + +feature -- Test routines + + test_roles_empty + do + assert ("Not elements",role_provider.roles.after) + assert ("Count = 0", role_provider.count = 0) + end + + test_roles_by_id_not_exist + do + assert ("Void", role_provider.role (1) = Void) + end + + test_roles_by_name_not_exist + do + assert ("Void", role_provider.role_by_name ("admin") = Void) + end + + test_new_role + do + assert ("Count = 0", role_provider.count = 0) + role_provider.new_role ("admin") + assert ("Count = 1", role_provider.count = 1) + assert ("Expected role", attached role_provider.role (1) as l_role and then l_role.name ~ "admin") + assert ("Expected role", attached role_provider.role_by_name ("admin") as l_role and then l_role.id = 1) + end + + test_permissions_empty_not_exist_role + do + assert ("Not elements",role_provider.permission_by_role (1).after) + end + + test_permissions_empty_exist_role + do + assert ("Count = 0", role_provider.count = 0) + role_provider.new_role ("admin") + assert ("Count = 1", role_provider.count = 1) + assert ("Exist role",not role_provider.roles.after) + assert ("Not permission by role 1 elements",role_provider.permission_by_role (1).after) + end + + test_new_role_with_permissions + do + assert ("Count = 0", role_provider.count = 0) + role_provider.new_role ("admin") + role_provider.save_role_permission (1, "Create Page") + role_provider.save_role_permission (1, "Edit Page") + role_provider.save_role_permission (1, "Delete Page") + assert ("Count = 1", role_provider.count = 1) + assert ("Exist role",not role_provider.roles.after) + assert ("Exist role permissions",not role_provider.permission_by_role (1).after) + assert ("Not Exist role permissions, for id 2",role_provider.permission_by_role (2).after) + end + + + + +feature {NONE} -- Implementation + + role_provider: ROLE_DATA_PROVIDER + -- role provider. + once + create Result.make (connection) + end +end diff --git a/persistence/implementation/mysql/tests/storage/storage_test_set.e b/persistence/implementation/mysql/tests/storage/storage_test_set.e index 8a02131..909a8dc 100644 --- a/persistence/implementation/mysql/tests/storage/storage_test_set.e +++ b/persistence/implementation/mysql/tests/storage/storage_test_set.e @@ -200,8 +200,6 @@ feature -- Test routines end test_node_does_not_exist - local - l_nodes: LIST[CMS_NODE] do across 1 |..| 10 as c loop storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) @@ -210,8 +208,6 @@ feature -- Test routines end test_node - local - l_nodes: LIST[CMS_NODE] do across 1 |..| 10 as c loop storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) @@ -221,7 +217,6 @@ feature -- Test routines test_update_node local - l_nodes: LIST[CMS_NODE] l_node: CMS_NODE do storage.save_node (custom_node ("Content", "Summary", "Title")) @@ -237,9 +232,6 @@ feature -- Test routines end test_update_node_title - local - l_nodes: LIST[CMS_NODE] - l_node: CMS_NODE do storage.save_node (custom_node ("Content", "Summary", "Title")) if attached {CMS_NODE} storage.node (1) as ll_node then @@ -249,9 +241,6 @@ feature -- Test routines end test_update_node_summary - local - l_nodes: LIST[CMS_NODE] - l_node: CMS_NODE do storage.save_node (custom_node ("Content", "Summary", "Title")) if attached {CMS_NODE} storage.node (1) as ll_node then @@ -261,9 +250,6 @@ feature -- Test routines end test_update_node_content - local - l_nodes: LIST[CMS_NODE] - l_node: CMS_NODE do storage.save_node (custom_node ("Content", "Summary", "Title")) if attached {CMS_NODE} storage.node (1) as ll_node then @@ -273,8 +259,6 @@ feature -- Test routines end test_delete_node - local - l_nodes: LIST[CMS_NODE] do across 1 |..| 10 as c loop storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) diff --git a/persistence/implementation/mysql/tests/users/user_test_set.e b/persistence/implementation/mysql/tests/users/user_test_set.e index 6b06e9f..ac40562 100644 --- a/persistence/implementation/mysql/tests/users/user_test_set.e +++ b/persistence/implementation/mysql/tests/users/user_test_set.e @@ -65,6 +65,14 @@ feature -- Test routines assert ("Not void", attached user_provider.user_by_name ("test")) end + test_new_user_with_roles + do + user_provider.new_user ("test", "test","test@admin.com") + role_provider.new_role ("Admin") + assert ("Empty roles for given user", user_provider.user_roles (1).after) + user_provider.add_role (1, 1) + assert ("Not empty roles for given user", not user_provider.user_roles (1).after) + end feature {NONE} -- Implementation @@ -73,6 +81,13 @@ feature {NONE} -- Implementation once create Result.make (connection) end + + role_provider: ROLE_DATA_PROVIDER + -- user provider. + once + create Result.make (connection) + end + end diff --git a/persistence/implementation/mysql/tests/util/clean_db.e b/persistence/implementation/mysql/tests/util/clean_db.e index ef00839..7c6217e 100644 --- a/persistence/implementation/mysql/tests/util/clean_db.e +++ b/persistence/implementation/mysql/tests/util/clean_db.e @@ -23,6 +23,20 @@ feature do create l_parameters.make (0) + + -- Clean Permissions + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_permissions, l_parameters)) + db_handler(a_connection).execute_change + + -- Clean Users Roles + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_users_roles, l_parameters)) + db_handler(a_connection).execute_change + + -- Clean Roles + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_roles, l_parameters)) + db_handler(a_connection).execute_change + + -- Clean Nodes db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_nodes, l_parameters)) db_handler(a_connection).execute_change @@ -30,12 +44,20 @@ feature db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_users, l_parameters)) db_handler(a_connection).execute_change + -- Reset Autoincremente db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_users_autoincrement, l_parameters)) db_handler(a_connection).execute_change db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_nodes_autoincrement, l_parameters)) db_handler(a_connection).execute_change + + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_roles_autoincrement, l_parameters)) + db_handler(a_connection).execute_change + + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_permissions_autoincrement, l_parameters)) + db_handler(a_connection).execute_change + end @@ -57,10 +79,25 @@ feature -- Sql delete queries Sql_delete_nodes: STRING = "delete from Nodes" -- Clean Nodes. + Sql_delete_roles: STRING = "delete from Roles" + -- Clean Roles. + + Sql_delete_permissions: STRING = "delete from Permissions" + -- Clean Permissions. + + Sql_delete_users_roles: STRING = "delete from Users_roles" + -- Clean User roles. + Rest_users_autoincrement: STRING = "ALTER TABLE Users AUTO_INCREMENT = 1" -- reset autoincrement Rest_nodes_autoincrement: STRING = "ALTER TABLE Nodes AUTO_INCREMENT = 1" -- reset autoincrement. + Rest_roles_autoincrement: STRING = "ALTER TABLE Roles AUTO_INCREMENT = 1" + -- reset autoincrement. + + Rest_permissions_autoincrement: STRING = "ALTER TABLE Permissions AUTO_INCREMENT = 1" + -- reset autoincrement. + end From cf3c4060c8ae3fb18282f90ee767c445b2b13919 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 18 Sep 2014 18:05:20 -0300 Subject: [PATCH 009/386] Updated Database Query error handling. Add support for user profiles. Updated test case. --- .../common/database/database_query.e | 8 +- .../mysql/src/provider/user_data_provider.e | 76 +++++++++++++++++++ .../mysql/tests/users/user_test_set.e | 37 ++++++++- .../mysql/tests/util/clean_db.e | 16 ++++ 4 files changed, 132 insertions(+), 5 deletions(-) diff --git a/persistence/implementation/common/database/database_query.e b/persistence/implementation/common/database/database_query.e index 5dba725..1a5f8cd 100644 --- a/persistence/implementation/common/database/database_query.e +++ b/persistence/implementation/common/database/database_query.e @@ -8,7 +8,7 @@ class inherit - SHARED_LOGGER + SHARED_ERROR REFACTORING_HELPER @@ -42,6 +42,7 @@ feature -- Intialization a_base_selection.load_result Result := a_base_selection.container else + set_last_error (a_base_selection.error_message_32, generator + ".execute_reader" ) log.write_error (generator + "." + a_base_selection.error_message_32) end unset_map_name (a_base_selection) @@ -55,6 +56,11 @@ feature -- Intialization set_map_name (a_base_change) a_base_change.set_query (query) a_base_change.execute_query + if a_base_change.is_ok then + else + set_last_error (a_base_change.error_message_32, generator + ".execute_reader" ) + log.write_error (generator + "." + a_base_change.error_message_32) + end unset_map_name (a_base_change) end diff --git a/persistence/implementation/mysql/src/provider/user_data_provider.e b/persistence/implementation/mysql/src/provider/user_data_provider.e index 3a31df8..a1ac47b 100644 --- a/persistence/implementation/mysql/src/provider/user_data_provider.e +++ b/persistence/implementation/mysql/src/provider/user_data_provider.e @@ -149,6 +149,8 @@ feature -- Basic Operations post_execution end +feature -- Basic operations: User Roles + add_role (a_user_id: INTEGER; a_role_id: INTEGER) -- Add Role `a_role_id' to user `a_user_id' local @@ -177,6 +179,72 @@ feature -- Basic Operations post_execution end + +feature -- Basic operations: User Profiles + + save_profile_item (a_user_id: INTEGER_64; a_key: READABLE_STRING_32; a_value: READABLE_STRING_32) + -- Save a profile item with (a_key and a_value) to the given user `user_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".save_profile_item") + create l_parameters.make (3) + l_parameters.put (a_key, "key") + l_parameters.put (a_value, "value") + l_parameters.put (a_user_id, "users_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_instert_profile_item, l_parameters)) + db_handler.execute_change + post_execution + end + + save_profile (a_user_id: INTEGER_64; a_user_profile: CMS_USER_PROFILE) + -- Save a profile item with (a_key and a_value) to the given user `user_id'. + local + l_cursor: TABLE_ITERATION_CURSOR [READABLE_STRING_8, READABLE_STRING_8] + do + log.write_information (generator + ".save_profile") + from + l_cursor := a_user_profile.new_cursor + until + l_cursor.after + loop + save_profile_item (a_user_id, l_cursor.key, l_cursor.item) + l_cursor.forth + end + + post_execution + end + + user_profile (a_user_id: INTEGER_64): CMS_USER_PROFILE + -- User profile for a user with id `a_user_id'. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".user_profile") + create l_parameters.make (1) + l_parameters.put (a_user_id, "users_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_user_profile, l_parameters)) + db_handler.execute_query + create Result.make + if not db_handler.after then + from + db_handler.start + until + db_handler.after + loop + if + attached db_handler.read_string (1) as l_key and then + attached db_handler.read_string (2) as l_value + then + Result.force (l_value, l_key) + end + db_handler.forth + end + end + post_execution + end + + feature -- New Object fetch_user: CMS_USER @@ -230,6 +298,14 @@ feature {NONE} -- Sql Queries: USER_ROLES Select_user_roles: STRING = "Select roles_id from users_roles where users_id = :user_id" +feature {NONE} -- SQL Queries: Profile + + Select_instert_profile_item: STRING = "insert into profiles (profiles.key, value, users_id) values (:key, :value, :users_id);" + + Select_user_profile: STRING = "Select profiles.key, value from profiles where users_id = :users_id;" + + + feature {NONE} -- Implementation post_execution diff --git a/persistence/implementation/mysql/tests/users/user_test_set.e b/persistence/implementation/mysql/tests/users/user_test_set.e index ac40562..1f1e049 100644 --- a/persistence/implementation/mysql/tests/users/user_test_set.e +++ b/persistence/implementation/mysql/tests/users/user_test_set.e @@ -5,7 +5,7 @@ note author: "EiffelStudio test wizard" date: "$Date$" revision: "$Revision$" - testing: "type/manual" + testing:"execution/serial" class USER_TEST_SET @@ -36,6 +36,7 @@ feature {NONE} -- Events on_clean -- do +-- (create {CLEAN_DB}).clean_db(connection) end feature -- Test routines @@ -51,9 +52,9 @@ feature -- Test routines test_user_not_exist -- Uset test does not exist. do - assert ("Void", user_provider.user_by_email ("test@admin.com") = Void) - assert ("Void", user_provider.user(2) = Void ) - assert ("Void", user_provider.user_by_name ("test") = Void) + assert ("User by email: Void", user_provider.user_by_email ("test1@test.com") = Void) + assert ("User by id: Void", user_provider.user(5) = Void ) + assert ("User by name: Void", user_provider.user_by_name ("test1") = Void) end test_new_user @@ -74,6 +75,34 @@ feature -- Test routines assert ("Not empty roles for given user", not user_provider.user_roles (1).after) end + test_new_user_without_profile + do + user_provider.new_user ("test", "test","test@admin.com") + assert ("Empty", user_provider.user_profile (1).new_cursor.after) + end + + test_new_user_with_profile + local + l_profile: CMS_USER_PROFILE + l_db_profile: CMS_USER_PROFILE + do + user_provider.new_user ("test", "test","test@admin.com") + if attached {CMS_USER} user_provider.user_by_name ("test") as l_user then + assert ("Empty", user_provider.user_profile (l_user.id).new_cursor.after) + create l_profile.make + l_profile.force ("Eiffel", "language") + l_profile.force ("Argentina", "country") + l_profile.force ("GMT-3", "time zone") + user_provider.save_profile (l_user.id, l_profile) + l_db_profile := user_provider.user_profile (l_user.id) + assert ("Not Empty", not l_db_profile.new_cursor.after) + + assert ("Expected language Eiffel", attached l_db_profile.item ("language") as l_language and then l_language ~ "Eiffel") + assert ("Expected time zone GMT-3", attached l_db_profile.item ("time zone") as l_language and then l_language ~ "GMT-3") + end + end + + feature {NONE} -- Implementation user_provider: USER_DATA_PROVIDER diff --git a/persistence/implementation/mysql/tests/util/clean_db.e b/persistence/implementation/mysql/tests/util/clean_db.e index 7c6217e..6b894ca 100644 --- a/persistence/implementation/mysql/tests/util/clean_db.e +++ b/persistence/implementation/mysql/tests/util/clean_db.e @@ -10,6 +10,7 @@ note date: "$Date$" revision: "$Revision$" EIS: "name=Database Testing", "src=http://www.agiledata.org/essays/databaseTesting.html", "protocol=uri" + testing:"execution/serial" class CLEAN_DB @@ -24,6 +25,10 @@ feature create l_parameters.make (0) + -- Clean Profiles + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_user_profiles, l_parameters)) + db_handler(a_connection).execute_change + -- Clean Permissions db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_permissions, l_parameters)) db_handler(a_connection).execute_change @@ -58,6 +63,9 @@ feature db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_permissions_autoincrement, l_parameters)) db_handler(a_connection).execute_change + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_profiles_autoincrement, l_parameters)) + db_handler(a_connection).execute_change + end @@ -88,6 +96,9 @@ feature -- Sql delete queries Sql_delete_users_roles: STRING = "delete from Users_roles" -- Clean User roles. + Sql_delete_user_profiles: STRING = "delete from profiles" + -- Clean profiles. + Rest_users_autoincrement: STRING = "ALTER TABLE Users AUTO_INCREMENT = 1" -- reset autoincrement @@ -100,4 +111,9 @@ feature -- Sql delete queries Rest_permissions_autoincrement: STRING = "ALTER TABLE Permissions AUTO_INCREMENT = 1" -- reset autoincrement. + Rest_profiles_autoincrement: STRING = "ALTER TABLE Profiles AUTO_INCREMENT = 1" + -- reset autoincrement. + + + end From 17ceed1c9155e8b725332c9b6ba671e6dde618ee Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 18 Sep 2014 21:00:08 -0300 Subject: [PATCH 010/386] Added node collaborators, author. Added test cases. --- .../mysql/src/provider/node_data_provider.e | 105 ++++++++++++++++++ .../implementation/mysql/tests/application.e | 82 +++++++++----- .../mysql/tests/nodes/node_test_set.e | 50 ++++++++- .../mysql/tests/users/user_test_set.e | 2 +- .../mysql/tests/util/clean_db.e | 10 +- 5 files changed, 220 insertions(+), 29 deletions(-) diff --git a/persistence/implementation/mysql/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e index eef4756..7b63544 100644 --- a/persistence/implementation/mysql/src/provider/node_data_provider.e +++ b/persistence/implementation/mysql/src/provider/node_data_provider.e @@ -197,6 +197,80 @@ feature -- Basic operations post_execution end +feature -- Basic Operations: User_Nodes + + add_author (a_user_id: INTEGER_64; a_node_id: INTEGER_64) + -- Add author `a_user_id' to node `a_node_id' + local + l_parameters: STRING_TABLE [detachable ANY] + do + log.write_information (generator + ".add_author") + create l_parameters.make (2) + l_parameters.put (a_user_id,"user_id") + l_parameters.put (a_node_id,"id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Sql_update_node_author, l_parameters)) + db_handler.execute_change + post_execution + end + + add_collaborator (a_user_id: INTEGER_64; a_node_id: INTEGER_64) + -- Add collaborator `a_user_id' to node `a_node_id' + local + l_parameters: STRING_TABLE [detachable ANY] + do + log.write_information (generator + ".add_collaborator") + create l_parameters.make (2) + l_parameters.put (a_user_id,"users_id") + l_parameters.put (a_node_id,"nodes_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Sql_insert_users_nodes, l_parameters)) + db_handler.execute_change + post_execution + end + + user_nodes (a_id:INTEGER_64): DATABASE_ITERATION_CURSOR [CMS_NODE] + -- List of Nodes for the given user `a_id'. (the user is the author of the node) + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".user_nodes") + create l_parameters.make (1) + l_parameters.put (a_id, "user_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_user_author, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_node) + post_execution + end + + node_author (a_id: INTEGER_64): detachable CMS_USER + -- Node's author for the given node id. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".node_author") + create l_parameters.make (1) + l_parameters.put (a_id, "node_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_node_author, l_parameters)) + db_handler.execute_query + if not db_handler.after then + Result := fetch_user + end + post_execution + end + + node_collaborators (a_id: INTEGER_64): DATABASE_ITERATION_CURSOR [CMS_USER] + -- List of possible node's collaborator. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".node_collaborators") + create l_parameters.make (1) + l_parameters.put (a_id, "node_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_node_collaborators, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_user) + post_execution + end + feature -- Connection connect @@ -243,6 +317,20 @@ feature {NONE} -- Queries SQL_Delete_node: STRING = "delete from nodes where id=:id;" + Sql_update_node_author: STRING = "update nodes SET author_id=:user_id where id=:id;" + +feature {NONE} -- Sql Queries: USER_ROLES collaborators, author + + Sql_insert_users_nodes: STRING = "insert into users_nodes (users_id, nodes_id) values (:users_id, :nodes_id);" + + select_node_collaborators: STRING = "SELECT * FROM Users INNER JOIN users_nodes ON users.id=users_nodes.users_id and users_nodes.nodes_id = :nodes_id;" + + Select_user_author: STRING = "SELECT * FROM Nodes INNER JOIN users ON nodes.author_id=users.id and users.id = :user_id;" + + Select_node_author: STRING = "SELECT * FROM User INNER JOIN nodes ON nodes.author_id=users.id and node_id =:node_id;" + +feature -- + feature -- New Object @@ -272,6 +360,23 @@ feature -- New Object end end + fetch_user: CMS_USER + do + create Result.make ("") + if attached db_handler.read_integer_32 (1) as l_id then + Result.set_id (l_id) + end + if attached db_handler.read_string (2) as l_u then + Result.set_name (l_u) + end + if attached db_handler.read_string (3) as l_p then + Result.set_password (l_p) + end + if attached db_handler.read_string (5) as l_e then + Result.set_email (l_e) + end + end + feature {NONE} -- Implementation post_execution diff --git a/persistence/implementation/mysql/tests/application.e b/persistence/implementation/mysql/tests/application.e index aade7b5..04417b4 100644 --- a/persistence/implementation/mysql/tests/application.e +++ b/persistence/implementation/mysql/tests/application.e @@ -20,36 +20,68 @@ feature {NONE} -- Initialization user: USER_DATA_PROVIDER node: NODE_DATA_PROVIDER l_security: SECURITY_PROVIDER + l_profile, l_db_profile: CMS_USER_PROFILE + l_cursor: TABLE_ITERATION_CURSOR [READABLE_STRING_8, READABLE_STRING_8] + l_list: LIST[CMS_NODE] do - create connection.login_with_schema ("cms", "root", "") --- create user.make (connection) ----- user.new_user ("jv", "test","test@test.com") - --- if attached user.user (3) as l_user then --- if attached user.user_salt ("jv") as l_salt then --- create l_security --- if attached l_user.password as l_password then --- print (l_password) --- io.put_new_line --- print (l_security.password_hash ("test", l_salt)) - --- check same_string: l_security.password_hash ("test", l_salt).is_case_insensitive_equal (l_password) end --- end --- end --- end - --- if attached user.user_by_name ("jv") as l_user then --- check l_user.id = 3 end --- end - + create connection.login_with_schema ("cms_dev", "root", "") + create user.make (connection) create node.make (connection) - across node.recent_nodes (0, 5) as c loop - print (c.item) + (create {CLEAN_DB}).clean_db(connection) + + user.new_user ("test", "test","test@admin.com") + if attached {CMS_USER} user.user_by_name ("test") as l_user then + create l_profile.make + l_profile.force ("Eiffel", "language") + l_profile.force ("Argentina", "country") + l_profile.force ("GMT-3", "time zone") + user.save_profile (l_user.id, l_profile) + l_db_profile := user.user_profile (l_user.id) + from + l_cursor := l_db_profile.new_cursor + until + l_cursor.after + loop + print (l_cursor.item + " - " + l_cursor.key + "%N") + l_cursor.forth + end + + create {ARRAYED_LIST[CMS_NODE]} l_list.make (0) + node.new_node (default_node) + node.new_node (custom_node ("content1", "summary1", "title1")) + node.new_node (custom_node ("content2", "summary2", "title2")) + node.new_node (custom_node ("content3", "summary3", "title3")) + user.new_user ("u1", "u1", "email") + if attached user.user_by_name ("u1") as ll_user then + node.add_collaborator (l_user.id, 1) + node.add_collaborator (l_user.id, 2) + node.add_collaborator (l_user.id, 3) + node.add_collaborator (l_user.id, 4) + + across node.node_collaborators (1) as c loop + print (c.item.name) + end + + end + + end - - end + +feature {NONE} -- Implementation + connection: DATABASE_CONNECTION_MYSQL + + default_node: CMS_NODE + do + Result := custom_node ("Default content", "default summary", "Default") + end + + custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE + do + create Result.make (a_content, a_summary, a_title) + end + end diff --git a/persistence/implementation/mysql/tests/nodes/node_test_set.e b/persistence/implementation/mysql/tests/nodes/node_test_set.e index 3ae6665..adc87b4 100644 --- a/persistence/implementation/mysql/tests/nodes/node_test_set.e +++ b/persistence/implementation/mysql/tests/nodes/node_test_set.e @@ -5,7 +5,7 @@ note author: "EiffelStudio test wizard" date: "$Date$" revision: "$Revision$" - testing: "type/manual" + testing:"execution/isolated" class NODE_TEST_SET @@ -195,9 +195,49 @@ feature -- Test routines -- Scenrario 10..10 empty assert ("Empty", node_provider.recent_nodes (10, 10).after) - end + + test_new_node_without_user + do + node_provider.new_node (default_node) + user_provider.new_user ("u1", "u1", "email") + if attached user_provider.user_by_name ("u1") as l_user then + assert ("Empty nodes", node_provider.user_nodes (l_user.id).after) + end + end + + + test_new_node_add_author + do + node_provider.new_node (default_node) + user_provider.new_user ("u1", "u1", "email") + if attached user_provider.user_by_name ("u1") as l_user then + node_provider.add_collaborator (l_user.id, 1) + assert ("Not Empty nodes", not node_provider.node_collaborators (1).after) + end + end + + test_multiple_nodes_add_author + local + l_list: LIST[CMS_NODE] + do + create {ARRAYED_LIST[CMS_NODE]} l_list.make (0) + node_provider.new_node (default_node) + node_provider.new_node (custom_node ("content1", "summary1", "title1")) + node_provider.new_node (custom_node ("content2", "summary2", "title2")) + node_provider.new_node (custom_node ("content3", "summary3", "title3")) + user_provider.new_user ("u1", "u1", "email") + if attached user_provider.user_by_name ("u1") as l_user then + node_provider.add_collaborator (l_user.id, 1) + node_provider.add_collaborator (l_user.id, 2) + node_provider.add_collaborator (l_user.id, 3) + node_provider.add_collaborator (l_user.id, 4) + end + end + + + feature {NONE} -- Implementation node_provider: NODE_DATA_PROVIDER @@ -206,6 +246,12 @@ feature {NONE} -- Implementation create Result.make (connection) end + user_provider: USER_DATA_PROVIDER + -- user provider. + once + create Result.make (connection) + end + feature {NONE} -- Implementation Fixture Factories diff --git a/persistence/implementation/mysql/tests/users/user_test_set.e b/persistence/implementation/mysql/tests/users/user_test_set.e index 1f1e049..7768e0e 100644 --- a/persistence/implementation/mysql/tests/users/user_test_set.e +++ b/persistence/implementation/mysql/tests/users/user_test_set.e @@ -5,7 +5,7 @@ note author: "EiffelStudio test wizard" date: "$Date$" revision: "$Revision$" - testing:"execution/serial" + testing:"execution/isolated" class USER_TEST_SET diff --git a/persistence/implementation/mysql/tests/util/clean_db.e b/persistence/implementation/mysql/tests/util/clean_db.e index 6b894ca..965465f 100644 --- a/persistence/implementation/mysql/tests/util/clean_db.e +++ b/persistence/implementation/mysql/tests/util/clean_db.e @@ -33,6 +33,12 @@ feature db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_permissions, l_parameters)) db_handler(a_connection).execute_change + + -- Clean Users Nodes + db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_users_nodes, l_parameters)) + db_handler(a_connection).execute_change + + -- Clean Users Roles db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_users_roles, l_parameters)) db_handler(a_connection).execute_change @@ -96,9 +102,11 @@ feature -- Sql delete queries Sql_delete_users_roles: STRING = "delete from Users_roles" -- Clean User roles. - Sql_delete_user_profiles: STRING = "delete from profiles" + Sql_delete_user_profiles: STRING = "delete from profiles" -- Clean profiles. + Sql_delete_users_nodes: STRING = "delete from users_nodes" + Rest_users_autoincrement: STRING = "ALTER TABLE Users AUTO_INCREMENT = 1" -- reset autoincrement From feaef700beffe0d0cb4527769e3e5c28988a25ba Mon Sep 17 00:00:00 2001 From: jvelilla Date: Fri, 19 Sep 2014 08:19:47 -0300 Subject: [PATCH 011/386] Updated MySQL database script --- .../mysql/scripts/create_database.sql | 109 ++++++++++-------- 1 file changed, 59 insertions(+), 50 deletions(-) diff --git a/persistence/implementation/mysql/scripts/create_database.sql b/persistence/implementation/mysql/scripts/create_database.sql index 431938b..347934b 100644 --- a/persistence/implementation/mysql/scripts/create_database.sql +++ b/persistence/implementation/mysql/scripts/create_database.sql @@ -11,6 +11,24 @@ SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; CREATE SCHEMA IF NOT EXISTS `cms_dev` DEFAULT CHARACTER SET latin1 ; USE `cms_dev` ; +-- ----------------------------------------------------- +-- Table `cms_dev`.`users` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `cms_dev`.`users` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `username` VARCHAR(100) NOT NULL, + `password` VARCHAR(100) NOT NULL, + `salt` VARCHAR(100) NOT NULL, + `email` VARCHAR(250) NOT NULL, + `creation_date` DATETIME NULL DEFAULT NULL, + `last_login_date` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `username` (`username` ASC)) +ENGINE = InnoDB +AUTO_INCREMENT = 2 +DEFAULT CHARACTER SET = latin1; + + -- ----------------------------------------------------- -- Table `cms_dev`.`nodes` -- ----------------------------------------------------- @@ -22,9 +40,16 @@ CREATE TABLE IF NOT EXISTS `cms_dev`.`nodes` ( `title` VARCHAR(255) NOT NULL, `summary` TEXT NOT NULL, `content` MEDIUMTEXT NOT NULL, - PRIMARY KEY (`id`)) + `author_id` INT(10) UNSIGNED NULL DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `fk_nodes_users1_idx` (`author_id` ASC), + CONSTRAINT `fk_nodes_users1` + FOREIGN KEY (`author_id`) + REFERENCES `cms_dev`.`users` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) ENGINE = InnoDB -AUTO_INCREMENT = 1 +AUTO_INCREMENT = 2 DEFAULT CHARACTER SET = latin1; @@ -41,20 +66,41 @@ DEFAULT CHARACTER SET = latin1; -- ----------------------------------------------------- --- Table `cms_dev`.`users` +-- Table `cms_dev`.`permissions` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`users` ( - `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `username` VARCHAR(100) NOT NULL, - `password` VARCHAR(100) NOT NULL, - `salt` VARCHAR(100) NOT NULL, - `email` VARCHAR(250) NOT NULL, - `creation_date` DATETIME NULL, - `last_login_date` DATETIME NULL, +CREATE TABLE IF NOT EXISTS `cms_dev`.`permissions` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `name` VARCHAR(45) NOT NULL, + `roles_id` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`id`), - UNIQUE INDEX `username` (`username` ASC)) + UNIQUE INDEX `name_UNIQUE` (`name` ASC), + INDEX `fk_permissions_roles1_idx` (`roles_id` ASC), + CONSTRAINT `fk_permissions_roles1` + FOREIGN KEY (`roles_id`) + REFERENCES `cms_dev`.`roles` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB +DEFAULT CHARACTER SET = latin1; + + +-- ----------------------------------------------------- +-- Table `cms_dev`.`profiles` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `cms_dev`.`profiles` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `key` VARCHAR(45) NOT NULL, + `value` VARCHAR(100) NULL DEFAULT NULL, + `users_id` INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `key_UNIQUE` (`key` ASC), + INDEX `fk_profiles_users1_idx` (`users_id` ASC), + CONSTRAINT `fk_profiles_users1` + FOREIGN KEY (`users_id`) + REFERENCES `cms_dev`.`users` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) ENGINE = InnoDB -AUTO_INCREMENT = 2 DEFAULT CHARACTER SET = latin1; @@ -104,43 +150,6 @@ ENGINE = InnoDB DEFAULT CHARACTER SET = latin1; --- ----------------------------------------------------- --- Table `cms_dev`.`permissions` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`permissions` ( - `id` INT NOT NULL AUTO_INCREMENT, - `name` VARCHAR(45) NOT NULL, - `roles_id` INT(10) UNSIGNED NOT NULL, - PRIMARY KEY (`id`), - UNIQUE INDEX `name_UNIQUE` (`name` ASC), - INDEX `fk_permissions_roles1_idx` (`roles_id` ASC), - CONSTRAINT `fk_permissions_roles1` - FOREIGN KEY (`roles_id`) - REFERENCES `cms_dev`.`roles` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB; - - --- ----------------------------------------------------- --- Table `cms_dev`.`profiles` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `cms_dev`.`profiles` ( - `id` INT NOT NULL AUTO_INCREMENT, - `key` VARCHAR(45) NOT NULL, - `value` VARCHAR(100) NULL, - `users_id` INT(10) UNSIGNED NOT NULL, - PRIMARY KEY (`id`), - UNIQUE INDEX `key_UNIQUE` (`key` ASC), - INDEX `fk_profiles_users1_idx` (`users_id` ASC), - CONSTRAINT `fk_profiles_users1` - FOREIGN KEY (`users_id`) - REFERENCES `cms_dev`.`users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB; - - SET SQL_MODE=@OLD_SQL_MODE; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; From 878daa656c9e79c598f0fe459f1cca598141a1ba Mon Sep 17 00:00:00 2001 From: jvelilla Date: Fri, 19 Sep 2014 14:48:20 -0300 Subject: [PATCH 012/386] Updated database script. Database Node support versions and editor id. Updated NODE_DATA_PROVIDER with new features. collaborator_nodes: Possible list of nodes edited by a given user id as collaborator author_nodes: Possible list of nodes of a given users as creator of them. Updated test cases --- .../implementation/mysql/scripts/create_database.sql | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/persistence/implementation/mysql/scripts/create_database.sql b/persistence/implementation/mysql/scripts/create_database.sql index 347934b..7569e9d 100644 --- a/persistence/implementation/mysql/scripts/create_database.sql +++ b/persistence/implementation/mysql/scripts/create_database.sql @@ -25,7 +25,6 @@ CREATE TABLE IF NOT EXISTS `cms_dev`.`users` ( PRIMARY KEY (`id`), UNIQUE INDEX `username` (`username` ASC)) ENGINE = InnoDB -AUTO_INCREMENT = 2 DEFAULT CHARACTER SET = latin1; @@ -41,15 +40,22 @@ CREATE TABLE IF NOT EXISTS `cms_dev`.`nodes` ( `summary` TEXT NOT NULL, `content` MEDIUMTEXT NOT NULL, `author_id` INT(10) UNSIGNED NULL DEFAULT NULL, + `version` INT(10) ZEROFILL NULL, + `editor_id` INT(10) UNSIGNED NULL DEFAULT NULL, PRIMARY KEY (`id`), INDEX `fk_nodes_users1_idx` (`author_id` ASC), + INDEX `fk_nodes_users2_idx` (`editor_id` ASC), CONSTRAINT `fk_nodes_users1` FOREIGN KEY (`author_id`) REFERENCES `cms_dev`.`users` (`id`) ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `fk_nodes_users2` + FOREIGN KEY (`editor_id`) + REFERENCES `cms_dev`.`users` (`id`) + ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB -AUTO_INCREMENT = 2 DEFAULT CHARACTER SET = latin1; From ce05da15b5faf468ad169131f1c08104163466eb Mon Sep 17 00:00:00 2001 From: jvelilla Date: Fri, 19 Sep 2014 15:31:58 -0300 Subject: [PATCH 013/386] Updated CMS_STORAGE interaface. --- .../implementation/common/cms_storage_null.e | 56 +++++++++- .../implementation/mysql/scripts/triggers.sql | 8 ++ .../mysql/src/cms_storage_mysql.e | 64 ++++++++++- .../mysql/src/provider/node_data_provider.e | 31 ++++-- .../implementation/mysql/tests/application.e | 8 ++ .../mysql/tests/nodes/node_test_set.e | 48 ++++++++- persistence/interface/cms_storage.e | 102 +++++++++++------- 7 files changed, 264 insertions(+), 53 deletions(-) create mode 100644 persistence/implementation/mysql/scripts/triggers.sql diff --git a/persistence/implementation/common/cms_storage_null.e b/persistence/implementation/common/cms_storage_null.e index 0917b13..88b12cb 100644 --- a/persistence/implementation/common/cms_storage_null.e +++ b/persistence/implementation/common/cms_storage_null.e @@ -41,6 +41,18 @@ feature -- Access: user do end +feature -- User Nodes + + user_collaborator_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] + -- Possible list of nodes where the user identified by `a_id', is a collaborator. + do + end + + user_author_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] + -- Possible list of nodes where the user identified by `a_id', is the author. + do + end + feature -- Change: user save_user (a_user: CMS_USER) @@ -48,6 +60,24 @@ feature -- Change: user do end +feature -- Access: roles and permissions + + user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE + do + end + + user_roles: LIST [CMS_USER_ROLE] + do + end + + +feature -- Change: roles and permissions + + save_user_role (a_user_role: CMS_USER_ROLE) + do + end + + feature -- Access: node nodes: LIST[CMS_NODE] @@ -63,10 +93,19 @@ feature -- Access: node end node (a_id: INTEGER_64): detachable CMS_NODE - -- + -- do end + node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER + -- Node's author. if any. + do + end + + node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER] + -- Possible list of node's collaborator. + do + end feature -- Node @@ -76,22 +115,37 @@ feature -- Node end delete_node (a_id: INTEGER_64) + -- do end update_node (a_node: CMS_NODE) + -- do end update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + -- do end update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + -- do end update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + -- + do + end + + add_node_author (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) + -- Add author `a_user_id' to the node `a_node_id'. + do + end + + add_node_collaborator (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) + -- Add/Update collaborator with `a_user_id' to the node `a_node_id'. do end diff --git a/persistence/implementation/mysql/scripts/triggers.sql b/persistence/implementation/mysql/scripts/triggers.sql new file mode 100644 index 0000000..b3e10a4 --- /dev/null +++ b/persistence/implementation/mysql/scripts/triggers.sql @@ -0,0 +1,8 @@ +DELIMITER $$ +CREATE TRIGGER update_editor +AFTER INSERT ON `users_nodes` FOR EACH ROW + UPDATE Nodes + SET editor_id = NEW.users_id + WHERE id = NEW.nodes_id; +$$ +DELIMITER ; \ No newline at end of file diff --git a/persistence/implementation/mysql/src/cms_storage_mysql.e b/persistence/implementation/mysql/src/cms_storage_mysql.e index b134f72..0470fa7 100644 --- a/persistence/implementation/mysql/src/cms_storage_mysql.e +++ b/persistence/implementation/mysql/src/cms_storage_mysql.e @@ -85,6 +85,42 @@ feature -- Access: user post_user_provider_execution end +feature -- User Nodes + + user_collaborator_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] + -- Possible list of nodes where the user identified by `a_id', is a collaborator. + do + fixme ("Not implemented!!!") + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + end + + user_author_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] + -- Possible list of nodes where the user identified by `a_id', is the author. + do + fixme ("Not implemented!!!") + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + end + +feature -- Access: roles and permissions + + user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE + do + fixme ("Not Implemented!!!") + end + + user_roles: LIST [CMS_USER_ROLE] + do + fixme ("Not Implemented!!!") + create {ARRAYED_LIST[CMS_USER_ROLE]} Result.make (0) + end + +feature -- Change: roles and permissions + + save_user_role (a_user_role: CMS_USER_ROLE) + do + fixme ("Not Implemented!!!") + end + feature -- Change: user save_user (a_user: CMS_USER) @@ -123,13 +159,27 @@ feature -- Access: node end node (a_id: INTEGER_64): detachable CMS_NODE - -- + -- do Result := node_provider.node (a_id) post_node_provider_execution end + node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER + -- + do + fixme ("Not implemented") + end + + node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER] + -- Possible list of node's collaborator. + do + fixme ("Not implemented") + create {ARRAYED_LIST[CMS_USER]} Result.make (0) + end + + feature -- Node save_node (a_node: CMS_NODE) @@ -170,6 +220,18 @@ feature -- Node end + add_node_author (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) + -- Add author `a_user_id' to the node `a_node_id'. + do + fixme ("Not Implemented") + end + + add_node_collaborator (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) + -- Add/Update collaborator with `a_user_id' to the node `a_node_id'. + do + fixme ("Not implemented") + end + feature -- User new_user (a_user: CMS_USER) diff --git a/persistence/implementation/mysql/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e index 7b63544..525b118 100644 --- a/persistence/implementation/mysql/src/provider/node_data_provider.e +++ b/persistence/implementation/mysql/src/provider/node_data_provider.e @@ -227,12 +227,12 @@ feature -- Basic Operations: User_Nodes post_execution end - user_nodes (a_id:INTEGER_64): DATABASE_ITERATION_CURSOR [CMS_NODE] + author_nodes (a_id:INTEGER_64): DATABASE_ITERATION_CURSOR [CMS_NODE] -- List of Nodes for the given user `a_id'. (the user is the author of the node) local l_parameters: STRING_TABLE [ANY] do - log.write_information (generator + ".user_nodes") + log.write_information (generator + ".author_nodes") create l_parameters.make (1) l_parameters.put (a_id, "user_id") db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_user_author, l_parameters)) @@ -241,6 +241,21 @@ feature -- Basic Operations: User_Nodes post_execution end + collaborator_nodes (a_id:INTEGER_64): DATABASE_ITERATION_CURSOR [CMS_NODE] + -- List of Nodes for the given user `a_id' as collaborator. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".collaborator_nodes") + create l_parameters.make (1) + l_parameters.put (a_id, "user_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_user_collaborator, l_parameters)) + db_handler.execute_query + create Result.make (db_handler, agent fetch_node) + post_execution + end + + node_author (a_id: INTEGER_64): detachable CMS_USER -- Node's author for the given node id. local @@ -303,13 +318,13 @@ feature {NONE} -- Queries SQL_Insert_node: STRING = "insert into nodes (title, summary, content, publication_date, creation_date, modification_date) values (:title, :summary, :content, :publication_date, :creation_date, :modification_date);" -- SQL Insert to add a new node. - SQL_Update_node_title: STRING ="update nodes SET title=:title, modification_date=:modification_date where id=:id;" + SQL_Update_node_title: STRING ="update nodes SET title=:title, modification_date=:modification_date, version = version + 1 where id=:id;" -- SQL update node title. - SQL_Update_node_summary: STRING ="update nodes SET summary=:summary, modification_date=:modification_date where id=:id;" + SQL_Update_node_summary: STRING ="update nodes SET summary=:summary, modification_date=:modification_date, version = version + 1 where id=:id;" -- SQL update node summary. - SQL_Update_node_content: STRING ="update nodes SET content=:content, modification_date=:modification_date where id=:id;" + SQL_Update_node_content: STRING ="update nodes SET content=:content, modification_date=:modification_date, version = version + 1 where id=:id;" -- SQL node content. SQL_Update_node : STRING = "update nodes SET title=:title, summary=:summary, content=:content, publication_date=:publication_date, creation_date=:creation_date, modification_date=:modification_date where id=:id;" @@ -323,11 +338,13 @@ feature {NONE} -- Sql Queries: USER_ROLES collaborators, author Sql_insert_users_nodes: STRING = "insert into users_nodes (users_id, nodes_id) values (:users_id, :nodes_id);" - select_node_collaborators: STRING = "SELECT * FROM Users INNER JOIN users_nodes ON users.id=users_nodes.users_id and users_nodes.nodes_id = :nodes_id;" + select_node_collaborators: STRING = "SELECT * FROM Users INNER JOIN users_nodes ON users.id=users_nodes.users_id and users_nodes.nodes_id = :node_id;" Select_user_author: STRING = "SELECT * FROM Nodes INNER JOIN users ON nodes.author_id=users.id and users.id = :user_id;" - Select_node_author: STRING = "SELECT * FROM User INNER JOIN nodes ON nodes.author_id=users.id and node_id =:node_id;" + Select_node_author: STRING = "SELECT * FROM Users INNER JOIN nodes ON nodes.author_id=users.id and nodes.id =:node_id;" + + Select_user_collaborator: STRING = "SELECT * FROM Nodes INNER JOIN users_nodes ON users_nodes.nodes_id = nodes.id and users_nodes.users_id = :user_id;" feature -- diff --git a/persistence/implementation/mysql/tests/application.e b/persistence/implementation/mysql/tests/application.e index 04417b4..03f877f 100644 --- a/persistence/implementation/mysql/tests/application.e +++ b/persistence/implementation/mysql/tests/application.e @@ -65,6 +65,14 @@ feature {NONE} -- Initialization end + if attached user.user_by_name ("u1") as ll_user then + node.add_author (ll_user.id, 1) + if attached node.node_author (1) as l_author then + print (l_author.name) + end + end + + end end diff --git a/persistence/implementation/mysql/tests/nodes/node_test_set.e b/persistence/implementation/mysql/tests/nodes/node_test_set.e index adc87b4..3b6713e 100644 --- a/persistence/implementation/mysql/tests/nodes/node_test_set.e +++ b/persistence/implementation/mysql/tests/nodes/node_test_set.e @@ -203,22 +203,33 @@ feature -- Test routines node_provider.new_node (default_node) user_provider.new_user ("u1", "u1", "email") if attached user_provider.user_by_name ("u1") as l_user then - assert ("Empty nodes", node_provider.user_nodes (l_user.id).after) + assert ("Empty nodes", node_provider.author_nodes (l_user.id).after) end end - test_new_node_add_author do node_provider.new_node (default_node) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1)) user_provider.new_user ("u1", "u1", "email") if attached user_provider.user_by_name ("u1") as l_user then - node_provider.add_collaborator (l_user.id, 1) - assert ("Not Empty nodes", not node_provider.node_collaborators (1).after) + node_provider.add_author (l_user.id, 1) + assert ("Author not void for node 1", attached node_provider.node_author (1)) end end - test_multiple_nodes_add_author + test_new_node_add_collaborator + do + node_provider.new_node (default_node) + assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1)) + user_provider.new_user ("u1", "u1", "email") + if attached user_provider.user_by_name ("u1") as l_user then + node_provider.add_collaborator (l_user.id, 1) + assert ("Not Empty Collaborator for node 1", not node_provider.node_collaborators (1).after) + end + end + + test_multiple_nodes_add_collaborator local l_list: LIST[CMS_NODE] do @@ -233,6 +244,33 @@ feature -- Test routines node_provider.add_collaborator (l_user.id, 2) node_provider.add_collaborator (l_user.id, 3) node_provider.add_collaborator (l_user.id, 4) + assert ("Not Empty Collaborator for node 1", not node_provider.node_collaborators (1).after) + assert ("Not Empty Collaborator for node 2", not node_provider.node_collaborators (2).after) + assert ("Not Empty Collaborator for node 3", not node_provider.node_collaborators (3).after) + assert ("Not Empty Collaborator for node 4", not node_provider.node_collaborators (4).after) + end + end + + test_nodes_collaborator + local + l_list: LIST[CMS_NODE] + do + create {ARRAYED_LIST[CMS_NODE]} l_list.make (0) + node_provider.new_node (default_node) + node_provider.new_node (custom_node ("content1", "summary1", "title1")) + node_provider.new_node (custom_node ("content2", "summary2", "title2")) + node_provider.new_node (custom_node ("content3", "summary3", "title3")) + user_provider.new_user ("u1", "u1", "email") + if attached user_provider.user_by_name ("u1") as l_user then + node_provider.add_collaborator (l_user.id, 1) + node_provider.add_collaborator (l_user.id, 2) + node_provider.add_collaborator (l_user.id, 3) + node_provider.add_collaborator (l_user.id, 4) + across node_provider.collaborator_nodes (l_user.id) as c loop + l_list.force (c.item) + end + + assert ("User is collaborating in 4 nodes", l_list.count = 4) end end diff --git a/persistence/interface/cms_storage.e b/persistence/interface/cms_storage.e index 22ea3a1..d93f64b 100644 --- a/persistence/interface/cms_storage.e +++ b/persistence/interface/cms_storage.e @@ -59,6 +59,22 @@ feature -- Access: user deferred end +feature -- User Nodes + + user_collaborator_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] + -- Possible list of nodes where the user identified by `a_id', is a collaborator. + require + a_id > 0 + deferred + end + + user_author_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] + -- Possible list of nodes where the user identified by `a_id', is the author. + require + a_id > 0 + deferred + end + feature -- Change: user save_user (a_user: CMS_USER) @@ -67,14 +83,14 @@ feature -- Change: user feature -- Access: roles and permissions --- user_has_permission (u: detachable CMS_USER; s: detachable READABLE_STRING_8): BOOLEAN --- -- Anonymous or user `u' has permission for `s' ? --- --| `s' could be "create page", --- do + user_has_permission (u: detachable CMS_USER; s: detachable READABLE_STRING_8): BOOLEAN + -- Anonymous or user `u' has permission for `s' ? + --| `s' could be "create page", + do -- if s = Void then -- Result := True -- elseif u = Void then --- Result := user_role_has_permission (anonymous_user_role, s) +---- Result := user_role_has_permission (anonymous_user_role, s) -- else -- Result := user_role_has_permission (authenticated_user_role, s) -- if not Result and attached u.roles as l_roles then @@ -89,44 +105,26 @@ feature -- Access: roles and permissions -- end -- end -- end --- end + end --- anonymous_user_role: CMS_USER_ROLE --- do --- if attached user_role_by_id (1) as l_anonymous then --- Result := l_anonymous --- else --- create Result.make ("anonymous") --- end --- end + user_role_has_permission (a_role: CMS_USER_ROLE; s: READABLE_STRING_8): BOOLEAN + do + Result := a_role.has_permission (s) + end --- authenticated_user_role: CMS_USER_ROLE --- do --- if attached user_role_by_id (2) as l_authenticated then --- Result := l_authenticated --- else --- create Result.make ("authenticated") --- end --- end + user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE + deferred + end --- user_role_has_permission (a_role: CMS_USER_ROLE; s: READABLE_STRING_8): BOOLEAN --- do --- Result := a_role.has_permission (s) --- end - --- user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE --- deferred --- end - --- user_roles: LIST [CMS_USER_ROLE] --- deferred --- end + user_roles: LIST [CMS_USER_ROLE] + deferred + end feature -- Change: roles and permissions --- save_user_role (a_user_role: CMS_USER_ROLE) --- deferred --- end + save_user_role (a_user_role: CMS_USER_ROLE) + deferred + end feature -- Email @@ -153,6 +151,7 @@ feature -- Email feature -- Access: Node recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] + -- List of recent `a_count' nodes with an offset of `lower'. deferred end @@ -163,6 +162,19 @@ feature -- Access: Node deferred end + node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER + -- Node's author. if any. + require + valid_node: a_id >0 + deferred + end + + node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER] + -- Possible list of node's collaborator. + require + valid_node: a_id > 0 + deferred + end feature -- Change: Node @@ -207,9 +219,21 @@ feature -- Change: Node deferred end + add_node_author (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) + -- Add author `a_user_id' to the node `a_node_id'. + require + valid_node: a_node_id > 0 + valid_user: a_user_id > 0 + deferred + end - - + add_node_collaborator (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) + -- Add/Update collaborator with `a_user_id' to the node `a_node_id'. + require + valid_node: a_node_id > 0 + valid_user: a_user_id > 0 + deferred + end --feature -- Misc From fb9157dbb4dee990b523dabb367b430feeefecee Mon Sep 17 00:00:00 2001 From: jvelilla Date: Sat, 20 Sep 2014 19:55:43 -0300 Subject: [PATCH 014/386] Updated CMS_STORAGE interface. Added test cased for MySQL implementation. --- .../implementation/common/cms_storage_null.e | 19 +-- .../mysql/src/cms_storage_mysql.e | 56 ++++---- .../mysql/src/provider/node_data_provider.e | 78 +++++++++++- .../implementation/mysql/tests/application.e | 120 +++++++++++------- .../mysql/tests/nodes/node_test_set.e | 4 +- .../mysql/tests/storage/storage_test_set.e | 118 ++++++++++++++--- persistence/interface/cms_storage.e | 61 ++++----- 7 files changed, 317 insertions(+), 139 deletions(-) diff --git a/persistence/implementation/common/cms_storage_null.e b/persistence/implementation/common/cms_storage_null.e index 88b12cb..6b4fe01 100644 --- a/persistence/implementation/common/cms_storage_null.e +++ b/persistence/implementation/common/cms_storage_null.e @@ -19,7 +19,6 @@ feature -- Access: user do end - all_users: LIST [CMS_USER] do create {ARRAYED_LIST[CMS_USER]} Result.make (0) @@ -119,36 +118,26 @@ feature -- Node do end - update_node (a_node: CMS_NODE) + update_node (a_id: like {CMS_NODE}.id; a_node: CMS_NODE) -- do end - update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + update_node_title (a_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) -- do end - update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + update_node_summary (a_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) -- do end - update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + update_node_content (a_id: like {CMS_NODE}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) -- do end - add_node_author (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) - -- Add author `a_user_id' to the node `a_node_id'. - do - end - - add_node_collaborator (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) - -- Add/Update collaborator with `a_user_id' to the node `a_node_id'. - do - end - feature -- User new_user (a_user: CMS_USER) diff --git a/persistence/implementation/mysql/src/cms_storage_mysql.e b/persistence/implementation/mysql/src/cms_storage_mysql.e index 0470fa7..c655891 100644 --- a/persistence/implementation/mysql/src/cms_storage_mysql.e +++ b/persistence/implementation/mysql/src/cms_storage_mysql.e @@ -38,7 +38,6 @@ feature -- Access: user post_user_provider_execution end - all_users: LIST [CMS_USER] do to_implement("Not implemented!!!") @@ -132,7 +131,7 @@ feature -- Change: user then user_provider.new_user (a_user.name, l_password, l_email) else - -- set error + set_last_error ("User or Password not attached", generator + ".save_user") end end @@ -165,25 +164,24 @@ feature -- Access: node post_node_provider_execution end - node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER -- do - fixme ("Not implemented") + Result := node_provider.node_author (a_id) + post_node_provider_execution end node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER] -- Possible list of node's collaborator. do - fixme ("Not implemented") create {ARRAYED_LIST[CMS_USER]} Result.make (0) + across node_provider.node_collaborators (a_id) as c loop Result.force (c.item) end end - feature -- Node save_node (a_node: CMS_NODE) - -- Add a new node + -- do node_provider.new_node (a_node) post_node_provider_execution @@ -195,42 +193,48 @@ feature -- Node post_node_provider_execution end - update_node (a_node: CMS_NODE) + update_node (a_id: like {CMS_USER}.id; a_node: CMS_NODE) + -- do - node_provider.update_node (a_node) + node_provider.update_node (a_id, a_node) post_node_provider_execution end - update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + update_node_title (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) + -- do - node_provider.update_node_title (a_id, a_title) + node_provider.update_node_title (a_node_id, a_title) + internal_node_update (a_id, a_node_id) post_node_provider_execution end - update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + update_node_summary (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) + -- do - node_provider.update_node_summary (a_id, a_summary) + node_provider.update_node_summary (a_node_id, a_summary) + internal_node_update (a_id, a_node_id) post_node_provider_execution end - update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + update_node_content (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) + -- do - node_provider.update_node_content (a_id, a_content) + node_provider.update_node_content (a_node_id, a_content) + internal_node_update (a_id, a_node_id) post_node_provider_execution end +feature {NONE} -- NODE Implemenation - add_node_author (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) - -- Add author `a_user_id' to the node `a_node_id'. - do - fixme ("Not Implemented") - end - - add_node_collaborator (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) - -- Add/Update collaborator with `a_user_id' to the node `a_node_id'. - do - fixme ("Not implemented") - end + internal_node_update (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id) + -- Update node editor or add collaborator. + do + if not node_provider.is_collaborator (a_id, a_node_id) then + node_provider.add_collaborator (a_id, a_node_id) + else + node_provider.update_node_last_editor (a_id, a_node_id) + end + end feature -- User diff --git a/persistence/implementation/mysql/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e index 525b118..e44d69b 100644 --- a/persistence/implementation/mysql/src/provider/node_data_provider.e +++ b/persistence/implementation/mysql/src/provider/node_data_provider.e @@ -100,6 +100,22 @@ feature -- Access post_execution end + + last_inserted_node_id: INTEGER + -- Last insert node id. + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".last_inserted_node_id") + create l_parameters.make (0) + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Sql_last_insert_node_id, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := db_handler.read_integer_32 (1) + end + post_execution + end + feature -- Basic operations new_node (a_node: CMS_NODE) @@ -108,16 +124,27 @@ feature -- Basic operations l_parameters: STRING_TABLE [ANY] do log.write_information (generator + ".new_node") - create l_parameters.make (6) + create l_parameters.make (7) l_parameters.put (a_node.title, "title") l_parameters.put (a_node.summary, "summary") l_parameters.put (a_node.content, "content") l_parameters.put (a_node.publication_date, "publication_date") l_parameters.put (a_node.creation_date, "creation_date") l_parameters.put (a_node.modification_date, "modification_date") + if + attached a_node.author as l_author and then + l_author.id > 0 + then + l_parameters.put (l_author.id, "author_id") + end db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_insert_node, l_parameters)) db_handler.execute_change + + a_node.set_id (last_inserted_node_id) + post_execution + + end update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) @@ -165,7 +192,7 @@ feature -- Basic operations post_execution end - update_node (a_node: CMS_NODE) + update_node (a_id: like {CMS_USER}.id; a_node: CMS_NODE) -- Update node. local l_parameters: STRING_TABLE [ANY] @@ -176,9 +203,9 @@ feature -- Basic operations l_parameters.put (a_node.summary, "summary") l_parameters.put (a_node.content, "content") l_parameters.put (a_node.publication_date, "publication_date") - l_parameters.put (a_node.creation_date, "creation_date") l_parameters.put (create {DATE_TIME}.make_now_utc, "modification_date") l_parameters.put (a_node.id, "id") + l_parameters.put (a_id, "editor") db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_update_node, l_parameters)) db_handler.execute_change post_execution @@ -227,6 +254,21 @@ feature -- Basic Operations: User_Nodes post_execution end + update_node_last_editor (a_user_id: INTEGER_64; a_node_id: INTEGER_64) + -- Update node last editor. + local + l_parameters: STRING_TABLE [detachable ANY] + do + log.write_information (generator + ".add_collaborator") + create l_parameters.make (2) + l_parameters.put (a_user_id,"users_id") + l_parameters.put (a_node_id,"nodes_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (Slq_update_editor, l_parameters)) + db_handler.execute_change + post_execution + end + + author_nodes (a_id:INTEGER_64): DATABASE_ITERATION_CURSOR [CMS_NODE] -- List of Nodes for the given user `a_id'. (the user is the author of the node) local @@ -286,6 +328,24 @@ feature -- Basic Operations: User_Nodes post_execution end + is_collaborator (a_user_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id): BOOLEAN + -- Is the user `a_user_id' a collaborator of node `a_node_id' + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".node_collaborators") + create l_parameters.make (2) + l_parameters.put (a_user_id, "user_id") + l_parameters.put (a_node_id, "node_id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_exist_user_node, l_parameters)) + db_handler.execute_query + if db_handler.count = 1 then + Result := db_handler.read_integer_32 (1) = 1 + end + + post_execution + end + feature -- Connection connect @@ -315,7 +375,7 @@ feature {NONE} -- Queries Select_recent_nodes: STRING = "select * from Nodes order by id desc, publication_date desc Limit $offset , :rows " - SQL_Insert_node: STRING = "insert into nodes (title, summary, content, publication_date, creation_date, modification_date) values (:title, :summary, :content, :publication_date, :creation_date, :modification_date);" + SQL_Insert_node: STRING = "insert into nodes (title, summary, content, publication_date, creation_date, modification_date, author_id) values (:title, :summary, :content, :publication_date, :creation_date, :modification_date, :author_id);" -- SQL Insert to add a new node. SQL_Update_node_title: STRING ="update nodes SET title=:title, modification_date=:modification_date, version = version + 1 where id=:id;" @@ -327,13 +387,18 @@ feature {NONE} -- Queries SQL_Update_node_content: STRING ="update nodes SET content=:content, modification_date=:modification_date, version = version + 1 where id=:id;" -- SQL node content. - SQL_Update_node : STRING = "update nodes SET title=:title, summary=:summary, content=:content, publication_date=:publication_date, creation_date=:creation_date, modification_date=:modification_date where id=:id;" + Slq_update_editor: STRING ="update nodes SET editor_id=:users_id where id=:nodes_id;" + -- SQL node content. + + SQL_Update_node : STRING = "update nodes SET title=:title, summary=:summary, content=:content, publication_date=:publication_date, modification_date=:modification_date, version = version + 1, editor_id=:editor where id=:id;" -- SQL node. SQL_Delete_node: STRING = "delete from nodes where id=:id;" Sql_update_node_author: STRING = "update nodes SET author_id=:user_id where id=:id;" + Sql_last_insert_node_id: STRING = "SELECT MAX(id) from nodes;" + feature {NONE} -- Sql Queries: USER_ROLES collaborators, author Sql_insert_users_nodes: STRING = "insert into users_nodes (users_id, nodes_id) values (:users_id, :nodes_id);" @@ -346,6 +411,9 @@ feature {NONE} -- Sql Queries: USER_ROLES collaborators, author Select_user_collaborator: STRING = "SELECT * FROM Nodes INNER JOIN users_nodes ON users_nodes.nodes_id = nodes.id and users_nodes.users_id = :user_id;" + Select_exist_user_node: STRING= "Select Count(*) from Users_nodes where users_id=:user_id and nodes_id=:node_id;" + + feature -- diff --git a/persistence/implementation/mysql/tests/application.e b/persistence/implementation/mysql/tests/application.e index 03f877f..72cfa2c 100644 --- a/persistence/implementation/mysql/tests/application.e +++ b/persistence/implementation/mysql/tests/application.e @@ -23,62 +23,92 @@ feature {NONE} -- Initialization l_profile, l_db_profile: CMS_USER_PROFILE l_cursor: TABLE_ITERATION_CURSOR [READABLE_STRING_8, READABLE_STRING_8] l_list: LIST[CMS_NODE] + storage: CMS_STORAGE + l_node: CMS_NODE do create connection.login_with_schema ("cms_dev", "root", "") - create user.make (connection) - create node.make (connection) +-- create user.make (connection) +-- create node.make (connection) (create {CLEAN_DB}).clean_db(connection) - user.new_user ("test", "test","test@admin.com") - if attached {CMS_USER} user.user_by_name ("test") as l_user then - create l_profile.make - l_profile.force ("Eiffel", "language") - l_profile.force ("Argentina", "country") - l_profile.force ("GMT-3", "time zone") - user.save_profile (l_user.id, l_profile) - l_db_profile := user.user_profile (l_user.id) - from - l_cursor := l_db_profile.new_cursor - until - l_cursor.after - loop - print (l_cursor.item + " - " + l_cursor.key + "%N") - l_cursor.forth + create {CMS_STORAGE_MYSQL} storage.make (connection) + l_node := custom_node ("Content", "Summary", "Title") + storage.save_user (default_user) + storage.save_user (custom_user ("u2", "p2", "e2")) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_title (2,ll_node.id, "New Title") + check + attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary end - - create {ARRAYED_LIST[CMS_NODE]} l_list.make (0) - node.new_node (default_node) - node.new_node (custom_node ("content1", "summary1", "title1")) - node.new_node (custom_node ("content2", "summary2", "title2")) - node.new_node (custom_node ("content3", "summary3", "title3")) - user.new_user ("u1", "u1", "email") - if attached user.user_by_name ("u1") as ll_user then - node.add_collaborator (l_user.id, 1) - node.add_collaborator (l_user.id, 2) - node.add_collaborator (l_user.id, 3) - node.add_collaborator (l_user.id, 4) - - across node.node_collaborators (1) as c loop - print (c.item.name) - end - - end - - - if attached user.user_by_name ("u1") as ll_user then - node.add_author (ll_user.id, 1) - if attached node.node_author (1) as l_author then - print (l_author.name) - end - end - - end + +-- user.new_user ("test", "test","test@admin.com") +-- if attached {CMS_USER} user.user_by_name ("test") as l_user then +-- create l_profile.make +-- l_profile.force ("Eiffel", "language") +-- l_profile.force ("Argentina", "country") +-- l_profile.force ("GMT-3", "time zone") +-- user.save_profile (l_user.id, l_profile) +-- l_db_profile := user.user_profile (l_user.id) +-- from +-- l_cursor := l_db_profile.new_cursor +-- until +-- l_cursor.after +-- loop +-- print (l_cursor.item + " - " + l_cursor.key + "%N") +-- l_cursor.forth +-- end + +-- create {ARRAYED_LIST[CMS_NODE]} l_list.make (0) +-- node.new_node (default_node) +-- node.new_node (custom_node ("content1", "summary1", "title1")) +-- node.new_node (custom_node ("content2", "summary2", "title2")) +-- node.new_node (custom_node ("content3", "summary3", "title3")) +-- user.new_user ("u1", "u1", "email") +-- if attached user.user_by_name ("u1") as ll_user then +-- node.add_collaborator (ll_user.id, 1) +-- node.add_collaborator (ll_user.id, 2) +-- node.add_collaborator (ll_user.id, 3) +-- node.add_collaborator (ll_user.id, 4) + +-- across node.collaborator_nodes (l_user.id) as c loop +-- print (c.item.title) +-- end + +-- end + + +-- if attached user.user_by_name ("u1") as ll_user then +-- node.add_author (ll_user.id, 1) +-- if attached node.node_author (1) as l_author then +-- print (l_author.name) +-- end +-- end + + +-- end end +feature {NONE} -- Fixture Factory: Users + + default_user: CMS_USER + do + Result := custom_user ("test", "password", "test@test.com") + end + + custom_user (a_name, a_password, a_email: READABLE_STRING_32): CMS_USER + do + create Result.make (a_name) + Result.set_password (a_password) + Result.set_email (a_email) + end + feature {NONE} -- Implementation + connection: DATABASE_CONNECTION_MYSQL diff --git a/persistence/implementation/mysql/tests/nodes/node_test_set.e b/persistence/implementation/mysql/tests/nodes/node_test_set.e index 3b6713e..7993a72 100644 --- a/persistence/implementation/mysql/tests/nodes/node_test_set.e +++ b/persistence/implementation/mysql/tests/nodes/node_test_set.e @@ -68,7 +68,7 @@ feature -- Test routines if attached {CMS_NODE} node_provider.node (1) as l_un then l_un.set_content ("

Updating test node udpate

") l_un.set_summary ("updating summary") - node_provider.update_node (l_un) + node_provider.update_node (0,l_un) assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then not (ll_node.content ~ l_node.content) and then not (ll_node.summary ~ l_node.summary) and then ll_node.title ~ l_node.title ) assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title ) end @@ -78,7 +78,7 @@ feature -- Test routines l_un.set_content ("

Updating test node udpate

") l_un.set_summary ("updating summary") l_un.set_title ("Updating Test case") - node_provider.update_node (l_un) + node_provider.update_node (0,l_un) assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then not (ll_node.content ~ l_node.content) and then not (ll_node.summary ~ l_node.summary) and then not (ll_node.title ~ l_node.title) ) assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then ll_node.content ~ l_un.content and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title ) end diff --git a/persistence/implementation/mysql/tests/storage/storage_test_set.e b/persistence/implementation/mysql/tests/storage/storage_test_set.e index 909a8dc..99779c1 100644 --- a/persistence/implementation/mysql/tests/storage/storage_test_set.e +++ b/persistence/implementation/mysql/tests/storage/storage_test_set.e @@ -175,9 +175,13 @@ feature -- Test routines test_recent_nodes local l_nodes: LIST[CMS_NODE] + l_node: CMS_NODE do + storage.save_user (default_user) across 1 |..| 10 as c loop - storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) end l_nodes := storage.recent_nodes (0, 10) assert ("10 recent nodes", l_nodes.count = 10) @@ -200,70 +204,152 @@ feature -- Test routines end test_node_does_not_exist + local + l_node: CMS_NODE do + storage.save_user (default_user) across 1 |..| 10 as c loop - storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) end assert ("Not exist node id: 12", storage.node (12) = Void) end test_node + local + l_node: CMS_NODE do + storage.save_user (default_user) across 1 |..| 10 as c loop - storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) end - assert ("Node id: 10", attached storage.node (10) as l_node and then l_node.title ~ "Title_10" ) + assert ("Node id: 10", attached storage.node (10) as ll_node and then ll_node.title ~ "Title_10" ) end test_update_node local l_node: CMS_NODE do - storage.save_node (custom_node ("Content", "Summary", "Title")) + l_node := custom_node ("Content", "Summary", "Title") + storage.save_user (default_user) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) if attached {CMS_NODE} storage.node (1) as ll_node then l_node := ll_node.twin l_node.set_content ("New Content") l_node.set_summary ("New Summary") l_node.set_title("New Title") - - storage.update_node (l_node) - assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary)) + if attached storage.user_by_email (default_user.email) as l_user then + storage.update_node (l_user.id,l_node) + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary)) + end end end test_update_node_title + local + l_node: CMS_NODE do - storage.save_node (custom_node ("Content", "Summary", "Title")) + l_node := custom_node ("Content", "Summary", "Title") + storage.save_user (default_user) + storage.save_user (custom_user ("u2", "p2", "e2")) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) if attached {CMS_NODE} storage.node (1) as ll_node then - storage.update_node_title (ll_node.id, "New Title") + storage.update_node_title (2,ll_node.id, "New Title") assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary) end end test_update_node_summary + local + l_node: CMS_NODE do - storage.save_node (custom_node ("Content", "Summary", "Title")) + l_node := custom_node ("Content", "Summary", "Title") + storage.save_user (default_user) + storage.save_user (custom_user ("u2", "p2", "e2")) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) if attached {CMS_NODE} storage.node (1) as ll_node then - storage.update_node_summary (ll_node.id, "New Summary") + storage.update_node_summary (2,ll_node.id, "New Summary") assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary)) end end test_update_node_content + local + l_node: CMS_NODE do - storage.save_node (custom_node ("Content", "Summary", "Title")) + l_node := custom_node ("Content", "Summary", "Title") + storage.save_user (default_user) + storage.save_user (custom_user ("u2", "p2", "e2")) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) if attached {CMS_NODE} storage.node (1) as ll_node then - storage.update_node_content (ll_node.id, "New Content") + storage.update_node_content (2,ll_node.id, "New Content") + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary) + end + end + + + test_update_node_title_by_author + local + l_node: CMS_NODE + do + l_node := custom_node ("Content", "Summary", "Title") + storage.save_user (default_user) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_title (1,ll_node.id, "New Title") + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary) + end + end + + test_update_node_summary_by_author + local + l_node: CMS_NODE + do + l_node := custom_node ("Content", "Summary", "Title") + storage.save_user (default_user) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_summary (1,ll_node.id, "New Summary") + assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary)) + end + end + + test_update_node_content_by_author + local + l_node: CMS_NODE + do + l_node := custom_node ("Content", "Summary", "Title") + storage.save_user (default_user) + l_node.set_author (storage.user_by_email (default_user.email)) + storage.save_node (l_node) + if attached {CMS_NODE} storage.node (1) as ll_node then + storage.update_node_content (1,ll_node.id, "New Content") assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary) end end test_delete_node + local + l_node: CMS_NODE + l_user: like {CMS_NODE}.author do + storage.save_user (custom_user ("test_delete", "testu", "email")) + l_user := storage.user_by_name ("test_delete") across 1 |..| 10 as c loop - storage.save_node (custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out)) + l_node := custom_node ("Content_" + c.item.out, "Summary_" + c.item.out, "Title_" + c.item.out) + l_node.set_author (l_user) + storage.save_node (l_node) end - assert ("Exist node id: 10", attached storage.node (10) as l_node and then l_node.title ~ "Title_10" ) + assert ("Exist node id: 10", attached storage.node (10) as ll_node and then ll_node.title ~ "Title_10" ) storage.delete_node (10) assert ("Not exist node id: 10", storage.node (10) = Void) end diff --git a/persistence/interface/cms_storage.e b/persistence/interface/cms_storage.e index d93f64b..eb5a44a 100644 --- a/persistence/interface/cms_storage.e +++ b/persistence/interface/cms_storage.e @@ -27,10 +27,12 @@ feature -- Access: user end all_users: LIST [CMS_USER] + -- Possible list of users. deferred end user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER + -- User with id `a_id', if any. require a_id > 0 deferred @@ -40,6 +42,7 @@ feature -- Access: user end user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER + -- User with name `a_name', if any. require a_name /= Void and then not a_name.is_empty deferred @@ -49,13 +52,15 @@ feature -- Access: user end user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER + -- User with name `a_email', if any. deferred ensure same_email: Result /= Void implies a_email ~ Result.email password: Result /= Void implies Result.password /= Void end - is_valid_credential (u, p: READABLE_STRING_32): BOOLEAN + is_valid_credential (a_u, a_p: READABLE_STRING_32): BOOLEAN + -- Does account with username `a_username' and password `a_password' exist? deferred end @@ -78,6 +83,7 @@ feature -- User Nodes feature -- Change: user save_user (a_user: CMS_USER) + -- Save user `a_user'. deferred end @@ -113,16 +119,19 @@ feature -- Access: roles and permissions end user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE + -- User role by id `a_id', if any. deferred end user_roles: LIST [CMS_USER_ROLE] + -- Possible list of user roles. deferred end feature -- Change: roles and permissions save_user_role (a_user_role: CMS_USER_ROLE) + -- Save user role `a_user_role' deferred end @@ -180,58 +189,50 @@ feature -- Change: Node save_node (a_node: CMS_NODE) -- Save node `a_node'. + require + valid_user: attached a_node.author as l_author and then l_author.id > 0 deferred end - delete_node (a_id: INTEGER_64) -- Remove node by id `a_id'. require - valid_id: a_id > 0 + valid_node_id: a_id > 0 deferred end - update_node (a_node: CMS_NODE) + update_node (a_id: like {CMS_USER}.id; a_node: CMS_NODE) -- Update node content `a_node'. + -- The user `a_id' is an existing or new collaborator. require - valid_id: a_node.id > 0 + valid_node_id: a_node.id > 0 + valid_user_id: a_id > 0 deferred end - update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) - -- Update node title to `a_title', node identified by id `a_id'. + update_node_title (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) + -- Update node title to `a_title', node identified by id `a_node_id'. + -- The user `a_id' is an existing or new collaborator. require - valid_id: a_id > 0 + valid_node_id: a_node_id > 0 + valid_user_id: a_id > 0 deferred end - update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) - -- Update node summary to `a_summary', node identified by id `a_id'. + update_node_summary (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) + -- Update node summary to `a_summary', node identified by id `a_node_id'. + -- The user `a_id' is an existing or new collaborator. require - valid_id: a_id > 0 + valid_id: a_node_id > 0 deferred end - update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) - -- Update node content to `a_content', node identified by id `a_id'. + update_node_content (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) + -- Update node content to `a_content', node identified by id `a_node_id'. + -- The user `a_id' is an existing or new collaborator. require - valid_id: a_id > 0 - deferred - end - - add_node_author (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) - -- Add author `a_user_id' to the node `a_node_id'. - require - valid_node: a_node_id > 0 - valid_user: a_user_id > 0 - deferred - end - - add_node_collaborator (a_node_id: like {CMS_NODE}.id; a_user_id: like {CMS_USER}.id) - -- Add/Update collaborator with `a_user_id' to the node `a_node_id'. - require - valid_node: a_node_id > 0 - valid_user: a_user_id > 0 + valid_id: a_node_id > 0 + valid_user_id: a_id > 0 deferred end From 588827b4955b721b77630c8724eaa889c1db8636 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Sun, 21 Sep 2014 15:53:09 -0300 Subject: [PATCH 015/386] Updated Example (API) to use the new CMS_STORAGE interface. --- .../service/filter/authentication_filter.e | 8 ++++-- .../service/handler/node_content_handler.e | 5 ++-- api/src/service/handler/node_handler.e | 12 +++++--- .../service/handler/node_summary_handler.e | 5 ++-- api/src/service/handler/node_title_handler.e | 5 ++-- api/src/service/roc_api_service.e | 28 ++++++++++++------- .../implementation/common/cms_storage_null.e | 4 +++ .../mysql/scripts/create_database.sql | 4 ++- .../mysql/src/cms_storage_mysql.e | 1 + .../mysql/src/provider/node_data_provider.e | 14 ++++++++++ 10 files changed, 63 insertions(+), 23 deletions(-) diff --git a/api/src/service/filter/authentication_filter.e b/api/src/service/filter/authentication_filter.e index 296c7ac..183bb5d 100644 --- a/api/src/service/filter/authentication_filter.e +++ b/api/src/service/filter/authentication_filter.e @@ -37,8 +37,12 @@ feature -- Basic operations if (attached l_auth.type as l_auth_type and then l_auth_type.is_case_insensitive_equal ("basic")) and then attached l_auth.login as l_auth_login and then attached l_auth.password as l_auth_password then if api_service.login_valid (l_auth_login, l_auth_password) then - req.set_execution_variable ("user", create {CMS_USER}.make (l_auth_login)) - execute_next (req, res) + if attached api_service.user_by_name (l_auth_login) as l_user then + req.set_execution_variable ("user", l_user) + execute_next (req, res) + else + -- Internal server error + end else log.write_error (generator + ".execute login_valid failed for: " + l_auth_login ) execute_next (req, res) diff --git a/api/src/service/handler/node_content_handler.e b/api/src/service/handler/node_content_handler.e index 9614ed1..286929b 100644 --- a/api/src/service/handler/node_content_handler.e +++ b/api/src/service/handler/node_content_handler.e @@ -133,12 +133,13 @@ feature -- HTTP Methods u_node: CMS_NODE l_page: ROC_RESPONSE do - if attached current_user_name (req) then + to_implement ("Check if user has permissions") + if attached current_user (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then u_node := extract_data_form (req) u_node.set_id (l_id.integer_value) - api_service.update_node_content (u_node.id, u_node.content) + api_service.update_node_content (l_user.id, u_node.id, u_node.content) (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) else do_error (req, res, l_id) diff --git a/api/src/service/handler/node_handler.e b/api/src/service/handler/node_handler.e index 4c9efaa..a0183c7 100644 --- a/api/src/service/handler/node_handler.e +++ b/api/src/service/handler/node_handler.e @@ -93,7 +93,8 @@ feature -- HTTP Methods u_node: CMS_NODE l_page: ROC_RESPONSE do - if attached current_user_name (req) then + to_implement ("Check user permissions!!!") + if attached current_user (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then if attached {WSF_STRING} req.form_parameter ("method") as l_method then @@ -113,7 +114,9 @@ feature -- HTTP Methods end else -- New node - api_service.new_node (extract_data_form (req)) + u_node := extract_data_form (req) + u_node.set_author (l_user) + api_service.new_node (u_node) (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) end else @@ -126,12 +129,13 @@ feature -- HTTP Methods local u_node: CMS_NODE do - if attached current_user_name (req) then + + if attached current_user (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then u_node := extract_data_form (req) u_node.set_id (l_id.integer_value) - api_service.update_node (u_node) + api_service.update_node (l_user.id,u_node) (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) else do_error (req, res, l_id) diff --git a/api/src/service/handler/node_summary_handler.e b/api/src/service/handler/node_summary_handler.e index 3d7caa3..4a4f995 100644 --- a/api/src/service/handler/node_summary_handler.e +++ b/api/src/service/handler/node_summary_handler.e @@ -132,12 +132,13 @@ feature -- HTTP Methods u_node: CMS_NODE l_page: ROC_RESPONSE do - if attached current_user_name (req) then + to_implement ("Check if user has permissions!!!") + if attached current_user (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then u_node := extract_data_form (req) u_node.set_id (l_id.integer_value) - api_service.update_node_summary (u_node.id, u_node.summary) + api_service.update_node_summary (l_user.id,u_node.id, u_node.summary) (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) else do_error (req, res, l_id) diff --git a/api/src/service/handler/node_title_handler.e b/api/src/service/handler/node_title_handler.e index 958a6d0..4c6665c 100644 --- a/api/src/service/handler/node_title_handler.e +++ b/api/src/service/handler/node_title_handler.e @@ -133,12 +133,13 @@ feature -- HTTP Methods u_node: CMS_NODE l_page: ROC_RESPONSE do - if attached current_user_name (req) then + to_implement ("Check if user has permissions") + if attached current_user (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then u_node := extract_data_form (req) u_node.set_id (l_id.integer_value) - api_service.update_node_title (u_node.id, u_node.title) + api_service.update_node_title (l_user.id,u_node.id, u_node.title) (create {ROC_RESPONSE}.make (req, "")).new_response_redirect (req, res, req.absolute_script_url ("")) else do_error (req, res, l_id) diff --git a/api/src/service/roc_api_service.e b/api/src/service/roc_api_service.e index 300365a..c4e7437 100644 --- a/api/src/service/roc_api_service.e +++ b/api/src/service/roc_api_service.e @@ -35,6 +35,8 @@ feature -- Access Result := storage.is_valid_credential (l_auth_login, l_auth_password) end +feature -- Access: Node + nodes: LIST[CMS_NODE] -- List of nodes. do @@ -56,7 +58,7 @@ feature -- Access end -feature -- Node +feature -- Change: Node new_node (a_node: CMS_NODE) -- Add a new node @@ -69,31 +71,37 @@ feature -- Node storage.delete_node (a_id) end - update_node (a_node: CMS_NODE) + update_node (a_id: like {CMS_USER}.id; a_node: CMS_NODE) do - storage.update_node (a_node) + storage.update_node (a_id,a_node) end - update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + update_node_title (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) do fixme ("Check preconditions") - storage.update_node_title (a_id, a_title) + storage.update_node_title (a_id,a_node_id,a_title) end - update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + update_node_summary (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) do fixme ("Check preconditions") - storage.update_node_summary (a_id, a_summary) + storage.update_node_summary (a_id,a_node_id, a_summary) end - update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + update_node_content (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) do fixme ("Check preconditions") - storage.update_node_content (a_id, a_content) + storage.update_node_content (a_id,a_node_id, a_content) end -feature -- User +feature -- Access: User + + user_by_name (a_username: READABLE_STRING_32): detachable CMS_USER + do + Result := storage.user_by_name (a_username) + end +feature -- Change User new_user (a_user: CMS_USER) -- Add a new user `a_user'. diff --git a/persistence/implementation/common/cms_storage_null.e b/persistence/implementation/common/cms_storage_null.e index 6b4fe01..c82e9b6 100644 --- a/persistence/implementation/common/cms_storage_null.e +++ b/persistence/implementation/common/cms_storage_null.e @@ -45,11 +45,13 @@ feature -- User Nodes user_collaborator_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] -- Possible list of nodes where the user identified by `a_id', is a collaborator. do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) end user_author_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] -- Possible list of nodes where the user identified by `a_id', is the author. do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) end feature -- Change: user @@ -67,6 +69,7 @@ feature -- Access: roles and permissions user_roles: LIST [CMS_USER_ROLE] do + create {ARRAYED_LIST[CMS_USER_ROLE]} Result.make (0) end @@ -104,6 +107,7 @@ feature -- Access: node node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER] -- Possible list of node's collaborator. do + create {ARRAYED_LIST[CMS_USER]} Result.make (0) end feature -- Node diff --git a/persistence/implementation/mysql/scripts/create_database.sql b/persistence/implementation/mysql/scripts/create_database.sql index 7569e9d..a78dd4e 100644 --- a/persistence/implementation/mysql/scripts/create_database.sql +++ b/persistence/implementation/mysql/scripts/create_database.sql @@ -25,6 +25,7 @@ CREATE TABLE IF NOT EXISTS `cms_dev`.`users` ( PRIMARY KEY (`id`), UNIQUE INDEX `username` (`username` ASC)) ENGINE = InnoDB +AUTO_INCREMENT = 2 DEFAULT CHARACTER SET = latin1; @@ -40,7 +41,7 @@ CREATE TABLE IF NOT EXISTS `cms_dev`.`nodes` ( `summary` TEXT NOT NULL, `content` MEDIUMTEXT NOT NULL, `author_id` INT(10) UNSIGNED NULL DEFAULT NULL, - `version` INT(10) ZEROFILL NULL, + `version` INT(10) UNSIGNED ZEROFILL NULL DEFAULT NULL, `editor_id` INT(10) UNSIGNED NULL DEFAULT NULL, PRIMARY KEY (`id`), INDEX `fk_nodes_users1_idx` (`author_id` ASC), @@ -56,6 +57,7 @@ CREATE TABLE IF NOT EXISTS `cms_dev`.`nodes` ( ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB +AUTO_INCREMENT = 11 DEFAULT CHARACTER SET = latin1; diff --git a/persistence/implementation/mysql/src/cms_storage_mysql.e b/persistence/implementation/mysql/src/cms_storage_mysql.e index c655891..74a72ce 100644 --- a/persistence/implementation/mysql/src/cms_storage_mysql.e +++ b/persistence/implementation/mysql/src/cms_storage_mysql.e @@ -189,6 +189,7 @@ feature -- Node delete_node (a_id: INTEGER_64) do + node_provider.delete_from_user_nodes(a_id) node_provider.delete_node (a_id) post_node_provider_execution end diff --git a/persistence/implementation/mysql/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e index e44d69b..3bcfc84 100644 --- a/persistence/implementation/mysql/src/provider/node_data_provider.e +++ b/persistence/implementation/mysql/src/provider/node_data_provider.e @@ -224,6 +224,18 @@ feature -- Basic operations post_execution end + delete_from_user_nodes (a_id: INTEGER_64) + local + l_parameters: STRING_TABLE [ANY] + do + log.write_information (generator + ".delete_from_user_nodes") + create l_parameters.make (1) + l_parameters.put (a_id, "id") + db_handler.set_query (create {DATABASE_QUERY}.data_reader (sql_delete_from_user_node, l_parameters)) + db_handler.execute_change + post_execution + end + feature -- Basic Operations: User_Nodes add_author (a_user_id: INTEGER_64; a_node_id: INTEGER_64) @@ -413,6 +425,8 @@ feature {NONE} -- Sql Queries: USER_ROLES collaborators, author Select_exist_user_node: STRING= "Select Count(*) from Users_nodes where users_id=:user_id and nodes_id=:node_id;" + sql_delete_from_user_node: STRING = "delete from users_nodes where nodes_id=:id" + feature -- From 27a11f2eeac58afd08967ec44fd0425b9f75eb59 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 1 Oct 2014 12:17:39 -0300 Subject: [PATCH 016/386] Initial CMS API commmit. --- cms/Readme.md | 0 cms/cms.ecf | 25 + cms/src/configuration/cms_configuration.e | 298 + cms/src/configuration/cms_custom_setup.e | 14 + cms/src/configuration/cms_default_setup.e | 123 + .../configuration/cms_json_configuration.e | 47 + cms/src/configuration/cms_layout.e | 32 + cms/src/configuration/cms_setup.e | 85 + cms/src/kernel/cms_html_page.e | 183 + cms/src/modules/cms_module.e | 48 + .../node/handler/node_content_handler.e | 189 + cms/src/modules/node/handler/node_handler.e | 221 + .../node/handler/node_summary_handler.e | 189 + .../modules/node/handler/node_title_handler.e | 187 + cms/src/modules/node/handler/nodes_handler.e | 52 + cms/src/modules/node/node_module.e | 129 + .../node/response/node_view_cms_response.e | 37 + .../node/response/nodes_view_cms_response.e | 35 + cms/src/service/cms_api_service.e | 127 + cms/src/service/cms_request_util.e | 58 + cms/src/service/cms_service.e | 149 + cms/src/service/handler/cms_handler.e | 36 + cms/src/service/handler/cms_root_handler.e | 54 + .../service/response/cms_generic_response.e | 62 + cms/src/service/response/cms_response.e | 177 + cms/src/service/response/home_cms_response.e | 35 + cms/src/theme/cms_html_page_response.e | 77 + cms/src/theme/cms_html_template.e | 13 + cms/src/theme/cms_page_template.e | 12 + cms/src/theme/cms_template.e | 37 + cms/src/theme/cms_theme.e | 48 + cms/src/theme/cms_theme_information.e | 135 + cms/src/theme/default_cms_template.e | 75 + .../default_theme/default_cms_html_template.e | 100 + .../default_theme/default_cms_page_template.e | 102 + .../theme/default_theme/default_cms_theme.e | 96 + .../smarty_theme/smarty_cms_page_template.e | 134 + cms/src/theme/smarty_theme/smarty_cms_theme.e | 104 + examples/Readme.md | 4 + Readme.txt => examples/api/Readme.txt | 0 .../api}/launcher/any/application_launcher.e | 0 .../launcher/any/application_launcher_i.e | 0 .../launcher/default/application_launcher.e | 0 .../launcher/default/application_launcher_i.e | 0 {api => examples/api}/roc.ini | 2 +- {api => examples/api}/roc_api.ecf | 18 +- .../config/application_configuration.json | 32 + .../api}/site/www/static/css/bootstrap.css | 0 .../api}/site/www/static/css/dashboard.css | 0 .../site/www/static/images/ajax-loader.gif | Bin .../api}/site/www/static/images/favicon.ico | Bin .../api}/site/www/static/js/roc.js | 0 .../api}/site/www/template/html/layout2.tpl | 0 .../www/template/html/master2/content.tpl | 0 .../site/www/template/html/master2/error.tpl | 0 .../site/www/template/html/master2/footer.tpl | 0 .../site/www/template/html/master2/head.tpl | 0 .../site/www/template/html/master2/header.tpl | 0 .../site/www/template/html/master2/logoff.tpl | 0 .../template/html/master2/main_navigation.tpl | 0 .../html/master2/optional_enhancement_js.tpl | 0 .../html/master2/optional_styling_css.tpl | 0 .../template/html/master2/site_navigation.tpl | 0 .../www/template/html/modules/navigation.tpl | 0 .../site/www/template/html/modules/node.tpl | 0 .../template/html/modules/node_content.tpl | 0 .../template/html/modules/node_summary.tpl | 0 .../www/template/html/modules/node_title.tpl | 0 .../site/www/template/html/modules/nodes.tpl | 0 .../www/template/html/modules/register.tpl | 0 .../src/configuration/application_constants.e | 0 .../src/configuration/configuration_factory.e | 0 .../configuration/roc_json_configuration.e | 0 .../representation/common/template_shared.e | 0 .../api}/src/representation/roc_response.e | 0 .../src/representation/roc_template_page.e | 0 {api => examples/api}/src/roc_abstract_api.e | 0 {api => examples/api}/src/roc_config.e | 0 {api => examples/api}/src/roc_server.e | 0 .../service/filter/authentication_filter.e | 0 .../api}/src/service/filter/cors_filter.e | 0 .../api}/src/service/filter/error_filter.e | 0 .../api}/src/service/filter/logger_filter.e | 0 .../service/handler/app_abstract_handler.e | 0 .../api}/src/service/handler/app_handler.e | 0 .../src/service/handler/navigation_handler.e | 0 .../service/handler/node_content_handler.e | 0 .../api}/src/service/handler/node_handler.e | 0 .../service/handler/node_summary_handler.e | 0 .../src/service/handler/node_title_handler.e | 0 .../api}/src/service/handler/nodes_handler.e | 0 .../src/service/handler/roc_login_handler.e | 0 .../src/service/handler/roc_logoff_handler.e | 0 .../src/service/handler/roc_root_handler.e | 0 .../api}/src/service/handler/user_handler.e | 0 .../api}/src/service/roc_api_service.e | 0 .../api}/src/service/roc_email_service.e | 0 .../api}/src/service/roc_rest_api.e | 0 .../api}/src/service/shared_conneg_helper.e | 0 .../api}/src/validator/roc_input_validator.e | 0 examples/roc_api/Readme.md | 0 .../launcher/any/application_launcher.e | 19 + .../launcher/any/application_launcher_i.e | 102 + .../launcher/default/application_launcher.e | 19 + .../launcher/default/application_launcher_i.e | 26 + examples/roc_api/roc.ini | 2 + examples/roc_api/roc_api.ecf | 50 + .../config/application_configuration.json | 2 +- examples/roc_api/site/config/cms.ini | 6 + .../roc_api/site/www/static/css/bootstrap.css | 5849 +++++++++++++++++ .../roc_api/site/www/static/css/dashboard.css | 357 + .../site/www/static/images/ajax-loader.gif | Bin 0 -> 1456 bytes .../site/www/static/images/favicon.ico | Bin 0 -> 16446 bytes examples/roc_api/site/www/static/js/roc.js | 108 + .../site/www/template/html/layout2.tpl | 40 + .../www/template/html/master2/content.tpl | 11 + .../site/www/template/html/master2/error.tpl | 18 + .../site/www/template/html/master2/footer.tpl | 7 + .../site/www/template/html/master2/head.tpl | 10 + .../site/www/template/html/master2/header.tpl | 2 + .../site/www/template/html/master2/logoff.tpl | 5 + .../template/html/master2/main_navigation.tpl | 5 + .../html/master2/optional_enhancement_js.tpl | 5 + .../html/master2/optional_styling_css.tpl | 9 + .../template/html/master2/site_navigation.tpl | 27 + .../www/template/html/modules/navigation.tpl | 8 + .../site/www/template/html/modules/node.tpl | 174 + .../template/html/modules/node_content.tpl | 70 + .../template/html/modules/node_summary.tpl | 71 + .../www/template/html/modules/node_title.tpl | 70 + .../site/www/template/html/modules/nodes.tpl | 52 + .../www/template/html/modules/register.tpl | 100 + .../roc_api/site/www/themes/api/layout2.tpl | 40 + .../site/www/themes/api/master2/content.tpl | 11 + .../site/www/themes/api/master2/error.tpl | 18 + .../site/www/themes/api/master2/footer.tpl | 7 + .../site/www/themes/api/master2/head.tpl | 10 + .../site/www/themes/api/master2/header.tpl | 2 + .../site/www/themes/api/master2/logoff.tpl | 5 + .../themes/api/master2/main_navigation.tpl | 5 + .../api/master2/optional_enhancement_js.tpl | 5 + .../api/master2/optional_styling_css.tpl | 9 + .../themes/api/master2/site_navigation.tpl | 27 + .../www/themes/api/modules/navigation.tpl | 8 + .../site/www/themes/api/modules/node.tpl | 174 + .../www/themes/api/modules/node_content.tpl | 70 + .../www/themes/api/modules/node_summary.tpl | 71 + .../www/themes/api/modules/node_title.tpl | 70 + .../site/www/themes/api/modules/nodes.tpl | 52 + .../site/www/themes/api/modules/register.tpl | 100 + .../roc_api/site/www/themes/api/theme.info | 9 + .../site/www/themes/smarty/front-page.tpl | 12 + .../roc_api/site/www/themes/smarty/page.tpl | 11 + .../site/www/themes/smarty/res/ewfcms.js | 91 + .../site/www/themes/smarty/res/favicon.ico | Bin 0 -> 786 bytes .../site/www/themes/smarty/res/logo.png | Bin 0 -> 3193 bytes .../www/themes/smarty/res/menu-collapsed.png | Bin 0 -> 108 bytes .../www/themes/smarty/res/menu-expanded.png | Bin 0 -> 106 bytes .../site/www/themes/smarty/res/style.css | 251 + .../roc_api/site/www/themes/smarty/theme.info | 10 + .../www/themes/smarty/tpl/page-footer.tpl | 6 + .../www/themes/smarty/tpl/page-header.tpl | 14 + examples/roc_api/src/ewf_roc_server.e | 139 + ...{error_handler.e => basic_error_handler.e} | 2 +- layout/src/error/shared_error.e | 4 +- persistence/Readme.md | 0 .../common/database/database_error_handler.e | 35 + .../database/database_storage_manager.e | 371 ++ .../common/database/shared_error_handler.e | 27 + .../mysql/persistence_mysql.ecf | 2 + 170 files changed, 12459 insertions(+), 14 deletions(-) create mode 100644 cms/Readme.md create mode 100644 cms/cms.ecf create mode 100644 cms/src/configuration/cms_configuration.e create mode 100644 cms/src/configuration/cms_custom_setup.e create mode 100644 cms/src/configuration/cms_default_setup.e create mode 100644 cms/src/configuration/cms_json_configuration.e create mode 100644 cms/src/configuration/cms_layout.e create mode 100644 cms/src/configuration/cms_setup.e create mode 100644 cms/src/kernel/cms_html_page.e create mode 100644 cms/src/modules/cms_module.e create mode 100644 cms/src/modules/node/handler/node_content_handler.e create mode 100644 cms/src/modules/node/handler/node_handler.e create mode 100644 cms/src/modules/node/handler/node_summary_handler.e create mode 100644 cms/src/modules/node/handler/node_title_handler.e create mode 100644 cms/src/modules/node/handler/nodes_handler.e create mode 100644 cms/src/modules/node/node_module.e create mode 100644 cms/src/modules/node/response/node_view_cms_response.e create mode 100644 cms/src/modules/node/response/nodes_view_cms_response.e create mode 100644 cms/src/service/cms_api_service.e create mode 100644 cms/src/service/cms_request_util.e create mode 100644 cms/src/service/cms_service.e create mode 100644 cms/src/service/handler/cms_handler.e create mode 100644 cms/src/service/handler/cms_root_handler.e create mode 100644 cms/src/service/response/cms_generic_response.e create mode 100644 cms/src/service/response/cms_response.e create mode 100644 cms/src/service/response/home_cms_response.e create mode 100644 cms/src/theme/cms_html_page_response.e create mode 100644 cms/src/theme/cms_html_template.e create mode 100644 cms/src/theme/cms_page_template.e create mode 100644 cms/src/theme/cms_template.e create mode 100644 cms/src/theme/cms_theme.e create mode 100644 cms/src/theme/cms_theme_information.e create mode 100644 cms/src/theme/default_cms_template.e create mode 100644 cms/src/theme/default_theme/default_cms_html_template.e create mode 100644 cms/src/theme/default_theme/default_cms_page_template.e create mode 100644 cms/src/theme/default_theme/default_cms_theme.e create mode 100644 cms/src/theme/smarty_theme/smarty_cms_page_template.e create mode 100644 cms/src/theme/smarty_theme/smarty_cms_theme.e create mode 100644 examples/Readme.md rename Readme.txt => examples/api/Readme.txt (100%) rename {api => examples/api}/launcher/any/application_launcher.e (100%) rename {api => examples/api}/launcher/any/application_launcher_i.e (100%) rename {api => examples/api}/launcher/default/application_launcher.e (100%) rename {api => examples/api}/launcher/default/application_launcher_i.e (100%) rename {api => examples/api}/roc.ini (56%) rename {api => examples/api}/roc_api.ecf (90%) create mode 100644 examples/api/site/config/application_configuration.json rename {api => examples/api}/site/www/static/css/bootstrap.css (100%) rename {api => examples/api}/site/www/static/css/dashboard.css (100%) rename {api => examples/api}/site/www/static/images/ajax-loader.gif (100%) rename {api => examples/api}/site/www/static/images/favicon.ico (100%) rename {api => examples/api}/site/www/static/js/roc.js (100%) rename {api => examples/api}/site/www/template/html/layout2.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/content.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/error.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/footer.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/head.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/header.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/logoff.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/main_navigation.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/optional_enhancement_js.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/optional_styling_css.tpl (100%) rename {api => examples/api}/site/www/template/html/master2/site_navigation.tpl (100%) rename {api => examples/api}/site/www/template/html/modules/navigation.tpl (100%) rename {api => examples/api}/site/www/template/html/modules/node.tpl (100%) rename {api => examples/api}/site/www/template/html/modules/node_content.tpl (100%) rename {api => examples/api}/site/www/template/html/modules/node_summary.tpl (100%) rename {api => examples/api}/site/www/template/html/modules/node_title.tpl (100%) rename {api => examples/api}/site/www/template/html/modules/nodes.tpl (100%) rename {api => examples/api}/site/www/template/html/modules/register.tpl (100%) rename {api => examples/api}/src/configuration/application_constants.e (100%) rename {api => examples/api}/src/configuration/configuration_factory.e (100%) rename {api => examples/api}/src/configuration/roc_json_configuration.e (100%) rename {api => examples/api}/src/representation/common/template_shared.e (100%) rename {api => examples/api}/src/representation/roc_response.e (100%) rename {api => examples/api}/src/representation/roc_template_page.e (100%) rename {api => examples/api}/src/roc_abstract_api.e (100%) rename {api => examples/api}/src/roc_config.e (100%) rename {api => examples/api}/src/roc_server.e (100%) rename {api => examples/api}/src/service/filter/authentication_filter.e (100%) rename {api => examples/api}/src/service/filter/cors_filter.e (100%) rename {api => examples/api}/src/service/filter/error_filter.e (100%) rename {api => examples/api}/src/service/filter/logger_filter.e (100%) rename {api => examples/api}/src/service/handler/app_abstract_handler.e (100%) rename {api => examples/api}/src/service/handler/app_handler.e (100%) rename {api => examples/api}/src/service/handler/navigation_handler.e (100%) rename {api => examples/api}/src/service/handler/node_content_handler.e (100%) rename {api => examples/api}/src/service/handler/node_handler.e (100%) rename {api => examples/api}/src/service/handler/node_summary_handler.e (100%) rename {api => examples/api}/src/service/handler/node_title_handler.e (100%) rename {api => examples/api}/src/service/handler/nodes_handler.e (100%) rename {api => examples/api}/src/service/handler/roc_login_handler.e (100%) rename {api => examples/api}/src/service/handler/roc_logoff_handler.e (100%) rename {api => examples/api}/src/service/handler/roc_root_handler.e (100%) rename {api => examples/api}/src/service/handler/user_handler.e (100%) rename {api => examples/api}/src/service/roc_api_service.e (100%) rename {api => examples/api}/src/service/roc_email_service.e (100%) rename {api => examples/api}/src/service/roc_rest_api.e (100%) rename {api => examples/api}/src/service/shared_conneg_helper.e (100%) rename {api => examples/api}/src/validator/roc_input_validator.e (100%) create mode 100644 examples/roc_api/Readme.md create mode 100644 examples/roc_api/launcher/any/application_launcher.e create mode 100644 examples/roc_api/launcher/any/application_launcher_i.e create mode 100644 examples/roc_api/launcher/default/application_launcher.e create mode 100644 examples/roc_api/launcher/default/application_launcher_i.e create mode 100644 examples/roc_api/roc.ini create mode 100644 examples/roc_api/roc_api.ecf rename {api => examples/roc_api}/site/config/application_configuration.json (96%) create mode 100644 examples/roc_api/site/config/cms.ini create mode 100644 examples/roc_api/site/www/static/css/bootstrap.css create mode 100644 examples/roc_api/site/www/static/css/dashboard.css create mode 100644 examples/roc_api/site/www/static/images/ajax-loader.gif create mode 100644 examples/roc_api/site/www/static/images/favicon.ico create mode 100644 examples/roc_api/site/www/static/js/roc.js create mode 100644 examples/roc_api/site/www/template/html/layout2.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/content.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/error.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/footer.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/head.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/header.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/logoff.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/main_navigation.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/optional_enhancement_js.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/optional_styling_css.tpl create mode 100644 examples/roc_api/site/www/template/html/master2/site_navigation.tpl create mode 100644 examples/roc_api/site/www/template/html/modules/navigation.tpl create mode 100644 examples/roc_api/site/www/template/html/modules/node.tpl create mode 100644 examples/roc_api/site/www/template/html/modules/node_content.tpl create mode 100644 examples/roc_api/site/www/template/html/modules/node_summary.tpl create mode 100644 examples/roc_api/site/www/template/html/modules/node_title.tpl create mode 100644 examples/roc_api/site/www/template/html/modules/nodes.tpl create mode 100644 examples/roc_api/site/www/template/html/modules/register.tpl create mode 100644 examples/roc_api/site/www/themes/api/layout2.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/content.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/error.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/footer.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/head.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/header.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/logoff.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/main_navigation.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/optional_enhancement_js.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/optional_styling_css.tpl create mode 100644 examples/roc_api/site/www/themes/api/master2/site_navigation.tpl create mode 100644 examples/roc_api/site/www/themes/api/modules/navigation.tpl create mode 100644 examples/roc_api/site/www/themes/api/modules/node.tpl create mode 100644 examples/roc_api/site/www/themes/api/modules/node_content.tpl create mode 100644 examples/roc_api/site/www/themes/api/modules/node_summary.tpl create mode 100644 examples/roc_api/site/www/themes/api/modules/node_title.tpl create mode 100644 examples/roc_api/site/www/themes/api/modules/nodes.tpl create mode 100644 examples/roc_api/site/www/themes/api/modules/register.tpl create mode 100644 examples/roc_api/site/www/themes/api/theme.info create mode 100644 examples/roc_api/site/www/themes/smarty/front-page.tpl create mode 100644 examples/roc_api/site/www/themes/smarty/page.tpl create mode 100644 examples/roc_api/site/www/themes/smarty/res/ewfcms.js create mode 100644 examples/roc_api/site/www/themes/smarty/res/favicon.ico create mode 100644 examples/roc_api/site/www/themes/smarty/res/logo.png create mode 100644 examples/roc_api/site/www/themes/smarty/res/menu-collapsed.png create mode 100644 examples/roc_api/site/www/themes/smarty/res/menu-expanded.png create mode 100644 examples/roc_api/site/www/themes/smarty/res/style.css create mode 100644 examples/roc_api/site/www/themes/smarty/theme.info create mode 100644 examples/roc_api/site/www/themes/smarty/tpl/page-footer.tpl create mode 100644 examples/roc_api/site/www/themes/smarty/tpl/page-header.tpl create mode 100644 examples/roc_api/src/ewf_roc_server.e rename layout/src/error/{error_handler.e => basic_error_handler.e} (98%) create mode 100644 persistence/Readme.md create mode 100644 persistence/implementation/common/database/database_error_handler.e create mode 100644 persistence/implementation/common/database/database_storage_manager.e create mode 100644 persistence/implementation/common/database/shared_error_handler.e diff --git a/cms/Readme.md b/cms/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/cms/cms.ecf b/cms/cms.ecf new file mode 100644 index 0000000..6aa2623 --- /dev/null +++ b/cms/cms.ecf @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + diff --git a/cms/src/configuration/cms_configuration.e b/cms/src/configuration/cms_configuration.e new file mode 100644 index 0000000..b6f6b12 --- /dev/null +++ b/cms/src/configuration/cms_configuration.e @@ -0,0 +1,298 @@ +class + CMS_CONFIGURATION + +inherit + ANY + + SHARED_EXECUTION_ENVIRONMENT + export + {NONE} all + end + +create + make + +feature {NONE} -- Initialization + + make (a_layout: CMS_LAYOUT) + -- Initialize `Current'. + local + p: PATH + do + layout := a_layout + create options.make_equal (10) + configuration_location := layout.cms_config_ini_path + import_from_path (layout.cms_config_ini_path) + analyze + end + + analyze + do + get_root_location + get_var_location + get_themes_location + get_files_location + end + +feature -- Access + + + configuration_location: detachable PATH + + option (a_name: READABLE_STRING_GENERAL): detachable ANY + do + Result := options.item (a_name) + end + + options: STRING_TABLE [STRING_32] + +feature -- Conversion + + append_to_string (s: STRING) + local + utf: UTF_CONVERTER + do + s.append ("Options:%N") + across + options as c + loop + s.append (c.key.to_string_8) + s.append_character ('=') + utf.string_32_into_utf_8_string_8 (c.item, s) + s.append_character ('%N') + end + + s.append ("Specific:%N") + s.append ("root_location=" + root_location.utf_8_name + "%N") + s.append ("var_location=" + var_location.utf_8_name + "%N") + s.append ("files_location=" + files_location.utf_8_name + "%N") + s.append ("themes_location=" + themes_location.utf_8_name + "%N") + end + +feature -- Element change + + set_option (a_name: READABLE_STRING_GENERAL; a_value: STRING_32) + do + options.force (a_value, a_name.as_string_8) + end + +feature -- Access + + var_location: PATH + + root_location: PATH + + files_location: PATH + + themes_location: PATH + + theme_name (dft: detachable like theme_name): READABLE_STRING_8 + do + if attached options.item ("theme") as s then + Result := s + elseif dft /= Void then + Result := dft + else + Result := "default" + end + end + + site_id: READABLE_STRING_8 + do + if attached options.item ("site.id") as s then + Result := s + else + Result := "_EWF_CMS_NO_ID_" + end + end + + site_name (dft: like site_name): READABLE_STRING_8 + do + if attached options.item ("site.name") as s then + Result := s + else + Result := dft + end + end + + site_url (dft: like site_url): READABLE_STRING_8 + do + if attached options.item ("site.url") as s then + Result := s + else + Result := dft + end + if Result /= Void then + if Result.is_empty then + -- ok + elseif not Result.ends_with ("/") then + Result := Result + "/" + end + end + end + + site_script_url (dft: like site_script_url): detachable READABLE_STRING_8 + do + if attached options.item ("site.script_url") as s then + Result := s + else + Result := dft + end + if Result /= Void then + if Result.is_empty then + elseif not Result.ends_with ("/") then + Result := Result + "/" + end + end + end + + site_email (dft: like site_email): READABLE_STRING_8 + do + if attached options.item ("site.email") as s then + Result := s + else + Result := dft + end + end + +feature -- Change + + get_var_location + local + utf: UTF_CONVERTER + do + if attached options.item ("var-dir") as s then + create var_location.make_from_string (utf.utf_8_string_8_to_escaped_string_32 (s)) + else + var_location := execution_environment.current_working_path + end + end + + get_root_location + local + utf: UTF_CONVERTER + do + root_location := layout.www_path + end + + get_files_location + local + utf: UTF_CONVERTER + do + if attached options.item ("files-dir") as s then + create files_location.make_from_string (utf.utf_8_string_8_to_escaped_string_32 (s)) + else + create files_location.make_from_string ("files") + end + end + + get_themes_location + local + utf: UTF_CONVERTER + do + if attached options.item ("themes-dir") as s then + create themes_location.make_from_string (utf.utf_8_string_8_to_escaped_string_32 (s)) + else + themes_location := root_location.extended ("themes") + end + end + +feature {NONE} -- Implementation + + import_from_file (fn: READABLE_STRING_GENERAL) + do + import_from_path (create {PATH}.make_from_string (fn)) + end + + import_from_path (a_filename: PATH) + -- Import ini file content + local + f: PLAIN_TEXT_FILE + l,v: STRING_8 + p: INTEGER + do + create f.make_with_path (a_filename) + if f.exists and f.is_readable then + f.open_read + from + f.read_line + until + f.exhausted + loop + l := f.last_string + l.left_adjust + if not l.is_empty then + if l[1] = '#' then + -- commented line + else + p := l.index_of ('=', 1) + if p > 1 then + v := l.substring (p + 1, l.count) + l.keep_head (p - 1) + v.left_adjust + v.right_adjust + l.right_adjust + + if l.is_case_insensitive_equal ("@include") then + import_from_file (resolved_string (v)) + else + set_option (l.as_lower, resolved_string (v)) + end + end + end + end + f.read_line + end + f.close + end + end + +feature {NONE} -- Environment + + resolved_string (s: READABLE_STRING_8): STRING_32 + -- Resolved `s' using `options' or else environment variables. + local + i,n,b,e: INTEGER + k: detachable READABLE_STRING_8 + do + from + i := 1 + n := s.count + create Result.make (s.count) + until + i > n + loop + if i + 1 < n and then s[i] = '$' and then s[i+1] = '{' then + b := i + 2 + e := s.index_of ('}', b) - 1 + if e > 0 then + k := s.substring (b, e) + if attached option (k) as v then + if attached {READABLE_STRING_32} v as s32 then + Result.append (s32) + else + Result.append (v.out) + end + i := e + 1 + elseif attached execution_environment.item (k) as v then + Result.append (v) + i := e + 1 + else + Result.extend (s[i]) + end + else + Result.extend (s[i]) + end + else + Result.extend (s[i]) + end + i := i + 1 + end + end + + +feature -- Implementation + + layout: CMS_LAYOUT + -- Cms layout +end diff --git a/cms/src/configuration/cms_custom_setup.e b/cms/src/configuration/cms_custom_setup.e new file mode 100644 index 0000000..c8640b1 --- /dev/null +++ b/cms/src/configuration/cms_custom_setup.e @@ -0,0 +1,14 @@ +note + description: "Summary description for {CMS_CUSTOM_SETUP}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_CUSTOM_SETUP + +inherit + CMS_DEFAULT_SETUP + +create + make +end diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e new file mode 100644 index 0000000..74f0754 --- /dev/null +++ b/cms/src/configuration/cms_default_setup.e @@ -0,0 +1,123 @@ +note + description: "Summary description for {CMS_DEFAULT_SETUP}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_DEFAULT_SETUP + +inherit + CMS_SETUP + + REFACTORING_HELPER +create + make +feature {NONE} -- Initialization + + make (a_layout: CMS_LAYOUT) + do + layout := a_layout + create configuration.make (layout) + + site_id := configuration.site_id + site_url := configuration.site_url ("") + site_name := configuration.site_name ("EWF::CMS") + site_email := configuration.site_email ("webmaster") + site_dir := configuration.root_location + site_var_dir := configuration.var_location + files_location := configuration.files_location + themes_location := configuration.themes_location + theme_name := configuration.theme_name ("default") + + compute_theme_location + compute_theme_resource_location + + initialize + end + + + initialize + do + build_api_service + build_auth_engine + build_mailer + build_modules + end + +feature -- Access + + modules: ARRAYED_LIST [CMS_MODULE] + -- List of possible modules + + is_html: BOOLEAN + -- + do + -- Enable change the mode + Result := (create {CMS_JSON_CONFIGURATION}).is_html_mode(layout.application_config_path) + end + + is_web: BOOLEAN + -- + do + Result := (create {CMS_JSON_CONFIGURATION}).is_web_mode(layout.application_config_path) + + end + +feature {NONE} -- Initialization + + build_modules + -- Core modules. (User, Admin, Node) + -- At the moment only node is supported. + local + m: CMS_MODULE + do + create modules.make (3) + +-- -- Core +-- create {USER_MODULE} m.make +-- m.enable +-- modules.extend (m) + +-- create {ADMIN_MODULE} m.make +-- m.enable +-- modules.extend (m) + + create {NODE_MODULE} m.make (Current) + m.enable + modules.extend (m) + end + + build_api_service + local + dn: PATH + l_database: DATABASE_CONNECTION + do + to_implement ("Refactor database setup") + if attached (create {JSON_CONFIGURATION}).new_database_configuration (layout.application_config_path) as l_database_config then + create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string) + create api_service.make (create {CMS_STORAGE_MYSQL}.make (l_database)) + else + create {DATABASE_CONNECTION_NULL} l_database.make_common + create api_service.make (create {CMS_STORAGE_NULL}) + end + end + + build_auth_engine + do + to_implement ("Not implemented authentication") + end + + build_mailer + do + to_implement ("Not implemented mailer") + end + +feature -- Change + + add_module (m: CMS_MODULE) + -- Add a module `m' to the list of modules `modules'. + do + modules.force (m) + end + +end diff --git a/cms/src/configuration/cms_json_configuration.e b/cms/src/configuration/cms_json_configuration.e new file mode 100644 index 0000000..6462a30 --- /dev/null +++ b/cms/src/configuration/cms_json_configuration.e @@ -0,0 +1,47 @@ +note + description: "Summary description for {CMS_JSON_CONFIGURATION}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_JSON_CONFIGURATION + + +inherit + + JSON_CONFIGURATION + +feature -- Access + + is_html_mode (a_path: PATH): BOOLEAN + -- Is the server running on web mode? + local + l_parser: JSON_PARSER + do + if attached json_file_from (a_path) as json_file then + l_parser := new_json_parser (json_file) + if attached {JSON_OBJECT} l_parser.parse as jv and then l_parser.is_parsed and then + attached {JSON_OBJECT} jv.item ("server") as l_server and then + attached {JSON_STRING} l_server.item ("mode") as l_mode then + Result := l_mode.item.is_case_insensitive_equal_general ("html") + end + end + end + + + + is_web_mode (a_path: PATH): BOOLEAN + -- Is the server running on web mode? + local + l_parser: JSON_PARSER + do + if attached json_file_from (a_path) as json_file then + l_parser := new_json_parser (json_file) + if attached {JSON_OBJECT} l_parser.parse as jv and then l_parser.is_parsed and then + attached {JSON_OBJECT} jv.item ("server") as l_server and then + attached {JSON_STRING} l_server.item ("mode") as l_mode then + Result := l_mode.item.is_case_insensitive_equal_general ("web") + end + end + end +end diff --git a/cms/src/configuration/cms_layout.e b/cms/src/configuration/cms_layout.e new file mode 100644 index 0000000..b48684e --- /dev/null +++ b/cms/src/configuration/cms_layout.e @@ -0,0 +1,32 @@ +note + description: "Summary description for {CMS_LAYOUT}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_LAYOUT + +inherit + + APPLICATION_LAYOUT + +create + make_default, + make_with_path + + +feature -- Access + + theme_path: PATH + -- Directory for templates (HTML, etc). + once + Result := www_path.extended ("theme") + end + + cms_config_ini_path: PATH + -- Database Configuration file path. + once + Result := config_path.extended ("cms.ini") + end + +end diff --git a/cms/src/configuration/cms_setup.e b/cms/src/configuration/cms_setup.e new file mode 100644 index 0000000..a9f8327 --- /dev/null +++ b/cms/src/configuration/cms_setup.e @@ -0,0 +1,85 @@ +note + description: "Summary description for {CMS_SETUP}." + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_SETUP + +feature -- Access + + configuration: CMS_CONFIGURATION + -- cms configuration. + + layout: CMS_LAYOUT + -- CMS layout. + + api_service: CMS_API_SERVICE + -- cms api service. + + modules: LIST[CMS_MODULE] + -- Possible list of modules. + -- |If we remove Modules from setup. + -- |we can let the CMS_SERVICE define the basic modules. + deferred + end + + is_html: BOOLEAN + -- api with progresive enhacements css and js, server side rendering. + deferred + end + + is_web: BOOLEAN + -- web: Web Site with progresive enhacements css and js and Ajax calls. + deferred + end + +feature -- Access: Site + + site_id: READABLE_STRING_8 + + site_name: READABLE_STRING_32 + + site_email: READABLE_STRING_8 + + site_url: READABLE_STRING_8 + + site_dir: PATH + + site_var_dir: PATH + + files_location: PATH + +feature -- Access:Theme + + themes_location: PATH + + theme_location: PATH + + theme_resource_location: PATH + -- + + theme_information_location: PATH + -- theme informations. + do + Result := theme_location.extended ("theme.info") + end + + theme_name: READABLE_STRING_32 + -- theme name + +feature -- Compute location + + compute_theme_location + do + theme_location := themes_location.extended (theme_name) + end + + compute_theme_resource_location + -- assets (js, css, images, etc) + -- Not used at the moment. + do + theme_resource_location := theme_location + end + +end diff --git a/cms/src/kernel/cms_html_page.e b/cms/src/kernel/cms_html_page.e new file mode 100644 index 0000000..5c6a5c0 --- /dev/null +++ b/cms/src/kernel/cms_html_page.e @@ -0,0 +1,183 @@ +note + description: "Summary description for {CMS_HTML_PAGE}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_HTML_PAGE + +create + make, + make_typed + +feature {NONE} -- Initialization + + make_typed (a_type: like type) + -- Make current page with optional page type `a_type'. + do + make + type := a_type + end + + make + do + create regions.make (5) + language := "en" + status_code := {HTTP_STATUS_CODE}.ok + create header.make + create {ARRAYED_LIST [STRING]} head_lines.make (5) + header.put_content_type_text_html + create variables.make (0) + end + +feature -- Access + + type: detachable READABLE_STRING_8 + -- Optional page type. + -- such as "front", "about", ... that could be customized by themes. + + title: detachable STRING + + language: STRING + + head_lines: LIST [STRING] + + head_lines_to_string: STRING + do + create Result.make_empty + across + head_lines as h + loop + Result.append (h.item) + Result.append_character ('%N') + end + end + + variables: STRING_TABLE [detachable ANY] + +feature -- Status + + status_code: INTEGER + +feature -- Header + + header: HTTP_HEADER + +feature -- Region + + regions: HASH_TABLE [STRING_8, STRING_8] + -- header + -- content + -- footer + -- could have sidebar first, sidebar second, ... + + region (n: STRING_8): STRING_8 + do + if attached regions.item (n) as r then + Result := r + else + Result := "" + debug + Result := "{{" + n + "}}" + end + end + end + + header_region: STRING_8 + do + Result := region ("header") + end + + content_region: STRING_8 + do + Result := region ("content") + end + + footer_region: STRING_8 + do + Result := region ("content") + end + +feature -- Element change + + register_variable (a_value: detachable ANY; k: READABLE_STRING_GENERAL) + do + variables.force (a_value, k) + end + + add_to_region (s: STRING; k: STRING) + local + r: detachable STRING + do + r := regions.item (k) + if r = Void then + create r.make_from_string (s) + set_region (r, k) + else + r.append (s) + end + end + + add_to_header_region (s: STRING) + do + add_to_region (s, "header") + end + + add_to_content_region (s: STRING) + do + add_to_region (s, "content") + end + + add_to_footer_region (s: STRING) + do + add_to_region (s, "footer") + end + + set_region (s: STRING; k: STRING) + do + regions.force (s, k) + end + + set_header_region (s: STRING) + do + set_region (s, "header") + end + + set_content_region (s: STRING) + do + set_region (s, "content") + end + + set_footer_region (s: STRING) + do + set_region (s, "footer") + end + +feature -- Element change + + set_status_code (c: like status_code) + do + status_code := c + end + + set_language (s: like language) + do + language := s + end + + set_title (s: like title) + do + title := s + end + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/modules/cms_module.e b/cms/src/modules/cms_module.e new file mode 100644 index 0000000..4a40053 --- /dev/null +++ b/cms/src/modules/cms_module.e @@ -0,0 +1,48 @@ +note + description: "Summary description for {WSF_CMS_MODULE}." + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_MODULE + +feature -- Access + + is_enabled: BOOLEAN + + name: STRING + + description: STRING + + package: STRING + + version: STRING + +feature -- Router + + router: WSF_ROUTER + -- Router configuration. + deferred + end + +feature -- Settings + + enable + do + is_enabled := True + end + + disable + do + is_enabled := False + end + +feature -- Hooks + + help_text (a_path: STRING): STRING + do + create Result.make_empty + end + + +end diff --git a/cms/src/modules/node/handler/node_content_handler.e b/cms/src/modules/node/handler/node_content_handler.e new file mode 100644 index 0000000..7fe8fc3 --- /dev/null +++ b/cms/src/modules/node/handler/node_content_handler.e @@ -0,0 +1,189 @@ +note + description: "Summary description for {NEW_CONTENT_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NODE_CONTENT_HANDLER + +inherit + + CMS_HANDLER + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post, + do_put + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + do + if attached current_user_name (req) then + -- Existing node + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_content") + l_page.add_variable (l_node.content, "content") + l_page.add_variable (l_id.value, "id") + l_page.execute + else + do_error (req, res, l_id) + end + else + -- Todo extract method + to_implement ("Check how to implemet API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + if attached {WSF_STRING} req.form_parameter ("method") as l_method then + if l_method.is_case_insensitive_equal ("PUT") then + do_put (req, res) + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + end + else + do_error (req, res, l_id) + end + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + + do_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE +-- l_page: ROC_RESPONSE + do + to_implement ("Check if user has permissions") + if attached current_user (req) as l_user then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + u_node := extract_data_form (req) + u_node.set_id (l_id.integer_value) + api_service.update_node_content (l_user.id, u_node.id, u_node.content) + (create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end +feature -- Error + + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + -- Handling error. + local + do + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- if a_id.is_integer then +-- -- resource not found +-- l_page.set_value ("404", "code") +-- else +-- -- bad request +-- l_page.set_value ("400", "code") +-- end +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to(res) + end + + +feature -- {NONE} Form data + + + extract_data_form (req: WSF_REQUEST): CMS_NODE + -- Extract request form data and build a object + -- Node + do + create Result.make ("", "", "") + if attached {WSF_STRING}req.form_parameter ("content") as l_content then + Result.set_content (l_content.value) + end + end + +end diff --git a/cms/src/modules/node/handler/node_handler.e b/cms/src/modules/node/handler/node_handler.e new file mode 100644 index 0000000..1580099 --- /dev/null +++ b/cms/src/modules/node/handler/node_handler.e @@ -0,0 +1,221 @@ +note + description: "Summary description for {NODE_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NODE_HANDLER + +inherit + CMS_HANDLER + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post, + do_put, + do_delete + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + do + -- Existing node + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup,"modules/node") + l_page.add_variable (l_node, "node") + l_page.execute + else + do_error (req, res, l_id) + end + else + -- Factory + new_node (req, res) + end + end + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + l_page: CMS_RESPONSE + do + to_implement ("Check user permissions!!!") + if attached current_user (req) as l_user then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + if attached {WSF_STRING} req.form_parameter ("method") as l_method then + if l_method.is_case_insensitive_equal ("DELETE") then + do_delete (req, res) + elseif l_method.is_case_insensitive_equal ("PUT") then + do_put (req, res) + else + to_implement ("Implement specific responses for 500 pages") + create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") + l_page.add_variable ("500", "code") + l_page.add_variable (req.absolute_script_url (req.path_info), "request") + l_page.execute + end + end + else + do_error (req, res, l_id) + end + else + -- New node + u_node := extract_data_form (req) + u_node.set_author (l_user) + api_service.new_node (u_node) + (create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url ("")) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + + do_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + do + + if attached current_user (req) as l_user then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + u_node := extract_data_form (req) + u_node.set_id (l_id.integer_value) + api_service.update_node (l_user.id,u_node) + (create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + -- Internal server error + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + + end + + do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + api_service.delete_node (l_id.integer_value) + (create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + -- Internal server error + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + +feature -- Error + + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + -- Handling error. + local + l_page: CMS_RESPONSE + do + to_implement ("Not implemented") +-- create l_page.make (req, "master2/error.tpl") +-- if a_id.is_integer then +-- -- resource not found +-- l_page.set_value ("404", "code") +-- else +-- -- bad request +-- l_page.set_value ("400", "code") +-- end +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + +feature {NONE} -- Node + + new_node (req: WSF_REQUEST; res: WSF_RESPONSE) + local + l_page: CMS_RESPONSE + do + if attached current_user_name (req) then + create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node") + l_page.add_variable (setup.is_html, "html") + l_page.add_variable (setup.is_web, "web") + l_page.execute + + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + +feature -- {NONE} Form data + + extract_data_form (req: WSF_REQUEST): CMS_NODE + -- Extract request form data and build a object + -- Node + do + create Result.make ("", "", "") + if attached {WSF_STRING} req.form_parameter ("title") as l_title then + Result.set_title (l_title.value) + end + if attached {WSF_STRING} req.form_parameter ("summary") as l_summary then + Result.set_summary (l_summary.value) + end + if attached {WSF_STRING} req.form_parameter ("content") as l_content then + Result.set_content (l_content.value) + end + end + +end diff --git a/cms/src/modules/node/handler/node_summary_handler.e b/cms/src/modules/node/handler/node_summary_handler.e new file mode 100644 index 0000000..9e38e96 --- /dev/null +++ b/cms/src/modules/node/handler/node_summary_handler.e @@ -0,0 +1,189 @@ +note + description: "Summary description for {NODE_SUMMARY_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NODE_SUMMARY_HANDLER + +inherit + CMS_HANDLER + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post, + do_put + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + do + if attached current_user_name (req) then + -- Existing node + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_summary") + l_page.add_variable (setup.is_html, "html") + l_page.add_variable (setup.is_web, "web") + l_page.add_variable (l_node.summary, "summary") + l_page.add_variable (l_id.value, "id") + l_page.execute + else + do_error (req, res, l_id) + end + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + do + if attached current_user_name (req) then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + if attached {WSF_STRING} req.form_parameter ("method") as l_method then + if l_method.is_case_insensitive_equal ("PUT") then + do_put (req, res) + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + end + else + do_error (req, res, l_id) + end + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + + do_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + do + to_implement ("Check if user has permissions!!!") + if attached current_user (req) as l_user then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + u_node := extract_data_form (req) + u_node.set_id (l_id.integer_value) + api_service.update_node_summary (l_user.id,u_node.id, u_node.summary) + (create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + +feature -- Error + + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + -- Handling error. + local +-- l_page: ROC_RESPONSE + do +-- create l_page.make (req, "master2/error.tpl") +-- if a_id.is_integer then +-- -- resource not found +-- l_page.set_value ("404", "code") +-- else +-- -- bad request +-- l_page.set_value ("400", "code") +-- end +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to(res) + end + + +feature -- {NONE} Form data + + + extract_data_form (req: WSF_REQUEST): CMS_NODE + -- Extract request form data and build a object + -- Node + do + create Result.make ("", "", "") + if attached {WSF_STRING}req.form_parameter ("summary") as l_summary then + Result.set_summary (l_summary.value) + end + end + +end diff --git a/cms/src/modules/node/handler/node_title_handler.e b/cms/src/modules/node/handler/node_title_handler.e new file mode 100644 index 0000000..7218ddc --- /dev/null +++ b/cms/src/modules/node/handler/node_title_handler.e @@ -0,0 +1,187 @@ +note + description: "Summary description for {NODE_TITLE_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NODE_TITLE_HANDLER + +inherit + CMS_HANDLER + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_URI_TEMPLATE_HANDLER + rename + execute as uri_template_execute, + new_mapping as new_uri_template_mapping + select + new_uri_template_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get, + do_post, + do_put + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + + uri_template_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + do + if attached current_user_name (req) as l_user then + -- Existing node + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_title") + l_page.add_variable (setup.is_html, "html") + l_page.add_variable (setup.is_web, "web") + l_page.add_variable (l_node.title, "title") + l_page.add_variable (l_id.value, "id") + l_page.execute + else + do_error (req, res, l_id) + end + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + + do_post (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + do + if attached current_user_name (req) as l_user then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + if attached {WSF_STRING} req.form_parameter ("method") as l_method then + if l_method.is_case_insensitive_equal ("PUT") then + do_put (req, res) + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + end + else + do_error (req, res, l_id) + end + else + to_implement ("Check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + + do_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + u_node: CMS_NODE + do + to_implement ("Check if user has permissions") + if attached current_user (req) as l_user then + if attached {WSF_STRING} req.path_parameter ("id") as l_id then + if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then + u_node := extract_data_form (req) + u_node.set_id (l_id.integer_value) + api_service.update_node_title (l_user.id,u_node.id, u_node.title) + (create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url ("")) + else + do_error (req, res, l_id) + end + else + to_implement ("check how to implement API error") +-- create l_page.make (req, "master2/error.tpl") +-- l_page.set_value ("500", "code") +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + else + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + end + end + +feature -- Error + + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) + -- Handling error. + local + do + to_implement ("Check how to add API error") +-- create l_page.make (req, "master2/error.tpl") +-- if a_id.is_integer then +-- -- resource not found +-- l_page.set_value ("404", "code") +-- else +-- -- bad request +-- l_page.set_value ("400", "code") +-- end +-- l_page.set_value (req.absolute_script_url (req.path_info), "request") +-- l_page.send_to (res) + end + + +feature -- {NONE} Form data + + extract_data_form (req: WSF_REQUEST): CMS_NODE + -- Extract request form data and build a object + -- Node + do + create Result.make ("", "", "") + if attached {WSF_STRING} req.form_parameter ("title") as l_title then + Result.set_title (l_title.value) + end + end + +end diff --git a/cms/src/modules/node/handler/nodes_handler.e b/cms/src/modules/node/handler/nodes_handler.e new file mode 100644 index 0000000..6b99297 --- /dev/null +++ b/cms/src/modules/node/handler/nodes_handler.e @@ -0,0 +1,52 @@ +note + description: "Summary description for {NODES_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + NODES_HANDLER + +inherit + CMS_HANDLER + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + do + (create {NODES_VIEW_CMS_RESPONSE}.make (req, res, setup,"modules/nodes")).execute + end +end diff --git a/cms/src/modules/node/node_module.e b/cms/src/modules/node/node_module.e new file mode 100644 index 0000000..0470288 --- /dev/null +++ b/cms/src/modules/node/node_module.e @@ -0,0 +1,129 @@ +note + description: "Summary description for {CMS_MODULE}." + date: "$Date$" + revision: "$Revision$" + +class + NODE_MODULE + +inherit + + CMS_MODULE + +create + make + +feature {NONE} -- Initialization + + make (a_config: CMS_SETUP) + do + name := "node" + version := "1.0" + description := "Service to manage content based on 'node'" + package := "core" + config := a_config + setup_router + enable + end + +feature -- Access + + router: WSF_ROUTER + -- Node router. + + config: CMS_SETUP + -- Node configuration. + +feature -- Implementation + + setup_router + -- Setup `router'. + local + fhdl: WSF_FILE_SYSTEM_HANDLER + do + create router.make (5) + configure_api_node + configure_api_nodes + configure_api_node_title + configure_api_node_summary + configure_api_node_content + end + +feature -- Configure Node Resources Routes + + configure_api_node + local + l_node_handler: NODE_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_node_handler.make (config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + router.handle_with_request_methods ("/node", l_node_handler, l_methods) + + create l_node_handler.make (config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + l_methods.enable_delete + router.handle_with_request_methods ("/node/{id}", l_node_handler, l_methods) + end + + + configure_api_nodes + local + l_nodes_handler: NODES_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_nodes_handler.make (config) + create l_methods + l_methods.enable_get + router.handle_with_request_methods ("/nodes", l_nodes_handler, l_methods) + end + + + configure_api_node_summary + local + l_report_handler: NODE_SUMMARY_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + router.handle_with_request_methods ("/node/{id}/summary", l_report_handler, l_methods) + end + + + configure_api_node_title + local + l_report_handler: NODE_TITLE_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + router.handle_with_request_methods ("/node/{id}/title", l_report_handler, l_methods) + end + + + configure_api_node_content + local + l_report_handler: NODE_CONTENT_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_report_handler.make (config) + create l_methods + l_methods.enable_get + l_methods.enable_post + l_methods.enable_put + router.handle_with_request_methods ("/node/{id}/content", l_report_handler, l_methods) + end + +end diff --git a/cms/src/modules/node/response/node_view_cms_response.e b/cms/src/modules/node/response/node_view_cms_response.e new file mode 100644 index 0000000..f115673 --- /dev/null +++ b/cms/src/modules/node/response/node_view_cms_response.e @@ -0,0 +1,37 @@ +note + description: "Summary description for {NODE_VIEW_CMS_RESPONSE}." + date: "$Date$" + revision: "$Revision$" + +class + NODE_VIEW_CMS_RESPONSE + +inherit + + CMS_RESPONSE + redefine + custom_prepare + end + +create + make + +feature -- Generation + + custom_prepare (page: CMS_HTML_PAGE) + do + if attached variables as l_variables then + across l_variables as c loop page.register_variable (c.item, c.key) end + end + end + +feature -- Execution + + process + -- Computed response message. + do + set_title ("List of Nodes") + set_page_title (Void) + end +end + diff --git a/cms/src/modules/node/response/nodes_view_cms_response.e b/cms/src/modules/node/response/nodes_view_cms_response.e new file mode 100644 index 0000000..ecddfe0 --- /dev/null +++ b/cms/src/modules/node/response/nodes_view_cms_response.e @@ -0,0 +1,35 @@ +note + description: "Summary description for {NODE_VIEW_CMS_RESPONSE}." + date: "$Date$" + revision: "$Revision$" + +class + NODES_VIEW_CMS_RESPONSE + +inherit + + CMS_RESPONSE + redefine + custom_prepare + end + +create + make + +feature -- Generation + + custom_prepare (page: CMS_HTML_PAGE) + do + page.register_variable (setup.api_service.nodes, "nodes") + end + +feature -- Execution + + process + -- Computed response message. + do + set_title ("List of Nodes") + set_page_title (Void) + end +end + diff --git a/cms/src/service/cms_api_service.e b/cms/src/service/cms_api_service.e new file mode 100644 index 0000000..547d4c6 --- /dev/null +++ b/cms/src/service/cms_api_service.e @@ -0,0 +1,127 @@ +note + description: "Summary description for {CMS_API_SERVICE}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_API_SERVICE + +inherit + + SHARED_ERROR + REFACTORING_HELPER + + +create make + + +feature -- Initialize + + make (a_storage: CMS_STORAGE) + -- Create the API service with an storege `a_storage'. + do + storage := a_storage + set_successful + ensure + storage_set: storage = a_storage + end + +feature -- Access + + login_valid (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN + local + l_security: SECURITY_PROVIDER + do + Result := storage.is_valid_credential (l_auth_login, l_auth_password) + end + +feature -- Access: Node + + nodes: LIST[CMS_NODE] + -- List of nodes. + do + fixme ("Implementation") + Result := storage.recent_nodes (0, 10) + end + + recent_nodes (a_offset, a_rows: INTEGER): LIST[CMS_NODE] + -- List of the `a_rows' most recent nodes starting from `a_offset'. + do + Result := storage.recent_nodes (a_offset, a_rows) + end + + node (a_id: INTEGER_64): detachable CMS_NODE + -- Node by ID. + do + fixme ("Check preconditions") + Result := storage.node (a_id) + end + + +feature -- Change: Node + + new_node (a_node: CMS_NODE) + -- Add a new node + do + storage.save_node (a_node) + end + + delete_node (a_id: INTEGER_64) + do + storage.delete_node (a_id) + end + + update_node (a_id: like {CMS_USER}.id; a_node: CMS_NODE) + do + storage.update_node (a_id,a_node) + end + + update_node_title (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) + do + fixme ("Check preconditions") + storage.update_node_title (a_id,a_node_id,a_title) + end + + update_node_summary (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) + do + fixme ("Check preconditions") + storage.update_node_summary (a_id,a_node_id, a_summary) + end + + update_node_content (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) + do + fixme ("Check preconditions") + storage.update_node_content (a_id,a_node_id, a_content) + end + + +feature -- Access: User + + user_by_name (a_username: READABLE_STRING_32): detachable CMS_USER + do + Result := storage.user_by_name (a_username) + end +feature -- Change User + + new_user (a_user: CMS_USER) + -- Add a new user `a_user'. + do + if + attached a_user.password as l_password and then + attached a_user.email as l_email + then + storage.save_user (a_user) + else + fixme ("Add error") + end + end + + +feature {NONE} -- Implemenataion + + + storage: CMS_STORAGE + -- Persistence storage + +end + diff --git a/cms/src/service/cms_request_util.e b/cms/src/service/cms_request_util.e new file mode 100644 index 0000000..300d41d --- /dev/null +++ b/cms/src/service/cms_request_util.e @@ -0,0 +1,58 @@ +note + description: "Summary description for {CMS_REQUEST_UTIL}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_REQUEST_UTIL + +inherit + + REFACTORING_HELPER + +feature -- User + + current_user_name (req: WSF_REQUEST): detachable READABLE_STRING_32 + -- Current user name or Void in case of Guest users. + note + EIS: "src=eiffel:?class=AUTHENTICATION_FILTER&feature=execute" + do + if attached {CMS_USER} current_user (req) as l_user then + Result := l_user.name + end + fixme ("Workaround to add nodes!!!") + Result := "admin" -- Workaround + end + + current_user (req: WSF_REQUEST): detachable CMS_USER + -- Current user or Void in case of Guest user. + note + EIS: "eiffel:?class=AUTHENTICATION_FILTER&feature=execute" + do + if attached {CMS_USER} req.execution_variable ("user") as l_user then + Result := l_user + end + end + +feature -- Media Type + + current_media_type (req: WSF_REQUEST): detachable READABLE_STRING_32 + -- Current media type or Void if it's not acceptable. + do + if attached {STRING} req.execution_variable ("media_type") as l_type then + Result := l_type + end + end + +feature -- Absolute Host + + absolute_host (req: WSF_REQUEST; a_path:STRING): STRING + do + Result := req.absolute_script_url (a_path) + if Result.last_index_of ('/', Result.count) = Result.count then + Result.remove_tail (1) + end + end + +end diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e new file mode 100644 index 0000000..6343ab0 --- /dev/null +++ b/cms/src/service/cms_service.e @@ -0,0 +1,149 @@ +note + description: "[ + This class implements the CMS service + + It could be used to implement the main EWF service, or + even for a specific handler. + ]" + +class + CMS_SERVICE + +inherit + WSF_ROUTED_SKELETON_SERVICE + undefine + requires_proxy + redefine + execute_default + end + + WSF_NO_PROXY_POLICY + + WSF_URI_HELPER_FOR_ROUTED_SERVICE + + WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_SERVICE + + REFACTORING_HELPER + + SHARED_LOGGER + +create + make + +feature {NONE} -- Initialization + + make (a_setup: CMS_SETUP) + do + setup := a_setup + configuration := a_setup.configuration + modules := a_setup.modules + initialize_auth_engine + initialize_mailer + initialize_router + initialize_modules + end + + + + initialize_users + do + to_implement ("To Implement initialize users") + end + + initialize_mailer + do + to_implement ("To Implement mailer") + end + + setup_router + do + configure_api_root + end + + initialize_modules + -- Intialize modules, import router definitions + -- from enabled modules. + do + log.write_debug (generator + ".initialize_modules") + across + modules as m + loop + if m.item.is_enabled then + router.import (m.item.router) + end + end + configure_api_file_handler + end + + initialize_auth_engine + do + to_implement ("To Implement authentication engine") + end + + + configure_api_root + local + l_root_handler: CMS_ROOT_HANDLER + l_methods: WSF_REQUEST_METHODS + do + log.write_debug (generator + ".configure_api_root") + create l_root_handler.make (setup) + create l_methods + l_methods.enable_get + router.handle_with_request_methods ("/", l_root_handler, l_methods) + end + + configure_api_file_handler + local + fhdl: WSF_FILE_SYSTEM_HANDLER + do + log.write_debug (generator + ".configure_api_file_handler") + create fhdl.make_hidden_with_path (setup.layout.www_path) + fhdl.disable_index + fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE) + do + execute_default (ia_req, ia_res) + end) + router.handle_with_request_methods ("/", fhdl, router.methods_GET) + end + + +feature -- Access + + setup: CMS_SETUP + -- CMS setup. + + configuration: CMS_CONFIGURATION + -- CMS configuration. + -- | Maybe we can compute it (using `setup') instead of using memory. + + modules: LIST [CMS_MODULE] + -- List of possible modules. + -- | Maybe we can compute it (using `setup') instead of using memory. + +feature -- Logging + +feature -- Notification + + +feature -- Execution + + execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Default request handler if no other are relevant + local + do + fixme ("To Implement") + end + + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/service/handler/cms_handler.e b/cms/src/service/handler/cms_handler.e new file mode 100644 index 0000000..ebe75b7 --- /dev/null +++ b/cms/src/service/handler/cms_handler.e @@ -0,0 +1,36 @@ +note + description: "Summary description for {CMS_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_HANDLER + +inherit + + CMS_REQUEST_UTIL + + SHARED_LOGGER + + REFACTORING_HELPER + +feature {NONE} -- Initialization + + make (a_setup: CMS_SETUP) + do + setup := a_setup + end + +feature -- Setup + + setup: CMS_SETUP + +feature -- API Service + + api_service: CMS_API_SERVICE + do + Result := setup.api_service + end + +end diff --git a/cms/src/service/handler/cms_root_handler.e b/cms/src/service/handler/cms_root_handler.e new file mode 100644 index 0000000..fdb2783 --- /dev/null +++ b/cms/src/service/handler/cms_root_handler.e @@ -0,0 +1,54 @@ +note + description: "Summary description for {CMS_ROOT_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_ROOT_HANDLER + +inherit + + CMS_HANDLER + + WSF_FILTER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + do + (create {HOME_CMS_RESPONSE}.make (req, res, setup,"layout2")).execute + end + +end diff --git a/cms/src/service/response/cms_generic_response.e b/cms/src/service/response/cms_generic_response.e new file mode 100644 index 0000000..872e852 --- /dev/null +++ b/cms/src/service/response/cms_generic_response.e @@ -0,0 +1,62 @@ +note + description: "Summary description for {CMS_GENERIC_RESPONSE}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_GENERIC_RESPONSE + +feature -- Responses + + new_response_redirect (req: WSF_REQUEST; res: WSF_RESPONSE; a_location: READABLE_STRING_32) + -- Redirect to `a_location' + local + h: HTTP_HEADER + do + create h.make + h.put_content_type_text_html + h.put_current_date + h.put_location (a_location) + res.set_status_code ({HTTP_STATUS_CODE}.see_other) + res.put_header_text (h.string) + end + + new_response_authenticate (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Handle authenticate. + local + h: HTTP_HEADER + do + create h.make + h.put_content_type_text_html + h.put_current_date + h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"CMS-User%"") + res.set_status_code ({HTTP_STATUS_CODE}.unauthorized) + res.put_header_text (h.string) + end + + new_response_denied (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Handle access denied. + local + h: HTTP_HEADER + do + create h.make + h.put_content_type_text_html + h.put_current_date + res.set_status_code ({HTTP_STATUS_CODE}.unauthorized) + res.put_header_text (h.string) + end + + new_response_unauthorized (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Handle not authorized. + local + h: HTTP_HEADER + output: STRING + do + create h.make + h.put_content_type_text_html + h.put_current_date + res.set_status_code ({HTTP_STATUS_CODE}.forbidden) + res.put_header_text (h.string) + end + +end diff --git a/cms/src/service/response/cms_response.e b/cms/src/service/response/cms_response.e new file mode 100644 index 0000000..1f96824 --- /dev/null +++ b/cms/src/service/response/cms_response.e @@ -0,0 +1,177 @@ +note + description: "Generic CMS Response, place to add HOOKS features as collaborators." + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_RESPONSE + +inherit + + CMS_REQUEST_UTIL + + REFACTORING_HELPER + +feature {NONE} -- Initialization + + make (req: WSF_REQUEST; res: WSF_RESPONSE; a_setup: like setup; a_template: like template) + do + status_code := {HTTP_STATUS_CODE}.ok + setup := a_setup + request := req + response := res + template := a_template + create header.make + initialize + end + + initialize + do + get_theme + end + +feature -- Access + + request: WSF_REQUEST + + response: WSF_RESPONSE + + setup: CMS_SETUP + -- Current setup + + status_code: INTEGER + + header: WSF_HEADER + + title: detachable READABLE_STRING_32 + + page_title: detachable READABLE_STRING_32 + -- Page title + + main_content: detachable STRING_8 + + template: READABLE_STRING_32 + -- Current template. + +feature -- Element change + + set_title (t: like title) + do + title := t + set_page_title (t) + end + + set_page_title (t: like page_title) + do + page_title := t + end + + set_main_content (s: like main_content) + do + main_content := s + end + +feature -- Theme + + theme: CMS_THEME + -- Current theme + + get_theme + local + l_info: CMS_THEME_INFORMATION + do + if attached setup.theme_information_location as fn then + create l_info.make (fn) + else + create l_info.make_default + end + if l_info.engine.is_case_insensitive_equal_general ("smarty") then + create {SMARTY_CMS_THEME} theme.make (setup, l_info, template) + else + create {DEFAULT_CMS_THEME} theme.make (setup, l_info) + end + end + +feature -- Generation + + prepare (page: CMS_HTML_PAGE) + do + common_prepare (page) + custom_prepare (page) + end + + common_prepare (page: CMS_HTML_PAGE) + do + fixme ("Fix generacion common") + page.register_variable (request.absolute_script_url (""), "host") + page.register_variable (setup.is_web, "web") + page.register_variable (setup.is_html, "html") + if attached current_user_name (request) as l_user then + page.register_variable (l_user, "user") + end + end + + custom_prepare (page: CMS_HTML_PAGE) + do + end + + +feature -- Custom Variables + + variables: detachable STRING_TABLE[ANY] + -- Custom variables to feed the templates. + + +feature -- Element change: Add custom variables. + + add_variable (a_element: ANY; a_key:READABLE_STRING_32) + local + l_variables: like variables + do + l_variables := variables + if l_variables = Void then + create l_variables.make (5) + variables := l_variables + end + l_variables.force (a_element, a_key) + end + + +feature -- Execution + + execute + do + begin + process + terminate + end + +feature {NONE} -- Execution + + begin + do + end + + process + deferred + end + + frozen terminate + local + cms_page: CMS_HTML_PAGE + page: CMS_HTML_PAGE_RESPONSE + do + create cms_page.make + prepare (cms_page) + create page.make (theme.page_html (cms_page)) + page.set_status_code (status_code) + response.send (page) + on_terminated + end + + on_terminated + do + + end + +end diff --git a/cms/src/service/response/home_cms_response.e b/cms/src/service/response/home_cms_response.e new file mode 100644 index 0000000..b38c0e4 --- /dev/null +++ b/cms/src/service/response/home_cms_response.e @@ -0,0 +1,35 @@ +note + description: "Summary description for {HOME_CMS_RESPONSE}." + date: "$Date$" + revision: "$Revision$" + +class + HOME_CMS_RESPONSE + +inherit + + CMS_RESPONSE + redefine + custom_prepare + end + +create + make + +feature -- Generation + + custom_prepare (page: CMS_HTML_PAGE) + do + page.register_variable (setup.api_service.recent_nodes (0, 5), "nodes") + end + +feature -- Execution + + process + -- Computed response message. + do + set_title ("Home") + set_page_title (Void) + end +end + diff --git a/cms/src/theme/cms_html_page_response.e b/cms/src/theme/cms_html_page_response.e new file mode 100644 index 0000000..97af15f --- /dev/null +++ b/cms/src/theme/cms_html_page_response.e @@ -0,0 +1,77 @@ +note + description: "Summary description for {CMS_HTML_PAGE_RESPONSE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_HTML_PAGE_RESPONSE + +inherit + WSF_RESPONSE_MESSAGE + +create + make + +feature {NONE} -- Initialization + + make (a_html: like html) + do + html := a_html + status_code := {HTTP_STATUS_CODE}.ok + create header.make + header.put_content_type_text_html + end + +feature -- Status + + status_code: INTEGER + +feature -- Header + + header: HTTP_HEADER + +feature -- Html access + + html: STRING + +feature -- Element change + + set_status_code (c: like status_code) + do + status_code := c + end + +feature {WSF_RESPONSE} -- Output + + send_to (res: WSF_RESPONSE) + local + h: like header + s: STRING_8 + do + h := header + res.set_status_code (status_code) + s := html + + if not h.has_content_length then + h.put_content_length (s.count) + end + if not h.has_content_type then + h.put_content_type_text_html + end + res.put_header_text (h.string) + res.put_string (s) + end + +note + copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end + diff --git a/cms/src/theme/cms_html_template.e b/cms/src/theme/cms_html_template.e new file mode 100644 index 0000000..86a93f5 --- /dev/null +++ b/cms/src/theme/cms_html_template.e @@ -0,0 +1,13 @@ +note + description: "Summary description for {WSF_CMS_HTML_TEMPLATE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_HTML_TEMPLATE + +inherit + CMS_TEMPLATE + +end diff --git a/cms/src/theme/cms_page_template.e b/cms/src/theme/cms_page_template.e new file mode 100644 index 0000000..74c3664 --- /dev/null +++ b/cms/src/theme/cms_page_template.e @@ -0,0 +1,12 @@ +note + description: "Summary description for {CMS_PAGE_TEMPLATE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_PAGE_TEMPLATE + +inherit + CMS_TEMPLATE +end diff --git a/cms/src/theme/cms_template.e b/cms/src/theme/cms_template.e new file mode 100644 index 0000000..58eb0f0 --- /dev/null +++ b/cms/src/theme/cms_template.e @@ -0,0 +1,37 @@ +note + description: "Summary description for {WSF_CMS_PAGE_TEMPLATE}." + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_TEMPLATE + +feature -- Access + + theme: CMS_THEME + deferred + end + + variables: STRING_TABLE [detachable ANY] + deferred + end + + prepare (page: CMS_HTML_PAGE) + deferred + end + + to_html (page: CMS_HTML_PAGE): STRING + deferred + end + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/theme/cms_theme.e b/cms/src/theme/cms_theme.e new file mode 100644 index 0000000..4557a61 --- /dev/null +++ b/cms/src/theme/cms_theme.e @@ -0,0 +1,48 @@ +note + description: "Summary description for {WSF_CMS_THEME}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_THEME + +feature {NONE} -- Access + + setup: CMS_SETUP + +feature -- Access + + name: STRING + deferred + end + + regions: ARRAY [STRING] + deferred + end + + page_template: CMS_TEMPLATE + deferred + end + +feature -- Conversion + + page_html (page: CMS_HTML_PAGE): STRING_8 + -- Render `page' as html. + deferred + end + +feature {NONE} -- Implementation + + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/theme/cms_theme_information.e b/cms/src/theme/cms_theme_information.e new file mode 100644 index 0000000..ee0800a --- /dev/null +++ b/cms/src/theme/cms_theme_information.e @@ -0,0 +1,135 @@ +note + description: "Summary description for {CMS_THEME_INFORMATION}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_THEME_INFORMATION + +create + make, + make_default + +feature {NONE} -- Initialization + + make_default + do + engine := {STRING_32} "default" + create items.make_caseless (1) + create regions.make (5) + across + (<<"header", "content", "footer", "first_sidebar", "second_sidebar">>) as ic + loop + regions.force (ic.item, ic.item) + end + end + + make (fn: PATH) + local + f: PLAIN_TEXT_FILE + s: STRING_8 + h,k: STRING_8 + v: STRING_32 + i: INTEGER + utf: UTF_CONVERTER + l_engine: detachable READABLE_STRING_32 + done: BOOLEAN + do + make_default + create f.make_with_path (fn) + if f.exists and then f.is_access_readable then + f.open_read + from + until + done or f.exhausted or f.end_of_file + loop + f.read_line_thread_aware + s := f.last_string + s.left_adjust + if + s.is_empty + or else s.starts_with_general (";") + or else s.starts_with_general ("#") + or else s.starts_with_general ("--") + then + -- Ignore + else + i := s.index_of ('=', 1) + if i > 0 then + h := s.substring (1, i - 1) + h.left_adjust + h.right_adjust + if h.is_case_insensitive_equal_general ("engine") then + s.remove_head (i) + s.right_adjust + v := utf.utf_8_string_8_to_string_32 (s) + l_engine := v + elseif h.starts_with_general ("regions[") and h[h.count] = ']' then + s.remove_head (i) + s.right_adjust + v := utf.utf_8_string_8_to_string_32 (s) + i := h.index_of ('[', 1) + k := h.substring (i + 1, h.count - 1) + k.left_adjust + k.right_adjust + if k.starts_with_general ("-") then + --| If name is prefixed by "-" + --| remove the related region + --| If name is "-*", clear all regions. + if k.is_case_insensitive_equal_general ("-*") then + regions.wipe_out + else + k.remove_head (1) + regions.remove (k) + end + else + regions.force (v, k) + end + else + s.remove_head (i) + s.right_adjust + v := utf.utf_8_string_8_to_string_32 (s) + end + items.force (v, h) + end + end + end + f.close + end + if l_engine /= Void and then not l_engine.is_empty then + engine := l_engine + end + end + +feature -- Access + + engine: STRING_32 + -- Template engine. + --| Could be: default, smarty, ... + + regions: STRING_TABLE [READABLE_STRING_GENERAL] + -- Regions available in this theme + + item (k: READABLE_STRING_GENERAL): detachable STRING_32 + -- Item associated with name `k' if any. + do + Result := items[k] + end + + items: STRING_TABLE [STRING_32] + -- Items indexed by key name. + +invariant + engine_set: not engine.is_empty + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/theme/default_cms_template.e b/cms/src/theme/default_cms_template.e new file mode 100644 index 0000000..e919318 --- /dev/null +++ b/cms/src/theme/default_cms_template.e @@ -0,0 +1,75 @@ +note + description: "Summary description for {DEFAULT_CMS_TEMPLATE}." + date: "$Date$" + revision: "$Revision$" + +deferred class + DEFAULT_CMS_TEMPLATE + +inherit + CMS_TEMPLATE + +feature {NONE} -- Implementation + + apply_template_engine (s: STRING_8) + local + p,n: INTEGER + k: STRING + sv: detachable STRING + do + from + n := s.count + p := 1 + until + p = 0 + loop + p := s.index_of ('$', p) + if p > 0 then + k := next_identifier (s, p + 1) + s.remove_substring (p, p + k.count) + sv := Void + if attached variables.item (k) as l_value then + + if attached {STRING_8} l_value as s8 then + sv := s8 + elseif attached {STRING_32} l_value as s32 then + sv := s32.as_string_8 -- FIXME: use html encoder + else + sv := l_value.out + end + s.insert_string (sv, p) + p := p + sv.count + else + debug + s.insert_string ("$" + k, p) + end + end + end + end + end + + next_identifier (s: STRING; a_index: INTEGER): STRING + local + i: INTEGER + do + from + i := a_index + until + not (s[i].is_alpha_numeric or s[i] = '_' or s[i] = '.') + loop + i := i + 1 + end + Result := s.substring (a_index, i - 1) + end + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/theme/default_theme/default_cms_html_template.e b/cms/src/theme/default_theme/default_cms_html_template.e new file mode 100644 index 0000000..3667e2b --- /dev/null +++ b/cms/src/theme/default_theme/default_cms_html_template.e @@ -0,0 +1,100 @@ +note + description: "Summary description for {CMS_HTML_TEMPLATE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + DEFAULT_CMS_HTML_TEMPLATE + +inherit + CMS_HTML_TEMPLATE + + DEFAULT_CMS_TEMPLATE + +create + make + +feature {NONE} -- Initialization + + make (t: DEFAULT_CMS_THEME) + do + theme := t + create variables.make (0) + end + + variables: STRING_TABLE [detachable ANY] + +feature -- Access + + register (v: STRING_8; k: STRING_8) + do + variables.force (v, k) + end + + theme: DEFAULT_CMS_THEME + + prepare (page: CMS_HTML_PAGE) + do + variables.make (10) + + across + page.variables as ic + loop + variables.force (ic.item, ic.key) + end + + if attached page.title as l_title then + variables.force (l_title, "title") + variables.force (l_title, "head_title") + else + variables.force ("", "title") + variables.force ("", "head_title") + end + + variables.force (page.language, "language") + variables.force (page.head_lines_to_string, "head_lines") + end + + to_html (page: CMS_HTML_PAGE): STRING + do + -- Process html generation + create Result.make_from_string (template) + apply_template_engine (Result) + end + +feature {NONE} -- Implementation + + template: STRING + once + Result := "[ + + + + $head + $head_title + $styles + $scripts + $head_lines + + + $page_top + $page + $page_bottom + + + ]" + end + + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/theme/default_theme/default_cms_page_template.e b/cms/src/theme/default_theme/default_cms_page_template.e new file mode 100644 index 0000000..0e4ceab --- /dev/null +++ b/cms/src/theme/default_theme/default_cms_page_template.e @@ -0,0 +1,102 @@ +note + description: "Summary description for {CMS_PAGE_TEMPLATE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + DEFAULT_CMS_PAGE_TEMPLATE + +inherit + CMS_PAGE_TEMPLATE + + DEFAULT_CMS_TEMPLATE + +create + make + +feature {NONE} -- Initialization + + make (t: DEFAULT_CMS_THEME) + do + theme := t + create variables.make (0) + end + + variables: STRING_TABLE [detachable ANY] + +feature -- Access + + theme: DEFAULT_CMS_THEME + + prepare (page: CMS_HTML_PAGE) + do + variables.make (10) + + across + page.variables as ic + loop + variables.force (ic.item, ic.key) + end + + if attached page.title as l_title then + variables.force (l_title, "title") + else + variables.force ("", "title") + end + across + theme.regions as r + loop + variables.force (page.region (r.item), r.item) + end + end + + to_html (page: CMS_HTML_PAGE): STRING + do + -- Process html generation + create Result.make_from_string (template) + apply_template_engine (Result) + end + +feature -- Registration + + register (v: STRING_8; k: STRING_8) + do + variables.force (v, k) + end + +feature {NONE} -- Implementation + + template: STRING + once + Result := "[ +
+
+ +
+
+ +
$content
+ +
+
+ +
+
+ ]" + end + + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/theme/default_theme/default_cms_theme.e b/cms/src/theme/default_theme/default_cms_theme.e new file mode 100644 index 0000000..dcf4950 --- /dev/null +++ b/cms/src/theme/default_theme/default_cms_theme.e @@ -0,0 +1,96 @@ +note + description: "Summary description for {CMS_THEME}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + DEFAULT_CMS_THEME + +inherit + CMS_THEME + +create + make + +feature {NONE} -- Initialization + + make (a_setup: like setup; a_info: like information) + do + setup := a_setup + information := a_info + end + + information: CMS_THEME_INFORMATION + +feature -- Access + + name: STRING = "CMS" + + regions: ARRAY [STRING] + once + Result := <<"header", "content", "footer", "first_sidebar", "second_sidebar">> + end + + html_template: DEFAULT_CMS_HTML_TEMPLATE + local + tpl: like internal_html_template + do + tpl := internal_html_template + if tpl = Void then + create tpl.make (Current) + internal_html_template := tpl + end + Result := tpl + end + + page_template: DEFAULT_CMS_PAGE_TEMPLATE + local + tpl: like internal_page_template + do + tpl := internal_page_template + if tpl = Void then + create tpl.make (Current) + internal_page_template := tpl + end + Result := tpl + end + +feature -- Conversion + + prepare (page: CMS_HTML_PAGE) + do +-- page.add_style (url ("/theme/style.css", Void), Void) + end + + page_html (page: CMS_HTML_PAGE): STRING_8 + local + l_content: STRING_8 + do + prepare (page) + page_template.prepare (page) + l_content := page_template.to_html (page) + html_template.prepare (page) + html_template.register (l_content, "page") + Result := html_template.to_html (page) + end + +feature {NONE} -- Internal + + internal_page_template: detachable like page_template + + internal_html_template: detachable like html_template + +invariant + attached internal_page_template as inv_p implies inv_p.theme = Current +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/theme/smarty_theme/smarty_cms_page_template.e b/cms/src/theme/smarty_theme/smarty_cms_page_template.e new file mode 100644 index 0000000..427c186 --- /dev/null +++ b/cms/src/theme/smarty_theme/smarty_cms_page_template.e @@ -0,0 +1,134 @@ +note + description: "Summary description for {CMS_PAGE_TEMPLATE}." + date: "$Date$" + revision: "$Revision$" + +class + SMARTY_CMS_PAGE_TEMPLATE + +inherit + CMS_PAGE_TEMPLATE + + SHARED_TEMPLATE_CONTEXT + +create + make + +feature {NONE} -- Initialization + + make (tpl: READABLE_STRING_GENERAL; t: SMARTY_CMS_THEME) + do + theme := t + create variables.make (0) + template_name := tpl + end + + variables: STRING_TABLE [detachable ANY] + +feature -- Access + + template_name: READABLE_STRING_GENERAL + + theme: SMARTY_CMS_THEME + + prepare (page: CMS_HTML_PAGE) + do + variables.make (10) + + across + page.variables as ic + loop + variables.force (ic.item, ic.key) + end + + if attached page.title as l_title then + variables.force (l_title, "title") + variables.force (l_title, "head_title") + else + variables.force ("", "title") + variables.force ("", "head_title") + end + + variables.force (page.language, "language") + variables.force (page.head_lines_to_string, "head_lines") + + across + theme.regions as r + loop + variables.force (page.region (r.item), r.item) + end + end + + to_html (page: CMS_HTML_PAGE): STRING + local + tpl: detachable TEMPLATE_FILE + ut: FILE_UTILITIES + p: detachable PATH + n: STRING_32 + do + -- Process html generation + template_context.set_template_folder (theme.templates_directory) + template_context.disable_verbose + debug ("smarty") + template_context.enable_verbose + end + p := template_context.template_folder + if p = Void then + create p.make_current + end + if attached page.type as l_page_type then + create n.make_from_string_general (l_page_type) + n.append_character ('-') + n.append_string_general (template_name) + n.append_string_general (".tpl") + if ut.file_path_exists (p.extended (n)) then + create tpl.make_from_file (n) + end + end + if tpl = Void then + create n.make_from_string_general (template_name) + n.append_string_general (".tpl") + if ut.file_path_exists (p.extended (n)) then + create tpl.make_from_file (n) + end + end + if tpl /= Void then + across + variables as ic + loop + tpl.add_value (ic.item, ic.key) + end + + debug ("cms") + template_context.enable_verbose + end + tpl.analyze + tpl.get_output + if attached tpl.output as l_output then + Result := l_output + else + create Result.make_from_string ("ERROR: template issue.") + end + else + create Result.make_from_string ("ERROR: template issue.") + end + end + +feature -- Registration + + register (v: STRING_8; k: STRING_8) + do + variables.force (v, k) + end + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/theme/smarty_theme/smarty_cms_theme.e b/cms/src/theme/smarty_theme/smarty_cms_theme.e new file mode 100644 index 0000000..f315f57 --- /dev/null +++ b/cms/src/theme/smarty_theme/smarty_cms_theme.e @@ -0,0 +1,104 @@ +note + description: "Summary description for {SMARTY_CMS_THEME}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + SMARTY_CMS_THEME + +inherit + CMS_THEME + +create + make + +feature {NONE} -- Initialization + + make (a_setup: like setup; a_info: like information; a_template: like template) + do + setup := a_setup + information := a_info + template := a_template + if attached a_info.item ("template_dir") as s then + templates_directory := a_setup.theme_location.extended (s) + else + templates_directory := a_setup.theme_location + end + ensure + setup_set: setup = a_setup + information_set: information = a_info + template_set: template = a_template + end + +feature -- Access + + name: STRING = "smarty-CMS" + + template: STRING; + + templates_directory: PATH + + information: CMS_THEME_INFORMATION + + regions: ARRAY [STRING] + local + i: INTEGER + utf: UTF_CONVERTER + once + if attached information.regions as tb and then not tb.is_empty then + i := 1 + create Result.make_filled ("", i, i + tb.count - 1) + across + tb as ic + loop + Result.force (utf.utf_32_string_to_utf_8_string_8 (ic.key), i) -- NOTE: UTF-8 encoded ! + i := i + 1 + end + else + Result := <<"header", "content", "footer", "first_sidebar", "second_sidebar">> + end + end + + page_template: SMARTY_CMS_PAGE_TEMPLATE + local + tpl: like internal_page_template + do + tpl := internal_page_template + if tpl = Void then + create tpl.make (template, Current) + internal_page_template := tpl + end + Result := tpl + end + +feature -- Conversion + + prepare (page: CMS_HTML_PAGE) + do + end + + page_html (page: CMS_HTML_PAGE): STRING_8 + do + prepare (page) + page_template.prepare (page) + Result := page_template.to_html (page) + end + +feature {NONE} -- Internal + + internal_page_template: detachable like page_template + +invariant + attached internal_page_template as inv_p implies inv_p.theme = Current +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/examples/Readme.md b/examples/Readme.md new file mode 100644 index 0000000..beadd23 --- /dev/null +++ b/examples/Readme.md @@ -0,0 +1,4 @@ +Examples CMS: examples + +api: API is a simple example showing how to build a custom CMS using EWF. +roc_api: api build using a CMS library using modules, themes, etc. \ No newline at end of file diff --git a/Readme.txt b/examples/api/Readme.txt similarity index 100% rename from Readme.txt rename to examples/api/Readme.txt diff --git a/api/launcher/any/application_launcher.e b/examples/api/launcher/any/application_launcher.e similarity index 100% rename from api/launcher/any/application_launcher.e rename to examples/api/launcher/any/application_launcher.e diff --git a/api/launcher/any/application_launcher_i.e b/examples/api/launcher/any/application_launcher_i.e similarity index 100% rename from api/launcher/any/application_launcher_i.e rename to examples/api/launcher/any/application_launcher_i.e diff --git a/api/launcher/default/application_launcher.e b/examples/api/launcher/default/application_launcher.e similarity index 100% rename from api/launcher/default/application_launcher.e rename to examples/api/launcher/default/application_launcher.e diff --git a/api/launcher/default/application_launcher_i.e b/examples/api/launcher/default/application_launcher_i.e similarity index 100% rename from api/launcher/default/application_launcher_i.e rename to examples/api/launcher/default/application_launcher_i.e diff --git a/api/roc.ini b/examples/api/roc.ini similarity index 56% rename from api/roc.ini rename to examples/api/roc.ini index 70c3255..17eb967 100644 --- a/api/roc.ini +++ b/examples/api/roc.ini @@ -1,2 +1,2 @@ -port=7070 +port=8088 #verbose=true \ No newline at end of file diff --git a/api/roc_api.ecf b/examples/api/roc_api.ecf similarity index 90% rename from api/roc_api.ecf rename to examples/api/roc_api.ecf index ab56bd2..adf512d 100644 --- a/api/roc_api.ecf +++ b/examples/api/roc_api.ecf @@ -1,5 +1,5 @@  - + /EIFGENs$ @@ -16,12 +16,12 @@ - + - + @@ -29,7 +29,7 @@ - + @@ -37,19 +37,19 @@ - + - + - + @@ -60,7 +60,7 @@ - - + diff --git a/examples/api/site/config/application_configuration.json b/examples/api/site/config/application_configuration.json new file mode 100644 index 0000000..793f8ef --- /dev/null +++ b/examples/api/site/config/application_configuration.json @@ -0,0 +1,32 @@ +{ + "database": { + "datasource": { + "driver": "MySQL", + "environment": "development" + }, + "environments": { + "test": { + "connection_string":"Server=localhost;Port=3306;Database=cms_dev;Uid=root;Pwd=;" + }, + "development": { + "connection_string":"Server=localhost;Port=3306;Database=cms_dev;Uid=root;Pwd=;" + }, + "production": { + "connection_string":"" + } + } + }, + "smtp": { + "server": "localhost" + }, + "logger": { + "level":"debug", + "backup_count":"4" + }, + "server": { + "mode":"html" + } + +} + + \ No newline at end of file diff --git a/api/site/www/static/css/bootstrap.css b/examples/api/site/www/static/css/bootstrap.css similarity index 100% rename from api/site/www/static/css/bootstrap.css rename to examples/api/site/www/static/css/bootstrap.css diff --git a/api/site/www/static/css/dashboard.css b/examples/api/site/www/static/css/dashboard.css similarity index 100% rename from api/site/www/static/css/dashboard.css rename to examples/api/site/www/static/css/dashboard.css diff --git a/api/site/www/static/images/ajax-loader.gif b/examples/api/site/www/static/images/ajax-loader.gif similarity index 100% rename from api/site/www/static/images/ajax-loader.gif rename to examples/api/site/www/static/images/ajax-loader.gif diff --git a/api/site/www/static/images/favicon.ico b/examples/api/site/www/static/images/favicon.ico similarity index 100% rename from api/site/www/static/images/favicon.ico rename to examples/api/site/www/static/images/favicon.ico diff --git a/api/site/www/static/js/roc.js b/examples/api/site/www/static/js/roc.js similarity index 100% rename from api/site/www/static/js/roc.js rename to examples/api/site/www/static/js/roc.js diff --git a/api/site/www/template/html/layout2.tpl b/examples/api/site/www/template/html/layout2.tpl similarity index 100% rename from api/site/www/template/html/layout2.tpl rename to examples/api/site/www/template/html/layout2.tpl diff --git a/api/site/www/template/html/master2/content.tpl b/examples/api/site/www/template/html/master2/content.tpl similarity index 100% rename from api/site/www/template/html/master2/content.tpl rename to examples/api/site/www/template/html/master2/content.tpl diff --git a/api/site/www/template/html/master2/error.tpl b/examples/api/site/www/template/html/master2/error.tpl similarity index 100% rename from api/site/www/template/html/master2/error.tpl rename to examples/api/site/www/template/html/master2/error.tpl diff --git a/api/site/www/template/html/master2/footer.tpl b/examples/api/site/www/template/html/master2/footer.tpl similarity index 100% rename from api/site/www/template/html/master2/footer.tpl rename to examples/api/site/www/template/html/master2/footer.tpl diff --git a/api/site/www/template/html/master2/head.tpl b/examples/api/site/www/template/html/master2/head.tpl similarity index 100% rename from api/site/www/template/html/master2/head.tpl rename to examples/api/site/www/template/html/master2/head.tpl diff --git a/api/site/www/template/html/master2/header.tpl b/examples/api/site/www/template/html/master2/header.tpl similarity index 100% rename from api/site/www/template/html/master2/header.tpl rename to examples/api/site/www/template/html/master2/header.tpl diff --git a/api/site/www/template/html/master2/logoff.tpl b/examples/api/site/www/template/html/master2/logoff.tpl similarity index 100% rename from api/site/www/template/html/master2/logoff.tpl rename to examples/api/site/www/template/html/master2/logoff.tpl diff --git a/api/site/www/template/html/master2/main_navigation.tpl b/examples/api/site/www/template/html/master2/main_navigation.tpl similarity index 100% rename from api/site/www/template/html/master2/main_navigation.tpl rename to examples/api/site/www/template/html/master2/main_navigation.tpl diff --git a/api/site/www/template/html/master2/optional_enhancement_js.tpl b/examples/api/site/www/template/html/master2/optional_enhancement_js.tpl similarity index 100% rename from api/site/www/template/html/master2/optional_enhancement_js.tpl rename to examples/api/site/www/template/html/master2/optional_enhancement_js.tpl diff --git a/api/site/www/template/html/master2/optional_styling_css.tpl b/examples/api/site/www/template/html/master2/optional_styling_css.tpl similarity index 100% rename from api/site/www/template/html/master2/optional_styling_css.tpl rename to examples/api/site/www/template/html/master2/optional_styling_css.tpl diff --git a/api/site/www/template/html/master2/site_navigation.tpl b/examples/api/site/www/template/html/master2/site_navigation.tpl similarity index 100% rename from api/site/www/template/html/master2/site_navigation.tpl rename to examples/api/site/www/template/html/master2/site_navigation.tpl diff --git a/api/site/www/template/html/modules/navigation.tpl b/examples/api/site/www/template/html/modules/navigation.tpl similarity index 100% rename from api/site/www/template/html/modules/navigation.tpl rename to examples/api/site/www/template/html/modules/navigation.tpl diff --git a/api/site/www/template/html/modules/node.tpl b/examples/api/site/www/template/html/modules/node.tpl similarity index 100% rename from api/site/www/template/html/modules/node.tpl rename to examples/api/site/www/template/html/modules/node.tpl diff --git a/api/site/www/template/html/modules/node_content.tpl b/examples/api/site/www/template/html/modules/node_content.tpl similarity index 100% rename from api/site/www/template/html/modules/node_content.tpl rename to examples/api/site/www/template/html/modules/node_content.tpl diff --git a/api/site/www/template/html/modules/node_summary.tpl b/examples/api/site/www/template/html/modules/node_summary.tpl similarity index 100% rename from api/site/www/template/html/modules/node_summary.tpl rename to examples/api/site/www/template/html/modules/node_summary.tpl diff --git a/api/site/www/template/html/modules/node_title.tpl b/examples/api/site/www/template/html/modules/node_title.tpl similarity index 100% rename from api/site/www/template/html/modules/node_title.tpl rename to examples/api/site/www/template/html/modules/node_title.tpl diff --git a/api/site/www/template/html/modules/nodes.tpl b/examples/api/site/www/template/html/modules/nodes.tpl similarity index 100% rename from api/site/www/template/html/modules/nodes.tpl rename to examples/api/site/www/template/html/modules/nodes.tpl diff --git a/api/site/www/template/html/modules/register.tpl b/examples/api/site/www/template/html/modules/register.tpl similarity index 100% rename from api/site/www/template/html/modules/register.tpl rename to examples/api/site/www/template/html/modules/register.tpl diff --git a/api/src/configuration/application_constants.e b/examples/api/src/configuration/application_constants.e similarity index 100% rename from api/src/configuration/application_constants.e rename to examples/api/src/configuration/application_constants.e diff --git a/api/src/configuration/configuration_factory.e b/examples/api/src/configuration/configuration_factory.e similarity index 100% rename from api/src/configuration/configuration_factory.e rename to examples/api/src/configuration/configuration_factory.e diff --git a/api/src/configuration/roc_json_configuration.e b/examples/api/src/configuration/roc_json_configuration.e similarity index 100% rename from api/src/configuration/roc_json_configuration.e rename to examples/api/src/configuration/roc_json_configuration.e diff --git a/api/src/representation/common/template_shared.e b/examples/api/src/representation/common/template_shared.e similarity index 100% rename from api/src/representation/common/template_shared.e rename to examples/api/src/representation/common/template_shared.e diff --git a/api/src/representation/roc_response.e b/examples/api/src/representation/roc_response.e similarity index 100% rename from api/src/representation/roc_response.e rename to examples/api/src/representation/roc_response.e diff --git a/api/src/representation/roc_template_page.e b/examples/api/src/representation/roc_template_page.e similarity index 100% rename from api/src/representation/roc_template_page.e rename to examples/api/src/representation/roc_template_page.e diff --git a/api/src/roc_abstract_api.e b/examples/api/src/roc_abstract_api.e similarity index 100% rename from api/src/roc_abstract_api.e rename to examples/api/src/roc_abstract_api.e diff --git a/api/src/roc_config.e b/examples/api/src/roc_config.e similarity index 100% rename from api/src/roc_config.e rename to examples/api/src/roc_config.e diff --git a/api/src/roc_server.e b/examples/api/src/roc_server.e similarity index 100% rename from api/src/roc_server.e rename to examples/api/src/roc_server.e diff --git a/api/src/service/filter/authentication_filter.e b/examples/api/src/service/filter/authentication_filter.e similarity index 100% rename from api/src/service/filter/authentication_filter.e rename to examples/api/src/service/filter/authentication_filter.e diff --git a/api/src/service/filter/cors_filter.e b/examples/api/src/service/filter/cors_filter.e similarity index 100% rename from api/src/service/filter/cors_filter.e rename to examples/api/src/service/filter/cors_filter.e diff --git a/api/src/service/filter/error_filter.e b/examples/api/src/service/filter/error_filter.e similarity index 100% rename from api/src/service/filter/error_filter.e rename to examples/api/src/service/filter/error_filter.e diff --git a/api/src/service/filter/logger_filter.e b/examples/api/src/service/filter/logger_filter.e similarity index 100% rename from api/src/service/filter/logger_filter.e rename to examples/api/src/service/filter/logger_filter.e diff --git a/api/src/service/handler/app_abstract_handler.e b/examples/api/src/service/handler/app_abstract_handler.e similarity index 100% rename from api/src/service/handler/app_abstract_handler.e rename to examples/api/src/service/handler/app_abstract_handler.e diff --git a/api/src/service/handler/app_handler.e b/examples/api/src/service/handler/app_handler.e similarity index 100% rename from api/src/service/handler/app_handler.e rename to examples/api/src/service/handler/app_handler.e diff --git a/api/src/service/handler/navigation_handler.e b/examples/api/src/service/handler/navigation_handler.e similarity index 100% rename from api/src/service/handler/navigation_handler.e rename to examples/api/src/service/handler/navigation_handler.e diff --git a/api/src/service/handler/node_content_handler.e b/examples/api/src/service/handler/node_content_handler.e similarity index 100% rename from api/src/service/handler/node_content_handler.e rename to examples/api/src/service/handler/node_content_handler.e diff --git a/api/src/service/handler/node_handler.e b/examples/api/src/service/handler/node_handler.e similarity index 100% rename from api/src/service/handler/node_handler.e rename to examples/api/src/service/handler/node_handler.e diff --git a/api/src/service/handler/node_summary_handler.e b/examples/api/src/service/handler/node_summary_handler.e similarity index 100% rename from api/src/service/handler/node_summary_handler.e rename to examples/api/src/service/handler/node_summary_handler.e diff --git a/api/src/service/handler/node_title_handler.e b/examples/api/src/service/handler/node_title_handler.e similarity index 100% rename from api/src/service/handler/node_title_handler.e rename to examples/api/src/service/handler/node_title_handler.e diff --git a/api/src/service/handler/nodes_handler.e b/examples/api/src/service/handler/nodes_handler.e similarity index 100% rename from api/src/service/handler/nodes_handler.e rename to examples/api/src/service/handler/nodes_handler.e diff --git a/api/src/service/handler/roc_login_handler.e b/examples/api/src/service/handler/roc_login_handler.e similarity index 100% rename from api/src/service/handler/roc_login_handler.e rename to examples/api/src/service/handler/roc_login_handler.e diff --git a/api/src/service/handler/roc_logoff_handler.e b/examples/api/src/service/handler/roc_logoff_handler.e similarity index 100% rename from api/src/service/handler/roc_logoff_handler.e rename to examples/api/src/service/handler/roc_logoff_handler.e diff --git a/api/src/service/handler/roc_root_handler.e b/examples/api/src/service/handler/roc_root_handler.e similarity index 100% rename from api/src/service/handler/roc_root_handler.e rename to examples/api/src/service/handler/roc_root_handler.e diff --git a/api/src/service/handler/user_handler.e b/examples/api/src/service/handler/user_handler.e similarity index 100% rename from api/src/service/handler/user_handler.e rename to examples/api/src/service/handler/user_handler.e diff --git a/api/src/service/roc_api_service.e b/examples/api/src/service/roc_api_service.e similarity index 100% rename from api/src/service/roc_api_service.e rename to examples/api/src/service/roc_api_service.e diff --git a/api/src/service/roc_email_service.e b/examples/api/src/service/roc_email_service.e similarity index 100% rename from api/src/service/roc_email_service.e rename to examples/api/src/service/roc_email_service.e diff --git a/api/src/service/roc_rest_api.e b/examples/api/src/service/roc_rest_api.e similarity index 100% rename from api/src/service/roc_rest_api.e rename to examples/api/src/service/roc_rest_api.e diff --git a/api/src/service/shared_conneg_helper.e b/examples/api/src/service/shared_conneg_helper.e similarity index 100% rename from api/src/service/shared_conneg_helper.e rename to examples/api/src/service/shared_conneg_helper.e diff --git a/api/src/validator/roc_input_validator.e b/examples/api/src/validator/roc_input_validator.e similarity index 100% rename from api/src/validator/roc_input_validator.e rename to examples/api/src/validator/roc_input_validator.e diff --git a/examples/roc_api/Readme.md b/examples/roc_api/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/examples/roc_api/launcher/any/application_launcher.e b/examples/roc_api/launcher/any/application_launcher.e new file mode 100644 index 0000000..0ef505c --- /dev/null +++ b/examples/roc_api/launcher/any/application_launcher.e @@ -0,0 +1,19 @@ +note + description: "[ + Effective class for APPLICATION_LAUNCHER_I + + You can put modification in this class + ]" + date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $" + revision: "$Revision: 36 $" + +class + APPLICATION_LAUNCHER + +inherit + APPLICATION_LAUNCHER_I + +feature -- Custom + +end + diff --git a/examples/roc_api/launcher/any/application_launcher_i.e b/examples/roc_api/launcher/any/application_launcher_i.e new file mode 100644 index 0000000..0744c80 --- /dev/null +++ b/examples/roc_api/launcher/any/application_launcher_i.e @@ -0,0 +1,102 @@ +note + description: "[ + Specific application launcher + + DO NOT EDIT THIS CLASS + + you can customize APPLICATION_LAUNCHER + ]" + date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $" + revision: "$Revision: 36 $" + +deferred class + APPLICATION_LAUNCHER_I + +inherit + SHARED_EXECUTION_ENVIRONMENT + +feature -- Execution + + launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + nature: like launcher_nature + do + nature := launcher_nature + if nature = Void or else nature = nature_nino then + launch_nino (a_service, opts) + elseif nature = nature_cgi then + launch_cgi (a_service, opts) + elseif nature = nature_libfcgi then + launch_libfcgi (a_service, opts) + else + -- bye bye + (create {EXCEPTIONS}).die (-1) + end + end + +feature {NONE} -- Access + + launcher_nature: detachable READABLE_STRING_8 + -- Initialize the launcher nature + -- either cgi, libfcgi, or nino. + --| We could extend with more connector if needed. + --| and we could use WSF_DEFAULT_SERVICE_LAUNCHER to configure this at compilation time. + local + p: PATH + l_entry_name: READABLE_STRING_32 + ext: detachable READABLE_STRING_32 + do + create p.make_from_string (execution_environment.arguments.command_name) + if attached p.entry as l_entry then + ext := l_entry.extension + end + if ext /= Void then + if ext.same_string (nature_nino) then + Result := nature_nino + end + if ext.same_string (nature_cgi) then + Result := nature_cgi + end + if ext.same_string (nature_libfcgi) or else ext.same_string ("fcgi") then + Result := nature_libfcgi + end + end + end + +feature {NONE} -- nino + + nature_nino: STRING = "nino" + + launch_nino (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + launcher: WSF_NINO_SERVICE_LAUNCHER + do + create launcher.make_and_launch (a_service, opts) + end + +feature {NONE} -- cgi + + nature_cgi: STRING = "cgi" + + launch_cgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + launcher: WSF_CGI_SERVICE_LAUNCHER + do + create launcher.make_and_launch (a_service, opts) + end + +feature {NONE} -- libfcgi + + nature_libfcgi: STRING = "libfcgi" + + launch_libfcgi (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + launcher: WSF_LIBFCGI_SERVICE_LAUNCHER + do + create launcher.make_and_launch (a_service, opts) + end + + +end + + diff --git a/examples/roc_api/launcher/default/application_launcher.e b/examples/roc_api/launcher/default/application_launcher.e new file mode 100644 index 0000000..0ef505c --- /dev/null +++ b/examples/roc_api/launcher/default/application_launcher.e @@ -0,0 +1,19 @@ +note + description: "[ + Effective class for APPLICATION_LAUNCHER_I + + You can put modification in this class + ]" + date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $" + revision: "$Revision: 36 $" + +class + APPLICATION_LAUNCHER + +inherit + APPLICATION_LAUNCHER_I + +feature -- Custom + +end + diff --git a/examples/roc_api/launcher/default/application_launcher_i.e b/examples/roc_api/launcher/default/application_launcher_i.e new file mode 100644 index 0000000..2cd4a73 --- /dev/null +++ b/examples/roc_api/launcher/default/application_launcher_i.e @@ -0,0 +1,26 @@ +note + description: "[ + Specific application launcher + + DO NOT EDIT THIS CLASS + + you can customize APPLICATION_LAUNCHER + ]" + date: "$Date: 2013-06-12 13:55:42 +0200 (mer., 12 juin 2013) $" + revision: "$Revision: 36 $" + +deferred class + APPLICATION_LAUNCHER_I + +feature -- Execution + + launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + launcher: WSF_SERVICE_LAUNCHER + do + create {WSF_DEFAULT_SERVICE_LAUNCHER} launcher.make_and_launch (a_service, opts) + end + +end + + diff --git a/examples/roc_api/roc.ini b/examples/roc_api/roc.ini new file mode 100644 index 0000000..17eb967 --- /dev/null +++ b/examples/roc_api/roc.ini @@ -0,0 +1,2 @@ +port=8088 +#verbose=true \ No newline at end of file diff --git a/examples/roc_api/roc_api.ecf b/examples/roc_api/roc_api.ecf new file mode 100644 index 0000000..68833eb --- /dev/null +++ b/examples/roc_api/roc_api.ecf @@ -0,0 +1,50 @@ + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/site/config/application_configuration.json b/examples/roc_api/site/config/application_configuration.json similarity index 96% rename from api/site/config/application_configuration.json rename to examples/roc_api/site/config/application_configuration.json index 7a93a9a..d493ed8 100644 --- a/api/site/config/application_configuration.json +++ b/examples/roc_api/site/config/application_configuration.json @@ -9,7 +9,7 @@ "connection_string":"Server=localhost;Port=3306;Database=cms_dev;Uid=root;Pwd=;" }, "development": { - "connection_string":"Server=localhost;Port=3306;Database=cms;Uid=root;Pwd=;" + "connection_string":"Server=localhost;Port=3306;Database=cms_dev;Uid=root;Pwd=;" }, "production": { "connection_string":"" diff --git a/examples/roc_api/site/config/cms.ini b/examples/roc_api/site/config/cms.ini new file mode 100644 index 0000000..27ebf1f --- /dev/null +++ b/examples/roc_api/site/config/cms.ini @@ -0,0 +1,6 @@ +engine=smarty +site.name=EWF Web CMS +site.email=your@email.com +var-dir=var +files-dir=files +theme=api diff --git a/examples/roc_api/site/www/static/css/bootstrap.css b/examples/roc_api/site/www/static/css/bootstrap.css new file mode 100644 index 0000000..dede210 --- /dev/null +++ b/examples/roc_api/site/www/static/css/bootstrap.css @@ -0,0 +1,5849 @@ +/*! + * Bootstrap v3.1.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! normalize.css v3.0.0 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + select { + background: #fff !important; + } + .navbar { + display: none; + } + .table td, + .table th { + background-color: #fff !important; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 62.5%; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.428571429; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #428bca; + text-decoration: none; +} +a:hover, +a:focus { + color: #2a6496; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.428571429; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #999; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 200; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +cite { + font-style: normal; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-muted { + color: #999; +} +.text-primary { + color: #428bca; +} +a.text-primary:hover { + color: #3071a9; +} +.text-success { + color: #3c763d; +} +a.text-success:hover { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #428bca; +} +a.bg-primary:hover { + background-color: #3071a9; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +.list-inline > li:first-child { + padding-left: 0; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.428571429; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.428571429; + color: #999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +blockquote:before, +blockquote:after { + content: ""; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.428571429; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + white-space: nowrap; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.428571429; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + min-width: 100%; +} +.col-xs-11 { + min-width: 91.66666666666666%; +} +.col-xs-10 { + min-width: 83.33333333333334%; +} +.col-xs-9 { + min-width: 75%; +} +.col-xs-8 { + min-width: 66.66666666666666%; +} +.col-xs-7 { + min-width: 58.333333333333336%; +} +.col-xs-6 { + min-width: 50%; +} +.col-xs-5 { + min-width: 41.66666666666667%; +} +.col-xs-4 { + min-width: 33.33333333333333%; +} +.col-xs-3 { + min-width: 25%; +} +.col-xs-2 { + min-width: 16.666666666666664%; +} +.col-xs-1 { + min-width: 8.333333333333332%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666666666666%; +} +.col-xs-pull-10 { + right: 83.33333333333334%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666666666666%; +} +.col-xs-pull-7 { + right: 58.333333333333336%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666666666667%; +} +.col-xs-pull-4 { + right: 33.33333333333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.666666666666664%; +} +.col-xs-pull-1 { + right: 8.333333333333332%; +} +.col-xs-pull-0 { + right: 0; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666666666666%; +} +.col-xs-push-10 { + left: 83.33333333333334%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666666666666%; +} +.col-xs-push-7 { + left: 58.333333333333336%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666666666667%; +} +.col-xs-push-4 { + left: 33.33333333333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.666666666666664%; +} +.col-xs-push-1 { + left: 8.333333333333332%; +} +.col-xs-push-0 { + left: 0; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666666666666%; +} +.col-xs-offset-10 { + margin-left: 83.33333333333334%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666666666666%; +} +.col-xs-offset-7 { + margin-left: 58.333333333333336%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666666666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.666666666666664%; +} +.col-xs-offset-1 { + margin-left: 8.333333333333332%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + min-width: 100%; + } + .col-sm-11 { + min-width: 91.66666666666666%; + } + .col-sm-10 { + min-width: 83.33333333333334%; + } + .col-sm-9 { + min-width: 75%; + } + .col-sm-8 { + min-width: 66.66666666666666%; + } + .col-sm-7 { + min-width: 58.333333333333336%; + } + .col-sm-6 { + min-width: 50%; + } + .col-sm-5 { + min-width: 41.66666666666667%; + } + .col-sm-4 { + min-width: 33.33333333333333%; + } + .col-sm-3 { + min-width: 25%; + } + .col-sm-2 { + min-width: 16.666666666666664%; + } + .col-sm-1 { + min-width: 8.333333333333332%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666666666666%; + } + .col-sm-pull-10 { + right: 83.33333333333334%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666666666666%; + } + .col-sm-pull-7 { + right: 58.333333333333336%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666666666667%; + } + .col-sm-pull-4 { + right: 33.33333333333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.666666666666664%; + } + .col-sm-pull-1 { + right: 8.333333333333332%; + } + .col-sm-pull-0 { + right: 0; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666666666666%; + } + .col-sm-push-10 { + left: 83.33333333333334%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666666666666%; + } + .col-sm-push-7 { + left: 58.333333333333336%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666666666667%; + } + .col-sm-push-4 { + left: 33.33333333333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.666666666666664%; + } + .col-sm-push-1 { + left: 8.333333333333332%; + } + .col-sm-push-0 { + left: 0; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666666666666%; + } + .col-sm-offset-10 { + margin-left: 83.33333333333334%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666666666666%; + } + .col-sm-offset-7 { + margin-left: 58.333333333333336%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666666666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.666666666666664%; + } + .col-sm-offset-1 { + margin-left: 8.333333333333332%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + min-width: 100%; + } + .col-md-11 { + min-width: 91.66666666666666%; + } + .col-md-10 { + min-width: 83.33333333333334%; + } + .col-md-9 { + min-width: 75%; + } + .col-md-8 { + min-width: 66.66666666666666%; + } + .col-md-7 { + min-width: 58.333333333333336%; + } + .col-md-6 { + min-width: 50%; + } + .col-md-5 { + min-width: 41.66666666666667%; + } + .col-md-4 { + min-width: 33.33333333333333%; + } + .col-md-3 { + min-width: 25%; + } + .col-md-2 { + min-width: 16.666666666666664%; + } + .col-md-1 { + min-width: 8.333333333333332%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666666666666%; + } + .col-md-pull-10 { + right: 83.33333333333334%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666666666666%; + } + .col-md-pull-7 { + right: 58.333333333333336%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666666666667%; + } + .col-md-pull-4 { + right: 33.33333333333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.666666666666664%; + } + .col-md-pull-1 { + right: 8.333333333333332%; + } + .col-md-pull-0 { + right: 0; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666666666666%; + } + .col-md-push-10 { + left: 83.33333333333334%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666666666666%; + } + .col-md-push-7 { + left: 58.333333333333336%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666666666667%; + } + .col-md-push-4 { + left: 33.33333333333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.666666666666664%; + } + .col-md-push-1 { + left: 8.333333333333332%; + } + .col-md-push-0 { + left: 0; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666666666666%; + } + .col-md-offset-10 { + margin-left: 83.33333333333334%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666666666666%; + } + .col-md-offset-7 { + margin-left: 58.333333333333336%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666666666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.666666666666664%; + } + .col-md-offset-1 { + margin-left: 8.333333333333332%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + min-width: 100%; + } + .col-lg-11 { + min-width: 91.66666666666666%; + } + .col-lg-10 { + min-width: 83.33333333333334%; + } + .col-lg-9 { + min-width: 75%; + } + .col-lg-8 { + min-width: 66.66666666666666%; + } + .col-lg-7 { + min-width: 58.333333333333336%; + } + .col-lg-6 { + min-width: 50%; + } + .col-lg-5 { + min-width: 41.66666666666667%; + } + .col-lg-4 { + min-width: 33.33333333333333%; + } + .col-lg-3 { + min-width: 25%; + } + .col-lg-2 { + min-width: 16.666666666666664%; + } + .col-lg-1 { + min-width: 8.333333333333332%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666666666666%; + } + .col-lg-pull-10 { + right: 83.33333333333334%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666666666666%; + } + .col-lg-pull-7 { + right: 58.333333333333336%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666666666667%; + } + .col-lg-pull-4 { + right: 33.33333333333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.666666666666664%; + } + .col-lg-pull-1 { + right: 8.333333333333332%; + } + .col-lg-pull-0 { + right: 0; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666666666666%; + } + .col-lg-push-10 { + left: 83.33333333333334%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666666666666%; + } + .col-lg-push-7 { + left: 58.333333333333336%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666666666667%; + } + .col-lg-push-4 { + left: 33.33333333333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.666666666666664%; + } + .col-lg-push-1 { + left: 8.333333333333332%; + } + .col-lg-push-0 { + left: 0; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666666666666%; + } + .col-lg-offset-10 { + margin-left: 83.33333333333334%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666666666666%; + } + .col-lg-offset-7 { + margin-left: 58.333333333333336%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666666666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.666666666666664%; + } + .col-lg-offset-1 { + margin-left: 8.333333333333332%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + max-width: 100%; + background-color: transparent; +} +th { + text-align: left; +} +.table { + width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.428571429; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover > td, +.table-hover > tbody > tr:hover > th { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +@media (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-x: scroll; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + /* IE8-9 */ + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.428571429; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.428571429; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control:-moz-placeholder { + color: #999; +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eee; + opacity: 1; +} +textarea.form-control { + height: auto; +} +input[type="date"] { + line-height: 34px; +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + display: block; + min-height: 20px; + padding-left: 20px; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + display: inline; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + float: left; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +.radio[disabled], +.radio-inline[disabled], +.checkbox[disabled], +.checkbox-inline[disabled], +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"], +fieldset[disabled] .radio, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.has-feedback .form-control-feedback { + position: absolute; + top: 25px; + right: 0; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.form-control-static { + margin-bottom: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .control-label, +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +.form-horizontal .form-control-static { + padding-top: 7px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + top: 0; + right: 15px; +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.428571429; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + color: #333; + background-color: #ebebeb; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #428bca; + border-color: #357ebd; +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + color: #fff; + background-color: #3276b1; + border-color: #285e8e; +} +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #428bca; + border-color: #357ebd; +} +.btn-primary .badge { + color: #428bca; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + color: #fff; + background-color: #47a447; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + color: #fff; + background-color: #39b3d7; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ed9c28; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + color: #fff; + background-color: #d2322d; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #428bca; + cursor: pointer; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #2a6496; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #999; + text-decoration: none; +} +.btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height .35s ease; + transition: height .35s ease; +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\2a"; +} +.glyphicon-plus:before { + content: "\2b"; +} +.glyphicon-euro:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.428571429; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #428bca; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.428571429; + color: #999; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px solid; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus { + outline: none; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child > .btn:last-child, +.btn-group > .btn-group:first-child > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +[data-toggle="buttons"] > .btn > input[type="radio"], +[data-toggle="buttons"] > .btn > input[type="checkbox"] { + display: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #428bca; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.428571429; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #428bca; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + max-height: 340px; + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 20px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: none; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #999; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #999; +} +.navbar-inverse .navbar-nav > li > a { + color: #999; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #999; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #999; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #999; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.428571429; + color: #428bca; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: #2a6496; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #428bca; + border-color: #428bca; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +.label[href]:hover, +.label[href]:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #999; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #808080; +} +.label-primary { + background-color: #428bca; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #3071a9; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #999; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #428bca; + background-color: #fff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.container .jumbotron { + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.428571429; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + display: block; + max-width: 100%; + height: auto; + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #428bca; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable { + padding-right: 35px; +} +.alert-dismissable .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #428bca; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-size: 40px 40px; +} +.progress.active .progress-bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media-object { + display: block; +} +.media-heading { + margin: 0 0 5px; +} +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +a.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +a.list-group-item:focus { + text-decoration: none; + background-color: #f5f5f5; +} +a.list-group-item.active, +a.list-group-item.active:hover, +a.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #428bca; + border-color: #428bca; +} +a.list-group-item.active .list-group-item-heading, +a.list-group-item.active:hover .list-group-item-heading, +a.list-group-item.active:focus .list-group-item-heading { + color: inherit; +} +a.list-group-item.active .list-group-item-text, +a.list-group-item.active:hover .list-group-item-text, +a.list-group-item.active:focus .list-group-item-text { + color: #e1edf7; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +a.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +a.list-group-item-success.active:hover, +a.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +a.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +a.list-group-item-info.active:hover, +a.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +a.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +a.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group .list-group-item:first-child { + border-top: 0; +} +.panel > .list-group .list-group-item:last-child { + border-bottom: 0; +} +.panel > .list-group:first-child .list-group-item:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table { + margin-bottom: 0; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th, +.panel > .table-bordered > tfoot > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:first-child > th, +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > tfoot > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:first-child > td { + border-top: 0; +} +.panel > .table-bordered > thead > tr:last-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:last-child > th, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-bordered > thead > tr:last-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + overflow: hidden; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse .panel-body { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #428bca; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #428bca; + border-color: #428bca; +} +.panel-primary > .panel-heading + .panel-collapse .panel-body { + border-top-color: #428bca; +} +.panel-primary > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #428bca; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #ebccd1; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: auto; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -moz-transition: -moz-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + min-height: 16.428571429px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.428571429; +} +.modal-body { + position: relative; + padding: 20px; +} +.modal-footer { + padding: 19px 20px 20px; + margin-top: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1030; + display: block; + font-size: 12px; + line-height: 1.4; + visibility: visible; + filter: alpha(opacity=0); + opacity: 0; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + text-decoration: none; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + left: 5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + right: 5px; + bottom: 0; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + left: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + right: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover .arrow { + border-width: 11px; +} +.popover .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; + line-height: 1; +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .5) 0%), color-stop(rgba(0, 0, 0, .0001) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .0001) 0%), color-stop(rgba(0, 0, 0, .5) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: none; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + margin-left: -10px; + font-family: serif; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicons-chevron-left, + .carousel-control .glyphicons-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + margin-left: -15px; + font-size: 30px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +tr.visible-xs, +th.visible-xs, +td.visible-xs { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +.visible-sm, +tr.visible-sm, +th.visible-sm, +td.visible-sm { + display: none !important; +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +.visible-md, +tr.visible-md, +th.visible-md, +td.visible-md { + display: none !important; +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +.visible-lg, +tr.visible-lg, +th.visible-lg, +td.visible-lg { + display: none !important; +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .hidden-xs, + tr.hidden-xs, + th.hidden-xs, + td.hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm, + tr.hidden-sm, + th.hidden-sm, + td.hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md, + tr.hidden-md, + th.hidden-md, + td.hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg, + tr.hidden-lg, + th.hidden-lg, + td.hidden-lg { + display: none !important; + } +} +.visible-print, +tr.visible-print, +th.visible-print, +td.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +@media print { + .hidden-print, + tr.hidden-print, + th.hidden-print, + td.hidden-print { + display: none !important; + } +} + +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.glyphicon-home:before { + content: "\e021"; +} + +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/examples/roc_api/site/www/static/css/dashboard.css b/examples/roc_api/site/www/static/css/dashboard.css new file mode 100644 index 0000000..a126fc3 --- /dev/null +++ b/examples/roc_api/site/www/static/css/dashboard.css @@ -0,0 +1,357 @@ +/* + * Base structure + */ + +/* Move down content because we have a fixed navbar that is 36px tall on small screen */ +body { + padding-top: 40px; +} +/* On large screen, we give it more space and the navbar is 30px tall. */ +@media (min-width: 768px) { + body { + padding-top: 45px; + } +} + + +/* + * Global add-ons + */ + +h1 { + margin-top: initial; + margin-bottom: 5px; +} + +h2.sub-header{ + margin-top: 1px; + margin-bottom: 1px; + border-bottom: 1px solid #eee; +} + + +.container .jumbotron { + padding: 10px; + text-align: center; +} + + +/* + * Sidebar + */ + +/* Hide for mobile, show later */ +.sidebar { + display: none; +} +@media (min-width: 768px) { + .sidebar { + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: 1000; + display: block; + padding: 70px 20px 20px; + background-color: #f5f5f5; + border-right: 1px solid #eee; + } +} + +/* Sidebar navigation */ +.nav-sidebar { + margin-left: -20px; + margin-right: -21px; /* 20px padding + 1px border */ + margin-bottom: 20px; +} +.nav-sidebar > li > a { + padding-left: 20px; + padding-right: 20px; +} +.nav-sidebar > .active > a { + color: #fff; + background-color: #428bca; +} + + +/* + * Main content + */ + +.main { + padding: 3px; +} +@media (min-width: 768px) { + .main { + padding-left: 15px; + padding-right: 15px; + } +} +.main .page-header { + margin-top: 0; +} + + +/* + * Placeholder dashboard ideas + */ + +.placeholders { + margin-bottom: 30px; + text-align: center; +} +.placeholders h4 { + margin-bottom: 0; +} +.placeholder { + margin-bottom: 20px; +} +.placeholder img { + border-radius: 50%; +} + +.navbar-default { + background-color:#194573; + border-color: #400040; +} +.navbar-default .navbar-brand { + color: #ffffff; +} +.navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { + color: #ffffff; +} +.navbar-default .navbar-text { + color: #ffffff; +} +.navbar-default .navbar-nav > li > a { + color: #ffffff; +} +.navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { + color: #ffffff; +} +.navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #400040; +} +.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { + color: #ffffff; + background-color: #400040; +} +.navbar-default .navbar-toggle { + border-color: #400040; +} +.navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { + background-color: #400040; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #ffffff; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #ffffff; +} +.navbar-default .navbar-link { + color: #ffffff; +} +.navbar-default .navbar-link:hover { + color: #ffffff; +} + +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #ffffff; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #400040; + } +} + +.navbar-nav > li > a {padding-top:5px !important; padding-bottom:5px !important;} +.navbar {min-height:30px !important} + +.navbar-brand { + float: left; + padding: 15px; + padding-top: 5px; + padding-right: 15px; + padding-bottom: 5px; + padding-left: 15px; + font-size: 18px; + line-height: 18px; + height: 30px; +} + + +/* Tooltips */ +.blue-tooltip + .tooltip > .tooltip-inner {background-color: #FF;} +.blue-tooltip + .tooltip > .tooltip-arrow { border-bottom-color:#FF; } + + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} +.tooltip-inner { + text-align: left; + color: #000; + background: #fff; + border: solid 1px #000000; + max-width: 450px +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +/* pre */ +pre { + word-wrap: code; + white-space: pre-wrap; + background-color:white; +} + + +/* Container -Fluid */ +.container-fluid { + padding: 0 2px; +} +@media (min-width: 768px) { + .container-fluid { + padding: 0 5px; + } +} + +.container-fluid .row { + margin: 0px; +} + +.row-padding { + margin-top: 25px; + margin-bottom: 25px; +} + +/* Width for the text field to enter a bug report number in the reports page. + * We put a maximum width to override the width value coming from `form-control'. */ +.form-bug-number-entry { + max-width: 100px; +} + +/* Default width for the entries in a table like layout. */ +.form-inline .form-control { + width: 95%; +} + +.form-inline .checkbox { + font-weight: initial; + vertical-align: top; +} + +/* Note that there is also a class called label. */ +label { + padding-right: 5px; +} + +.label { + padding: 0px; + padding-right: 5px; +} + +.label-primary-api-default { + display: inline-block; + width: 105px; + text-align: left; + background: #fff; + color: #000; + font-size: 100%; + text-align: right; +} + + +.label-primary-api-interactions { + display: inline-block; + padding-right: 5px; + text-align: left; + color: #000; + font-size: 100%; +} + + +pre { + padding: 1.5px; + display: block; + margin: 0 0 10px; + font-size: 12px; + font-family: monospace; + line-height: 1.428571429; + word-break: break-word; + word-wrap: break-word; + color: #333; + border: 0px; + border-radius: 4px; +} + + +/* No padding, so that nested columns are always properly aligned. */ +.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12 { + padding-left: 0px; + padding-right: 0px; +} + +.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td { + padding:2px; + vertical-align: middle; +} + +.form-control{ + height:inherit; + padding: 1px 2px; + margin: 1px; +} + +.btn { + padding: 1px 12px; + margin: 1px; + min-width: 100px; +} + +.dropdown-toggle, .login { + cursor: pointer; +} + +.pager { + margin:10px 0; +} + +.pager li>a,.pager li>span { + padding:1px 12px; + border-radius:8px; +} + +.well { + padding: 9px; + margin-bottom: 10px; + min-height: 44px; +} + +.panel-heading { + background-color: #ddeaf2 !important; +} + +.private-panel-border { + border: solid 1px #DBA458 !important; +} + +.private-panel { + background-color: #f2eadd !important; +} diff --git a/examples/roc_api/site/www/static/images/ajax-loader.gif b/examples/roc_api/site/www/static/images/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..cd1a9983479910cc31f9a99c7a228084945ba12e GIT binary patch literal 1456 zcmb`GYfw~W9EV?FS2%*gVywGFvTJ9_6=K&#ZB0bExTqnbn0dj=nwnaZjpIyxXqtS_ zIeT>x6TB4{%_PvulG(|0RFc%xoX|i;xr&-*h^Q!tKRSn$*{M&>^nQH5Jn!?o|9{#P zi+QXyl!THFLV9|7NOe!w6MS+Tj$3GbjFS&=@gWLZA>QZwf#dgKs>94?+p#Bkl@4}&b7;zb6>R@TaZx2y< z9|`ryXhLEGCO6{O4xD;`c~?>1j$2)L?*{g^;rn*{a1RSxkbM(aQD?=D|Ww4E{%C?d5zK^c=~Wu+u# zPBO(A{ribmvC>PVPb2{=J|>jY42#xrP7)LrL9`4r+(r8bGtUp*!sw~ODccyS40`dU z5k#dfq5%U3zYvwDat(S;p6Bul3^|bJzcKtUVI4ad<*}cLd_d@sV^!x$50^U?>KbR+ zUapR1>#KB*^HuAfzNeor{A~#yFlzKGoYNK0Fovybm|)`KjQd^22^!ar$B2%tq^yn}KS@rVtvbi4Dyv2M1*b}_ za5emVf;Ls^PM<&djPK{m@0AxdIERz?GfX(qg^h43sLZ3Bn8ZX+;uLCCuah~^V{zhpYPJ z{e9tYN%Fj4qdJ|PQ*bOxQiC#P@*-0RnZnEvwh5D`PS0`+QAYW0L5f6lxo)Y_xBKk| zuXzs@vy|W|o-iOYQ1z2d&?^@7N? zsni}m-~Oq)u!xFP9J%hlqwKcJN|nXEC5pva5(>DO*>h$eqMP!i3a=-YTKH|+(&1@u z%uSwe4=&Y{w0Ti(X=eK1a$oqr#QzDm>v9WO7ui|#MX_6%8Dd+YS-@EaGg>vDniU(t zWiu6esfSo;F{e&p5=`+GhNy*$g7|4OOvF%O-Xjf6AG`m5VT(epLMknR_=4aPeVI%0 z*u}?W%=D7}Ns-g7oB1&%kxDTS-Lj2Xv**SS@-GReztH*fCKFkS@}RT>ee3{V_S5Lp zZ^`(n;-}lF$241q<$Hx~Kyljr4zgKJqc7x~4^>t*NwBNnwFUfB%NB{r; literal 0 HcmV?d00001 diff --git a/examples/roc_api/site/www/static/images/favicon.ico b/examples/roc_api/site/www/static/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..676d74466d01b2c9b3e0d2c9300c68952145a9c8 GIT binary patch literal 16446 zcmeI330PBC7Jy^NX*-?q``S_Kg32l`D7dwV;{rN}Q=QJ}H`dmr>;XkoWIzR%Xt7pt z1Cp=?0)#DsY=RpFtRNtYjvxvug2+}7M7Br*XU=^gF(9#D(UMG<@V&g7``&%${P&!D z?|JuLXtZJYXKYNvZw=a1Jby}~(Wc^oNg4wWlK=nD|4t16!0^Q58%^U5{@pIx&D4gs zXOV5N(;Rwwh>Mj-)M<&=tpx&smZFF<_(ycIVh)?+#+{g-lW^MbMgr3?FZq;F@sAPe z??R`h>N0gcV`WkNo_P@MG8dx$JqyCO>Oy%gAJG9sB2gutroRhW19?W?Sx=+5BWtp` z+vn8>I_lSOw$7{$ahzElxp&#E`&o%o2J%eVGG%!e48mNNJPNRx2);I&z_d^Up6fzr zdG?gBcXxGl9by>MK0InQWyC29jgLd^b=6oFlV^Et9K9m(___^Qu|McBXteiapZPA+ z85xY>DFL>+(G0WH$Q^nRMAxK_C}#t>!zK6iJ#!rO4A{0> zWgL4Q@Uzth?+v5jTFhZA0?^v}to00Il=SNZBYPS2iTUAfU{K}s_y4d{12`tgRi9;1legp5Y`Vl z{B6`B*kL+6xpkhpr@6K?uP{AqjlAc-%lzB~@9AMK3-594wa`}LIzXRIJGS?_WPr_3FAPM>uJ(zPIft_^|cmtN~fo%~N)$RhNCyAo*08SS5k>5;N}F&MP> zSeDv8q}`KxWSWl$-tNWlyzVhoOwif{+?}6^`E9h2eGDWX`x*xX0W{TB)ZR#D&XIlI zV5j2`|Lgnk9r~zyPaR-gFfGRA6=g*JB;oFJc9iJIU#s`Nua)K@UplsXv^{A{gg@2C zT3V!2&kj*+cc4L1)`f%)@h%H05 z&jCK;3rO-@4;NYXkaBVp`t!V6KWohPbE0^+<1dbip9?C(` z`kcyteEV{+qYmwZzIrI~_mwD*1rfWJNXKu&L+XO`TRz{8r4FXK8Zf^d2Z3}AY78Uy z$R6b0M9;55)G6r2FZ-kNUS4Lz?vx zOn}_jqr~8{$kKFUWy!t#xKmp4hRLW)fif{r#( zNlwzqw7~89za&>#u2)yz_$cPU^3YI+$=D8F`8cTu!s?H)V)n76P{dvZAr4c4jWI2W zZ?1&9LpR3zvY*(}U!;$`(!sVj?)sNo!PHpLyDwm&y)PeH2ze(=A<%9zu(3W) zuOA6_GsN#al1gZ)FK?^3o}{M?JOj}o%H7l_YNx?#V}XyQ23%kmLAK{oajb}OFB|>j z;f)jtI(2TSD&?^l#(x@!Y-K2u#oNCw*l|v40L~T3_JK?*O^Dw;2U7PNVt*ixdxN*l zf~UpVQht%(1(e^uY^4kw1J&X2+D@(bglbig59i}1Y>*IhV{_(-FFAm54mWEf;Mvc{_DRaluK2jEr zx6vcWVOm?@8{#4Fji17~(-!!i+$Oevw-CzjWS)E*naWc38sF&;(eBG1kvU7*K0r22 z`u?=Q9a7(Ke_q#_7w2iGEDUd>2f%|55B~>nI>@|7K?+-C`H4+Joj>pHZ(e}B_XXNdh5OlOdic9Q8EyEsI>j6` ztqkbreTdBY1aF@W6?f7p{w`q$T#fNK`nK|vt$gG@v(OOx-f<9V3*@~w)Y%YfAKs++ z1?{aJ$w98mludtGeO{biJD~N>(4O@j**<{G2}a<2e^d1Xiod0KY+lU1IdyJH^iT@lX-lX?iSp8}=12^7CPpHZd!aHn~|lO&*O{$$lhUY)5@e(a7} z;3MJ3ScuGfw>H+mOOa?u@F%$%0TahOYQF=l&a-TTLbuJJ_C%4jpBqVD6hH3wxZ$c#o1iGY21{w;?XzXD_}P^6rM%5XsRjC= 0) { + var bracket_position = selector.indexOf('<'); + if (bracket_position > hash_position) { + throw 'Syntax error, unrecognized expression: ' + selector; + } + } + } + return jquery_init.call(this, selector, context, rootjQuery); + }; + jQuery.fn.init.prototype = jquery_init.prototype; +})(); + + +var ROC = ROC || { }; + +$('body').on('click',"a[rel='node']",function(e){ + + e.preventDefault(); + /* + if uncomment the above line, html5 nonsupported browers won't change the url but will display the ajax content; + if commented, html5 nonsupported browers will reload the page to the specified link. + */ + + //get the link location that was clicked + pageurl = $(this).attr('href'); + + spinner = "

Loading content..

loading...
"; + //to get the ajax content and display in div with class 'main' + $.ajax({url:pageurl+'?rel=node',success: function(data){ + $('.main').html(data); + }}); + + //to change the browser URL to the given link location + //if(pageurl!=window.location){ + //window.history.pushState({path:pageurl},'',pageurl); + //} + //stop refreshing to the page given in + return false; +}); + +$('body').on('click',"a[rel='register']",function(e){ + + e.preventDefault(); + /* + if uncomment the above line, html5 nonsupported browers won't change the url but will display the ajax content; + if commented, html5 nonsupported browers will reload the page to the specified link. + */ + + //get the link location that was clicked + pageurl = $(this).attr('href'); + + spinner = "

Loading content..

loading...
"; + //to get the ajax content and display in div with class 'main' + $.ajax({url:pageurl+'?rel=node',success: function(data){ + $('.main').html(data); + }}); + + //to change the browser URL to the given link location + //if(pageurl!=window.location){ + //window.history.pushState({path:pageurl},'',pageurl); + //} + //stop refreshing to the page given in + return false; +}); + + + +$("a[rel='node']").click(function(e){ + e.preventDefault(); + /* + if uncomment the above line, html5 nonsupported browers won't change the url but will display the ajax content; + if commented, html5 nonsupported browers will reload the page to the specified link. + */ + + //get the link location that was clicked + pageurl = $(this).attr('href'); + + spinner = "

Loading content..

loading...
"; + //to get the ajax content and display in div with class 'main' + $.ajax({url:pageurl+'?rel=node',success: function(data){ + $('.main').html(data); + }}); + + //to change the browser URL to the given link location + //if(pageurl!=window.location){ + //window.history.pushState({path:pageurl},'',pageurl); + //} + //stop refreshing to the page given in + return false; +}); + + diff --git a/examples/roc_api/site/www/template/html/layout2.tpl b/examples/roc_api/site/www/template/html/layout2.tpl new file mode 100644 index 0000000..34a5659 --- /dev/null +++ b/examples/roc_api/site/www/template/html/layout2.tpl @@ -0,0 +1,40 @@ + + + + {include file="master2/head.tpl"/} + + + + + {include file="master2/site_navigation.tpl"/} + + + +
+
+
+
+ {include file="master2/content.tpl"/} +
+
+
+ + + + {if condition="$web"} + {include file="master2/optional_enhancement_js.tpl"/} + {/if} + + {if condition="$html"} + {include file="master2/optional_enhancement_js.tpl"/} + {/if} + + + + \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/content.tpl b/examples/roc_api/site/www/template/html/master2/content.tpl new file mode 100644 index 0000000..b18c5dd --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/content.tpl @@ -0,0 +1,11 @@ + +
+

Top most recent nodes


+ + + {foreach from="$nodes" item="item"} + + {/foreach} +
diff --git a/examples/roc_api/site/www/template/html/master2/error.tpl b/examples/roc_api/site/www/template/html/master2/error.tpl new file mode 100644 index 0000000..0ca7ad0 --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/error.tpl @@ -0,0 +1,18 @@ +

Error: {$code/}

+ +{assign name="status400" value="400"/} +{assign name="status404" value="404"/} +{assign name="status500" value="500"/} + +{if condition="$code ~ $status500"} +

Internal server error, for the request {$request/}

+{/if} + + +{if condition="$code ~ $status404"} +

Resourse not found, for the request {$request/}

+{/if} + +{if condition="$code ~ $status400"} +

Bad request, the request {$request/} is not valid

+{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/footer.tpl b/examples/roc_api/site/www/template/html/master2/footer.tpl new file mode 100644 index 0000000..11fe183 --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/footer.tpl @@ -0,0 +1,7 @@ + +
+

API Documentation     + Questions? Comments? Let us know!

+

© Copyright 2014 Eiffel Software -- Privacy Policy +

+
\ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/head.tpl b/examples/roc_api/site/www/template/html/master2/head.tpl new file mode 100644 index 0000000..4ba4616 --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/head.tpl @@ -0,0 +1,10 @@ + + +Eiffel RESTonCMS +{if condition="$web"} + {include file="master2/optional_styling_css.tpl"/} +{/if} +{if condition="$html"} + {include file="master2/optional_styling_css.tpl"/} +{/if} + diff --git a/examples/roc_api/site/www/template/html/master2/header.tpl b/examples/roc_api/site/www/template/html/master2/header.tpl new file mode 100644 index 0000000..ce8ec71 --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/header.tpl @@ -0,0 +1,2 @@ +

RESTonCMS

+

Tagline

\ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/logoff.tpl b/examples/roc_api/site/www/template/html/master2/logoff.tpl new file mode 100644 index 0000000..8139dc7 --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/logoff.tpl @@ -0,0 +1,5 @@ +

You have successfully signed out

+ +You may want to return + +Press this neat little button:Take Me Home diff --git a/examples/roc_api/site/www/template/html/master2/main_navigation.tpl b/examples/roc_api/site/www/template/html/master2/main_navigation.tpl new file mode 100644 index 0000000..88365d7 --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/main_navigation.tpl @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/optional_enhancement_js.tpl b/examples/roc_api/site/www/template/html/master2/optional_enhancement_js.tpl new file mode 100644 index 0000000..1353b83 --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/optional_enhancement_js.tpl @@ -0,0 +1,5 @@ + + +{if condition="$web"} + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/optional_styling_css.tpl b/examples/roc_api/site/www/template/html/master2/optional_styling_css.tpl new file mode 100644 index 0000000..f56d770 --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/optional_styling_css.tpl @@ -0,0 +1,9 @@ +{if condition="$html"} + + + +{/if} +{if condition="$web"} + + +{/if} diff --git a/examples/roc_api/site/www/template/html/master2/site_navigation.tpl b/examples/roc_api/site/www/template/html/master2/site_navigation.tpl new file mode 100644 index 0000000..a75760c --- /dev/null +++ b/examples/roc_api/site/www/template/html/master2/site_navigation.tpl @@ -0,0 +1,27 @@ + + + + \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/navigation.tpl b/examples/roc_api/site/www/template/html/modules/navigation.tpl new file mode 100644 index 0000000..ee2d15e --- /dev/null +++ b/examples/roc_api/site/www/template/html/modules/navigation.tpl @@ -0,0 +1,8 @@ +{if isset="$user"} + Logoff +{/if} +{unless isset="$user"} + Login + Register +{/unless} +List of Nodes diff --git a/examples/roc_api/site/www/template/html/modules/node.tpl b/examples/roc_api/site/www/template/html/modules/node.tpl new file mode 100644 index 0000000..0f6173f --- /dev/null +++ b/examples/roc_api/site/www/template/html/modules/node.tpl @@ -0,0 +1,174 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + + +{if condition="html"} + +
+
+
+
+{/if} + + +
+ {if condition="$web"} + + {/if} + {if condition="$html"} + + {/if} +
+
+ {if isset="$node"} +
+
+
+
+

{$node.title/}

+
+
+
{$node.content/}
+
+
+
+ {/if} +
+ +
+ {if isset="$user"} +
+
+
+ {if isset="$node"} + +
+ {/if} +
+
+
+ {if isset="$user"} +
+
+ + {if isset="$node"} +
+ +
+ Delete Node + +
+ +
+
+
+ {/if} +
+ {/if} +
+
+
+ +{if condition="html"} +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/node_content.tpl b/examples/roc_api/site/www/template/html/modules/node_content.tpl new file mode 100644 index 0000000..4825500 --- /dev/null +++ b/examples/roc_api/site/www/template/html/modules/node_content.tpl @@ -0,0 +1,70 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + +{if condition="html"} + +
+
+
+
+{/if} + + +
+
+
+ +
+ Edit Node Content + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+{if condition="html"} + +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/node_summary.tpl b/examples/roc_api/site/www/template/html/modules/node_summary.tpl new file mode 100644 index 0000000..24f69e6 --- /dev/null +++ b/examples/roc_api/site/www/template/html/modules/node_summary.tpl @@ -0,0 +1,71 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + + + {unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} + {/unless} + +{if condition="html"} + +
+
+
+
+{/if} + +
+
+
+ +
+ Edit Node Summary + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+ +{if condition="html"} + +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/node_title.tpl b/examples/roc_api/site/www/template/html/modules/node_title.tpl new file mode 100644 index 0000000..2e02d02 --- /dev/null +++ b/examples/roc_api/site/www/template/html/modules/node_title.tpl @@ -0,0 +1,70 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + + {unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} + {/unless} + + +{if condition="html"} + +
+
+
+
+{/if} + +
+
+
+ +
+ Edit Node Title + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+{if condition="html"} + +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/nodes.tpl b/examples/roc_api/site/www/template/html/modules/nodes.tpl new file mode 100644 index 0000000..23b24c7 --- /dev/null +++ b/examples/roc_api/site/www/template/html/modules/nodes.tpl @@ -0,0 +1,52 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + +{if condition="html"} + +
+
+
+
+{/if} +
+

List nodes


+ + + {foreach from="$nodes" item="item"} + + {/foreach} +
+ +{if condition="html"} +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/register.tpl b/examples/roc_api/site/www/template/html/modules/register.tpl new file mode 100644 index 0000000..3a14667 --- /dev/null +++ b/examples/roc_api/site/www/template/html/modules/register.tpl @@ -0,0 +1,100 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + +{if condition="html"} + +
+
+
+
+{/if} + + +
+
+
+
+ Register +
+

Register new user

+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ s
+ +
+ +
+
+
+
+
+ +{if condition="html"} +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/layout2.tpl b/examples/roc_api/site/www/themes/api/layout2.tpl new file mode 100644 index 0000000..34a5659 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/layout2.tpl @@ -0,0 +1,40 @@ + + + + {include file="master2/head.tpl"/} + + + + + {include file="master2/site_navigation.tpl"/} + + + +
+
+
+
+ {include file="master2/content.tpl"/} +
+
+
+ + + + {if condition="$web"} + {include file="master2/optional_enhancement_js.tpl"/} + {/if} + + {if condition="$html"} + {include file="master2/optional_enhancement_js.tpl"/} + {/if} + + + + \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/master2/content.tpl b/examples/roc_api/site/www/themes/api/master2/content.tpl new file mode 100644 index 0000000..b18c5dd --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/content.tpl @@ -0,0 +1,11 @@ + +
+

Top most recent nodes


+ + + {foreach from="$nodes" item="item"} + + {/foreach} +
diff --git a/examples/roc_api/site/www/themes/api/master2/error.tpl b/examples/roc_api/site/www/themes/api/master2/error.tpl new file mode 100644 index 0000000..0ca7ad0 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/error.tpl @@ -0,0 +1,18 @@ +

Error: {$code/}

+ +{assign name="status400" value="400"/} +{assign name="status404" value="404"/} +{assign name="status500" value="500"/} + +{if condition="$code ~ $status500"} +

Internal server error, for the request {$request/}

+{/if} + + +{if condition="$code ~ $status404"} +

Resourse not found, for the request {$request/}

+{/if} + +{if condition="$code ~ $status400"} +

Bad request, the request {$request/} is not valid

+{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/master2/footer.tpl b/examples/roc_api/site/www/themes/api/master2/footer.tpl new file mode 100644 index 0000000..11fe183 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/footer.tpl @@ -0,0 +1,7 @@ + +
+

API Documentation     + Questions? Comments? Let us know!

+

© Copyright 2014 Eiffel Software -- Privacy Policy +

+
\ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/master2/head.tpl b/examples/roc_api/site/www/themes/api/master2/head.tpl new file mode 100644 index 0000000..4ba4616 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/head.tpl @@ -0,0 +1,10 @@ + + +Eiffel RESTonCMS +{if condition="$web"} + {include file="master2/optional_styling_css.tpl"/} +{/if} +{if condition="$html"} + {include file="master2/optional_styling_css.tpl"/} +{/if} + diff --git a/examples/roc_api/site/www/themes/api/master2/header.tpl b/examples/roc_api/site/www/themes/api/master2/header.tpl new file mode 100644 index 0000000..ce8ec71 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/header.tpl @@ -0,0 +1,2 @@ +

RESTonCMS

+

Tagline

\ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/master2/logoff.tpl b/examples/roc_api/site/www/themes/api/master2/logoff.tpl new file mode 100644 index 0000000..8139dc7 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/logoff.tpl @@ -0,0 +1,5 @@ +

You have successfully signed out

+ +You may want to return + +Press this neat little button:Take Me Home diff --git a/examples/roc_api/site/www/themes/api/master2/main_navigation.tpl b/examples/roc_api/site/www/themes/api/master2/main_navigation.tpl new file mode 100644 index 0000000..88365d7 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/main_navigation.tpl @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/master2/optional_enhancement_js.tpl b/examples/roc_api/site/www/themes/api/master2/optional_enhancement_js.tpl new file mode 100644 index 0000000..1353b83 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/optional_enhancement_js.tpl @@ -0,0 +1,5 @@ + + +{if condition="$web"} + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/master2/optional_styling_css.tpl b/examples/roc_api/site/www/themes/api/master2/optional_styling_css.tpl new file mode 100644 index 0000000..f56d770 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/optional_styling_css.tpl @@ -0,0 +1,9 @@ +{if condition="$html"} + + + +{/if} +{if condition="$web"} + + +{/if} diff --git a/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl b/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl new file mode 100644 index 0000000..a75760c --- /dev/null +++ b/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl @@ -0,0 +1,27 @@ + + + + \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/modules/navigation.tpl b/examples/roc_api/site/www/themes/api/modules/navigation.tpl new file mode 100644 index 0000000..ee2d15e --- /dev/null +++ b/examples/roc_api/site/www/themes/api/modules/navigation.tpl @@ -0,0 +1,8 @@ +{if isset="$user"} + Logoff +{/if} +{unless isset="$user"} + Login + Register +{/unless} +List of Nodes diff --git a/examples/roc_api/site/www/themes/api/modules/node.tpl b/examples/roc_api/site/www/themes/api/modules/node.tpl new file mode 100644 index 0000000..0f6173f --- /dev/null +++ b/examples/roc_api/site/www/themes/api/modules/node.tpl @@ -0,0 +1,174 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + + +{if condition="html"} + +
+
+
+
+{/if} + + +
+ {if condition="$web"} + + {/if} + {if condition="$html"} + + {/if} +
+
+ {if isset="$node"} +
+
+
+
+

{$node.title/}

+
+
+
{$node.content/}
+
+
+
+ {/if} +
+ +
+ {if isset="$user"} +
+
+
+ {if isset="$node"} + +
+ {/if} +
+
+
+ {if isset="$user"} +
+
+ + {if isset="$node"} +
+ +
+ Delete Node + +
+ +
+
+
+ {/if} +
+ {/if} +
+
+
+ +{if condition="html"} +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/modules/node_content.tpl b/examples/roc_api/site/www/themes/api/modules/node_content.tpl new file mode 100644 index 0000000..4825500 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/modules/node_content.tpl @@ -0,0 +1,70 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + +{if condition="html"} + +
+
+
+
+{/if} + + +
+
+
+ +
+ Edit Node Content + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+{if condition="html"} + +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/modules/node_summary.tpl b/examples/roc_api/site/www/themes/api/modules/node_summary.tpl new file mode 100644 index 0000000..24f69e6 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/modules/node_summary.tpl @@ -0,0 +1,71 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + + + {unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} + {/unless} + +{if condition="html"} + +
+
+
+
+{/if} + +
+
+
+ +
+ Edit Node Summary + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+ +{if condition="html"} + +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/modules/node_title.tpl b/examples/roc_api/site/www/themes/api/modules/node_title.tpl new file mode 100644 index 0000000..2e02d02 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/modules/node_title.tpl @@ -0,0 +1,70 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + + {unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} + {/unless} + + +{if condition="html"} + +
+
+
+
+{/if} + +
+
+
+ +
+ Edit Node Title + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+{if condition="html"} + +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/modules/nodes.tpl b/examples/roc_api/site/www/themes/api/modules/nodes.tpl new file mode 100644 index 0000000..23b24c7 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/modules/nodes.tpl @@ -0,0 +1,52 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + +{if condition="html"} + +
+
+
+
+{/if} +
+

List nodes


+ + + {foreach from="$nodes" item="item"} + + {/foreach} +
+ +{if condition="html"} +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/modules/register.tpl b/examples/roc_api/site/www/themes/api/modules/register.tpl new file mode 100644 index 0000000..3a14667 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/modules/register.tpl @@ -0,0 +1,100 @@ +{if condition="html"} + + + + {include file="master2/head.tpl"/} + + +{/if} + + +{unless condition="$web"} + + {include file="master2/site_navigation.tpl"/} +{/unless} + +{if condition="html"} + +
+
+
+
+{/if} + + +
+
+
+
+ Register +
+

Register new user

+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ s
+ +
+ +
+
+
+
+
+ +{if condition="html"} +
+
+
+{/if} + + +{if condition="html"} + + + + {include file="master2/optional_enhancement_js.tpl"/} + + + +{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/theme.info b/examples/roc_api/site/www/themes/api/theme.info new file mode 100644 index 0000000..54b5f6e --- /dev/null +++ b/examples/roc_api/site/www/themes/api/theme.info @@ -0,0 +1,9 @@ +name=api +engine=smarty +author=jvelilla +version=0.1 +regions[header] = Header +regions[content] = Content +regions[footer] = Footer +regions[first_sidebar] = first sidebar +regions[second_sidebar] = second sidebar diff --git a/examples/roc_api/site/www/themes/smarty/front-page.tpl b/examples/roc_api/site/www/themes/smarty/front-page.tpl new file mode 100644 index 0000000..ae932ee --- /dev/null +++ b/examples/roc_api/site/www/themes/smarty/front-page.tpl @@ -0,0 +1,12 @@ +{include file="tpl/page-header.tpl"/} +
+
+ +
+ Welcome ... this is the front page + {if isset="$content"}{$content/}{/if} +
+ +
+
+{include file="tpl/page-footer.tpl"/} diff --git a/examples/roc_api/site/www/themes/smarty/page.tpl b/examples/roc_api/site/www/themes/smarty/page.tpl new file mode 100644 index 0000000..5dde43c --- /dev/null +++ b/examples/roc_api/site/www/themes/smarty/page.tpl @@ -0,0 +1,11 @@ +{include file="tpl/page-header.tpl"/} +
+
+ +
+ {if isset="$content"}{$content/}{/if} +
+ +
+
+{include file="tpl/page-footer.tpl"/} diff --git a/examples/roc_api/site/www/themes/smarty/res/ewfcms.js b/examples/roc_api/site/www/themes/smarty/res/ewfcms.js new file mode 100644 index 0000000..d4ebbef --- /dev/null +++ b/examples/roc_api/site/www/themes/smarty/res/ewfcms.js @@ -0,0 +1,91 @@ +/* + * EWF CMS javascript based on JQuery + */ + +/** + * Override jQuery.fn.init to guard against XSS attacks. + * + * See http://bugs.jquery.com/ticket/9521 + */ + +(function () { + var jquery_init = jQuery.fn.init; + jQuery.fn.init = function (selector, context, rootjQuery) { + // If the string contains a "#" before a "<", treat it as invalid HTML. + if (selector && typeof selector === 'string') { + var hash_position = selector.indexOf('#'); + if (hash_position >= 0) { + var bracket_position = selector.indexOf('<'); + if (bracket_position > hash_position) { + throw 'Syntax error, unrecognized expression: ' + selector; + } + } + } + return jquery_init.call(this, selector, context, rootjQuery); + }; + jQuery.fn.init.prototype = jquery_init.prototype; +})(); + + +var EWFCMS = EWFCMS || { }; + +EWFCMS.toggleFieldset = function(fieldset) { + if ($(fieldset).is('.collapsed')) { + var content = $('> div:not(.action)', fieldset); + $(fieldset).removeClass('collapsed'); + content.hide(); + content.slideDown( { + duration: 'fast', + easing: 'linear', + complete: function() { + //Drupal.collapseScrollIntoView(this.parentNode); + this.parentNode.animating = false; + $('div.action', fieldset).show(); + }, + step: function() { + // Scroll the fieldset into view + //Drupal.collapseScrollIntoView(this.parentNode); + } + }); + } else { + var content = $('> div:not(.action)', fieldset).slideUp('fast', function() { + $(this.parentNode).addClass('collapsed'); + this.parentNode.animating = false; + }); + } + }; + +jQuery(document).ready(function(){ + //$('.collapsed').hide(); + $('fieldset.collapsible > legend').each(function() { + var fieldset = $(this.parentNode); + // turn legen into clickable link and wrap contents + var text = this.innerHTML; + $(this).empty() + .append($(''+ text + '').click(function() { + var fieldset = $(this).parents('fieldset:first')[0]; + if (!fieldset.animating) { + fieldset.animating = true; + EWFCMS.toggleFieldset(fieldset); + } + return false; + } + )) + .after($('
') + .append(fieldset.children(':not(legend):not(.action)'))) + .addClass('collapse-processed'); + }); + $('fieldset.collapsed').each(function() { + $(this).removeClass('collapsed'); + EWFCMS.toggleFieldset(this); + }); +}); + +jQuery(document).ready(function(){ + $('#tabs').tabs(); +}); + +//jQuery(document).ready(function(){ + //$('#second_sidebar').hide(); +//}); + diff --git a/examples/roc_api/site/www/themes/smarty/res/favicon.ico b/examples/roc_api/site/www/themes/smarty/res/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..343067f9fc7b0829212336f7f67fa96328ba71a3 GIT binary patch literal 786 zcmV+t1MU0(0096201yxW0096X0Q>>~02TlM0EtjeM-2)Z3IG5A4M|8uQUCw|5C8xG z5C{eU001BJ|6u?C0>eo}K~y-)rITAo({U8XKfnL}o7;5Gf7`#NrbcCZOZiN?Bp`q9v&rArV=Mld@&Dx&PKSEvK3Odzg!e zdg==Y&VlnioXhtJ{y^)!D+Qj;_Oh_%%bysJ<^fo3_I^c8?_0KhQ`6SsL$80&7YRq# zb#dK&yr#S5R<$jsp}U>;>vzx2%kf@ zM}q!Xb8AIj|+G1a}*ss!q;$!u9gNI=~);}Nn|WuPWQtmEGag2R2(H5 z3NY+>%k|po(4MN(TVzjXdzm)kb-1#Z@a91;2P!kRBXF{9OHCrxz`4UXc>%)w-~5YhasShJar1HA|#NK;+d z6ej>-&6h7HPSxMj!LGwqAOt3h72l8tAxZd#Jh&DuB@!Iv+gOBp_laLiofwbi$%)`; z^EJW)FG#g3jQhP@J9&Vw(-Mozt@+e#{WR~MDKqtg;aSx~(aa#Q0)2BUG}^lZF?d<9f9#AXDmi%Q+wN-BEAFDV}@ zZyxPvD3+Ne5QAi6ybF^AsiyeiTvnJ75(_r$yB$0D_Y3q952X)P+y6pC29P$?E$7oY+GgzT9tlVmapStr|M zoB7>8CbMOhYhGiQFk_kEuCeZKGeJn#AwL{a1hA*#2t{l1>Awq>H#mSC~;DTn&| zl|z<(6@XkBG9V8Lu_%?H79mK|8Xgt*Nmz935uocvR4&YrNJ_iW@MvF8_ub94mFh44 zdO%%y>8z}`yCa}dSY!-2`HM$xU%TF<$;dHdSste&O?i z(}(_`sVymkcHOWNn#9 zb|NCG)_VO1MXzqZ?d- zgmWhjhnzcc=+XA(#=`M=<6|WH+S$_=UU+0hh9e>$RP3a8#}s|$&YO#tBJ{@-C89U(@YOu@2M zNM*qYg7*mj&m;R7=r!~F{(scYUA+7jpwpQtilXypGU)ehz9ZLme+J_H8Hf++WMcJ| zFU3H3cjr&VKmOuL=ldUa{6uu4%-QBfP>#h~AFzU;lP4TUj_mP#iaDonp`tmET zLUQu}kOj+GQTQWgx?HJZ*#l1i5Ti~eG%^|hL-|(#tlGF0S+IP8;hL}uBE#3!et3<>j)b&yOO z`v<5WBa)b!E`sI-y_;=9{T;g-OlItc5O>an?3(cb(-$`?nJZ|7rw~rFg1E8 zN@c;$=Z}lt;)6F{^djQG^~O5llhaAbnTsHi5TBIp%o`l&L!k;KJ~`ck5P+e=PDCPc zImgRCUQ5}RpZcu}pu^Ni#f2~O)M+{`V3Yu~8S6KkI{2nKkfL0nA}%S->!%D22VCwY zSE@)%&2kzoiXxR4&j2{dIeHCTuH_D?EZApVK|xXvxl;$x`6c1eIpV@-u-raQ&@GFQN- z&B}B3NPBYwEe1Vc-+Lry&vwRxQD2UAbY$9filT_Ira}WmN_#uo@B94N0nJ28=8o(1 zo4t3Xeckqg+Gc{i_EgD@5k?mF(c-dM-z$go$sue8sP9nj3X z?R)o0d%D_|S6}{8HW86yZ@t9vw_ov_-_}%5e@{0d;gO`~=Alr90`{P6sndRlsdb-+{OGKM3aiTpofHq^+gfb0d!*EPu5+UJ{fW1fB zjRvgNQ2e-f=u^6K7y{^gHc{KtoY zedNkr_-&8;_T~nx)=}Dx1`<*-J@VY=pP68I$fA-CSqA(PsV}?i@xEZ`N-vr6^Pf4N zD~ivs^3kW9@1iKuSX~N0ZOJ7MBBr`3dOF*tZ@n9aECVXZ@Zg|dBE_E^=i{Qcyk=N` z`1eOgXsEpAdjH#BXs)dU;OHAWv01IoyrMncL-}+uJUFNlu6*|Qnw^i`nGr}ZEHZ|K z)J)_GZ_X_SJsmBs(;JywLH>8XPhWQ@W#>P|eOQl*SChBoPAZGfV5+b7C?{i1fpbZt zzj~e_i{D+X7oL0P@3qokguFDDY#q=GI2WIpz#hic%XM(=b0xl%PCmC02WK@jjYv&$s#vpk3h z_MtrnN9!(%hwr*gs}E$_jjc4h%KA|*M2)S~=f!}<3VW_Xa1cD52jKx`vtU1)Zb1-` z$yFAKL?W0%BV&9DMau(|aL26)<0=4dj&r^1B3vXKD2Rg~0kXmalL&;+$QUFN!6Xfj ziaVW?zxY2cp8n9Qs8F|*jv;&N8G1cs933h5;rH1QuIE{i-uY6&?yjM3i(1nFym}3c z{hqSEVh$iDfAL6oRNQGvSaj@>C2QB4e2clzW&yn!Mr@wHQ6CkwSnU6q;UEz_iHJa} z#eY5RgZ9Q6UyscXktJ)_o5G@Fk4S-Tb-K{UW zlC#4hH58O%u2d(kn0v7C7s^#BR2T69{T-kO_D-<$Jp|98=GS^Df_{-XE*8c7Zpb* zXtkZK#u-p>8CGS6*->^Pj@}SODC>qbK6E}d!lx}TFB;?$K!67`T`s7LfC|^y1JMau zboq;l0TZ~LG-}dwHf?xzkKe1Wpef#nI9gsC0(0yqKh*X4Z*_oSt34jhw8EJd_{6PE z$5+4M**y)K^qftOclX_VQ|8=)Q_0!0pJ=Qu6~HyLf<0*G$3XYEP1@k7Ym{z#HV+rGyaZ$l=XR>ekAH4j5+w}1IS=o*4cV`UX?-Pxv2(`{$m{i1ZdQmiJqXwm9* z`G$(Geth=C;n3YzAwSl>jtcdX21DQ2f3K@mM#o01KiAktoM<_`Q)Q0+@(zQ z>J1pwX5}v3ytnA&?Q0(D`+7{A7`Lx^sBiP$qLYD0z$CAyTN-O#K7aCq#}2;qv)CK> z?Mgrhg22Q7^PBdCcir<&Vshrw6UtBegMsG8!nUURUHe{mDy6>c%GdhAK-x|7#mArD z+YpzO_KdH<_J!$u!tt`9;_Ax%yS61cuGnrA;F!vRhQ2R)7=6H5(&Bmi^OFQJYmY3cgy)?-JG8=wtkvYIWzS6#FmDd z4VE7B+7?5#=JR8J(o|phx2&hL&F2{a*nK}dI*#o5-x^u+gNK?EQ?r|u;SqmLOv!u? zXqm1dGbQ4V(C+Ik_xAU8t+d%h^~lhGYREF6bQ}N%D-;8ArOG0cs|F+z!4wh}^-*Me f;!)s+jEMgaKSv*+{l^)l00000NkvXXu0mjf$2x5w literal 0 HcmV?d00001 diff --git a/examples/roc_api/site/www/themes/smarty/res/menu-collapsed.png b/examples/roc_api/site/www/themes/smarty/res/menu-collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..95a214a6e6d17fee2f098804997f3826ffc9d4ca GIT binary patch literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j@3?%=}IXVGIu?6^qxc>kDAIJlF_r}R z1v5B2yO9Ruh>$j@3?%=}IXVGIu?6^qxc>kDAIJlF_r}R z1v5B2yO9Ru2zk0VhE&W+{&76uaKb@_0}N~oA{!VF-#vS9IZ&3t)78&qol`;+0EMF; ATL1t6 literal 0 HcmV?d00001 diff --git a/examples/roc_api/site/www/themes/smarty/res/style.css b/examples/roc_api/site/www/themes/smarty/res/style.css new file mode 100644 index 0000000..b0615ae --- /dev/null +++ b/examples/roc_api/site/www/themes/smarty/res/style.css @@ -0,0 +1,251 @@ +body { margin: 0; background-color: #eeeeff;} +div#header { background-color: #003; color: #fff; border: solid 1px #003; padding: 0px; margin: 0px;} +div#header img#logo { float: left; margin: 5px 15px 5px 10px; } +div#header div#title {font-size: 180%; font-weight: bold; margin-top: 10px; } +ul.horizontal { + list-style-type: none; +} +ul.horizontal li { + display: inline; + padding: 0 5px 0 5px; +} + +div#menu-bar li.active { + border: solid 1px #ff0; + color: #ff0; +} +div#menu-bar li:hover { + background-color: #fff; + color: #00f; +} +div#menu-bar li a { + text-decoration: none; + color: #fff; +} +div#menu-bar li:hover a { + color: #00f; + font-style: bold; +} + + +div#primary-tabs li { + color: #00f; + padding: 2px 5px 2px 5px; + background-color: #eee; + border: solid 1px #ccf; +} +div#primary-tabs li.active { + padding: 2px 7px 1px 7px; + border-top: solid 2px #99f; + border-left: solid 1px #99f; + border-right: solid 1px #99f; + border-bottom: 0; + background-color: #fff; + color: #00f; +} +div#primary-tabs li:hover { + background-color: #fff; + color: #00f; +} +div#primary-tabs li a { + text-decoration: none; + color: #00f; +} +div#primary-tabs li:hover a { + color: #00f; + font-style: bold; +} + + + +div#menu-first { margin-left: 20%; color: #ccf; background-color: #003; } +div#menu-first a { color: #ccf; } +div#menu-second { color: #99f; background-color: #333; } +div#menu-second a { color: #99f; } + +div#main-wrapper { + clear: both; + display: block; + height: 0; +} +div#main { margin: 0; padding: 0; clear: both; height:0; display: block; } + +div#content { + padding: 5px 3px 5px 20px; + margin-top: 10px; + min-width: 60%; + display: inline; + float: left; + position: relative; + background-color: #ffffff; + padding-bottom: 30px; +} + +div#first_sidebar { + width: 20%; + margin: 5px; + padding: 5px; + display: inline; + float: left; + position: relative; +} +div#second_sidebar { + width: 20%; + margin: 5px; + padding: 5px; + display: inline; + float: left; + position: relative; + background-color: #eee; +} +div.sidebar div.block { + margin-bottom: 5px; + padding: 0; + border: dotted 1px #999; + background-color: #fff; +} +div.sidebar div.block div.title { + padding: 3px 3px 3px 3px; + font-weight: bold; + background-color: #dedede; + border-bottom: dotted 1px #999; +} +div.sidebar div.block div.inside { + margin: 3px; +} +div#footer { margin: 10px 0 10px 0; clear: both; display: block; text-align: center; padding: 10px; border-top: solid 1px #00f; color: #fff; background-color: #333;} +div#footer a { color: #ff0; } + +form div.error { + border-top: dotted 1px #f00; + border-bottom: dotted 1px #f00; + border-left: solid 3px #f00; +} +div.node div.title { + font-weight: bold; + font-size: 110%; + border-bottom: dotted 1 px #009; +} +div.description { + font-style: italic; + font-color: #999; +} + +div.node-wrapper { + margin: 5px 2px 5px 2px; + border: dotted 1px #dddddd; + padding: 5px 3px 5px 3px; +} +div.node div.title { + font-weight: bold; + font-size: 110%; + border-bottom: dotted 1 px #009; + float: left; +} +div.node div.description { + text-align: right; +} +div.node div.inner { + padding: 5px 5px 5px 10px; + border-top: dotted 1px #dddddd; +} + +form#user-login { + border: dotted 1px #099; + display: inline-block; + padding: 10px; + margin: 10px; +} + +form#user-login>div { + margin-bottom: 10px; +} +form#user-login .input { + float: left; +} +form#user-login img.logo { +} + +div#message { + border: solid 1px #fc0; + background-color: #fed; + color: #000; + padding: 5px; + margin: 5px; +} + +div#message li { + padding-left: 5px; + margin-left: 3px; +} +div#message li.success { + color: #003300; + background-color: #ccffcc; +} + +div#message li.error { + color: #330000; + background-color: #ff9494; +} +div#message li.warning { + color: #aa2200; + background-color: #ffcc99; +} + +div.columns { + margin-top: 10px; + display: inline-block; + clear: both; +} + +div.columns>* { + padding-left: 10px; + padding-top: 5px; + border-top: dotted 1px #999; + border-left: dotted 1px #999; + margin-left: 1px; + margin-right: 10px; + float: left; +} + +/* Link */ + +a { + text-decoration: none; +} +a:hover { + text-decoration: underline; +} + +div.menu ul.vertical { + margin: 0; + padding-left: 10px; + list-style-type: none; +} + +div.menu ul.vertical ul { + border-left: solid 3px #eee; + list-style-type: none; + margin: 0; + padding-left: 5px; + margin-left: 5px; + margin-bottom: 5px; +} + +/* Fieldset and collapsible */ + +fieldset.collapsible legend a { + padding-left: 15px; + background: url(menu-expanded.png) 5px 75% no-repeat; +} + +fieldset.collapsed legend a { + padding-left: 15px; + background: url(menu-collapsed.png) 5px 50% no-repeat; +} + +fieldset.collapsed { + border: none; + border-top: dotted 1px #000; +} + diff --git a/examples/roc_api/site/www/themes/smarty/theme.info b/examples/roc_api/site/www/themes/smarty/theme.info new file mode 100644 index 0000000..38a712a --- /dev/null +++ b/examples/roc_api/site/www/themes/smarty/theme.info @@ -0,0 +1,10 @@ +name=smarty +engine=smarty +author=jfiat +version=0.1 +;template_dir=templates +regions[header] = Header +regions[content] = Content +regions[footer] = Footer +regions[first_sidebar] = first sidebar +regions[second_sidebar] = second sidebar diff --git a/examples/roc_api/site/www/themes/smarty/tpl/page-footer.tpl b/examples/roc_api/site/www/themes/smarty/tpl/page-footer.tpl new file mode 100644 index 0000000..693215b --- /dev/null +++ b/examples/roc_api/site/www/themes/smarty/tpl/page-footer.tpl @@ -0,0 +1,6 @@ + + + +{if isset="$page_bottom"}{$page_bottom/}{/if} + + diff --git a/examples/roc_api/site/www/themes/smarty/tpl/page-header.tpl b/examples/roc_api/site/www/themes/smarty/tpl/page-header.tpl new file mode 100644 index 0000000..c8eba5a --- /dev/null +++ b/examples/roc_api/site/www/themes/smarty/tpl/page-header.tpl @@ -0,0 +1,14 @@ + + + +{if isset="$head"}{$head/}{/if} +{$head_title/} +{if isset="$styles"}{$styles/}{/if} +{if isset="$scripts"}{$scripts/}{/if} +{if isset="$head_lines"}{$head_lines/}{/if} + +
+
+ diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e new file mode 100644 index 0000000..4f16373 --- /dev/null +++ b/examples/roc_api/src/ewf_roc_server.e @@ -0,0 +1,139 @@ +note + description: "[ + application service + ]" + date: "$Date$" + revision: "$Revision$" + +class + EWF_ROC_SERVER + +inherit + WSF_LAUNCHABLE_SERVICE + rename + make_and_launch as make_and_launch_service + redefine + initialize + end + WSF_SERVICE + redefine + execute + end + + REFACTORING_HELPER + + ARGUMENTS + + SHARED_LOGGER + +create + make_and_launch + +feature {NONE} -- Initialization + + make_and_launch + do + create launcher + make_and_launch_service + end + + initialize + -- Initialize current service. + do + Precursor + service_options := create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("roc.ini") + launch_cms (cms_setup) + end + +feature -- Service + + cms_service: CMS_SERVICE + -- cms service. + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + do + cms_service.execute (req, res) + end + +feature -- Layout + + layout: CMS_LAYOUT + -- cms layout + +feature {NONE} -- Launch operation + + launcher: APPLICATION_LAUNCHER + + launch (a_service: WSF_SERVICE; opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS) + local + l_retry: BOOLEAN + l_message: STRING + do + if not l_retry then + log.write_debug (generator + ".launch") + launcher.launch (a_service, opts) + else + -- error hanling. + create l_message.make (1024) + if attached ((create {EXCEPTION_MANAGER}).last_exception) as l_exception then + if attached l_exception.description as l_description then + l_message.append (l_description.as_string_32) + l_message.append ("%N%N") + elseif attached l_exception.trace as l_trace then + l_message.append (l_trace) + l_message.append ("%N%N") + else + l_message.append (l_exception.out) + l_message.append ("%N%N") + end + else + l_message.append ("The application crash without available information") + l_message.append ("%N%N") + end + -- send email shutdown + log.write_debug (generator + ".launch shutdown") + end + rescue + l_retry := True + retry + end + +feature -- CMS Initialization + + cms_setup: CMS_CUSTOM_SETUP + do + if attached separate_character_option_value ('d') as l_dir then + log.write_debug (generator + ".cms_setup using a command line argument -d " + l_dir ) + create layout.make_with_path (create {PATH}.make_from_string (l_dir)) + else + log.write_debug (generator + ".cms_setup using current directory" ) + create layout.make_default + end + create Result.make (layout) + setup_modules (Result) + setup_storage (Result) + end + + launch_cms (a_setup: CMS_SETUP) + local + cms: CMS_SERVICE + do + log.write_debug (generator + ".launch_cms") + create cms.make (a_setup) + cms_service := cms + end + +feature -- CMS setup + + setup_modules (a_setup: CMS_SETUP) + -- Setup modules to be added to the CMS ecosystem. + do + to_implement ("To implement custom modules") + end + + setup_storage (a_setup: CMS_SETUP) + do + + end +end + diff --git a/layout/src/error/error_handler.e b/layout/src/error/basic_error_handler.e similarity index 98% rename from layout/src/error/error_handler.e rename to layout/src/error/basic_error_handler.e index 2977f0a..3c29d2c 100644 --- a/layout/src/error/error_handler.e +++ b/layout/src/error/basic_error_handler.e @@ -4,7 +4,7 @@ note revision: "$Revision: 95678 $" class - ERROR_HANDLER + BASIC_ERROR_HANDLER create make diff --git a/layout/src/error/shared_error.e b/layout/src/error/shared_error.e index 3b0cfb9..82c87b6 100644 --- a/layout/src/error/shared_error.e +++ b/layout/src/error/shared_error.e @@ -12,7 +12,7 @@ inherit feature -- Access - last_error: detachable ERROR_HANDLER + last_error: detachable BASIC_ERROR_HANDLER -- Object represent last error. last_error_message: READABLE_STRING_32 @@ -82,7 +82,7 @@ feature -- Element Settings failed: not successful end - set_last_error_from_handler (a_error: detachable ERROR_HANDLER) + set_last_error_from_handler (a_error: detachable BASIC_ERROR_HANDLER) -- Set `last_error' with `a_error'. do last_error := a_error diff --git a/persistence/Readme.md b/persistence/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/persistence/implementation/common/database/database_error_handler.e b/persistence/implementation/common/database/database_error_handler.e new file mode 100644 index 0000000..a2718fe --- /dev/null +++ b/persistence/implementation/common/database/database_error_handler.e @@ -0,0 +1,35 @@ +note + description: "Database error handler" + date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $" + revision: "$Revision: 195 $" + +class + DATABASE_ERROR_HANDLER + +inherit + ERROR_HANDLER + +create + make + +feature -- Error operation + + add_database_error (a_message: READABLE_STRING_32; a_code: INTEGER) + -- Add a database error + local +-- l_error: DATABASE_ERROR + do +-- create l_error.make_from_message (a_message, a_code) +-- add_error (l_error) + end + + add_database_no_change_error (a_message: READABLE_STRING_32; a_code: INTEGER) + -- Add a database error + local +-- l_error: DATABASE_NO_CHANGE_ERROR + do +-- create l_error.make_from_message (a_message, a_code) +-- add_error (l_error) + end + +end diff --git a/persistence/implementation/common/database/database_storage_manager.e b/persistence/implementation/common/database/database_storage_manager.e new file mode 100644 index 0000000..e59ef5a --- /dev/null +++ b/persistence/implementation/common/database/database_storage_manager.e @@ -0,0 +1,371 @@ +note + description: "[ + Manager to initialize data api for database access, + create database connection and so on + ]" + date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $" + revision: "$Revision: 195 $" + +class + DATABASE_STORAGE_MANAGER + +inherit + GLOBAL_SETTINGS + + REFACTORING_HELPER + + SHARED_ERROR_HANDLER + +create + make + +feature -- Initialization + + make (a_data_app: like data_app; a_database_name: like database_name; a_name: like name; a_password: like password; + a_host_name: like host_name; a_role_id: like role_id; a_role_password: like role_password + a_data_source: like data_source; a_group: like group) + -- Initialize with login info. + -- + -- `a_database_name' is used for MySQL + -- `a_name', the login user name + -- `a_password', the login user password, unencrypted. + local + l_storage: STRING_8 + do + create l_storage.make (64) + storage_url := l_storage + l_storage.append (a_data_app.db_spec.database_handle_name.as_lower) + l_storage.append ("://") + + data_app := a_data_app + database_name := a_database_name + name := a_name + password := a_password + host_name := a_host_name + role_id := a_role_id + role_password := a_role_password + data_source := a_data_source + group := a_group + + set_use_extended_types (True) + set_map_zero_null_value (False) + + l_storage.append (a_name.as_string_8) + l_storage.append (":********@") + if a_host_name /= Void then + a_data_app.set_hostname (a_host_name) + l_storage.append (a_host_name) + end + + if a_database_name /= Void then + a_data_app.set_application (a_database_name.as_string_8) + l_storage.append ("/" + a_database_name.as_string_8) + end + + if a_data_source /= Void then + a_data_app.set_data_source (a_data_source) + end + if a_role_id /= Void and then a_role_password /= Void then + a_data_app.set_role (a_role_id, a_role_password) + end + if a_group /= Void then + a_data_app.set_group (a_group) + end + + a_data_app.login (a_name.as_string_8, a_password.as_string_8) + a_data_app.set_base + + create session_control.make + + end + + report_database_schema_incompatibility (a_output: BOOLEAN) + -- Report the application code is not compatible with database schema version + -- if `a_output' is True, write error in io.error as well + require +-- incompatible_database_schema_version: not is_database_schema_version_compatible + local + db_v: READABLE_STRING_8 + do +-- if attached database_schema_version as v then +-- db_v := v.version +-- else +-- db_v := "?.?.?.?" +-- end +-- database_error_handler.add_error_details (0, "MISC Error", "Schema version incompatible (application=" +-- + database_storage_version.version + " database=" + db_v + ")." +-- ) + if a_output then + io.error.put_string (database_error_handler.as_string_representation) + io.error.put_new_line + end + end + +feature -- System Update + + update_system + do +-- if is_database_schema_version_compatible then +-- misc_manager.initialize_reference_types +-- history_manager.initialize_data +-- user_role_permission_manager.initialize_built_in_user_role_permission +-- user_role_permission_manager.initialize_extra_user_role_permission +-- task_manager.initialize_data +-- else +-- -- If schema incompatible, report it and exit +-- report_database_schema_incompatibility (True) +-- (create {EXCEPTIONS}).die (-1) +-- end + +-- [2012-Mars-21] Idea about update system implementation +-- if update_version < 01.00.0012 then +-- if update_version < 01.00.0005 then +-- update_version_01_00_0005 +-- end +-- update_version_01_00_0012 +-- end + end + + reset_storage_manager + do +-- initialize_managers (Current) + end + +feature -- Storage + + storage_url: READABLE_STRING_8 + -- Associated storage URL + + storage_connection_kept_alive: BOOLEAN + -- Keep storage connection alive? + -- i.e: never disconnect between 2 transactions. + + keep_storage_connection_alive (b: BOOLEAN) + do + storage_connection_kept_alive := b + end + + connect_storage + -- Connect the database + do + if not session_control.is_connected then + session_control.connect + end + end + + disconnect_from_storage + -- Disconnect from the storage + require + is_connected_to_storage: is_connected_to_storage + do + if not storage_connection_kept_alive then + session_control.disconnect + end + end + + force_disconnect_from_storage + -- Force disconnection from the storage + -- i.e ignore any `storage_connection_kept_alive' + do + if session_control.is_connected then + session_control.disconnect + end + end + + is_connected_to_storage: BOOLEAN + -- Is connected to the database + do + Result := session_control.is_connected + end + +feature -- Transaction Status + + in_transaction_session: BOOLEAN + -- Is session started? + + transaction_session_depth: INTEGER + -- Depth in the transaction session + +feature -- Transaction Operation + + start_transaction_session + -- Start session + -- if already started, increase the `transaction_session_depth' + require + in_transaction_session implies transaction_session_depth > 0 + not in_transaction_session implies transaction_session_depth = 0 + local + l_session_control: like session_control + l_retried: INTEGER + do + if l_retried = 0 then + if not in_transaction_session then + database_error_handler.reset + + check transaction_session_depth = 0 end + + debug ("database_session") + print ("..Start session%N") + end + connect_storage -- connect the DB + if is_connected_to_storage then + in_transaction_session := True + session_control.begin -- start transaction + else + l_session_control := session_control + if not l_session_control.is_ok then + database_error_handler.add_database_error (l_session_control.error_message_32, l_session_control.error_code) + else + database_error_handler.add_database_error (Session_not_started_error_message, 0) + end + l_session_control.reset + end + end + transaction_session_depth := transaction_session_depth + 1 + else + if l_retried = 1 then + transaction_session_depth := transaction_session_depth + 1 + if attached (create {EXCEPTION_MANAGER}).last_exception as e then + if attached {ASSERTION_VIOLATION} e then + --| Ignore for now with MYSQL ... + else + exception_as_error (e) + end + end + + in_transaction_session := False + session_control.reset + else + in_transaction_session := False + end + end + ensure + transaction_session_depth = (old transaction_session_depth) + 1 + rescue + l_retried := l_retried + 1 + retry + end + + end_transaction_session + -- End session + local + l_retried: BOOLEAN + do + if not l_retried then + transaction_session_depth := transaction_session_depth - 1 + if transaction_session_depth = 0 then + debug ("database_session") + print ("..End session%N") + end + if is_connected_to_storage then + if not database_error_handler.has_error then + session_control.commit -- Commit transaction + else + session_control.rollback -- Rollback transaction + end + disconnect_from_storage + end + in_transaction_session := False + end + else + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + in_transaction_session := False + transaction_session_depth := transaction_session_depth - 1 + session_control.reset + end + rescue + l_retried := True + retry + end + + execute_query (a_query: STRING_32) + -- Execute `a_q' + require + is_session_started: in_transaction_session + local + rescued: BOOLEAN + l_change: like new_database_change + do + if not rescued then + session_control.reset + l_change := new_database_change + l_change.set_query (a_query) + l_change.execute_query + else + database_error_handler.add_error_details (0, "Unexpected Error", "Unexpected Error when executing query") + end + rescue + rescued := True + retry + end + +feature -- Element Change + + set_last_inserted_id_function (a_f: like last_inserted_id_function) + -- Set `last_inserted_id_function' with `a_f' + do + last_inserted_id_function := a_f + ensure + last_inserted_id_function_set: last_inserted_id_function = a_f + end + +feature -- Access + + database_name: detachable READABLE_STRING_GENERAL + -- Database to access + + name: READABLE_STRING_GENERAL + -- Login user name + + password: READABLE_STRING_8 + -- Password + + host_name: detachable READABLE_STRING_8 + -- Host name, and port if needed + + role_id: detachable READABLE_STRING_8 + -- Role id + + role_password: detachable READABLE_STRING_8 + -- Role password + + data_source: detachable READABLE_STRING_8 + -- Data source + + group: detachable READABLE_STRING_8 + -- Group + + data_app: DATABASE_APPL [DATABASE] + -- Database application + + last_inserted_id_function: detachable FUNCTION [ANY, TUPLE, NATURAL_64] + -- Function to get last inserted id. + +feature -- Factory + + new_database_change: DB_CHANGE + -- Database change + do + create Result.make + end + + new_database_selection: DB_SELECTION + -- Database selection + do + create Result.make + end + + new_procedure (a_name: like {DB_PROC}.name): DB_PROC + -- Database procedure + do + create Result.make (a_name) + end + +feature {NONE} -- Implementation + + session_not_started_error_message: STRING_32 = "Session could not be started for unknown reason" + + session_control: DB_CONTROL + -- Session + +end diff --git a/persistence/implementation/common/database/shared_error_handler.e b/persistence/implementation/common/database/shared_error_handler.e new file mode 100644 index 0000000..9c26bfd --- /dev/null +++ b/persistence/implementation/common/database/shared_error_handler.e @@ -0,0 +1,27 @@ +note + description: "Shared error handler for database" + date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $" + revision: "$Revision: 195 $" + +class + SHARED_ERROR_HANDLER + +feature -- Access + + database_error_handler: DATABASE_ERROR_HANDLER + -- Error handler + once + create Result.make + end + +feature -- Helper + + exception_as_error (a_e: like {EXCEPTION_MANAGER}.last_exception) + -- Record exception as an error. + do + if attached a_e as l_e and then attached l_e.exception_trace as l_trace then + database_error_handler.add_error_details (l_e.code, once "Exception", l_trace.as_string_32) + end + end + +end diff --git a/persistence/implementation/mysql/persistence_mysql.ecf b/persistence/implementation/mysql/persistence_mysql.ecf index 4577af1..8b0e9aa 100644 --- a/persistence/implementation/mysql/persistence_mysql.ecf +++ b/persistence/implementation/mysql/persistence_mysql.ecf @@ -10,6 +10,8 @@ + + From 52cc566e15af795bbd1140da3776550b107deb90 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 1 Oct 2014 14:11:17 -0300 Subject: [PATCH 017/386] Added SQLite ODBC example under test/APPLICATION.e class. Fixed compilation issues with SQLite implemenation. (need to be updated) --- .../sqlite/persistence_sqlite.ecf | 1 + .../sqlite/src/cms_storage_sqlite.e | 62 ++++++++++++++++-- .../implementation/sqlite/tests/application.e | 3 +- .../implementation/sqlite/tests/cms_lite.db | Bin 0 -> 18432 bytes .../sqlite/tests/storage/storage_test_set.e | 8 +-- 5 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 persistence/implementation/sqlite/tests/cms_lite.db diff --git a/persistence/implementation/sqlite/persistence_sqlite.ecf b/persistence/implementation/sqlite/persistence_sqlite.ecf index d93e74c..c4c203c 100644 --- a/persistence/implementation/sqlite/persistence_sqlite.ecf +++ b/persistence/implementation/sqlite/persistence_sqlite.ecf @@ -17,6 +17,7 @@ + diff --git a/persistence/implementation/sqlite/src/cms_storage_sqlite.e b/persistence/implementation/sqlite/src/cms_storage_sqlite.e index 2a580af..849685e 100644 --- a/persistence/implementation/sqlite/src/cms_storage_sqlite.e +++ b/persistence/implementation/sqlite/src/cms_storage_sqlite.e @@ -100,6 +100,46 @@ feature -- Change: user end end +feature -- User Nodes + + user_collaborator_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] + -- Possible list of nodes where the user identified by `a_id', is a collaborator. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + to_implement ("Not implemented") + end + + user_author_nodes (a_id: like {CMS_USER}.id): LIST[CMS_NODE] + -- Possible list of nodes where the user identified by `a_id', is the author. + do + create {ARRAYED_LIST[CMS_NODE]} Result.make (0) + to_implement ("Not implemented") + end + +feature -- Users roles and permissions + + user_role_by_id (a_id: like {CMS_USER_ROLE}.id): detachable CMS_USER_ROLE + -- User role by id `a_id', if any. + do + to_implement ("Not implemented") + end + + user_roles: LIST [CMS_USER_ROLE] + -- Possible list of user roles. + do + create {ARRAYED_LIST[CMS_USER_ROLE]} Result.make (0) + to_implement ("Not implemented") + end + +feature -- Change: roles and permissions + + save_user_role (a_user_role: CMS_USER_ROLE) + -- Save user role `a_user_role' + do + to_implement ("Not implemented") + end + + feature -- Access: node nodes: LIST[CMS_NODE] @@ -145,31 +185,45 @@ feature -- Node post_node_provider_execution end - update_node (a_node: CMS_NODE) + update_node (a_id: like {CMS_USER}.id; a_node: CMS_NODE) do node_provider.update_node (a_node) post_node_provider_execution end - update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) + update_node_title (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) do node_provider.update_node_title (a_id, a_title) post_node_provider_execution end - update_node_summary (a_id: INTEGER_64; a_summary: READABLE_STRING_32) + update_node_summary (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) do node_provider.update_node_summary (a_id, a_summary) post_node_provider_execution end - update_node_content (a_id: INTEGER_64; a_content: READABLE_STRING_32) + update_node_content (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) do node_provider.update_node_content (a_id, a_content) post_node_provider_execution end + node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER + -- Node's author. if any. + do + to_implement ("Not implemented") + end + + node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER] + -- Possible list of node's collaborator. + do + create {ARRAYED_LIST[CMS_USER]} Result.make (0) + to_implement ("Not implemented") + end + + feature -- User new_user (a_user: CMS_USER) diff --git a/persistence/implementation/sqlite/tests/application.e b/persistence/implementation/sqlite/tests/application.e index fc10bf5..82c32f0 100644 --- a/persistence/implementation/sqlite/tests/application.e +++ b/persistence/implementation/sqlite/tests/application.e @@ -21,7 +21,8 @@ feature {NONE} -- Initialization node: NODE_DATA_PROVIDER l_security: SECURITY_PROVIDER do - create connection.make_basic ("cms_dev") + -- Change the path. + create connection.login_with_connection_string ("Driver=SQLite3 ODBC Driver;Database=./cms_lite.db;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;") create user.make (connection) user.new_user ("test", "test", "test") end diff --git a/persistence/implementation/sqlite/tests/cms_lite.db b/persistence/implementation/sqlite/tests/cms_lite.db new file mode 100644 index 0000000000000000000000000000000000000000..cad98d57c65c91c5eab1bed25255d9c8ad40c714 GIT binary patch literal 18432 zcmeHO-EJC36rN%EF(f2yRC{k)X}Z1v#jU_DL6%C|?Xtuu_JY??$EqT-F-uCrPs}cg zdsX4Iy-(FA=o92MdQtVNPf)3k5Vh(}=LePrV=xU8b$8Zb*_}Ca=A3V4n9+Am4)%BJ zwux%(PSdc_7VrZI!KVlT0DSm!3x5Vz7(e(2FZdg}o-cazfe-!&25_xUcui#968;um z!xAiA)ZqV69uEe^j0i!!RW(nolLiL3Vsz~`eXn#avtw0~^efc+SO|%t2+s~}<8j06 zv>T>HZ+=Br<&ug@@@Km$l4zL}i$)NV>QxElwUT;I)lpH;@5%ZRy00D~xm+sbHQa7b z)k;V!;7@sX7b!ccaz92|-OVK88&m~RC!x<}UD=WKSTd1_JMvUODQE{JUBf_Dz+dzQ#oM>x5%skXjAfp5&DOCw_9~3{KUNuQ z8BKEow~U|Ng%rPUzo0r>?W$?fn{TAOM9UV3-?Q%HM*Y~Z>+M#hYSOM0Z$-6)<491Guxg$;WBrUBj9laR-hn;=?RqY{~n(T+MG6Gvm?dvVxR zvWy0f)J+jEn?}7cl-Nv7i`+VPjYe;sViLjS{Ga=`jz4n*yoCVg|GYKc+*^)-8v(Na z`(X{h8hj}vgeTxv@FV_P>Xo`46!YuT^XK}xW%MLhp)WUyC%tzfA@Sil?4@Y39jm6v zUmw<<_EYb^G^`3O>c7{kr!Ki@|EQSPvg!jQxoW0rkWfJzk(Ofpwm9)FK1W92i+4jI zaeWp4S%$fSuIBG)B&RXv(!&n1x|&mURa4XhG)U?rsg0NFg}OYc69)@? zA|`o`?oL=Enm6}~xhLlJxhCdN#iW_Z zEF`$`{eP)Hb-Z(qz)}(5>wl^6^UgT}OGSY5e@lg*cg_)5Dgu1}Un=~(bB@4L5n%EE zM*#ldiwi%4zrdr#n$ugmah~{~eBCIW#`ru+wj_td2Q9rdiTJpL3ODgV#YZT)-dc1H z!)W_)!``0Du%U`chS6Hem;Fa6%op!nqrBwz(8Wtqs~LMnA8$(1nY^SUIfF4|a`ZltmmKOjPZ`oWJlSURl1vMm^>0c{{yWl~@BfSaz2n+A0#^wE QzW%S$XmMLP0*gi9AA}@d5C8xG literal 0 HcmV?d00001 diff --git a/persistence/implementation/sqlite/tests/storage/storage_test_set.e b/persistence/implementation/sqlite/tests/storage/storage_test_set.e index 521d78b..4d893ed 100644 --- a/persistence/implementation/sqlite/tests/storage/storage_test_set.e +++ b/persistence/implementation/sqlite/tests/storage/storage_test_set.e @@ -231,7 +231,7 @@ feature -- Test routines l_node.set_summary ("New Summary") l_node.set_title("New Title") - storage.update_node (l_node) +-- storage.update_node (l_node) assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then not (u_node.content ~ ll_node.content) and then not (u_node.summary ~ ll_node.summary)) end end @@ -243,7 +243,7 @@ feature -- Test routines do storage.save_node (custom_node ("Content", "Summary", "Title")) if attached {CMS_NODE} storage.node (1) as ll_node then - storage.update_node_title (ll_node.id, "New Title") +-- storage.update_node_title (ll_node.id, "New Title") assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then not (u_node.title ~ ll_node.title) and then u_node.content ~ ll_node.content and then u_node.summary ~ ll_node.summary) end end @@ -255,7 +255,7 @@ feature -- Test routines do storage.save_node (custom_node ("Content", "Summary", "Title")) if attached {CMS_NODE} storage.node (1) as ll_node then - storage.update_node_summary (ll_node.id, "New Summary") +-- storage.update_node_summary (ll_node.id, "New Summary") assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then u_node.content ~ ll_node.content and then not (u_node.summary ~ ll_node.summary)) end end @@ -267,7 +267,7 @@ feature -- Test routines do storage.save_node (custom_node ("Content", "Summary", "Title")) if attached {CMS_NODE} storage.node (1) as ll_node then - storage.update_node_content (ll_node.id, "New Content") +-- storage.update_node_content (ll_node.id, "New Content") assert ("Updated", attached {CMS_NODE} storage.node (1) as u_node and then u_node.title ~ ll_node.title and then not (u_node.content ~ ll_node.content) and then u_node.summary ~ ll_node.summary) end end From 5a5eb0dea2c002188b938e06d7735e0d9526a14e Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 1 Oct 2014 14:14:09 -0300 Subject: [PATCH 018/386] Forgot to commit the README FILE --- .../implementation/sqlite/tests/Readme.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 persistence/implementation/sqlite/tests/Readme.md diff --git a/persistence/implementation/sqlite/tests/Readme.md b/persistence/implementation/sqlite/tests/Readme.md new file mode 100644 index 0000000..b81d64f --- /dev/null +++ b/persistence/implementation/sqlite/tests/Readme.md @@ -0,0 +1,26 @@ +SQLite ODBC. + +Install the odbc driver from http://www.ch-werner.de/sqliteodbc/ + +Current version + sqliteodbc.exe -- Win32 + sqliteodbc_w64.exe -- Win64 + + +Test the ODBC driver using Firefox SQLite DBManager https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/ + +1. Open the odbc manger from Windows, create a new database using the SQLite3 ODBC driver and then open it from Firefox. + + +EiffelStore + SQLiteODBC. + +Connection String: https://www.connectionstrings.com/sqlite3-odbc-driver/ + + "Driver=SQLite3 ODBC Driver;Database=./cms_lite.db;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;" + +Edit the database location based on your system. + + + + + From 55251341482e590888a0487e08062be4817a3665 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 1 Oct 2014 17:47:16 -0300 Subject: [PATCH 019/386] Updated CMS --- .gitignore | 2 + .../node/handler/node_content_handler.e | 55 +++++++------------ cms/src/modules/node/handler/node_handler.e | 37 ++++++------- .../node/handler/node_summary_handler.e | 54 +++++++----------- .../modules/node/handler/node_title_handler.e | 46 ++++++---------- cms/src/modules/node/handler/nodes_handler.e | 9 ++- cms/src/modules/node/node_module.e | 2 - cms/src/service/cms_request_util.e | 4 +- cms/src/service/cms_service.e | 9 ++- cms/src/service/response/cms_response.e | 13 +++++ .../response/error_500_cms_response.e} | 10 ++-- .../response/generic_view_cms_response.e} | 4 +- cms/src/{kernel => theme}/cms_html_page.e | 0 examples/roc_api/roc_api.ecf | 2 +- .../mysql/persistence_mysql.ecf | 8 ++- 15 files changed, 122 insertions(+), 133 deletions(-) rename cms/src/{modules/node/response/nodes_view_cms_response.e => service/response/error_500_cms_response.e} (51%) rename cms/src/{modules/node/response/node_view_cms_response.e => service/response/generic_view_cms_response.e} (90%) rename cms/src/{kernel => theme}/cms_html_page.e (100%) diff --git a/.gitignore b/.gitignore index 2ca11d6..1878f3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ EIFGENs *.swp +*.log* +*.rc \ No newline at end of file diff --git a/cms/src/modules/node/handler/node_content_handler.e b/cms/src/modules/node/handler/node_content_handler.e index 7fe8fc3..a15bf8a 100644 --- a/cms/src/modules/node/handler/node_content_handler.e +++ b/cms/src/modules/node/handler/node_content_handler.e @@ -70,7 +70,7 @@ feature -- HTTP Methods -- Existing node if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then - create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_content") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_content") l_page.add_variable (l_node.content, "content") l_page.add_variable (l_id.value, "id") l_page.execute @@ -78,12 +78,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - -- Todo extract method - to_implement ("Check how to implemet API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -94,7 +89,7 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- local - u_node: CMS_NODE + l_page: CMS_RESPONSE do if attached current_user_name (req) then if attached {WSF_STRING} req.path_parameter ("id") as l_id then @@ -103,22 +98,14 @@ feature -- HTTP Methods if l_method.is_case_insensitive_equal ("PUT") then do_put (req, res) else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end end else do_error (req, res, l_id) end else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -129,7 +116,7 @@ feature -- HTTP Methods -- local u_node: CMS_NODE --- l_page: ROC_RESPONSE + l_page: CMS_RESPONSE do to_implement ("Check if user has permissions") if attached current_user (req) as l_user then @@ -143,11 +130,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -158,18 +141,20 @@ feature -- Error do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) -- Handling error. local + l_page: CMS_RESPONSE do - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- if a_id.is_integer then --- -- resource not found --- l_page.set_value ("404", "code") --- else --- -- bad request --- l_page.set_value ("400", "code") --- end --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to(res) + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") + l_page.add_variable (req.absolute_script_url (req.path_info), "request") + if a_id.is_integer then + -- resource not found + l_page.add_variable ("404", "code") + l_page.set_status_code (404) + else + -- bad request + l_page.add_variable ("400", "code") + l_page.set_status_code (400) + end + l_page.execute end diff --git a/cms/src/modules/node/handler/node_handler.e b/cms/src/modules/node/handler/node_handler.e index 1580099..e9d789f 100644 --- a/cms/src/modules/node/handler/node_handler.e +++ b/cms/src/modules/node/handler/node_handler.e @@ -69,7 +69,7 @@ feature -- HTTP Methods -- Existing node if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then - create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup,"modules/node") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup,"modules/node") l_page.add_variable (l_node, "node") l_page.execute else @@ -97,11 +97,7 @@ feature -- HTTP Methods elseif l_method.is_case_insensitive_equal ("PUT") then do_put (req, res) else - to_implement ("Implement specific responses for 500 pages") - create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") - l_page.add_variable ("500", "code") - l_page.add_variable (req.absolute_script_url (req.path_info), "request") - l_page.execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end end else @@ -136,7 +132,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - -- Internal server error + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -156,7 +152,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - -- Internal server error + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -170,17 +166,18 @@ feature -- Error local l_page: CMS_RESPONSE do - to_implement ("Not implemented") --- create l_page.make (req, "master2/error.tpl") --- if a_id.is_integer then --- -- resource not found --- l_page.set_value ("404", "code") --- else --- -- bad request --- l_page.set_value ("400", "code") --- end --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") + l_page.add_variable (req.absolute_script_url (req.path_info), "request") + if a_id.is_integer then + -- resource not found + l_page.add_variable ("404", "code") + l_page.set_status_code (404) + else + -- bad request + l_page.add_variable ("400", "code") + l_page.set_status_code (400) + end + l_page.execute end feature {NONE} -- Node @@ -190,7 +187,7 @@ feature {NONE} -- Node l_page: CMS_RESPONSE do if attached current_user_name (req) then - create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node") l_page.add_variable (setup.is_html, "html") l_page.add_variable (setup.is_web, "web") l_page.execute diff --git a/cms/src/modules/node/handler/node_summary_handler.e b/cms/src/modules/node/handler/node_summary_handler.e index 9e38e96..8e24af2 100644 --- a/cms/src/modules/node/handler/node_summary_handler.e +++ b/cms/src/modules/node/handler/node_summary_handler.e @@ -69,9 +69,7 @@ feature -- HTTP Methods -- Existing node if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then - create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_summary") - l_page.add_variable (setup.is_html, "html") - l_page.add_variable (setup.is_web, "web") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_summary") l_page.add_variable (l_node.summary, "summary") l_page.add_variable (l_id.value, "id") l_page.execute @@ -79,11 +77,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -103,22 +97,14 @@ feature -- HTTP Methods if l_method.is_case_insensitive_equal ("PUT") then do_put (req, res) else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end end else do_error (req, res, l_id) end else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -130,7 +116,6 @@ feature -- HTTP Methods local u_node: CMS_NODE do - to_implement ("Check if user has permissions!!!") if attached current_user (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then @@ -142,11 +127,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -155,21 +136,24 @@ feature -- HTTP Methods feature -- Error + do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) -- Handling error. local --- l_page: ROC_RESPONSE + l_page: CMS_RESPONSE do --- create l_page.make (req, "master2/error.tpl") --- if a_id.is_integer then --- -- resource not found --- l_page.set_value ("404", "code") --- else --- -- bad request --- l_page.set_value ("400", "code") --- end --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to(res) + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") + l_page.add_variable (req.absolute_script_url (req.path_info), "request") + if a_id.is_integer then + -- resource not found + l_page.add_variable ("404", "code") + l_page.set_status_code (404) + else + -- bad request + l_page.add_variable ("400", "code") + l_page.set_status_code (400) + end + l_page.execute end diff --git a/cms/src/modules/node/handler/node_title_handler.e b/cms/src/modules/node/handler/node_title_handler.e index 7218ddc..e13dca7 100644 --- a/cms/src/modules/node/handler/node_title_handler.e +++ b/cms/src/modules/node/handler/node_title_handler.e @@ -69,7 +69,7 @@ feature -- HTTP Methods -- Existing node if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then - create {NODE_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_title") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_title") l_page.add_variable (setup.is_html, "html") l_page.add_variable (setup.is_web, "web") l_page.add_variable (l_node.title, "title") @@ -80,7 +80,7 @@ feature -- HTTP Methods end else to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") +-- create l_page.make (req, "master2/error") -- l_page.set_value ("500", "code") -- l_page.set_value (req.absolute_script_url (req.path_info), "request") -- l_page.send_to (res) @@ -102,22 +102,14 @@ feature -- HTTP Methods if l_method.is_case_insensitive_equal ("PUT") then do_put (req, res) else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end end else do_error (req, res, l_id) end else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -141,11 +133,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - to_implement ("check how to implement API error") --- create l_page.make (req, "master2/error.tpl") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -157,18 +145,20 @@ feature -- Error do_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_id: WSF_STRING) -- Handling error. local + l_page: CMS_RESPONSE do - to_implement ("Check how to add API error") --- create l_page.make (req, "master2/error.tpl") --- if a_id.is_integer then --- -- resource not found --- l_page.set_value ("404", "code") --- else --- -- bad request --- l_page.set_value ("400", "code") --- end --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") + l_page.add_variable (req.absolute_script_url (req.path_info), "request") + if a_id.is_integer then + -- resource not found + l_page.add_variable ("404", "code") + l_page.set_status_code (404) + else + -- bad request + l_page.add_variable ("400", "code") + l_page.set_status_code (400) + end + l_page.execute end diff --git a/cms/src/modules/node/handler/nodes_handler.e b/cms/src/modules/node/handler/nodes_handler.e index 6b99297..f1097db 100644 --- a/cms/src/modules/node/handler/nodes_handler.e +++ b/cms/src/modules/node/handler/nodes_handler.e @@ -46,7 +46,14 @@ feature -- HTTP Methods do_get (req: WSF_REQUEST; res: WSF_RESPONSE) -- + local + l_page: CMS_RESPONSE do - (create {NODES_VIEW_CMS_RESPONSE}.make (req, res, setup,"modules/nodes")).execute + -- At the moment the template is hardcoded, but we can + -- get them from the configuration file and load them into + -- the setup class. + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/nodes") + l_page.add_variable (api_service.nodes, "nodes") + l_page.execute end end diff --git a/cms/src/modules/node/node_module.e b/cms/src/modules/node/node_module.e index 0470288..9e8a805 100644 --- a/cms/src/modules/node/node_module.e +++ b/cms/src/modules/node/node_module.e @@ -38,8 +38,6 @@ feature -- Implementation setup_router -- Setup `router'. - local - fhdl: WSF_FILE_SYSTEM_HANDLER do create router.make (5) configure_api_node diff --git a/cms/src/service/cms_request_util.e b/cms/src/service/cms_request_util.e index 300d41d..e9e26c8 100644 --- a/cms/src/service/cms_request_util.e +++ b/cms/src/service/cms_request_util.e @@ -21,8 +21,8 @@ feature -- User if attached {CMS_USER} current_user (req) as l_user then Result := l_user.name end - fixme ("Workaround to add nodes!!!") - Result := "admin" -- Workaround + + end current_user (req: WSF_REQUEST): detachable CMS_USER diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index 6343ab0..85fb7a1 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -17,6 +17,13 @@ inherit execute_default end + WSF_FILTERED_SERVICE + + WSF_FILTER + rename + execute as execute_filter + end + WSF_NO_PROXY_POLICY WSF_URI_HELPER_FOR_ROUTED_SERVICE @@ -37,6 +44,7 @@ feature {NONE} -- Initialization setup := a_setup configuration := a_setup.configuration modules := a_setup.modules + initialize_users initialize_auth_engine initialize_mailer initialize_router @@ -47,7 +55,6 @@ feature {NONE} -- Initialization initialize_users do - to_implement ("To Implement initialize users") end initialize_mailer diff --git a/cms/src/service/response/cms_response.e b/cms/src/service/response/cms_response.e index 1f96824..3dd6f57 100644 --- a/cms/src/service/response/cms_response.e +++ b/cms/src/service/response/cms_response.e @@ -92,6 +92,19 @@ feature -- Theme end end +feature -- Element Change + + set_status_code (a_status: INTEGER) + -- Set `status_code' with `a_status'. + note + EIS: "src=eiffel:?class=HTTP_STATUS_CODE" + do + to_implement ("Feature to test if a_status is a valid status code!!!.") + status_code := a_status + ensure + status_code_set: status_code = a_status + end + feature -- Generation prepare (page: CMS_HTML_PAGE) diff --git a/cms/src/modules/node/response/nodes_view_cms_response.e b/cms/src/service/response/error_500_cms_response.e similarity index 51% rename from cms/src/modules/node/response/nodes_view_cms_response.e rename to cms/src/service/response/error_500_cms_response.e index ecddfe0..24c908e 100644 --- a/cms/src/modules/node/response/nodes_view_cms_response.e +++ b/cms/src/service/response/error_500_cms_response.e @@ -1,10 +1,10 @@ note - description: "Summary description for {NODE_VIEW_CMS_RESPONSE}." + description: "Summary description for {ERROR_500_CMS_RESPONSE}." date: "$Date$" revision: "$Revision$" class - NODES_VIEW_CMS_RESPONSE + ERROR_500_CMS_RESPONSE inherit @@ -20,7 +20,9 @@ feature -- Generation custom_prepare (page: CMS_HTML_PAGE) do - page.register_variable (setup.api_service.nodes, "nodes") + page.register_variable (request.absolute_script_url (request.path_info), "request") + page.register_variable ("500", "code") + page.set_status_code (500) end feature -- Execution @@ -28,7 +30,7 @@ feature -- Execution process -- Computed response message. do - set_title ("List of Nodes") + set_title ("Internal Server Error") set_page_title (Void) end end diff --git a/cms/src/modules/node/response/node_view_cms_response.e b/cms/src/service/response/generic_view_cms_response.e similarity index 90% rename from cms/src/modules/node/response/node_view_cms_response.e rename to cms/src/service/response/generic_view_cms_response.e index f115673..28d01bc 100644 --- a/cms/src/modules/node/response/node_view_cms_response.e +++ b/cms/src/service/response/generic_view_cms_response.e @@ -4,7 +4,7 @@ note revision: "$Revision$" class - NODE_VIEW_CMS_RESPONSE + GENERIC_VIEW_CMS_RESPONSE inherit @@ -30,7 +30,7 @@ feature -- Execution process -- Computed response message. do - set_title ("List of Nodes") + set_title ("CMS") set_page_title (Void) end end diff --git a/cms/src/kernel/cms_html_page.e b/cms/src/theme/cms_html_page.e similarity index 100% rename from cms/src/kernel/cms_html_page.e rename to cms/src/theme/cms_html_page.e diff --git a/examples/roc_api/roc_api.ecf b/examples/roc_api/roc_api.ecf index 68833eb..2cf8d79 100644 --- a/examples/roc_api/roc_api.ecf +++ b/examples/roc_api/roc_api.ecf @@ -17,7 +17,7 @@ - + diff --git a/persistence/implementation/mysql/persistence_mysql.ecf b/persistence/implementation/mysql/persistence_mysql.ecf index 8b0e9aa..d66d48e 100644 --- a/persistence/implementation/mysql/persistence_mysql.ecf +++ b/persistence/implementation/mysql/persistence_mysql.ecf @@ -15,7 +15,6 @@ - @@ -27,6 +26,11 @@ - + + + /database/database_connection_odbc.e + + + From e053ffceeffbd51b9ed190d7c3cc7e97c2bf57fc Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 2 Oct 2014 12:00:10 -0300 Subject: [PATCH 020/386] Fixed issue with Filters. --- cms/src/service/cms_service.e | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index 85fb7a1..3de2080 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -17,13 +17,6 @@ inherit execute_default end - WSF_FILTERED_SERVICE - - WSF_FILTER - rename - execute as execute_filter - end - WSF_NO_PROXY_POLICY WSF_URI_HELPER_FOR_ROUTED_SERVICE @@ -51,8 +44,6 @@ feature {NONE} -- Initialization initialize_modules end - - initialize_users do end From a0cb7c0ecc30eace5f202b8c1f56aed048d08094 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 2 Oct 2014 15:32:30 -0300 Subject: [PATCH 021/386] Added Initial Basic Auth Module --- cms/cms.ecf | 1 + cms/src/configuration/cms_default_setup.e | 4 + .../modules/basic_auth/basic_auth_module.e | 79 +++++++++++++++++++ .../basic_auth/filter/basic_auth_filter.e | 51 ++++++++++++ .../modules/basic_auth/filter/cors_filter.e | 28 +++++++ .../handler/basic_auth_login_handler.e | 62 +++++++++++++++ .../handler/basic_auth_logoff_handler.e | 59 ++++++++++++++ cms/src/modules/cms_module.e | 20 +++++ cms/src/service/cms_service.e | 61 +++++++++++++- .../service/response/cms_generic_response.e | 2 +- cms/src/service/response/home_cms_response.e | 1 + examples/api/src/roc_server.e | 10 ++- .../src/service/handler/roc_logoff_handler.e | 1 + .../api/master2/{ => basic_auth}/logoff.tpl | 0 .../themes/api/master2/site_navigation.tpl | 4 +- 15 files changed, 372 insertions(+), 11 deletions(-) create mode 100644 cms/src/modules/basic_auth/basic_auth_module.e create mode 100644 cms/src/modules/basic_auth/filter/basic_auth_filter.e create mode 100644 cms/src/modules/basic_auth/filter/cors_filter.e create mode 100644 cms/src/modules/basic_auth/handler/basic_auth_login_handler.e create mode 100644 cms/src/modules/basic_auth/handler/basic_auth_logoff_handler.e rename examples/roc_api/site/www/themes/api/master2/{ => basic_auth}/logoff.tpl (100%) diff --git a/cms/cms.ecf b/cms/cms.ecf index 6aa2623..dc20d24 100644 --- a/cms/cms.ecf +++ b/cms/cms.ecf @@ -10,6 +10,7 @@ + diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index 74f0754..dac8deb 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -85,6 +85,10 @@ feature {NONE} -- Initialization create {NODE_MODULE} m.make (Current) m.enable modules.extend (m) + + create {BASIC_AUTH_MODULE} m.make (Current) + m.enable + modules.extend (m) end build_api_service diff --git a/cms/src/modules/basic_auth/basic_auth_module.e b/cms/src/modules/basic_auth/basic_auth_module.e new file mode 100644 index 0000000..f669a6d --- /dev/null +++ b/cms/src/modules/basic_auth/basic_auth_module.e @@ -0,0 +1,79 @@ +note + description: "Summary description for {BASIC_AUTH_MODULE}." + date: "$Date$" + revision: "$Revision$" + +class + BASIC_AUTH_MODULE + +inherit + + CMS_MODULE + +create + make + +feature {NONE} -- Initialization + + make (a_config: CMS_SETUP) + do + name := "basic auth" + version := "1.0" + description := "Service to manage basic authentication" + package := "core" + config := a_config + setup_router + setup_filter + enable + end + +feature -- Access + + router: WSF_ROUTER + -- Node router. + + config: CMS_SETUP + -- Node configuration. + +feature -- Implementation + + setup_router + -- Setup `router'. + do + create router.make (2) + configure_api_login + configure_api_logoff + end + + setup_filter + -- Setup `filter'. + do + add_filter (create {CORS_FILTER}) + add_filter (create {BASIC_AUTH_FILTER}.make (config)) + end + +feature -- Configure Node Resources Routes + + configure_api_login + local + l_bal_handler: BASIC_AUTH_LOGIN_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_bal_handler.make (config) + create l_methods + l_methods.enable_get + router.handle_with_request_methods ("/basic_auth/login", l_bal_handler, l_methods) + end + + configure_api_logoff + local + l_bal_handler: BASIC_AUTH_LOGOFF_HANDLER + l_methods: WSF_REQUEST_METHODS + do + create l_bal_handler.make (config) + create l_methods + l_methods.enable_get + router.handle_with_request_methods ("/basic_auth/logoff", l_bal_handler, l_methods) + end + +end diff --git a/cms/src/modules/basic_auth/filter/basic_auth_filter.e b/cms/src/modules/basic_auth/filter/basic_auth_filter.e new file mode 100644 index 0000000..1b4502b --- /dev/null +++ b/cms/src/modules/basic_auth/filter/basic_auth_filter.e @@ -0,0 +1,51 @@ +note + description: "Summary description for {BASIC_AUTH_FILTER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + BASIC_AUTH_FILTER + +inherit + + WSF_URI_TEMPLATE_HANDLER + CMS_HANDLER + WSF_FILTER + +create + make + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter + local + l_auth: HTTP_AUTHORIZATION + do + log.write_debug (generator + ".execute " ) + create l_auth.make (req.http_authorization) + if attached req.raw_header_data as l_raw_data then + log.write_debug (generator + ".execute " + l_raw_data ) + end + -- A valid user + if (attached l_auth.type as l_auth_type and then l_auth_type.is_case_insensitive_equal_general ("basic")) and then + attached l_auth.login as l_auth_login and then attached l_auth.password as l_auth_password then + if api_service.login_valid (l_auth_login, l_auth_password) then + if attached api_service.user_by_name (l_auth_login) as l_user then + req.set_execution_variable ("user", l_user) + execute_next (req, res) + else + -- Internal server error + end + else + log.write_error (generator + ".execute login_valid failed for: " + l_auth_login ) + execute_next (req, res) + end + else + log.write_error (generator + ".execute Not valid") + execute_next (req, res) + end + end + +end diff --git a/cms/src/modules/basic_auth/filter/cors_filter.e b/cms/src/modules/basic_auth/filter/cors_filter.e new file mode 100644 index 0000000..bea5958 --- /dev/null +++ b/cms/src/modules/basic_auth/filter/cors_filter.e @@ -0,0 +1,28 @@ +note + description: "CORS filter" + date: "$Date: 2014-08-08 16:02:11 -0300 (vi., 08 ago. 2014) $" + revision: "$Revision: 95593 $" + +class + CORS_FILTER + +inherit + + WSF_FILTER + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + local + l_header: HTTP_HEADER + do + create l_header.make +-- l_header.add_header_key_value ("Access-Control-Allow-Origin", "localhost") + l_header.add_header_key_value ("Access-Control-Allow-Headers", "*") + l_header.add_header_key_value ("Access-Control-Allow-Methods", "*") + l_header.add_header_key_value ("Access-Control-Allow-Credentials", "true") + res.put_header_lines (l_header) + execute_next (req, res) + end +end diff --git a/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e new file mode 100644 index 0000000..8f27b2f --- /dev/null +++ b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e @@ -0,0 +1,62 @@ +note + description: "Summary description for {BASIC_AUTH_LOGIN_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + BASIC_AUTH_LOGIN_HANDLER + +inherit + CMS_HANDLER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_FILTER + + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + execute_next (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + do + log.write_information(generator + ".do_get Processing basic auth login") + if attached {STRING_32} current_user_name (req) as l_user then + (create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url("")) + else + (create {CMS_GENERIC_RESPONSE}).new_response_authenticate (req, res) + end + end + + +end diff --git a/cms/src/modules/basic_auth/handler/basic_auth_logoff_handler.e b/cms/src/modules/basic_auth/handler/basic_auth_logoff_handler.e new file mode 100644 index 0000000..b274ab4 --- /dev/null +++ b/cms/src/modules/basic_auth/handler/basic_auth_logoff_handler.e @@ -0,0 +1,59 @@ +note + description: "Summary description for {BASIC_AUTH_LOGOFF_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + BASIC_AUTH_LOGOFF_HANDLER + +inherit + CMS_HANDLER + + WSF_URI_HANDLER + rename + execute as uri_execute, + new_mapping as new_uri_mapping + end + + WSF_RESOURCE_HANDLER_HELPER + redefine + do_get + end + + REFACTORING_HELPER + +create + make + +feature -- execute + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + + uri_execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute request handler. + do + execute_methods (req, res) + end + +feature -- HTTP Methods + + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + local + l_page: CMS_RESPONSE + do + log.write_information(generator + ".do_get Processing basic auth logoff") + if attached req.query_parameter ("prompt") as l_prompt then + (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) + else + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/basic_auth/logoff") + l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) + l_page.execute + end + end + +end diff --git a/cms/src/modules/cms_module.e b/cms/src/modules/cms_module.e index 4a40053..3979fff 100644 --- a/cms/src/modules/cms_module.e +++ b/cms/src/modules/cms_module.e @@ -25,6 +25,26 @@ feature -- Router deferred end +feature -- Filter + + filters: detachable LIST[WSF_FILTER] + -- Possibly list of Filter's module. + +feature -- Element Change: Filter + + add_filter (a_filter: WSF_FILTER) + -- Add a filter `a_filter' to the list of module filters `filters'. + local + l_filters: like filters + do + l_filters := filters + if l_filters = Void then + create {ARRAYED_LIST[WSF_FILTER]}l_filters.make (1) + filters := l_filters + end + l_filters.force (a_filter) + end + feature -- Settings enable diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index 85fb7a1..f786a83 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -11,6 +11,8 @@ class inherit WSF_ROUTED_SKELETON_SERVICE + rename + execute as execute_service undefine requires_proxy redefine @@ -44,15 +46,15 @@ feature {NONE} -- Initialization setup := a_setup configuration := a_setup.configuration modules := a_setup.modules + create {ARRAYED_LIST[WSF_FILTER]} filters.make (0) initialize_users initialize_auth_engine initialize_mailer initialize_router initialize_modules + initialize_filter end - - initialize_users do end @@ -78,6 +80,9 @@ feature {NONE} -- Initialization if m.item.is_enabled then router.import (m.item.router) end + if attached m.item.filters as l_m_filters then + filters.append (l_m_filters) + end end configure_api_file_handler end @@ -98,6 +103,7 @@ feature {NONE} -- Initialization create l_methods l_methods.enable_get router.handle_with_request_methods ("/", l_root_handler, l_methods) + router.handle_with_request_methods ("", l_root_handler, l_methods) end configure_api_file_handler @@ -128,10 +134,57 @@ feature -- Access -- List of possible modules. -- | Maybe we can compute it (using `setup') instead of using memory. -feature -- Logging + filters: LIST[WSF_FILTER] + -- List of possible filters. -feature -- Notification +feature -- Execute Filter + execute_filter (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + do + + res.put_header_line ("Date: " + (create {HTTP_DATE}.make_now_utc).string) + execute_service (req, res) + end + +feature -- Filters + + create_filter + -- Create `filter'. + local + f, l_filter: detachable WSF_FILTER + fh: WSF_CUSTOM_HEADER_FILTER + do + l_filter := Void + -- Maintenance + create {WSF_MAINTENANCE_FILTER} f + f.set_next (l_filter) + l_filter := f + + if attached filters as l_filters then + across l_filters as c loop + c.item.set_next (l_filter) + l_filter := c.item + end + end + + filter := l_filter + end + + setup_filter + -- Setup `filter'. + local + f: WSF_FILTER + do + from + f := filter + until + not attached f.next as l_next + loop + f := l_next + end + f.set_next (Current) + end feature -- Execution diff --git a/cms/src/service/response/cms_generic_response.e b/cms/src/service/response/cms_generic_response.e index 872e852..380a391 100644 --- a/cms/src/service/response/cms_generic_response.e +++ b/cms/src/service/response/cms_generic_response.e @@ -29,7 +29,7 @@ feature -- Responses create h.make h.put_content_type_text_html h.put_current_date - h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"CMS-User%"") + h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"CMSRoc-User%"") res.set_status_code ({HTTP_STATUS_CODE}.unauthorized) res.put_header_text (h.string) end diff --git a/cms/src/service/response/home_cms_response.e b/cms/src/service/response/home_cms_response.e index b38c0e4..cf51ad9 100644 --- a/cms/src/service/response/home_cms_response.e +++ b/cms/src/service/response/home_cms_response.e @@ -20,6 +20,7 @@ feature -- Generation custom_prepare (page: CMS_HTML_PAGE) do + Precursor (page) page.register_variable (setup.api_service.recent_nodes (0, 5), "nodes") end diff --git a/examples/api/src/roc_server.e b/examples/api/src/roc_server.e index 0a4c090..20d9d7d 100644 --- a/examples/api/src/roc_server.e +++ b/examples/api/src/roc_server.e @@ -174,6 +174,12 @@ feature -- Filters f.set_next (l_filter) l_filter := f + -- Authentication + create {AUTHENTICATION_FILTER} f.make (roc_config) + f.set_next (l_filter) + l_filter := f + + -- Logger Filter create {LOGGER_FILTER} f.make (roc_config) f.set_next (l_filter) @@ -184,10 +190,6 @@ feature -- Filters f.set_next (l_filter) l_filter := f - -- Authentication - create {AUTHENTICATION_FILTER} f.make (roc_config) - f.set_next (l_filter) - l_filter := f filter := l_filter end diff --git a/examples/api/src/service/handler/roc_logoff_handler.e b/examples/api/src/service/handler/roc_logoff_handler.e index dcc0071..5e85400 100644 --- a/examples/api/src/service/handler/roc_logoff_handler.e +++ b/examples/api/src/service/handler/roc_logoff_handler.e @@ -52,6 +52,7 @@ feature -- HTTP Methods if attached req.query_parameter ("prompt") as l_prompt then (create {ROC_RESPONSE}.make(req,"")).new_response_unauthorized (req, res) else + req.unset_execution_variable ("user") (create {ROC_RESPONSE}.make(req,"master2/logoff.tpl")).new_response_denied (req, res) end end diff --git a/examples/roc_api/site/www/themes/api/master2/logoff.tpl b/examples/roc_api/site/www/themes/api/master2/basic_auth/logoff.tpl similarity index 100% rename from examples/roc_api/site/www/themes/api/master2/logoff.tpl rename to examples/roc_api/site/www/themes/api/master2/basic_auth/logoff.tpl diff --git a/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl b/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl index a75760c..d37ce73 100644 --- a/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl +++ b/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl @@ -12,10 +12,10 @@ {if isset="$user"}
  • {$user/}
  • New Node
  • -
  • Logoff
  • +
  • Logoff
  • {/if} {unless isset="$user"} -
  • Login
  • +
  • Login
  • Register
  • {/unless} From 3fe166b768d8f1f9e563aa508edffcf83111797e Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 2 Oct 2014 20:52:14 -0300 Subject: [PATCH 022/386] Updated CMS libary: Initial Basic Authentication Node with filters. Updated ROC API to use the new module. --- .../modules/basic_auth/basic_auth_module.e | 4 +-- .../handler/basic_auth_login_handler.e | 2 +- .../node/handler/node_content_handler.e | 2 +- .../node/handler/node_summary_handler.e | 2 +- .../modules/node/handler/node_title_handler.e | 10 ++---- cms/src/service/cms_service.e | 35 ++++++++++--------- cms/src/service/handler/cms_handler.e | 2 ++ cms/src/service/response/cms_response.e | 2 ++ .../response/generic_view_cms_response.e | 4 +-- .../smarty_theme/smarty_cms_page_template.e | 4 +-- examples/roc_api/roc.ini | 2 +- .../themes/api/master2/site_navigation.tpl | 4 +-- .../site/www/themes/api/modules/node.tpl | 2 +- .../www/themes/api/modules/node_content.tpl | 2 +- .../www/themes/api/modules/node_summary.tpl | 2 +- .../www/themes/api/modules/node_title.tpl | 2 +- examples/roc_api/src/ewf_roc_server.e | 1 + 17 files changed, 41 insertions(+), 41 deletions(-) diff --git a/cms/src/modules/basic_auth/basic_auth_module.e b/cms/src/modules/basic_auth/basic_auth_module.e index f669a6d..855c4e3 100644 --- a/cms/src/modules/basic_auth/basic_auth_module.e +++ b/cms/src/modules/basic_auth/basic_auth_module.e @@ -62,7 +62,7 @@ feature -- Configure Node Resources Routes create l_bal_handler.make (config) create l_methods l_methods.enable_get - router.handle_with_request_methods ("/basic_auth/login", l_bal_handler, l_methods) + router.handle_with_request_methods ("/basic_auth_login", l_bal_handler, l_methods) end configure_api_logoff @@ -73,7 +73,7 @@ feature -- Configure Node Resources Routes create l_bal_handler.make (config) create l_methods l_methods.enable_get - router.handle_with_request_methods ("/basic_auth/logoff", l_bal_handler, l_methods) + router.handle_with_request_methods ("/basic_auth_logoff", l_bal_handler, l_methods) end end diff --git a/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e index 8f27b2f..a0e6df8 100644 --- a/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e +++ b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e @@ -52,7 +52,7 @@ feature -- HTTP Methods do log.write_information(generator + ".do_get Processing basic auth login") if attached {STRING_32} current_user_name (req) as l_user then - (create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url("")) + (create {CMS_GENERIC_RESPONSE}).new_response_redirect (req, res, req.absolute_script_url("/")) else (create {CMS_GENERIC_RESPONSE}).new_response_authenticate (req, res) end diff --git a/cms/src/modules/node/handler/node_content_handler.e b/cms/src/modules/node/handler/node_content_handler.e index a15bf8a..a7113b7 100644 --- a/cms/src/modules/node/handler/node_content_handler.e +++ b/cms/src/modules/node/handler/node_content_handler.e @@ -71,7 +71,7 @@ feature -- HTTP Methods if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_content") - l_page.add_variable (l_node.content, "content") + l_page.add_variable (l_node.content, "node_content") l_page.add_variable (l_id.value, "id") l_page.execute else diff --git a/cms/src/modules/node/handler/node_summary_handler.e b/cms/src/modules/node/handler/node_summary_handler.e index 8e24af2..d3ec258 100644 --- a/cms/src/modules/node/handler/node_summary_handler.e +++ b/cms/src/modules/node/handler/node_summary_handler.e @@ -70,8 +70,8 @@ feature -- HTTP Methods if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_summary") - l_page.add_variable (l_node.summary, "summary") l_page.add_variable (l_id.value, "id") + l_page.add_variable (l_node.summary, "node_summary") l_page.execute else do_error (req, res, l_id) diff --git a/cms/src/modules/node/handler/node_title_handler.e b/cms/src/modules/node/handler/node_title_handler.e index e13dca7..570b155 100644 --- a/cms/src/modules/node/handler/node_title_handler.e +++ b/cms/src/modules/node/handler/node_title_handler.e @@ -70,20 +70,14 @@ feature -- HTTP Methods if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_title") - l_page.add_variable (setup.is_html, "html") - l_page.add_variable (setup.is_web, "web") - l_page.add_variable (l_node.title, "title") + l_page.add_variable (l_node.title, "node_title") l_page.add_variable (l_id.value, "id") l_page.execute else do_error (req, res, l_id) end else - to_implement ("Check how to implement API error") --- create l_page.make (req, "master2/error") --- l_page.set_value ("500", "code") --- l_page.set_value (req.absolute_script_url (req.path_info), "request") --- l_page.send_to (res) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index f786a83..b2089c1 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -19,6 +19,7 @@ inherit execute_default end + WSF_FILTERED_SERVICE WSF_FILTER @@ -120,23 +121,6 @@ feature {NONE} -- Initialization router.handle_with_request_methods ("/", fhdl, router.methods_GET) end - -feature -- Access - - setup: CMS_SETUP - -- CMS setup. - - configuration: CMS_CONFIGURATION - -- CMS configuration. - -- | Maybe we can compute it (using `setup') instead of using memory. - - modules: LIST [CMS_MODULE] - -- List of possible modules. - -- | Maybe we can compute it (using `setup') instead of using memory. - - filters: LIST[WSF_FILTER] - -- List of possible filters. - feature -- Execute Filter execute_filter (req: WSF_REQUEST; res: WSF_RESPONSE) @@ -186,6 +170,23 @@ feature -- Filters f.set_next (Current) end + +feature -- Access + + setup: CMS_SETUP + -- CMS setup. + + configuration: CMS_CONFIGURATION + -- CMS configuration. + -- | Maybe we can compute it (using `setup') instead of using memory. + + modules: LIST [CMS_MODULE] + -- List of possible modules. + -- | Maybe we can compute it (using `setup') instead of using memory. + + filters: LIST[WSF_FILTER] + -- List of possible filters. + feature -- Execution execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) diff --git a/cms/src/service/handler/cms_handler.e b/cms/src/service/handler/cms_handler.e index ebe75b7..c6d2902 100644 --- a/cms/src/service/handler/cms_handler.e +++ b/cms/src/service/handler/cms_handler.e @@ -9,6 +9,8 @@ deferred class inherit + WSF_HANDLER + CMS_REQUEST_UTIL SHARED_LOGGER diff --git a/cms/src/service/response/cms_response.e b/cms/src/service/response/cms_response.e index 3dd6f57..671c031 100644 --- a/cms/src/service/response/cms_response.e +++ b/cms/src/service/response/cms_response.e @@ -178,6 +178,8 @@ feature {NONE} -- Execution prepare (cms_page) create page.make (theme.page_html (cms_page)) page.set_status_code (status_code) + page.header.put_content_length (page.html.count) + page.header.put_current_date response.send (page) on_terminated end diff --git a/cms/src/service/response/generic_view_cms_response.e b/cms/src/service/response/generic_view_cms_response.e index 28d01bc..80ce094 100644 --- a/cms/src/service/response/generic_view_cms_response.e +++ b/cms/src/service/response/generic_view_cms_response.e @@ -30,8 +30,8 @@ feature -- Execution process -- Computed response message. do - set_title ("CMS") - set_page_title (Void) +-- set_title ("CMS") +-- set_page_title (Void) end end diff --git a/cms/src/theme/smarty_theme/smarty_cms_page_template.e b/cms/src/theme/smarty_theme/smarty_cms_page_template.e index 427c186..b02fe15 100644 --- a/cms/src/theme/smarty_theme/smarty_cms_page_template.e +++ b/cms/src/theme/smarty_theme/smarty_cms_page_template.e @@ -42,10 +42,10 @@ feature -- Access end if attached page.title as l_title then - variables.force (l_title, "title") + variables.force (l_title, "page_title") variables.force (l_title, "head_title") else - variables.force ("", "title") + variables.force ("", "page_title") variables.force ("", "head_title") end diff --git a/examples/roc_api/roc.ini b/examples/roc_api/roc.ini index 17eb967..f51fcfd 100644 --- a/examples/roc_api/roc.ini +++ b/examples/roc_api/roc.ini @@ -1,2 +1,2 @@ -port=8088 +port=8099 #verbose=true \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl b/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl index d37ce73..081d0f4 100644 --- a/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl +++ b/examples/roc_api/site/www/themes/api/master2/site_navigation.tpl @@ -12,10 +12,10 @@ {if isset="$user"}
  • {$user/}
  • New Node
  • -
  • Logoff
  • +
  • Logoff
  • {/if} {unless isset="$user"} -
  • Login
  • +
  • Login
  • Register
  • {/unless} diff --git a/examples/roc_api/site/www/themes/api/modules/node.tpl b/examples/roc_api/site/www/themes/api/modules/node.tpl index 0f6173f..815d55c 100644 --- a/examples/roc_api/site/www/themes/api/modules/node.tpl +++ b/examples/roc_api/site/www/themes/api/modules/node.tpl @@ -89,7 +89,7 @@
    {if isset="$node"} - + {/if} {unless isset="$node"} diff --git a/examples/roc_api/site/www/themes/api/modules/node_content.tpl b/examples/roc_api/site/www/themes/api/modules/node_content.tpl index 4825500..c1353aa 100644 --- a/examples/roc_api/site/www/themes/api/modules/node_content.tpl +++ b/examples/roc_api/site/www/themes/api/modules/node_content.tpl @@ -33,7 +33,7 @@
    - +
    diff --git a/examples/roc_api/site/www/themes/api/modules/node_summary.tpl b/examples/roc_api/site/www/themes/api/modules/node_summary.tpl index 24f69e6..b9022e6 100644 --- a/examples/roc_api/site/www/themes/api/modules/node_summary.tpl +++ b/examples/roc_api/site/www/themes/api/modules/node_summary.tpl @@ -33,7 +33,7 @@
    - +
    diff --git a/examples/roc_api/site/www/themes/api/modules/node_title.tpl b/examples/roc_api/site/www/themes/api/modules/node_title.tpl index 2e02d02..bf8a77e 100644 --- a/examples/roc_api/site/www/themes/api/modules/node_title.tpl +++ b/examples/roc_api/site/www/themes/api/modules/node_title.tpl @@ -33,7 +33,7 @@
    - +
    diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index 4f16373..e4a0425 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -15,6 +15,7 @@ inherit redefine initialize end + WSF_SERVICE redefine execute From c94e5391e98b1cad353bb896c4b5fe6355663901 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 8 Oct 2014 14:24:49 +0200 Subject: [PATCH 023/386] Layout: got rid of onces per thread function. Renamed `launch_cms' as `initialize_cms' which reflects better the implementation. Minor cosmetic or warning changes. --- cms/src/configuration/cms_configuration.e | 4 - cms/src/configuration/cms_default_setup.e | 1 - cms/src/configuration/cms_layout.e | 28 ++++- cms/src/modules/cms_module.e | 4 +- cms/src/service/cms_service.e | 1 - cms/src/theme/smarty_theme/smarty_cms_theme.e | 30 ++++-- examples/roc_api/roc_api.ecf | 10 +- examples/roc_api/src/ewf_roc_server.e | 6 +- layout/src/application_layout.e | 101 +++++++++++++++--- .../database/database_iteration_cursor.e | 2 +- 10 files changed, 139 insertions(+), 48 deletions(-) diff --git a/cms/src/configuration/cms_configuration.e b/cms/src/configuration/cms_configuration.e index b6f6b12..99de5c4 100644 --- a/cms/src/configuration/cms_configuration.e +++ b/cms/src/configuration/cms_configuration.e @@ -16,8 +16,6 @@ feature {NONE} -- Initialization make (a_layout: CMS_LAYOUT) -- Initialize `Current'. - local - p: PATH do layout := a_layout create options.make_equal (10) @@ -169,8 +167,6 @@ feature -- Change end get_root_location - local - utf: UTF_CONVERTER do root_location := layout.www_path end diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index dac8deb..ccac309 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -93,7 +93,6 @@ feature {NONE} -- Initialization build_api_service local - dn: PATH l_database: DATABASE_CONNECTION do to_implement ("Refactor database setup") diff --git a/cms/src/configuration/cms_layout.e b/cms/src/configuration/cms_layout.e index b48684e..3037b5b 100644 --- a/cms/src/configuration/cms_layout.e +++ b/cms/src/configuration/cms_layout.e @@ -19,14 +19,34 @@ feature -- Access theme_path: PATH -- Directory for templates (HTML, etc). - once - Result := www_path.extended ("theme") + local + p: detachable PATH + do + p := internal_theme_path + if p = Void then + p := www_path.extended ("theme") + internal_theme_path := p + end + Result := p end cms_config_ini_path: PATH -- Database Configuration file path. - once - Result := config_path.extended ("cms.ini") + local + p: detachable PATH + do + p := internal_cms_config_ini_path + if p = Void then + p := config_path.extended ("cms.ini") + internal_cms_config_ini_path := p + end + Result := p end +feature {NONE} -- Implementation + + internal_theme_path: detachable like theme_path + + internal_cms_config_ini_path: detachable like cms_config_ini_path + end diff --git a/cms/src/modules/cms_module.e b/cms/src/modules/cms_module.e index 3979fff..63c433b 100644 --- a/cms/src/modules/cms_module.e +++ b/cms/src/modules/cms_module.e @@ -27,7 +27,7 @@ feature -- Router feature -- Filter - filters: detachable LIST[WSF_FILTER] + filters: detachable LIST [WSF_FILTER] -- Possibly list of Filter's module. feature -- Element Change: Filter @@ -39,7 +39,7 @@ feature -- Element Change: Filter do l_filters := filters if l_filters = Void then - create {ARRAYED_LIST[WSF_FILTER]}l_filters.make (1) + create {ARRAYED_LIST [WSF_FILTER]} l_filters.make (1) filters := l_filters end l_filters.force (a_filter) diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index a14a96e..252c9ed 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -136,7 +136,6 @@ feature -- Filters -- Create `filter'. local f, l_filter: detachable WSF_FILTER - fh: WSF_CUSTOM_HEADER_FILTER do l_filter := Void -- Maintenance diff --git a/cms/src/theme/smarty_theme/smarty_cms_theme.e b/cms/src/theme/smarty_theme/smarty_cms_theme.e index f315f57..2b2e6a4 100644 --- a/cms/src/theme/smarty_theme/smarty_cms_theme.e +++ b/cms/src/theme/smarty_theme/smarty_cms_theme.e @@ -45,19 +45,25 @@ feature -- Access local i: INTEGER utf: UTF_CONVERTER - once - if attached information.regions as tb and then not tb.is_empty then - i := 1 - create Result.make_filled ("", i, i + tb.count - 1) - across - tb as ic - loop - Result.force (utf.utf_32_string_to_utf_8_string_8 (ic.key), i) -- NOTE: UTF-8 encoded ! - i := i + 1 + l_regions: like internal_regions + do + l_regions := internal_regions + if l_regions = Void then + if attached information.regions as tb and then not tb.is_empty then + i := 1 + create l_regions.make_filled ("", i, i + tb.count - 1) + across + tb as ic + loop + l_regions.force (utf.utf_32_string_to_utf_8_string_8 (ic.key), i) -- NOTE: UTF-8 encoded ! + i := i + 1 + end + else + l_regions := <<"header", "content", "footer", "first_sidebar", "second_sidebar">> end - else - Result := <<"header", "content", "footer", "first_sidebar", "second_sidebar">> + internaL_regions := l_regions end + Result := l_regions end page_template: SMARTY_CMS_PAGE_TEMPLATE @@ -87,6 +93,8 @@ feature -- Conversion feature {NONE} -- Internal + internal_regions: detachable like regions + internal_page_template: detachable like page_template invariant diff --git a/examples/roc_api/roc_api.ecf b/examples/roc_api/roc_api.ecf index 2cf8d79..fb9f85c 100644 --- a/examples/roc_api/roc_api.ecf +++ b/examples/roc_api/roc_api.ecf @@ -9,15 +9,11 @@ - - - - - - - + + + diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index e4a0425..e4eccea 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -43,7 +43,7 @@ feature {NONE} -- Initialization do Precursor service_options := create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("roc.ini") - launch_cms (cms_setup) + initialize_cms (cms_setup) end feature -- Service @@ -115,11 +115,11 @@ feature -- CMS Initialization setup_storage (Result) end - launch_cms (a_setup: CMS_SETUP) + initialize_cms (a_setup: CMS_SETUP) local cms: CMS_SERVICE do - log.write_debug (generator + ".launch_cms") + log.write_debug (generator + ".initialize_cms") create cms.make (a_setup) cms_service := cms end diff --git a/layout/src/application_layout.e b/layout/src/application_layout.e index 6108602..a76e3d3 100644 --- a/layout/src/application_layout.e +++ b/layout/src/application_layout.e @@ -36,45 +36,118 @@ feature -- Access: internal config_path: PATH -- Configuration file path. - once - Result := path.extended ("config") + local + p: detachable PATH + do + p := internal_config_path + if p = Void then + p := path.extended ("config") + internal_config_path := p + end + Result := p end application_config_path: PATH -- Database Configuration file path. - once - Result := config_path.extended ("application_configuration.json") + local + p: detachable PATH + do + p := internal_application_config_path + if p = Void then + p := config_path.extended ("application_configuration.json") + internal_application_config_path := p + end + Result := p end logs_path: PATH -- Directory for logs. - once - Result := path.extended ("logs") + local + p: detachable PATH + do + p := internal_logs_path + if p = Void then + p := path.extended ("logs") + internal_logs_path := p + end + Result := p end documentation_path: PATH -- Directory for API documentation. - once - Result := path.extended ("doc") + local + p: detachable PATH + do + p := internal_documentation_path + if p = Void then + p := path.extended ("doc") + internal_documentation_path := p + end + Result := p end www_path: PATH -- Directory for www. - once - Result := path.extended ("www") + local + p: detachable PATH + do + p := internal_www_path + if p = Void then + p := path.extended ("www") + internal_www_path := p + end + Result := p end assets_path: PATH -- Directory for public assets. -- css, images, js. - once - Result := path.extended ("www").extended ("assets") + local + p: detachable PATH + do + p := internal_assets_path + if p = Void then + p := www_path.extended ("assets") + internal_assets_path := p + end + Result := p end template_path: PATH -- Directory for templates (HTML, etc). - once - Result := www_path.extended ("template") + local + p: detachable PATH + do + p := internal_template_path + if p = Void then + p := www_path.extended ("template") + internal_template_path := p + end + Result := p end +feature {NONE} -- Implementation + + internal_config_path: detachable like config_path + -- Configuration file path. + + internal_application_config_path: detachable like application_config_path + -- Database Configuration file path. + + internal_logs_path: detachable like logs_path + -- Directory for logs. + + internal_documentation_path: detachable like documentation_path + -- Directory for API documentation. + + internal_www_path: detachable like www_path + -- Directory for www. + + internal_assets_path: detachable like assets_path + -- Directory for public assets. + -- css, images, js. + + internal_template_path: detachable like template_path + -- Directory for templates (HTML, etc). + end diff --git a/persistence/implementation/common/database/database_iteration_cursor.e b/persistence/implementation/common/database/database_iteration_cursor.e index 7c0f073..f38d844 100644 --- a/persistence/implementation/common/database/database_iteration_cursor.e +++ b/persistence/implementation/common/database/database_iteration_cursor.e @@ -80,7 +80,7 @@ feature -- Cursor feature -- Action - action: FUNCTION [ANY, detachable TUPLE [], G] + action: FUNCTION [ANY, detachable TUPLE, G] -- Agent to create a new item of type G. feature {NONE} -- Implementation From 965b0e9f7d99daf046c73a5afe4ccaf33266d9c5 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Tue, 7 Oct 2014 17:52:13 -0300 Subject: [PATCH 024/386] Added CMS_MODULE_CONFIGURATOR. Added a default implementation of CMS_MODULE_CONGIRATOR. Updated CMS_SERVICE, to use the new CMS_MODULE_CONFIGURATOR. Updated Example. --- cms/src/configuration/cms_default_setup.e | 47 ++-------------- cms/src/configuration/cms_setup.e | 7 --- cms/src/modules/cms_module_configurator.e | 30 +++++++++++ .../default/cms_default_module_configurator.e | 53 +++++++++++++++++++ cms/src/service/cms_service.e | 37 ++++++++++--- examples/roc_api/src/ewf_roc_server.e | 13 +++-- 6 files changed, 128 insertions(+), 59 deletions(-) create mode 100644 cms/src/modules/cms_module_configurator.e create mode 100644 cms/src/modules/default/cms_default_module_configurator.e diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index ccac309..79ed020 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -12,13 +12,18 @@ inherit REFACTORING_HELPER create make + feature {NONE} -- Initialization make (a_layout: CMS_LAYOUT) do layout := a_layout create configuration.make (layout) + configure + end + configure + do site_id := configuration.site_id site_url := configuration.site_url ("") site_name := configuration.site_name ("EWF::CMS") @@ -35,20 +40,14 @@ feature {NONE} -- Initialization initialize end - initialize do build_api_service - build_auth_engine build_mailer - build_modules end feature -- Access - modules: ARRAYED_LIST [CMS_MODULE] - -- List of possible modules - is_html: BOOLEAN -- do @@ -63,34 +62,6 @@ feature -- Access end -feature {NONE} -- Initialization - - build_modules - -- Core modules. (User, Admin, Node) - -- At the moment only node is supported. - local - m: CMS_MODULE - do - create modules.make (3) - --- -- Core --- create {USER_MODULE} m.make --- m.enable --- modules.extend (m) - --- create {ADMIN_MODULE} m.make --- m.enable --- modules.extend (m) - - create {NODE_MODULE} m.make (Current) - m.enable - modules.extend (m) - - create {BASIC_AUTH_MODULE} m.make (Current) - m.enable - modules.extend (m) - end - build_api_service local l_database: DATABASE_CONNECTION @@ -115,12 +86,4 @@ feature {NONE} -- Initialization to_implement ("Not implemented mailer") end -feature -- Change - - add_module (m: CMS_MODULE) - -- Add a module `m' to the list of modules `modules'. - do - modules.force (m) - end - end diff --git a/cms/src/configuration/cms_setup.e b/cms/src/configuration/cms_setup.e index a9f8327..f3bf51d 100644 --- a/cms/src/configuration/cms_setup.e +++ b/cms/src/configuration/cms_setup.e @@ -17,13 +17,6 @@ feature -- Access api_service: CMS_API_SERVICE -- cms api service. - modules: LIST[CMS_MODULE] - -- Possible list of modules. - -- |If we remove Modules from setup. - -- |we can let the CMS_SERVICE define the basic modules. - deferred - end - is_html: BOOLEAN -- api with progresive enhacements css and js, server side rendering. deferred diff --git a/cms/src/modules/cms_module_configurator.e b/cms/src/modules/cms_module_configurator.e new file mode 100644 index 0000000..c8fe906 --- /dev/null +++ b/cms/src/modules/cms_module_configurator.e @@ -0,0 +1,30 @@ +note + description: "Summary description for {CMS_MODULE_CONFIGURATOR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_MODULE_CONFIGURATOR + +feature -- Access + + modules: LIST[CMS_MODULE] + -- Possible list of modules. + deferred + end + +feature -- Add Module + + add_module (a_module: CMS_MODULE) + -- Add module + do + modules.force (a_module) + end + + remove_module (a_module: CMS_MODULE) + -- Remove module + do + modules.prune (a_module) + end +end diff --git a/cms/src/modules/default/cms_default_module_configurator.e b/cms/src/modules/default/cms_default_module_configurator.e new file mode 100644 index 0000000..85132eb --- /dev/null +++ b/cms/src/modules/default/cms_default_module_configurator.e @@ -0,0 +1,53 @@ +note + description: "Summary description for {CMS_DEFAULT_MODULE_CONFIGURATOR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_DEFAULT_MODULE_CONFIGURATOR + +inherit + + CMS_MODULE_CONFIGURATOR + +create + make + +feature {NONE} -- Initialization + + make (a_setup: CMS_SETUP) + do + build_modules (a_setup) + end + +feature -- Access + + modules: ARRAYED_LIST [CMS_MODULE] + -- List of possible modules + + +feature -- Configuration + + build_modules (a_setup: CMS_SETUP) + -- Core modules. (User, Admin, Node) + -- At the moment only node is supported. + local + m: CMS_MODULE + do + create modules.make (3) +-- -- Core +-- create {USER_MODULE} m.make (a_setup) +-- m.enable +-- modules.extend (m) + +-- create {ADMIN_MODULE} m.make (a_setup) +-- m.enable +-- modules.extend (m) + + create {NODE_MODULE} m.make (a_setup) + m.enable + modules.extend (m) + end + +end diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index 252c9ed..43ccad4 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -37,16 +37,34 @@ inherit SHARED_LOGGER create - make + make, + make_with_module_configurator feature {NONE} -- Initialization make (a_setup: CMS_SETUP) + -- Build a a default service with a CMS_DEFAULT_MODULE_CONFIGURATOR do setup := a_setup configuration := a_setup.configuration - modules := a_setup.modules + create {CMS_DEFAULT_MODULE_CONFIGURATOR} modules.make (a_setup) create {ARRAYED_LIST[WSF_FILTER]} filters.make (0) + initialize + end + + make_with_module_configurator (a_setup: CMS_SETUP; a_module_configurator: CMS_MODULE_CONFIGURATOR) + -- Build a a default service with a custom CMS_MODULE_CONFIGURATOR + do + setup := a_setup + configuration := a_setup.configuration + modules := a_module_configurator + create {ARRAYED_LIST[WSF_FILTER]} filters.make (0) + initialize + end + + + initialize + do initialize_users initialize_auth_engine initialize_mailer @@ -75,7 +93,7 @@ feature {NONE} -- Initialization do log.write_debug (generator + ".initialize_modules") across - modules as m + modules.modules as m loop if m.item.is_enabled then router.import (m.item.router) @@ -178,13 +196,20 @@ feature -- Access -- CMS configuration. -- | Maybe we can compute it (using `setup') instead of using memory. - modules: LIST [CMS_MODULE] - -- List of possible modules. - -- | Maybe we can compute it (using `setup') instead of using memory. + modules: CMS_MODULE_CONFIGURATOR + -- Configurator of possible modules. filters: LIST[WSF_FILTER] -- List of possible filters. +feature -- Element Change: Modules + + add_module (a_module: CMS_MODULE) + -- Add a module `a_module' to the module configurator `a_module'. + do + modules.add_module (a_module) + end + feature -- Execution execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index e4eccea..caf08b2 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -111,7 +111,6 @@ feature -- CMS Initialization create layout.make_default end create Result.make (layout) - setup_modules (Result) setup_storage (Result) end @@ -121,20 +120,26 @@ feature -- CMS Initialization do log.write_debug (generator + ".initialize_cms") create cms.make (a_setup) + setup_modules (cms) cms_service := cms end feature -- CMS setup - setup_modules (a_setup: CMS_SETUP) + setup_modules (a_service: CMS_SERVICE) -- Setup modules to be added to the CMS ecosystem. + local + m: CMS_MODULE do - to_implement ("To implement custom modules") + + create {NODE_MODULE} m.make (a_service.setup) + m.enable + a_service.add_module (m) end setup_storage (a_setup: CMS_SETUP) do - + to_implement ("To implement custom storage") end end From 51e105840954ba1ddcf8b65d4531565f9a7e933b Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 8 Oct 2014 17:04:34 +0200 Subject: [PATCH 025/386] Redesign part of CMS_MODULE, and implementation. Prepare the module before creating the service. Remove the possibility to add module after the creation of the service. Renamed CMS_MODULE_CONFIGURATOR as CMS_MODULE_COLLECTION. --- cms/src/configuration/cms_default_setup.e | 31 ++-- cms/src/configuration/cms_setup.e | 30 +--- .../modules/basic_auth/basic_auth_module.e | 44 +++--- .../basic_auth/filter/basic_auth_filter.e | 2 +- cms/src/modules/cms_module.e | 19 +-- cms/src/modules/cms_module_collection.e | 64 ++++++++ cms/src/modules/cms_module_configurator.e | 30 ---- ...ator.e => cms_default_module_collection.e} | 29 ++-- cms/src/modules/node/node_module.e | 55 +++---- cms/src/service/cms_api_service.e | 7 +- cms/src/service/cms_service.e | 137 ++++++++++-------- examples/roc_api/src/ewf_roc_server.e | 17 ++- 12 files changed, 245 insertions(+), 220 deletions(-) create mode 100644 cms/src/modules/cms_module_collection.e delete mode 100644 cms/src/modules/cms_module_configurator.e rename cms/src/modules/default/{cms_default_module_configurator.e => cms_default_module_collection.e} (63%) diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index 79ed020..015f35b 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -12,14 +12,21 @@ inherit REFACTORING_HELPER create make - + feature {NONE} -- Initialization make (a_layout: CMS_LAYOUT) do layout := a_layout create configuration.make (layout) + initialize + end + + initialize + do configure + build_api_service + build_mailer end configure @@ -36,14 +43,6 @@ feature {NONE} -- Initialization compute_theme_location compute_theme_resource_location - - initialize - end - - initialize - do - build_api_service - build_mailer end feature -- Access @@ -86,4 +85,18 @@ feature -- Access to_implement ("Not implemented mailer") end +feature -- Compute location + + compute_theme_location + do + theme_location := themes_location.extended (theme_name) + end + + compute_theme_resource_location + -- assets (js, css, images, etc) + -- Not used at the moment. + do + theme_resource_location := theme_location + end + end diff --git a/cms/src/configuration/cms_setup.e b/cms/src/configuration/cms_setup.e index f3bf51d..244c940 100644 --- a/cms/src/configuration/cms_setup.e +++ b/cms/src/configuration/cms_setup.e @@ -9,21 +9,21 @@ deferred class feature -- Access configuration: CMS_CONFIGURATION - -- cms configuration. + -- cms configuration. layout: CMS_LAYOUT - -- CMS layout. + -- CMS layout. api_service: CMS_API_SERVICE - -- cms api service. + -- cms api service. is_html: BOOLEAN - -- api with progresive enhacements css and js, server side rendering. + -- api with progressive enhancements css and js, server side rendering. deferred end is_web: BOOLEAN - -- web: Web Site with progresive enhacements css and js and Ajax calls. + -- web: Web Site with progressive enhancements css and js and Ajax calls. deferred end @@ -43,14 +43,14 @@ feature -- Access: Site files_location: PATH -feature -- Access:Theme +feature -- Access: Theme themes_location: PATH theme_location: PATH theme_resource_location: PATH - -- + -- theme_information_location: PATH -- theme informations. @@ -59,20 +59,6 @@ feature -- Access:Theme end theme_name: READABLE_STRING_32 - -- theme name - -feature -- Compute location - - compute_theme_location - do - theme_location := themes_location.extended (theme_name) - end - - compute_theme_resource_location - -- assets (js, css, images, etc) - -- Not used at the moment. - do - theme_resource_location := theme_location - end + -- theme name end diff --git a/cms/src/modules/basic_auth/basic_auth_module.e b/cms/src/modules/basic_auth/basic_auth_module.e index 855c4e3..2c589d1 100644 --- a/cms/src/modules/basic_auth/basic_auth_module.e +++ b/cms/src/modules/basic_auth/basic_auth_module.e @@ -9,6 +9,9 @@ class inherit CMS_MODULE + redefine + filters + end create make @@ -22,39 +25,34 @@ feature {NONE} -- Initialization description := "Service to manage basic authentication" package := "core" config := a_config - setup_router - setup_filter - enable end -feature -- Access - - router: WSF_ROUTER - -- Node router. - config: CMS_SETUP -- Node configuration. -feature -- Implementation +feature -- Access: router - setup_router - -- Setup `router'. + router: WSF_ROUTER + -- Node router. do - create router.make (2) - configure_api_login - configure_api_logoff + create Result.make (2) + configure_api_login (Result) + configure_api_logoff (Result) end - setup_filter - -- Setup `filter'. +feature -- Access: filter + + filters: detachable LIST [WSF_FILTER] + -- Possibly list of Filter's module. do - add_filter (create {CORS_FILTER}) - add_filter (create {BASIC_AUTH_FILTER}.make (config)) + create {ARRAYED_LIST [WSF_FILTER]} Result.make (2) + Result.extend (create {CORS_FILTER}) + Result.extend (create {BASIC_AUTH_FILTER}.make (config)) end -feature -- Configure Node Resources Routes +feature {NONE} -- Implementation: routes - configure_api_login + configure_api_login (a_router: WSF_ROUTER) local l_bal_handler: BASIC_AUTH_LOGIN_HANDLER l_methods: WSF_REQUEST_METHODS @@ -62,10 +60,10 @@ feature -- Configure Node Resources Routes create l_bal_handler.make (config) create l_methods l_methods.enable_get - router.handle_with_request_methods ("/basic_auth_login", l_bal_handler, l_methods) + a_router.handle_with_request_methods ("/basic_auth_login", l_bal_handler, l_methods) end - configure_api_logoff + configure_api_logoff (a_router: WSF_ROUTER) local l_bal_handler: BASIC_AUTH_LOGOFF_HANDLER l_methods: WSF_REQUEST_METHODS @@ -73,7 +71,7 @@ feature -- Configure Node Resources Routes create l_bal_handler.make (config) create l_methods l_methods.enable_get - router.handle_with_request_methods ("/basic_auth_logoff", l_bal_handler, l_methods) + a_router.handle_with_request_methods ("/basic_auth_logoff", l_bal_handler, l_methods) end end diff --git a/cms/src/modules/basic_auth/filter/basic_auth_filter.e b/cms/src/modules/basic_auth/filter/basic_auth_filter.e index 1b4502b..d5abd0a 100644 --- a/cms/src/modules/basic_auth/filter/basic_auth_filter.e +++ b/cms/src/modules/basic_auth/filter/basic_auth_filter.e @@ -31,7 +31,7 @@ feature -- Basic operations -- A valid user if (attached l_auth.type as l_auth_type and then l_auth_type.is_case_insensitive_equal_general ("basic")) and then attached l_auth.login as l_auth_login and then attached l_auth.password as l_auth_password then - if api_service.login_valid (l_auth_login, l_auth_password) then + if api_service.is_valid_credential (l_auth_login, l_auth_password) then if attached api_service.user_by_name (l_auth_login) as l_user then req.set_execution_variable ("user", l_user) execute_next (req, res) diff --git a/cms/src/modules/cms_module.e b/cms/src/modules/cms_module.e index 63c433b..a58805e 100644 --- a/cms/src/modules/cms_module.e +++ b/cms/src/modules/cms_module.e @@ -22,27 +22,18 @@ feature -- Router router: WSF_ROUTER -- Router configuration. + require + is_enabled: is_enabled deferred end feature -- Filter filters: detachable LIST [WSF_FILTER] - -- Possibly list of Filter's module. - -feature -- Element Change: Filter - - add_filter (a_filter: WSF_FILTER) - -- Add a filter `a_filter' to the list of module filters `filters'. - local - l_filters: like filters + -- Optional list of filter for Current module. + require + is_enabled: is_enabled do - l_filters := filters - if l_filters = Void then - create {ARRAYED_LIST [WSF_FILTER]} l_filters.make (1) - filters := l_filters - end - l_filters.force (a_filter) end feature -- Settings diff --git a/cms/src/modules/cms_module_collection.e b/cms/src/modules/cms_module_collection.e new file mode 100644 index 0000000..a7f94d2 --- /dev/null +++ b/cms/src/modules/cms_module_collection.e @@ -0,0 +1,64 @@ +note + description: "Summary description for {CMS_MODULE_COLLECTION}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_MODULE_COLLECTION + +inherit + ITERABLE [CMS_MODULE] + +create + make + +feature {NONE} -- Initialization + + make (nb: INTEGER) + do + create modules.make (nb) + end + +feature -- Access + + new_cursor: INDEXABLE_ITERATION_CURSOR [CMS_MODULE] + -- + do + Result := modules.new_cursor + end + +feature -- Status report + + has (a_module: CMS_MODULE): BOOLEAN + -- Has `a_module'? + do + Result := modules.has (a_module) + end + + count: INTEGER + -- Number of modules. + do + Result := modules.count + end + +feature -- Element change + + extend (a_module: CMS_MODULE) + -- Add module + do + modules.force (a_module) + end + + remove (a_module: CMS_MODULE) + -- Remove module + do + modules.prune_all (a_module) + end + +feature {NONE} -- Implementation + + modules: ARRAYED_LIST [CMS_MODULE] + -- List of available modules. + +end diff --git a/cms/src/modules/cms_module_configurator.e b/cms/src/modules/cms_module_configurator.e deleted file mode 100644 index c8fe906..0000000 --- a/cms/src/modules/cms_module_configurator.e +++ /dev/null @@ -1,30 +0,0 @@ -note - description: "Summary description for {CMS_MODULE_CONFIGURATOR}." - author: "" - date: "$Date$" - revision: "$Revision$" - -deferred class - CMS_MODULE_CONFIGURATOR - -feature -- Access - - modules: LIST[CMS_MODULE] - -- Possible list of modules. - deferred - end - -feature -- Add Module - - add_module (a_module: CMS_MODULE) - -- Add module - do - modules.force (a_module) - end - - remove_module (a_module: CMS_MODULE) - -- Remove module - do - modules.prune (a_module) - end -end diff --git a/cms/src/modules/default/cms_default_module_configurator.e b/cms/src/modules/default/cms_default_module_collection.e similarity index 63% rename from cms/src/modules/default/cms_default_module_configurator.e rename to cms/src/modules/default/cms_default_module_collection.e index 85132eb..24047bb 100644 --- a/cms/src/modules/default/cms_default_module_configurator.e +++ b/cms/src/modules/default/cms_default_module_collection.e @@ -1,15 +1,17 @@ note - description: "Summary description for {CMS_DEFAULT_MODULE_CONFIGURATOR}." + description: "Summary description for {CMS_DEFAULT_MODULE_COLLECTION}." author: "" date: "$Date$" revision: "$Revision$" class - CMS_DEFAULT_MODULE_CONFIGURATOR + CMS_DEFAULT_MODULE_COLLECTION inherit - - CMS_MODULE_CONFIGURATOR + CMS_MODULE_COLLECTION + rename + make as make_with_capacity + end create make @@ -18,15 +20,10 @@ feature {NONE} -- Initialization make (a_setup: CMS_SETUP) do + make_with_capacity (3) build_modules (a_setup) end -feature -- Access - - modules: ARRAYED_LIST [CMS_MODULE] - -- List of possible modules - - feature -- Configuration build_modules (a_setup: CMS_SETUP) @@ -35,19 +32,23 @@ feature -- Configuration local m: CMS_MODULE do - create modules.make (3) -- -- Core -- create {USER_MODULE} m.make (a_setup) -- m.enable --- modules.extend (m) +-- extend (m) -- create {ADMIN_MODULE} m.make (a_setup) -- m.enable --- modules.extend (m) +-- extend (m) + + + create {BASIC_AUTH_MODULE} m.make (a_setup) + m.enable + extend (m) create {NODE_MODULE} m.make (a_setup) m.enable - modules.extend (m) + extend (m) end end diff --git a/cms/src/modules/node/node_module.e b/cms/src/modules/node/node_module.e index 9e8a805..683a8c0 100644 --- a/cms/src/modules/node/node_module.e +++ b/cms/src/modules/node/node_module.e @@ -16,40 +16,34 @@ create feature {NONE} -- Initialization make (a_config: CMS_SETUP) + -- Create Current module, disabled by default. do name := "node" version := "1.0" description := "Service to manage content based on 'node'" package := "core" config := a_config - setup_router - enable end -feature -- Access - - router: WSF_ROUTER - -- Node router. - config: CMS_SETUP - -- Node configuration. + -- Node configuration. -feature -- Implementation +feature -- Access: router - setup_router - -- Setup `router'. + router: WSF_ROUTER + -- Node router. do - create router.make (5) - configure_api_node - configure_api_nodes - configure_api_node_title - configure_api_node_summary - configure_api_node_content + create Result.make (5) + configure_api_node (Result) + configure_api_nodes (Result) + configure_api_node_title (Result) + configure_api_node_summary (Result) + configure_api_node_content (Result) end -feature -- Configure Node Resources Routes +feature {NONE} -- Implementation: routes - configure_api_node + configure_api_node (a_router: WSF_ROUTER) local l_node_handler: NODE_HANDLER l_methods: WSF_REQUEST_METHODS @@ -59,7 +53,7 @@ feature -- Configure Node Resources Routes l_methods.enable_get l_methods.enable_post l_methods.enable_put - router.handle_with_request_methods ("/node", l_node_handler, l_methods) + a_router.handle_with_request_methods ("/node", l_node_handler, l_methods) create l_node_handler.make (config) create l_methods @@ -67,11 +61,10 @@ feature -- Configure Node Resources Routes l_methods.enable_post l_methods.enable_put l_methods.enable_delete - router.handle_with_request_methods ("/node/{id}", l_node_handler, l_methods) + a_router.handle_with_request_methods ("/node/{id}", l_node_handler, l_methods) end - - configure_api_nodes + configure_api_nodes (a_router: WSF_ROUTER) local l_nodes_handler: NODES_HANDLER l_methods: WSF_REQUEST_METHODS @@ -79,11 +72,10 @@ feature -- Configure Node Resources Routes create l_nodes_handler.make (config) create l_methods l_methods.enable_get - router.handle_with_request_methods ("/nodes", l_nodes_handler, l_methods) + a_router.handle_with_request_methods ("/nodes", l_nodes_handler, l_methods) end - - configure_api_node_summary + configure_api_node_summary (a_router: WSF_ROUTER) local l_report_handler: NODE_SUMMARY_HANDLER l_methods: WSF_REQUEST_METHODS @@ -93,11 +85,11 @@ feature -- Configure Node Resources Routes l_methods.enable_get l_methods.enable_post l_methods.enable_put - router.handle_with_request_methods ("/node/{id}/summary", l_report_handler, l_methods) + a_router.handle_with_request_methods ("/node/{id}/summary", l_report_handler, l_methods) end - configure_api_node_title + configure_api_node_title (a_router: WSF_ROUTER) local l_report_handler: NODE_TITLE_HANDLER l_methods: WSF_REQUEST_METHODS @@ -107,11 +99,10 @@ feature -- Configure Node Resources Routes l_methods.enable_get l_methods.enable_post l_methods.enable_put - router.handle_with_request_methods ("/node/{id}/title", l_report_handler, l_methods) + a_router.handle_with_request_methods ("/node/{id}/title", l_report_handler, l_methods) end - - configure_api_node_content + configure_api_node_content (a_router: WSF_ROUTER) local l_report_handler: NODE_CONTENT_HANDLER l_methods: WSF_REQUEST_METHODS @@ -121,7 +112,7 @@ feature -- Configure Node Resources Routes l_methods.enable_get l_methods.enable_post l_methods.enable_put - router.handle_with_request_methods ("/node/{id}/content", l_report_handler, l_methods) + a_router.handle_with_request_methods ("/node/{id}/content", l_report_handler, l_methods) end end diff --git a/cms/src/service/cms_api_service.e b/cms/src/service/cms_api_service.e index 547d4c6..c7b69ff 100644 --- a/cms/src/service/cms_api_service.e +++ b/cms/src/service/cms_api_service.e @@ -12,8 +12,8 @@ inherit REFACTORING_HELPER -create make - +create + make feature -- Initialize @@ -28,7 +28,7 @@ feature -- Initialize feature -- Access - login_valid (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN + is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN local l_security: SECURITY_PROVIDER do @@ -57,7 +57,6 @@ feature -- Access: Node Result := storage.node (a_id) end - feature -- Change: Node new_node (a_node: CMS_NODE) diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index 43ccad4..7dc0314 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -37,79 +37,88 @@ inherit SHARED_LOGGER create - make, - make_with_module_configurator + make feature {NONE} -- Initialization - make (a_setup: CMS_SETUP) - -- Build a a default service with a CMS_DEFAULT_MODULE_CONFIGURATOR + make (a_setup: CMS_SETUP; a_modules: CMS_MODULE_COLLECTION) + -- Build a a default service with a custom list of modules `a_modules' do setup := a_setup configuration := a_setup.configuration - create {CMS_DEFAULT_MODULE_CONFIGURATOR} modules.make (a_setup) - create {ARRAYED_LIST[WSF_FILTER]} filters.make (0) + modules := a_modules initialize end - make_with_module_configurator (a_setup: CMS_SETUP; a_module_configurator: CMS_MODULE_CONFIGURATOR) - -- Build a a default service with a custom CMS_MODULE_CONFIGURATOR - do - setup := a_setup - configuration := a_setup.configuration - modules := a_module_configurator - create {ARRAYED_LIST[WSF_FILTER]} filters.make (0) - initialize - end - - initialize + -- Initialize various parts of the CMS service. do + initialize_modules initialize_users initialize_auth_engine initialize_mailer initialize_router - initialize_modules initialize_filter end + initialize_modules + -- Intialize modules and keep only enabled modules. + local + l_module: CMS_MODULE + coll: CMS_MODULE_COLLECTION + do + log.write_debug (generator + ".initialize_modules") + create coll.make (modules.count) + across + modules as ic + loop + l_module := ic.item + if l_module.is_enabled then + coll.extend (l_module) + end + end + modules := coll + ensure + only_enabled_modules: across modules as ic all ic.item.is_enabled end + end + initialize_users + -- Initialize users. do end initialize_mailer + -- Initialize mailer engine. do to_implement ("To Implement mailer") end - setup_router - do - configure_api_root - end - - initialize_modules - -- Intialize modules, import router definitions - -- from enabled modules. - do - log.write_debug (generator + ".initialize_modules") - across - modules.modules as m - loop - if m.item.is_enabled then - router.import (m.item.router) - end - if attached m.item.filters as l_m_filters then - filters.append (l_m_filters) - end - end - configure_api_file_handler - end - initialize_auth_engine do to_implement ("To Implement authentication engine") end +feature -- Settings: router + + setup_router + -- + local + l_module: CMS_MODULE + do + log.write_debug (generator + ".setup_router") + -- Configure root of api handler. + configure_api_root + + -- Include routes from modules. + across + modules as ic + loop + l_module := ic.item + router.import (l_module.router) + end + -- Configure files handler. + configure_api_file_handler + end configure_api_root local @@ -143,7 +152,6 @@ feature -- Execute Filter execute_filter (req: WSF_REQUEST; res: WSF_RESPONSE) -- Execute the filter. do - res.put_header_line ("Date: " + (create {HTTP_DATE}.make_now_utc).string) execute_service (req, res) end @@ -154,17 +162,29 @@ feature -- Filters -- Create `filter'. local f, l_filter: detachable WSF_FILTER + l_module: CMS_MODULE do + log.write_debug (generator + ".create_filter") l_filter := Void -- Maintenance create {WSF_MAINTENANCE_FILTER} f f.set_next (l_filter) l_filter := f - if attached filters as l_filters then - across l_filters as c loop - c.item.set_next (l_filter) - l_filter := c.item + -- Include filters from modules + across + modules as ic + loop + l_module := ic.item + if + l_module.is_enabled and then + attached l_module.filters as l_m_filters + then + across l_m_filters as f_ic loop + f := f_ic.item + f.set_next (l_filter) + l_filter := f + end end end @@ -176,6 +196,8 @@ feature -- Filters local f: WSF_FILTER do + log.write_debug (generator + ".setup_filter") + from f := filter until @@ -190,36 +212,23 @@ feature -- Filters feature -- Access setup: CMS_SETUP - -- CMS setup. + -- CMS setup. configuration: CMS_CONFIGURATION - -- CMS configuration. - -- | Maybe we can compute it (using `setup') instead of using memory. + -- CMS configuration. + --| Maybe we can compute it (using `setup') instead of using memory. - modules: CMS_MODULE_CONFIGURATOR - -- Configurator of possible modules. - - filters: LIST[WSF_FILTER] - -- List of possible filters. - -feature -- Element Change: Modules - - add_module (a_module: CMS_MODULE) - -- Add a module `a_module' to the module configurator `a_module'. - do - modules.add_module (a_module) - end + modules: CMS_MODULE_COLLECTION + -- Configurator of possible modules. feature -- Execution execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) -- Default request handler if no other are relevant - local do fixme ("To Implement") end - note copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index caf08b2..50c1524 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -117,24 +117,27 @@ feature -- CMS Initialization initialize_cms (a_setup: CMS_SETUP) local cms: CMS_SERVICE + l_modules: CMS_MODULE_COLLECTION do log.write_debug (generator + ".initialize_cms") - create cms.make (a_setup) - setup_modules (cms) + + create {CMS_DEFAULT_MODULE_COLLECTION} l_modules.make (a_setup) + setup_modules (l_modules, a_setup) + + create cms.make (a_setup, l_modules) cms_service := cms end feature -- CMS setup - setup_modules (a_service: CMS_SERVICE) - -- Setup modules to be added to the CMS ecosystem. + setup_modules (a_modules: CMS_MODULE_COLLECTION; a_setup: CMS_SETUP) + -- Setup modules to be added to the available modules. local m: CMS_MODULE do - - create {NODE_MODULE} m.make (a_service.setup) + create {NODE_MODULE} m.make (a_setup) m.enable - a_service.add_module (m) + a_modules.extend (m) end setup_storage (a_setup: CMS_SETUP) From 6fc38272fe3f371c0d63cfc38b40bba4842434d9 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 8 Oct 2014 19:32:30 +0200 Subject: [PATCH 026/386] Merged back list of available modules in CMS_SETUP. --- cms/src/configuration/cms_custom_setup.e | 1 + cms/src/configuration/cms_default_setup.e | 29 ++++++++++ cms/src/configuration/cms_setup.e | 5 ++ .../default/cms_default_module_collection.e | 54 ------------------- cms/src/modules/node/node_module.e | 2 +- cms/src/service/cms_service.e | 15 +++--- examples/roc_api/src/ewf_roc_server.e | 11 ++-- 7 files changed, 48 insertions(+), 69 deletions(-) delete mode 100644 cms/src/modules/default/cms_default_module_collection.e diff --git a/cms/src/configuration/cms_custom_setup.e b/cms/src/configuration/cms_custom_setup.e index c8640b1..7aec82a 100644 --- a/cms/src/configuration/cms_custom_setup.e +++ b/cms/src/configuration/cms_custom_setup.e @@ -11,4 +11,5 @@ inherit create make + end diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index 015f35b..e1bf624 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -25,8 +25,11 @@ feature {NONE} -- Initialization initialize do configure + create modules.make (3) build_api_service build_mailer + + initialize_modules end configure @@ -45,8 +48,34 @@ feature {NONE} -- Initialization compute_theme_resource_location end + initialize_modules + local + m: CMS_MODULE + do +-- -- Core +-- create {USER_MODULE} m.make (Current) +-- m.enable +-- modules.extend (m) + +-- create {ADMIN_MODULE} m.make (Current) +-- m.enable +-- modules.extend (m) + + + create {BASIC_AUTH_MODULE} m.make (Current) + m.enable + modules.extend (m) + + create {NODE_MODULE} m.make (Current) + m.enable + modules.extend (m) + end + feature -- Access + modules: CMS_MODULE_COLLECTION + -- + is_html: BOOLEAN -- do diff --git a/cms/src/configuration/cms_setup.e b/cms/src/configuration/cms_setup.e index 244c940..f0af327 100644 --- a/cms/src/configuration/cms_setup.e +++ b/cms/src/configuration/cms_setup.e @@ -27,6 +27,11 @@ feature -- Access deferred end + modules: CMS_MODULE_COLLECTION + -- List of available modules. + deferred + end + feature -- Access: Site site_id: READABLE_STRING_8 diff --git a/cms/src/modules/default/cms_default_module_collection.e b/cms/src/modules/default/cms_default_module_collection.e deleted file mode 100644 index 24047bb..0000000 --- a/cms/src/modules/default/cms_default_module_collection.e +++ /dev/null @@ -1,54 +0,0 @@ -note - description: "Summary description for {CMS_DEFAULT_MODULE_COLLECTION}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - CMS_DEFAULT_MODULE_COLLECTION - -inherit - CMS_MODULE_COLLECTION - rename - make as make_with_capacity - end - -create - make - -feature {NONE} -- Initialization - - make (a_setup: CMS_SETUP) - do - make_with_capacity (3) - build_modules (a_setup) - end - -feature -- Configuration - - build_modules (a_setup: CMS_SETUP) - -- Core modules. (User, Admin, Node) - -- At the moment only node is supported. - local - m: CMS_MODULE - do --- -- Core --- create {USER_MODULE} m.make (a_setup) --- m.enable --- extend (m) - --- create {ADMIN_MODULE} m.make (a_setup) --- m.enable --- extend (m) - - - create {BASIC_AUTH_MODULE} m.make (a_setup) - m.enable - extend (m) - - create {NODE_MODULE} m.make (a_setup) - m.enable - extend (m) - end - -end diff --git a/cms/src/modules/node/node_module.e b/cms/src/modules/node/node_module.e index 683a8c0..1ecaea3 100644 --- a/cms/src/modules/node/node_module.e +++ b/cms/src/modules/node/node_module.e @@ -1,5 +1,5 @@ note - description: "Summary description for {CMS_MODULE}." + description: "CMS module that bring support for NODE management." date: "$Date$" revision: "$Revision$" diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index 7dc0314..37a4759 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -41,12 +41,11 @@ create feature {NONE} -- Initialization - make (a_setup: CMS_SETUP; a_modules: CMS_MODULE_COLLECTION) - -- Build a a default service with a custom list of modules `a_modules' + make (a_setup: CMS_SETUP) + -- Build a CMS service with `a_setup' configuration. do setup := a_setup configuration := a_setup.configuration - modules := a_modules initialize end @@ -65,19 +64,19 @@ feature {NONE} -- Initialization -- Intialize modules and keep only enabled modules. local l_module: CMS_MODULE - coll: CMS_MODULE_COLLECTION + l_available_modules: CMS_MODULE_COLLECTION do log.write_debug (generator + ".initialize_modules") - create coll.make (modules.count) + l_available_modules := setup.modules + create modules.make (l_available_modules.count) across - modules as ic + l_available_modules as ic loop l_module := ic.item if l_module.is_enabled then - coll.extend (l_module) + modules.extend (l_module) end end - modules := coll ensure only_enabled_modules: across modules as ic all ic.item.is_enabled end end diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index 50c1524..1e02a20 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -121,23 +121,22 @@ feature -- CMS Initialization do log.write_debug (generator + ".initialize_cms") - create {CMS_DEFAULT_MODULE_COLLECTION} l_modules.make (a_setup) - setup_modules (l_modules, a_setup) + setup_modules (a_setup) - create cms.make (a_setup, l_modules) + create cms.make (a_setup) cms_service := cms end feature -- CMS setup - setup_modules (a_modules: CMS_MODULE_COLLECTION; a_setup: CMS_SETUP) - -- Setup modules to be added to the available modules. + setup_modules (a_setup: CMS_SETUP) + -- Setup additional modules. local m: CMS_MODULE do create {NODE_MODULE} m.make (a_setup) m.enable - a_modules.extend (m) + a_setup.modules.extend (m) end setup_storage (a_setup: CMS_SETUP) From 36dc48125c5ad4988d117dfda45c664bfad27464 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 8 Oct 2014 16:33:41 -0300 Subject: [PATCH 027/386] Clean code: removed unused variables. Move BASIC_AUTH module to ROC_SERVER setup, as an example of module extension --- cms/src/configuration/cms_default_setup.e | 4 ---- cms/src/modules/basic_auth/handler/basic_auth_login_handler.e | 2 -- cms/src/modules/node/handler/node_content_handler.e | 3 --- cms/src/modules/node/handler/node_handler.e | 1 - cms/src/modules/node/handler/node_summary_handler.e | 2 -- cms/src/modules/node/handler/node_title_handler.e | 2 -- cms/src/service/cms_api_service.e | 2 -- cms/src/service/response/cms_generic_response.e | 1 - examples/roc_api/src/ewf_roc_server.e | 3 +-- 9 files changed, 1 insertion(+), 19 deletions(-) diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index e1bf624..8918a36 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -62,10 +62,6 @@ feature {NONE} -- Initialization -- modules.extend (m) - create {BASIC_AUTH_MODULE} m.make (Current) - m.enable - modules.extend (m) - create {NODE_MODULE} m.make (Current) m.enable modules.extend (m) diff --git a/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e index a0e6df8..4035e0f 100644 --- a/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e +++ b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e @@ -47,8 +47,6 @@ feature -- HTTP Methods do_get (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - l_page: CMS_RESPONSE do log.write_information(generator + ".do_get Processing basic auth login") if attached {STRING_32} current_user_name (req) as l_user then diff --git a/cms/src/modules/node/handler/node_content_handler.e b/cms/src/modules/node/handler/node_content_handler.e index a7113b7..31b911a 100644 --- a/cms/src/modules/node/handler/node_content_handler.e +++ b/cms/src/modules/node/handler/node_content_handler.e @@ -88,8 +88,6 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - l_page: CMS_RESPONSE do if attached current_user_name (req) then if attached {WSF_STRING} req.path_parameter ("id") as l_id then @@ -116,7 +114,6 @@ feature -- HTTP Methods -- local u_node: CMS_NODE - l_page: CMS_RESPONSE do to_implement ("Check if user has permissions") if attached current_user (req) as l_user then diff --git a/cms/src/modules/node/handler/node_handler.e b/cms/src/modules/node/handler/node_handler.e index e9d789f..e4257b6 100644 --- a/cms/src/modules/node/handler/node_handler.e +++ b/cms/src/modules/node/handler/node_handler.e @@ -85,7 +85,6 @@ feature -- HTTP Methods -- local u_node: CMS_NODE - l_page: CMS_RESPONSE do to_implement ("Check user permissions!!!") if attached current_user (req) as l_user then diff --git a/cms/src/modules/node/handler/node_summary_handler.e b/cms/src/modules/node/handler/node_summary_handler.e index d3ec258..96f3c36 100644 --- a/cms/src/modules/node/handler/node_summary_handler.e +++ b/cms/src/modules/node/handler/node_summary_handler.e @@ -87,8 +87,6 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - u_node: CMS_NODE do if attached current_user_name (req) then if attached {WSF_STRING} req.path_parameter ("id") as l_id then diff --git a/cms/src/modules/node/handler/node_title_handler.e b/cms/src/modules/node/handler/node_title_handler.e index 570b155..a5dfe9d 100644 --- a/cms/src/modules/node/handler/node_title_handler.e +++ b/cms/src/modules/node/handler/node_title_handler.e @@ -86,8 +86,6 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - u_node: CMS_NODE do if attached current_user_name (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then diff --git a/cms/src/service/cms_api_service.e b/cms/src/service/cms_api_service.e index c7b69ff..a7d4c8f 100644 --- a/cms/src/service/cms_api_service.e +++ b/cms/src/service/cms_api_service.e @@ -29,8 +29,6 @@ feature -- Initialize feature -- Access is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN - local - l_security: SECURITY_PROVIDER do Result := storage.is_valid_credential (l_auth_login, l_auth_password) end diff --git a/cms/src/service/response/cms_generic_response.e b/cms/src/service/response/cms_generic_response.e index 380a391..371bc05 100644 --- a/cms/src/service/response/cms_generic_response.e +++ b/cms/src/service/response/cms_generic_response.e @@ -50,7 +50,6 @@ feature -- Responses -- Handle not authorized. local h: HTTP_HEADER - output: STRING do create h.make h.put_content_type_text_html diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index 1e02a20..875128d 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -117,7 +117,6 @@ feature -- CMS Initialization initialize_cms (a_setup: CMS_SETUP) local cms: CMS_SERVICE - l_modules: CMS_MODULE_COLLECTION do log.write_debug (generator + ".initialize_cms") @@ -134,7 +133,7 @@ feature -- CMS setup local m: CMS_MODULE do - create {NODE_MODULE} m.make (a_setup) + create {BASIC_AUTH_MODULE} m.make (a_setup) m.enable a_setup.modules.extend (m) end From 8d79447cf8aed6763e88294c7d08f4371d7c3d13 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 9 Oct 2014 10:01:49 -0300 Subject: [PATCH 028/386] Initial Import: Database transaction handling. Initial Import: Error handling using ewf error library. Added a CMS_ERROR_FILTER as part of registered filters of CMS_SERVICE --- cms/cms.ecf | 2 + cms/src/configuration/cms_default_setup.e | 40 ++++++- cms/src/configuration/cms_setup.e | 6 + cms/src/service/cms_service.e | 6 + cms/src/service/filter/cms_error_filter.e | 33 ++++++ .../common/database/database_connection.e | 61 +++++++++- .../common/database/database_handler.e | 55 +++++++-- .../common/database/database_handler_impl.e | 107 +++++------------- .../database/database_iteration_cursor.e | 2 +- .../database/database_no_change_error.e | 27 +++++ .../common/database/database_query.e | 27 +---- .../common/database/error/database_error.e | 23 ++++ .../{ => error}/database_error_handler.e | 16 +-- .../{ => error}/shared_error_handler.e | 14 ++- .../mysql/src/cms_storage_mysql.e | 6 + .../src/database/database_connection_mysql.e | 16 +-- .../mysql/src/provider/node_data_provider.e | 8 -- .../mysql/src/provider/role_data_provider.e | 10 +- .../mysql/src/provider/user_data_provider.e | 8 -- .../mysql/tests/util/clean_db.e | 2 + 20 files changed, 302 insertions(+), 167 deletions(-) create mode 100644 cms/src/service/filter/cms_error_filter.e create mode 100644 persistence/implementation/common/database/database_no_change_error.e create mode 100644 persistence/implementation/common/database/error/database_error.e rename persistence/implementation/common/database/{ => error}/database_error_handler.e (60%) rename persistence/implementation/common/database/{ => error}/shared_error_handler.e (78%) diff --git a/cms/cms.ecf b/cms/cms.ecf index dc20d24..0248847 100644 --- a/cms/cms.ecf +++ b/cms/cms.ecf @@ -10,6 +10,8 @@ + + diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index 8918a36..eabc20c 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -17,6 +17,7 @@ feature {NONE} -- Initialization make (a_layout: CMS_LAYOUT) do + create error_handler.make layout := a_layout create configuration.make (layout) initialize @@ -28,7 +29,6 @@ feature {NONE} -- Initialization create modules.make (3) build_api_service build_mailer - initialize_modules end @@ -61,7 +61,6 @@ feature {NONE} -- Initialization -- m.enable -- modules.extend (m) - create {NODE_MODULE} m.make (Current) m.enable modules.extend (m) @@ -89,15 +88,44 @@ feature -- Access build_api_service local l_database: DATABASE_CONNECTION + l_retry: BOOLEAN + l_message: STRING do - to_implement ("Refactor database setup") - if attached (create {JSON_CONFIGURATION}).new_database_configuration (layout.application_config_path) as l_database_config then - create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string) - create api_service.make (create {CMS_STORAGE_MYSQL}.make (l_database)) + if not l_retry then + to_implement ("Refactor database setup") + if attached (create {JSON_CONFIGURATION}).new_database_configuration (layout.application_config_path) as l_database_config then + create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string) + create api_service.make (create {CMS_STORAGE_MYSQL}.make (l_database)) + else + create {DATABASE_CONNECTION_NULL} l_database.make_common + create api_service.make (create {CMS_STORAGE_NULL}) + end else + to_implement ("Workaround code, persistence layer does not implement yet this kind of error handling.") + -- error hanling. create {DATABASE_CONNECTION_NULL} l_database.make_common create api_service.make (create {CMS_STORAGE_NULL}) + create l_message.make (1024) + if attached ((create {EXCEPTION_MANAGER}).last_exception) as l_exception then + if attached l_exception.description as l_description then + l_message.append (l_description.as_string_32) + l_message.append ("%N%N") + elseif attached l_exception.trace as l_trace then + l_message.append (l_trace) + l_message.append ("%N%N") + else + l_message.append (l_exception.out) + l_message.append ("%N%N") + end + else + l_message.append ("The application crash without available information") + l_message.append ("%N%N") + end + error_handler.add_custom_error (0, " Database Connection ", l_message) end + rescue + l_retry := True + retry end build_auth_engine diff --git a/cms/src/configuration/cms_setup.e b/cms/src/configuration/cms_setup.e index f0af327..0e6189d 100644 --- a/cms/src/configuration/cms_setup.e +++ b/cms/src/configuration/cms_setup.e @@ -32,6 +32,12 @@ feature -- Access deferred end + +feature -- Status Report + + error_handler: ERROR_HANDLER + -- Error handler. + feature -- Access: Site site_id: READABLE_STRING_8 diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index 37a4759..84b67e6 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -165,9 +165,15 @@ feature -- Filters do log.write_debug (generator + ".create_filter") l_filter := Void + -- Maintenance create {WSF_MAINTENANCE_FILTER} f f.set_next (l_filter) + l_filter := f + + -- Error Filter + create {CMS_ERROR_FILTER} f.make (setup) + f.set_next (l_filter) l_filter := f -- Include filters from modules diff --git a/cms/src/service/filter/cms_error_filter.e b/cms/src/service/filter/cms_error_filter.e new file mode 100644 index 0000000..8a2f154 --- /dev/null +++ b/cms/src/service/filter/cms_error_filter.e @@ -0,0 +1,33 @@ +note + description: "Summary description for {CMS_ERROR_FILTER}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_ERROR_FILTER + +inherit + + WSF_URI_TEMPLATE_HANDLER + CMS_HANDLER + WSF_FILTER + +create + make + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter + do + if not setup.error_handler.has_error then + log.write_information (generator + ".execute") + execute_next (req, res) + else + log.write_critical (generator + ".execute" + setup.error_handler.as_string_representation ) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + setup.error_handler.reset + end + end + +end diff --git a/persistence/implementation/common/database/database_connection.e b/persistence/implementation/common/database/database_connection.e index 7591273..cfb9fd9 100644 --- a/persistence/implementation/common/database/database_connection.e +++ b/persistence/implementation/common/database/database_connection.e @@ -10,7 +10,7 @@ inherit DATABASE_CONFIG - SHARED_ERROR + SHARED_ERROR_HANDLER feature {NONE} -- Initialization @@ -72,6 +72,65 @@ feature -- Database Setup keep_connection: BOOLEAN -- Keep connection alive? +feature -- Transactions + + begin_transaction + -- Start a transaction which will be terminated by a call to `rollback' or `commit'. + local + rescued: BOOLEAN + do + if not rescued then + if db_control.is_ok then + db_control.begin + else + database_error_handler.add_database_error (db_control.error_message_32, db_control.error_code) + end + end + rescue + rescued := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + db_control.reset + retry + end + + commit + -- Commit updates in the database. + local + rescued: BOOLEAN + do + if not rescued then + if db_control.is_ok then + db_control.commit + else + database_error_handler.add_database_error (db_control.error_message_32, db_control.error_code) + end + end + rescue + rescued := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + db_control.reset + retry + end + + rollback + -- Rollback updates in the database. + local + rescued: BOOLEAN + do + if not rescued then + if db_control.is_ok then + db_control.rollback + else + database_error_handler.add_database_error (db_control.error_message_32, db_control.error_code) + end + end + rescue + rescued := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + db_control.reset + retry + end + feature -- Change Element not_keep_connection diff --git a/persistence/implementation/common/database/database_handler.e b/persistence/implementation/common/database/database_handler.e index 3551e4a..875b7a7 100644 --- a/persistence/implementation/common/database/database_handler.e +++ b/persistence/implementation/common/database/database_handler.e @@ -8,7 +8,7 @@ deferred class inherit - SHARED_ERROR + SHARED_ERROR_HANDLER feature -- Access @@ -42,15 +42,15 @@ feature -- Modifiers feature -- Functionality Store Procedures - execute_reader - -- Execute store. + execute_store_reader + -- Execute a `store' to read data. require store_not_void: store /= void deferred end - execute_writer - -- Execute store. + execute_store_writer + -- Execute a `store' to write data. require store_not_void: store /= void deferred @@ -59,14 +59,14 @@ feature -- Functionality Store Procedures feature -- SQL Queries execute_query - -- Execute query. + -- Execute sql query, the read data from the database. require query_not_void: query /= void deferred end execute_change - -- Execute sqlquery that update/add data. + -- Execute sql query that update/add data. require query_not_void: query /= void deferred @@ -147,14 +147,49 @@ feature -- Access feature -- Status Report - has_error: BOOLEAN - -- Is there an error? - count: INTEGER -- Number of rows, last execution. deferred end + connection: DATABASE_CONNECTION + -- Database connection. + + db_control: DB_CONTROL + -- Database control. + do + Result := connection.db_control + end + + db_result: detachable DB_RESULT + -- Database query result. + + db_selection: detachable DB_SELECTION + -- Database selection. + + db_change: detachable DB_CHANGE + -- Database modification. + +feature -- Error handling + + check_database_change_error + -- Check database error from `db_change'. + do + if attached db_change as l_change and then not l_change.is_ok then + database_error_handler.add_database_error (l_change.error_message_32, l_change.error_code) + log.write_error (generator + ".check_database_change_error: " + l_change.error_message_32) + end + end + + check_database_selection_error + -- Check database error from `db_selection'. + do + if attached db_selection as l_selection and then not l_selection.is_ok then + database_error_handler.add_database_error (l_selection.error_message_32, l_selection.error_code) + log.write_error (generator + ".check_database_selection_error: " + l_selection.error_message_32) + end + end + feature {NODE_DATA_PROVIDER}-- Implementation connect diff --git a/persistence/implementation/common/database/database_handler_impl.e b/persistence/implementation/common/database/database_handler_impl.e index 96078ee..55bde06 100644 --- a/persistence/implementation/common/database/database_handler_impl.e +++ b/persistence/implementation/common/database/database_handler_impl.e @@ -21,7 +21,6 @@ feature {NONE} -- Initialization do connection := a_connection create last_query.make_now - set_successful ensure connection_not_void: connection /= Void last_query_not_void: last_query /= Void @@ -29,71 +28,52 @@ feature {NONE} -- Initialization feature -- Functionality - execute_reader + execute_store_reader -- Execute stored procedure that returns data. local l_db_selection: DB_SELECTION l_retried: BOOLEAN do if not l_retried then - if not keep_connection then - connect - end - if attached store as l_store then create l_db_selection.make db_selection := l_db_selection items := l_store.execute_reader (l_db_selection) + check_database_selection_error end - - if not keep_connection then - disconnect - end - set_successful log.write_debug ( generator+".execute_reader Successful") end rescue - set_last_error_from_exception ("Store procedure execution") - log.write_critical (generator+ ".execute_reader " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_selection as l_selection then + l_selection.reset + end retry end - execute_writer + execute_store_writer -- Execute stored procedure that update/add data. local l_db_change: DB_CHANGE l_retried : BOOLEAN do if not l_retried then - if not keep_connection and not is_connected then - connect - end if attached store as l_store then create l_db_change.make - db_update := l_db_change + db_change := l_db_change l_store.execute_writer (l_db_change) - if not l_store.has_error then - db_control.commit - end + check_database_change_error end - if not keep_connection then - disconnect - end - set_successful log.write_debug ( generator+".execute_writer Successful") end rescue - set_last_error_from_exception ("Store procedure execution") - log.write_critical (generator+ ".execute_writer " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_change as l_change then + l_change.reset + end retry end @@ -106,60 +86,43 @@ feature -- SQL Queries l_retried: BOOLEAN do if not l_retried then - if not keep_connection then - connect - end - if attached query as l_query then create l_db_selection.make db_selection := l_db_selection items := l_query.execute_reader (l_db_selection) + check_database_selection_error end - if not keep_connection then - disconnect - end - set_successful end rescue - set_last_error_from_exception ("execute_query") - log.write_critical (generator+ ".execute_query " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_selection as l_selection then + l_selection.reset + end retry end execute_change - -- Execute sqlquery that update/add data. + -- Execute sql_query that update/add data. local l_db_change: DB_CHANGE l_retried : BOOLEAN do if not l_retried then - if not keep_connection and not is_connected then - connect - end - if attached query as l_query then create l_db_change.make - db_update := l_db_change + db_change := l_db_change l_query.execute_change (l_db_change) - db_control.commit + check_database_change_error end - if not keep_connection then - disconnect - end - set_successful end rescue - set_last_error_from_exception ("Store procedure execution") - log.write_critical (generator+ ".execute_writer " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_change as l_change then + l_change.reset + end retry end @@ -207,24 +170,6 @@ feature -- Iteration feature {NONE} -- Implementation - connection: DATABASE_CONNECTION - -- Database connection. - - db_control: DB_CONTROL - -- Database control. - do - Result := connection.db_control - end - - db_result: detachable DB_RESULT - -- Database query result. - - db_selection: detachable DB_SELECTION - -- Database selection. - - db_update: detachable DB_CHANGE - -- Database modification. - last_query: DATE_TIME -- Last time when a query was executed. @@ -263,7 +208,7 @@ feature {NONE} -- Implementation affected_row_count: INTEGER -- The number of rows changed, deleted, or inserted by the last statement. do - if attached db_update as l_update then + if attached db_change as l_update then Result := l_update.affected_row_count end end diff --git a/persistence/implementation/common/database/database_iteration_cursor.e b/persistence/implementation/common/database/database_iteration_cursor.e index f38d844..1688b76 100644 --- a/persistence/implementation/common/database/database_iteration_cursor.e +++ b/persistence/implementation/common/database/database_iteration_cursor.e @@ -1,5 +1,5 @@ note - description: "External iteration cursor for {ESA_DATABASE_HANDLER}" + description: "External iteration cursor for {DATABASE_HANDLER}" date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" revision: "$Revision: 95678 $" diff --git a/persistence/implementation/common/database/database_no_change_error.e b/persistence/implementation/common/database/database_no_change_error.e new file mode 100644 index 0000000..6855279 --- /dev/null +++ b/persistence/implementation/common/database/database_no_change_error.e @@ -0,0 +1,27 @@ +note + description: "Summary description for {DATABASE_NO_CHANGE_ERROR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + DATABASE_NO_CHANGE_ERROR + +inherit + DATABASE_ERROR + redefine + make_from_message + end + +create + make_from_message + +feature {NONE} -- Init + + make_from_message (a_m: like message; a_code: like code) + -- Create from `a_m' + do + make (a_code, once "Database No Change Error", a_m) + end + +end diff --git a/persistence/implementation/common/database/database_query.e b/persistence/implementation/common/database/database_query.e index 1a5f8cd..4acec0c 100644 --- a/persistence/implementation/common/database/database_query.e +++ b/persistence/implementation/common/database/database_query.e @@ -8,14 +8,14 @@ class inherit - SHARED_ERROR + SHARED_ERROR_HANDLER REFACTORING_HELPER create data_reader -feature -- Intialization +feature {NONE} -- Intialization data_reader (a_query: STRING; a_parameters: STRING_TABLE [detachable ANY]) -- SQL data reader for the query `a_query' with arguments `a_parameters' @@ -29,6 +29,8 @@ feature -- Intialization parameters_set: parameters = a_parameters end +feature -- Execution + execute_reader (a_base_selection: DB_SELECTION): detachable LIST [DB_RESULT] -- Execute the Current sql query. do @@ -42,7 +44,6 @@ feature -- Intialization a_base_selection.load_result Result := a_base_selection.container else - set_last_error (a_base_selection.error_message_32, generator + ".execute_reader" ) log.write_error (generator + "." + a_base_selection.error_message_32) end unset_map_name (a_base_selection) @@ -50,17 +51,12 @@ feature -- Intialization end execute_change (a_base_change: DB_CHANGE) - -- Execute the Current sql query . + -- Execute the Current sql query to change/update data in the database. do to_implement ("Check test dynamic sequel. to redesign.") set_map_name (a_base_change) a_base_change.set_query (query) a_base_change.execute_query - if a_base_change.is_ok then - else - set_last_error (a_base_change.error_message_32, generator + ".execute_reader" ) - log.write_error (generator + "." + a_base_change.error_message_32) - end unset_map_name (a_base_change) end @@ -72,17 +68,6 @@ feature -- Access parameters: STRING_TABLE [detachable ANY] -- query parameters. -feature -- Status Report - - has_error: BOOLEAN - -- is there an error? - - error_message: detachable STRING_32 - -- Error message if any. - - error_code: INTEGER - -- Error code. - feature {NONE} -- Implementation set_map_name (a_base_selection: DB_EXPRESSION) @@ -140,4 +125,4 @@ feature {NONE} -- Implementation end -end -- ESA_DATABASE_QUERY +end -- DATABASE_QUERY diff --git a/persistence/implementation/common/database/error/database_error.e b/persistence/implementation/common/database/error/database_error.e new file mode 100644 index 0000000..2dcd646 --- /dev/null +++ b/persistence/implementation/common/database/error/database_error.e @@ -0,0 +1,23 @@ +note + description: "Error from database" + date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $" + revision: "$Revision: 195 $" + +class + DATABASE_ERROR + +inherit + ERROR_CUSTOM + +create + make_from_message + +feature {NONE} -- Init + + make_from_message (a_m: like message; a_code: like code) + -- Create from `a_m' + do + make (a_code, once "Database Error", a_m) + end + +end diff --git a/persistence/implementation/common/database/database_error_handler.e b/persistence/implementation/common/database/error/database_error_handler.e similarity index 60% rename from persistence/implementation/common/database/database_error_handler.e rename to persistence/implementation/common/database/error/database_error_handler.e index a2718fe..5af3545 100644 --- a/persistence/implementation/common/database/database_error_handler.e +++ b/persistence/implementation/common/database/error/database_error_handler.e @@ -15,21 +15,21 @@ create feature -- Error operation add_database_error (a_message: READABLE_STRING_32; a_code: INTEGER) - -- Add a database error + -- Add a database error. local --- l_error: DATABASE_ERROR + l_error: DATABASE_ERROR do --- create l_error.make_from_message (a_message, a_code) --- add_error (l_error) + create l_error.make_from_message (a_message, a_code) + add_error (l_error) end add_database_no_change_error (a_message: READABLE_STRING_32; a_code: INTEGER) - -- Add a database error + -- Add a database error. local --- l_error: DATABASE_NO_CHANGE_ERROR + l_error: DATABASE_NO_CHANGE_ERROR do --- create l_error.make_from_message (a_message, a_code) --- add_error (l_error) + create l_error.make_from_message (a_message, a_code) + add_error (l_error) end end diff --git a/persistence/implementation/common/database/shared_error_handler.e b/persistence/implementation/common/database/error/shared_error_handler.e similarity index 78% rename from persistence/implementation/common/database/shared_error_handler.e rename to persistence/implementation/common/database/error/shared_error_handler.e index 9c26bfd..15c06c1 100644 --- a/persistence/implementation/common/database/shared_error_handler.e +++ b/persistence/implementation/common/database/error/shared_error_handler.e @@ -6,14 +6,26 @@ note class SHARED_ERROR_HANDLER +inherit + + SHARED_LOGGER + feature -- Access database_error_handler: DATABASE_ERROR_HANDLER - -- Error handler + -- Error handler. once create Result.make end +feature -- Status Report + + has_error: BOOLEAN + -- Has error? + do + Result := database_error_handler.has_error + end + feature -- Helper exception_as_error (a_e: like {EXCEPTION_MANAGER}.last_exception) diff --git a/persistence/implementation/mysql/src/cms_storage_mysql.e b/persistence/implementation/mysql/src/cms_storage_mysql.e index 74a72ce..47b74ca 100644 --- a/persistence/implementation/mysql/src/cms_storage_mysql.e +++ b/persistence/implementation/mysql/src/cms_storage_mysql.e @@ -21,6 +21,7 @@ feature {NONE} -- Initialization require is_connected: a_connection.is_connected do + connection := a_connection log.write_information (generator+".make_with_database is database connected? "+ a_connection.is_connected.out ) create node_provider.make (a_connection) create user_provider.make (a_connection) @@ -125,11 +126,14 @@ feature -- Change: user save_user (a_user: CMS_USER) -- Add a new user `a_user'. do + if attached a_user.password as l_password and then attached a_user.email as l_email then + connection.begin_transaction user_provider.new_user (a_user.name, l_password, l_email) + connection.commit else set_last_error ("User or Password not attached", generator + ".save_user") end @@ -282,4 +286,6 @@ feature {NONE} -- Post process user_provider: USER_DATA_PROVIDER -- User Data provider. + connection: DATABASE_CONNECTION + -- Current database connection. end diff --git a/persistence/implementation/mysql/src/database/database_connection_mysql.e b/persistence/implementation/mysql/src/database/database_connection_mysql.e index bc5f890..b9d173f 100644 --- a/persistence/implementation/mysql/src/database/database_connection_mysql.e +++ b/persistence/implementation/mysql/src/database/database_connection_mysql.e @@ -34,17 +34,11 @@ feature -- Initialization if keep_connection then connect end - set_successful else create db_control.make end rescue - create db_control.make - set_last_error_from_exception ("Connection execution") - log.write_critical (generator + ".make_common:" + last_error_message) - if is_connected then - disconnect - end + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) l_retried := True retry end @@ -66,17 +60,11 @@ feature -- Initialization if keep_connection then connect end - set_successful else create db_control.make end rescue - create db_control.make - set_last_error_from_exception ("Connection execution") - log.write_critical (generator + ".make_common:" + last_error_message) - if is_connected then - disconnect - end + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) l_retried := True retry end diff --git a/persistence/implementation/mysql/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e index 3bcfc84..59c9bae 100644 --- a/persistence/implementation/mysql/src/provider/node_data_provider.e +++ b/persistence/implementation/mysql/src/provider/node_data_provider.e @@ -34,7 +34,6 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do - Result := db_handler.successful end feature -- Access @@ -481,13 +480,6 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do - if db_handler.successful then - set_successful - else - if attached db_handler.last_error then - set_last_error_from_handler (db_handler.last_error) - end - end end end diff --git a/persistence/implementation/mysql/src/provider/role_data_provider.e b/persistence/implementation/mysql/src/provider/role_data_provider.e index 61590a0..8e82a64 100644 --- a/persistence/implementation/mysql/src/provider/role_data_provider.e +++ b/persistence/implementation/mysql/src/provider/role_data_provider.e @@ -35,7 +35,7 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do - Result := db_handler.successful +-- Result := db_handler.successful end has_roles: BOOLEAN @@ -210,13 +210,7 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do - if db_handler.successful then - set_successful - else - if attached db_handler.last_error then - set_last_error_from_handler (db_handler.last_error) - end - end + end end diff --git a/persistence/implementation/mysql/src/provider/user_data_provider.e b/persistence/implementation/mysql/src/provider/user_data_provider.e index a1ac47b..0d9b0bf 100644 --- a/persistence/implementation/mysql/src/provider/user_data_provider.e +++ b/persistence/implementation/mysql/src/provider/user_data_provider.e @@ -34,7 +34,6 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do - Result := db_handler.successful end has_user: BOOLEAN @@ -311,13 +310,6 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do - if db_handler.successful then - set_successful - else - if attached db_handler.last_error then - set_last_error_from_handler (db_handler.last_error) - end - end end end diff --git a/persistence/implementation/mysql/tests/util/clean_db.e b/persistence/implementation/mysql/tests/util/clean_db.e index 965465f..dce7176 100644 --- a/persistence/implementation/mysql/tests/util/clean_db.e +++ b/persistence/implementation/mysql/tests/util/clean_db.e @@ -24,6 +24,7 @@ feature do create l_parameters.make (0) + a_connection.begin_transaction -- Clean Profiles db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_user_profiles, l_parameters)) @@ -72,6 +73,7 @@ feature db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_profiles_autoincrement, l_parameters)) db_handler(a_connection).execute_change + a_connection.commit end From 609d71a0e9d8b2c0ac9fa56e430c1bfba8687dec Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 13 Oct 2014 18:45:45 -0300 Subject: [PATCH 029/386] Added nested transaction support. Added test cases showing transaction support scenarios. --- .../common/database/database_connection.e | 136 ++++++++++++++ .../tests/transactions/transaction_test_set.e | 168 ++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 persistence/implementation/mysql/tests/transactions/transaction_test_set.e diff --git a/persistence/implementation/common/database/database_connection.e b/persistence/implementation/common/database/database_connection.e index cfb9fd9..a6c2d2c 100644 --- a/persistence/implementation/common/database/database_connection.e +++ b/persistence/implementation/common/database/database_connection.e @@ -131,6 +131,142 @@ feature -- Transactions retry end + +feature -- + + is_connected_to_storage: BOOLEAN + -- Is connected to the database + do + Result := db_control.is_connected + end + +feature -- Transaction Status + + in_transaction_session: BOOLEAN + -- Is session started? + + transaction_session_depth: INTEGER + -- Depth in the transaction session + +feature -- Transaction Operation + + begin_transaction2 + -- Start session + -- if already started, increase the `transaction_session_depth' + require + in_transaction_session implies transaction_session_depth > 0 + not in_transaction_session implies transaction_session_depth = 0 + local + l_session_control: like db_control + l_retried: INTEGER + do + if l_retried = 0 then + if not in_transaction_session then + database_error_handler.reset + + check transaction_session_depth = 0 end + + debug ("database_session") + print ("..Start session%N") + end + connect -- connect the DB + if is_connected_to_storage then + in_transaction_session := True + db_control.begin -- start transaction + else + l_session_control := db_control + if not l_session_control.is_ok then + database_error_handler.add_database_error (l_session_control.error_message_32, l_session_control.error_code) + else + database_error_handler.add_database_error ("Session_not_started_error_message", 0) + end + l_session_control.reset + end + end + transaction_session_depth := transaction_session_depth + 1 + else + if l_retried = 1 then + transaction_session_depth := transaction_session_depth + 1 + if attached (create {EXCEPTION_MANAGER}).last_exception as e then + if attached {ASSERTION_VIOLATION} e then + --| Ignore for now with MYSQL ... + else + exception_as_error (e) + end + end + + in_transaction_session := False + db_control.reset + else + in_transaction_session := False + end + end + ensure + transaction_session_depth = (old transaction_session_depth) + 1 + rescue + l_retried := l_retried + 1 + retry + end + + commit2 + -- End session + local + l_retried: BOOLEAN + do + if not l_retried then + transaction_session_depth := transaction_session_depth - 1 + if transaction_session_depth = 0 then + debug ("database_session") + print ("..End session%N") + end + if is_connected_to_storage then + if not database_error_handler.has_error then + db_control.commit -- Commit transaction + else + db_control.rollback -- Rollback transaction + end + end + in_transaction_session := False + end + else + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + in_transaction_session := False + transaction_session_depth := transaction_session_depth - 1 + db_control.reset + end + rescue + l_retried := True + retry + end + + + + rollback2 + -- End session + local + l_retried: BOOLEAN + do + if not l_retried then + transaction_session_depth := transaction_session_depth - 1 + if transaction_session_depth = 0 then + debug ("database_session") + print ("..End session%N") + end + if is_connected_to_storage then + db_control.rollback -- Rollback transaction + end + in_transaction_session := False + end + else + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + in_transaction_session := False + transaction_session_depth := transaction_session_depth - 1 + db_control.reset + end + rescue + l_retried := True + retry + end feature -- Change Element not_keep_connection diff --git a/persistence/implementation/mysql/tests/transactions/transaction_test_set.e b/persistence/implementation/mysql/tests/transactions/transaction_test_set.e new file mode 100644 index 0000000..3199f71 --- /dev/null +++ b/persistence/implementation/mysql/tests/transactions/transaction_test_set.e @@ -0,0 +1,168 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + TRANSACTION_TEST_SET + +inherit + + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + end + + on_clean + -- + do + end + +feature -- Test routines + + test_user_rollback + note + testing: "execution/isolated" + do + connection.begin_transaction + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + connection.rollback + assert ("Not has user:", not user_provider.has_user) + end + + test_user_node_rollback + note + testing: "execution/isolated" + do + connection.begin_transaction + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + node_provider.add_author (1, 1) + assert ("Has one node:", node_provider.count = 1) + connection.rollback + assert ("Not has user:", not user_provider.has_user) + assert ("Not has nodes:", node_provider.count = 0) + end + + + + test_user_node_nested_transaction_with_rollback_not_supported + note + testing: "execution/isolated" + do + connection.begin_transaction2 + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + assert ("Has one node:", node_provider.count = 1) + + connection.begin_transaction2 + user_provider.new_user ("test1", "test1","test1@admin.com") + assert ("Has user: test1", attached user_provider.user_by_name ("test1")) + connection.rollback2 + + connection.commit2 + assert ("Has user test:", attached user_provider.user_by_name ("test")) + assert ("Has nodes:", node_provider.count = 1) + assert ("Not has user: test1", user_provider.user_by_name ("test1") = Void) + end + + + test_user_node_nested_transaction_with_commit + note + testing: "execution/isolated" + do + connection.begin_transaction2 + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + assert ("Has one node:", node_provider.count = 1) + + connection.begin_transaction2 + user_provider.new_user ("test1", "test1","test1@admin.com") + assert ("Has user: test1", attached user_provider.user_by_name ("test1")) + connection.commit2 + + connection.commit2 + assert ("Has user test:", attached user_provider.user_by_name ("test")) + assert ("Has user test1:", attached user_provider.user_by_name ("test1")) + assert ("Has nodes:", node_provider.count = 1) + end + + + test_user_node_nested_transaction_with_rollback + note + testing: "execution/isolated" + do + connection.begin_transaction2 + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + assert ("Has one node:", node_provider.count = 1) + + connection.begin_transaction2 + user_provider.new_user ("test1", "test1","test1@admin.com") + assert ("Has user: test1", attached user_provider.user_by_name ("test1")) + connection.commit2 + + connection.rollback2 + assert ("Not Has user test:", user_provider.user_by_name ("test") = void) + assert ("Not Has user test1:", user_provider.user_by_name ("test1") = void) + assert ("Has 0 nodes:", node_provider.count = 0) + end + + + + + +feature {NONE} -- Implementation + + node_provider: NODE_DATA_PROVIDER + -- node provider. + once + create Result.make (connection) + end + + user_provider: USER_DATA_PROVIDER + -- user provider. + once + create Result.make (connection) + end + + +feature {NONE} -- Implementation Fixture Factories + + default_node: CMS_NODE + do + Result := custom_node ("Default content", "default summary", "Default") + end + + custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE + do + create Result.make (a_content, a_summary, a_title) + end +end + + From 4841039d4aa78ab4f6ef9484146c49ebab62abdc Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 14 Oct 2014 15:24:24 +0200 Subject: [PATCH 030/386] Renamed void-safe ecf using -safe.ecf convention. Added a cms_demo_module, for now pretty empty, but it will be used to develop/test the cms core. --- cms/{cms.ecf => cms-safe.ecf} | 4 +- examples/api/roc_api.ecf | 4 +- .../modules/demo/cms_demo_module-safe.ecf | 23 +++++ .../roc_api/modules/demo/cms_demo_module.e | 93 +++++++++++++++++++ .../roc_api/{roc_api.ecf => roc_api-safe.ecf} | 5 +- examples/roc_api/src/ewf_roc_server.e | 5 + layout/{layout.ecf => layout-safe.ecf} | 0 ...e_mysql.ecf => persistence_mysql-safe.ecf} | 2 +- .../implementation/mysql/tests/tests.ecf | 2 +- ...sqlite.ecf => persistence_sqlite-safe.ecf} | 2 +- .../tests/{tests.ecf => tests-safe.ecf} | 2 +- 11 files changed, 132 insertions(+), 10 deletions(-) rename cms/{cms.ecf => cms-safe.ecf} (93%) create mode 100644 examples/roc_api/modules/demo/cms_demo_module-safe.ecf create mode 100644 examples/roc_api/modules/demo/cms_demo_module.e rename examples/roc_api/{roc_api.ecf => roc_api-safe.ecf} (91%) rename layout/{layout.ecf => layout-safe.ecf} (100%) rename persistence/implementation/mysql/{persistence_mysql.ecf => persistence_mysql-safe.ecf} (96%) rename persistence/implementation/sqlite/{persistence_sqlite.ecf => persistence_sqlite-safe.ecf} (96%) rename persistence/implementation/sqlite/tests/{tests.ecf => tests-safe.ecf} (97%) diff --git a/cms/cms.ecf b/cms/cms-safe.ecf similarity index 93% rename from cms/cms.ecf rename to cms/cms-safe.ecf index dc20d24..235bd74 100644 --- a/cms/cms.ecf +++ b/cms/cms-safe.ecf @@ -9,9 +9,9 @@ - + - + diff --git a/examples/api/roc_api.ecf b/examples/api/roc_api.ecf index adf512d..a68d4e1 100644 --- a/examples/api/roc_api.ecf +++ b/examples/api/roc_api.ecf @@ -16,12 +16,12 @@ - + - + diff --git a/examples/roc_api/modules/demo/cms_demo_module-safe.ecf b/examples/roc_api/modules/demo/cms_demo_module-safe.ecf new file mode 100644 index 0000000..dd678f2 --- /dev/null +++ b/examples/roc_api/modules/demo/cms_demo_module-safe.ecf @@ -0,0 +1,23 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + + + + + + diff --git a/examples/roc_api/modules/demo/cms_demo_module.e b/examples/roc_api/modules/demo/cms_demo_module.e new file mode 100644 index 0000000..aeabe7b --- /dev/null +++ b/examples/roc_api/modules/demo/cms_demo_module.e @@ -0,0 +1,93 @@ +note + description: "Summary description for {CMS_DEMO_MODULE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_DEMO_MODULE + +inherit + CMS_MODULE + +create + make + +feature {NONE} -- Initialization + + make (a_config: CMS_SETUP) + do + name := "Demo module" + version := "1.0" + description := "Service to demonstrate and test cms system" + package := "demo" + config := a_config + end + + config: CMS_SETUP + -- Node configuration. + +feature -- Access: router + + router: WSF_ROUTER + -- Node router. + do + create Result.make (2) + + map_uri_template_agent (Result, "/demo/", agent handle_demo) + map_uri_template_agent (Result, "/book/{id}", agent handle_demo_entry) + end + +feature -- Handler + + handle_demo, + handle_demo_entry (req: WSF_REQUEST; res: WSF_RESPONSE) + local + m: WSF_NOT_FOUND_RESPONSE + do + create m.make (req) + m.set_body ("Not yet implemented!") + res.send (m) + end + +feature -- Mapping helper: uri template + + map_uri_template (a_router: WSF_ROUTER; a_tpl: STRING; h: WSF_URI_TEMPLATE_HANDLER) + -- Map `h' as handler for `a_tpl' + require + a_tpl_attached: a_tpl /= Void + h_attached: h /= Void + do + map_uri_template_with_request_methods (a_router, a_tpl, h, Void) + end + + map_uri_template_with_request_methods (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; h: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS) + -- Map `h' as handler for `a_tpl' for request methods `rqst_methods'. + require + a_tpl_attached: a_tpl /= Void + h_attached: h /= Void + do + a_router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, h), rqst_methods) + end + +feature -- Mapping helper: uri template agent + + map_uri_template_agent (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]) + -- Map `proc' as handler for `a_tpl' + require + a_tpl_attached: a_tpl /= Void + proc_attached: proc /= Void + do + map_uri_template_agent_with_request_methods (a_router, a_tpl, proc, Void) + end + + map_uri_template_agent_with_request_methods (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS) + -- Map `proc' as handler for `a_tpl' for request methods `rqst_methods'. + require + a_tpl_attached: a_tpl /= Void + proc_attached: proc /= Void + do + map_uri_template_with_request_methods (a_router, a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods) + end + +end diff --git a/examples/roc_api/roc_api.ecf b/examples/roc_api/roc_api-safe.ecf similarity index 91% rename from examples/roc_api/roc_api.ecf rename to examples/roc_api/roc_api-safe.ecf index fb9f85c..c8576e2 100644 --- a/examples/roc_api/roc_api.ecf +++ b/examples/roc_api/roc_api-safe.ecf @@ -10,8 +10,9 @@ - - + + + diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index 1e02a20..4919a54 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -137,6 +137,11 @@ feature -- CMS setup create {NODE_MODULE} m.make (a_setup) m.enable a_setup.modules.extend (m) + + create {CMS_DEMO_MODULE} m.make (a_setup) + m.enable + a_setup.modules.extend (m) + end setup_storage (a_setup: CMS_SETUP) diff --git a/layout/layout.ecf b/layout/layout-safe.ecf similarity index 100% rename from layout/layout.ecf rename to layout/layout-safe.ecf diff --git a/persistence/implementation/mysql/persistence_mysql.ecf b/persistence/implementation/mysql/persistence_mysql-safe.ecf similarity index 96% rename from persistence/implementation/mysql/persistence_mysql.ecf rename to persistence/implementation/mysql/persistence_mysql-safe.ecf index d66d48e..6811629 100644 --- a/persistence/implementation/mysql/persistence_mysql.ecf +++ b/persistence/implementation/mysql/persistence_mysql-safe.ecf @@ -12,7 +12,7 @@ - + diff --git a/persistence/implementation/mysql/tests/tests.ecf b/persistence/implementation/mysql/tests/tests.ecf index cf85bf2..363c33c 100644 --- a/persistence/implementation/mysql/tests/tests.ecf +++ b/persistence/implementation/mysql/tests/tests.ecf @@ -8,7 +8,7 @@ - + diff --git a/persistence/implementation/sqlite/persistence_sqlite.ecf b/persistence/implementation/sqlite/persistence_sqlite-safe.ecf similarity index 96% rename from persistence/implementation/sqlite/persistence_sqlite.ecf rename to persistence/implementation/sqlite/persistence_sqlite-safe.ecf index c4c203c..90af783 100644 --- a/persistence/implementation/sqlite/persistence_sqlite.ecf +++ b/persistence/implementation/sqlite/persistence_sqlite-safe.ecf @@ -10,7 +10,7 @@ - + diff --git a/persistence/implementation/sqlite/tests/tests.ecf b/persistence/implementation/sqlite/tests/tests-safe.ecf similarity index 97% rename from persistence/implementation/sqlite/tests/tests.ecf rename to persistence/implementation/sqlite/tests/tests-safe.ecf index d401b33..cdeb22e 100644 --- a/persistence/implementation/sqlite/tests/tests.ecf +++ b/persistence/implementation/sqlite/tests/tests-safe.ecf @@ -8,7 +8,7 @@ - + From b8257e7bf07bc69be223cffa87256208e4be76ae Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 8 Oct 2014 16:33:41 -0300 Subject: [PATCH 031/386] Clean code: removed unused variables. Move BASIC_AUTH module to ROC_SERVER setup, as an example of module extension --- cms/src/configuration/cms_default_setup.e | 4 ---- cms/src/modules/basic_auth/handler/basic_auth_login_handler.e | 2 -- cms/src/modules/node/handler/node_content_handler.e | 3 --- cms/src/modules/node/handler/node_handler.e | 1 - cms/src/modules/node/handler/node_summary_handler.e | 2 -- cms/src/modules/node/handler/node_title_handler.e | 2 -- cms/src/service/cms_api_service.e | 2 -- cms/src/service/response/cms_generic_response.e | 1 - examples/roc_api/src/ewf_roc_server.e | 3 +-- 9 files changed, 1 insertion(+), 19 deletions(-) diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index e1bf624..8918a36 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -62,10 +62,6 @@ feature {NONE} -- Initialization -- modules.extend (m) - create {BASIC_AUTH_MODULE} m.make (Current) - m.enable - modules.extend (m) - create {NODE_MODULE} m.make (Current) m.enable modules.extend (m) diff --git a/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e index a0e6df8..4035e0f 100644 --- a/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e +++ b/cms/src/modules/basic_auth/handler/basic_auth_login_handler.e @@ -47,8 +47,6 @@ feature -- HTTP Methods do_get (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - l_page: CMS_RESPONSE do log.write_information(generator + ".do_get Processing basic auth login") if attached {STRING_32} current_user_name (req) as l_user then diff --git a/cms/src/modules/node/handler/node_content_handler.e b/cms/src/modules/node/handler/node_content_handler.e index a7113b7..31b911a 100644 --- a/cms/src/modules/node/handler/node_content_handler.e +++ b/cms/src/modules/node/handler/node_content_handler.e @@ -88,8 +88,6 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - l_page: CMS_RESPONSE do if attached current_user_name (req) then if attached {WSF_STRING} req.path_parameter ("id") as l_id then @@ -116,7 +114,6 @@ feature -- HTTP Methods -- local u_node: CMS_NODE - l_page: CMS_RESPONSE do to_implement ("Check if user has permissions") if attached current_user (req) as l_user then diff --git a/cms/src/modules/node/handler/node_handler.e b/cms/src/modules/node/handler/node_handler.e index e9d789f..e4257b6 100644 --- a/cms/src/modules/node/handler/node_handler.e +++ b/cms/src/modules/node/handler/node_handler.e @@ -85,7 +85,6 @@ feature -- HTTP Methods -- local u_node: CMS_NODE - l_page: CMS_RESPONSE do to_implement ("Check user permissions!!!") if attached current_user (req) as l_user then diff --git a/cms/src/modules/node/handler/node_summary_handler.e b/cms/src/modules/node/handler/node_summary_handler.e index d3ec258..96f3c36 100644 --- a/cms/src/modules/node/handler/node_summary_handler.e +++ b/cms/src/modules/node/handler/node_summary_handler.e @@ -87,8 +87,6 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - u_node: CMS_NODE do if attached current_user_name (req) then if attached {WSF_STRING} req.path_parameter ("id") as l_id then diff --git a/cms/src/modules/node/handler/node_title_handler.e b/cms/src/modules/node/handler/node_title_handler.e index 570b155..a5dfe9d 100644 --- a/cms/src/modules/node/handler/node_title_handler.e +++ b/cms/src/modules/node/handler/node_title_handler.e @@ -86,8 +86,6 @@ feature -- HTTP Methods do_post (req: WSF_REQUEST; res: WSF_RESPONSE) -- - local - u_node: CMS_NODE do if attached current_user_name (req) as l_user then if attached {WSF_STRING} req.path_parameter ("id") as l_id then diff --git a/cms/src/service/cms_api_service.e b/cms/src/service/cms_api_service.e index c7b69ff..a7d4c8f 100644 --- a/cms/src/service/cms_api_service.e +++ b/cms/src/service/cms_api_service.e @@ -29,8 +29,6 @@ feature -- Initialize feature -- Access is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN - local - l_security: SECURITY_PROVIDER do Result := storage.is_valid_credential (l_auth_login, l_auth_password) end diff --git a/cms/src/service/response/cms_generic_response.e b/cms/src/service/response/cms_generic_response.e index 380a391..371bc05 100644 --- a/cms/src/service/response/cms_generic_response.e +++ b/cms/src/service/response/cms_generic_response.e @@ -50,7 +50,6 @@ feature -- Responses -- Handle not authorized. local h: HTTP_HEADER - output: STRING do create h.make h.put_content_type_text_html diff --git a/examples/roc_api/src/ewf_roc_server.e b/examples/roc_api/src/ewf_roc_server.e index 4919a54..5fd497b 100644 --- a/examples/roc_api/src/ewf_roc_server.e +++ b/examples/roc_api/src/ewf_roc_server.e @@ -117,7 +117,6 @@ feature -- CMS Initialization initialize_cms (a_setup: CMS_SETUP) local cms: CMS_SERVICE - l_modules: CMS_MODULE_COLLECTION do log.write_debug (generator + ".initialize_cms") @@ -134,7 +133,7 @@ feature -- CMS setup local m: CMS_MODULE do - create {NODE_MODULE} m.make (a_setup) + create {BASIC_AUTH_MODULE} m.make (a_setup) m.enable a_setup.modules.extend (m) From 4b9a692c2cb8c5f197d4d1c18ff4acbd7072b116 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 9 Oct 2014 10:01:49 -0300 Subject: [PATCH 032/386] Initial Import: Database transaction handling. Initial Import: Error handling using ewf error library. Added a CMS_ERROR_FILTER as part of registered filters of CMS_SERVICE --- cms/cms-safe.ecf | 1 + cms/src/configuration/cms_default_setup.e | 40 ++++++- cms/src/configuration/cms_setup.e | 6 + cms/src/service/cms_service.e | 6 + cms/src/service/filter/cms_error_filter.e | 33 ++++++ .../common/database/database_connection.e | 61 +++++++++- .../common/database/database_handler.e | 55 +++++++-- .../common/database/database_handler_impl.e | 107 +++++------------- .../database/database_iteration_cursor.e | 2 +- .../database/database_no_change_error.e | 27 +++++ .../common/database/database_query.e | 27 +---- .../common/database/error/database_error.e | 23 ++++ .../{ => error}/database_error_handler.e | 16 +-- .../{ => error}/shared_error_handler.e | 14 ++- .../mysql/src/cms_storage_mysql.e | 6 + .../src/database/database_connection_mysql.e | 16 +-- .../mysql/src/provider/node_data_provider.e | 8 -- .../mysql/src/provider/role_data_provider.e | 10 +- .../mysql/src/provider/user_data_provider.e | 8 -- .../mysql/tests/util/clean_db.e | 2 + 20 files changed, 301 insertions(+), 167 deletions(-) create mode 100644 cms/src/service/filter/cms_error_filter.e create mode 100644 persistence/implementation/common/database/database_no_change_error.e create mode 100644 persistence/implementation/common/database/error/database_error.e rename persistence/implementation/common/database/{ => error}/database_error_handler.e (60%) rename persistence/implementation/common/database/{ => error}/shared_error_handler.e (78%) diff --git a/cms/cms-safe.ecf b/cms/cms-safe.ecf index 235bd74..5da317c 100644 --- a/cms/cms-safe.ecf +++ b/cms/cms-safe.ecf @@ -10,6 +10,7 @@ + diff --git a/cms/src/configuration/cms_default_setup.e b/cms/src/configuration/cms_default_setup.e index 8918a36..eabc20c 100644 --- a/cms/src/configuration/cms_default_setup.e +++ b/cms/src/configuration/cms_default_setup.e @@ -17,6 +17,7 @@ feature {NONE} -- Initialization make (a_layout: CMS_LAYOUT) do + create error_handler.make layout := a_layout create configuration.make (layout) initialize @@ -28,7 +29,6 @@ feature {NONE} -- Initialization create modules.make (3) build_api_service build_mailer - initialize_modules end @@ -61,7 +61,6 @@ feature {NONE} -- Initialization -- m.enable -- modules.extend (m) - create {NODE_MODULE} m.make (Current) m.enable modules.extend (m) @@ -89,15 +88,44 @@ feature -- Access build_api_service local l_database: DATABASE_CONNECTION + l_retry: BOOLEAN + l_message: STRING do - to_implement ("Refactor database setup") - if attached (create {JSON_CONFIGURATION}).new_database_configuration (layout.application_config_path) as l_database_config then - create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string) - create api_service.make (create {CMS_STORAGE_MYSQL}.make (l_database)) + if not l_retry then + to_implement ("Refactor database setup") + if attached (create {JSON_CONFIGURATION}).new_database_configuration (layout.application_config_path) as l_database_config then + create {DATABASE_CONNECTION_MYSQL} l_database.login_with_connection_string (l_database_config.connection_string) + create api_service.make (create {CMS_STORAGE_MYSQL}.make (l_database)) + else + create {DATABASE_CONNECTION_NULL} l_database.make_common + create api_service.make (create {CMS_STORAGE_NULL}) + end else + to_implement ("Workaround code, persistence layer does not implement yet this kind of error handling.") + -- error hanling. create {DATABASE_CONNECTION_NULL} l_database.make_common create api_service.make (create {CMS_STORAGE_NULL}) + create l_message.make (1024) + if attached ((create {EXCEPTION_MANAGER}).last_exception) as l_exception then + if attached l_exception.description as l_description then + l_message.append (l_description.as_string_32) + l_message.append ("%N%N") + elseif attached l_exception.trace as l_trace then + l_message.append (l_trace) + l_message.append ("%N%N") + else + l_message.append (l_exception.out) + l_message.append ("%N%N") + end + else + l_message.append ("The application crash without available information") + l_message.append ("%N%N") + end + error_handler.add_custom_error (0, " Database Connection ", l_message) end + rescue + l_retry := True + retry end build_auth_engine diff --git a/cms/src/configuration/cms_setup.e b/cms/src/configuration/cms_setup.e index f0af327..0e6189d 100644 --- a/cms/src/configuration/cms_setup.e +++ b/cms/src/configuration/cms_setup.e @@ -32,6 +32,12 @@ feature -- Access deferred end + +feature -- Status Report + + error_handler: ERROR_HANDLER + -- Error handler. + feature -- Access: Site site_id: READABLE_STRING_8 diff --git a/cms/src/service/cms_service.e b/cms/src/service/cms_service.e index 37a4759..84b67e6 100644 --- a/cms/src/service/cms_service.e +++ b/cms/src/service/cms_service.e @@ -165,9 +165,15 @@ feature -- Filters do log.write_debug (generator + ".create_filter") l_filter := Void + -- Maintenance create {WSF_MAINTENANCE_FILTER} f f.set_next (l_filter) + l_filter := f + + -- Error Filter + create {CMS_ERROR_FILTER} f.make (setup) + f.set_next (l_filter) l_filter := f -- Include filters from modules diff --git a/cms/src/service/filter/cms_error_filter.e b/cms/src/service/filter/cms_error_filter.e new file mode 100644 index 0000000..8a2f154 --- /dev/null +++ b/cms/src/service/filter/cms_error_filter.e @@ -0,0 +1,33 @@ +note + description: "Summary description for {CMS_ERROR_FILTER}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_ERROR_FILTER + +inherit + + WSF_URI_TEMPLATE_HANDLER + CMS_HANDLER + WSF_FILTER + +create + make + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter + do + if not setup.error_handler.has_error then + log.write_information (generator + ".execute") + execute_next (req, res) + else + log.write_critical (generator + ".execute" + setup.error_handler.as_string_representation ) + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + setup.error_handler.reset + end + end + +end diff --git a/persistence/implementation/common/database/database_connection.e b/persistence/implementation/common/database/database_connection.e index 7591273..cfb9fd9 100644 --- a/persistence/implementation/common/database/database_connection.e +++ b/persistence/implementation/common/database/database_connection.e @@ -10,7 +10,7 @@ inherit DATABASE_CONFIG - SHARED_ERROR + SHARED_ERROR_HANDLER feature {NONE} -- Initialization @@ -72,6 +72,65 @@ feature -- Database Setup keep_connection: BOOLEAN -- Keep connection alive? +feature -- Transactions + + begin_transaction + -- Start a transaction which will be terminated by a call to `rollback' or `commit'. + local + rescued: BOOLEAN + do + if not rescued then + if db_control.is_ok then + db_control.begin + else + database_error_handler.add_database_error (db_control.error_message_32, db_control.error_code) + end + end + rescue + rescued := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + db_control.reset + retry + end + + commit + -- Commit updates in the database. + local + rescued: BOOLEAN + do + if not rescued then + if db_control.is_ok then + db_control.commit + else + database_error_handler.add_database_error (db_control.error_message_32, db_control.error_code) + end + end + rescue + rescued := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + db_control.reset + retry + end + + rollback + -- Rollback updates in the database. + local + rescued: BOOLEAN + do + if not rescued then + if db_control.is_ok then + db_control.rollback + else + database_error_handler.add_database_error (db_control.error_message_32, db_control.error_code) + end + end + rescue + rescued := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + db_control.reset + retry + end + feature -- Change Element not_keep_connection diff --git a/persistence/implementation/common/database/database_handler.e b/persistence/implementation/common/database/database_handler.e index 3551e4a..875b7a7 100644 --- a/persistence/implementation/common/database/database_handler.e +++ b/persistence/implementation/common/database/database_handler.e @@ -8,7 +8,7 @@ deferred class inherit - SHARED_ERROR + SHARED_ERROR_HANDLER feature -- Access @@ -42,15 +42,15 @@ feature -- Modifiers feature -- Functionality Store Procedures - execute_reader - -- Execute store. + execute_store_reader + -- Execute a `store' to read data. require store_not_void: store /= void deferred end - execute_writer - -- Execute store. + execute_store_writer + -- Execute a `store' to write data. require store_not_void: store /= void deferred @@ -59,14 +59,14 @@ feature -- Functionality Store Procedures feature -- SQL Queries execute_query - -- Execute query. + -- Execute sql query, the read data from the database. require query_not_void: query /= void deferred end execute_change - -- Execute sqlquery that update/add data. + -- Execute sql query that update/add data. require query_not_void: query /= void deferred @@ -147,14 +147,49 @@ feature -- Access feature -- Status Report - has_error: BOOLEAN - -- Is there an error? - count: INTEGER -- Number of rows, last execution. deferred end + connection: DATABASE_CONNECTION + -- Database connection. + + db_control: DB_CONTROL + -- Database control. + do + Result := connection.db_control + end + + db_result: detachable DB_RESULT + -- Database query result. + + db_selection: detachable DB_SELECTION + -- Database selection. + + db_change: detachable DB_CHANGE + -- Database modification. + +feature -- Error handling + + check_database_change_error + -- Check database error from `db_change'. + do + if attached db_change as l_change and then not l_change.is_ok then + database_error_handler.add_database_error (l_change.error_message_32, l_change.error_code) + log.write_error (generator + ".check_database_change_error: " + l_change.error_message_32) + end + end + + check_database_selection_error + -- Check database error from `db_selection'. + do + if attached db_selection as l_selection and then not l_selection.is_ok then + database_error_handler.add_database_error (l_selection.error_message_32, l_selection.error_code) + log.write_error (generator + ".check_database_selection_error: " + l_selection.error_message_32) + end + end + feature {NODE_DATA_PROVIDER}-- Implementation connect diff --git a/persistence/implementation/common/database/database_handler_impl.e b/persistence/implementation/common/database/database_handler_impl.e index 96078ee..55bde06 100644 --- a/persistence/implementation/common/database/database_handler_impl.e +++ b/persistence/implementation/common/database/database_handler_impl.e @@ -21,7 +21,6 @@ feature {NONE} -- Initialization do connection := a_connection create last_query.make_now - set_successful ensure connection_not_void: connection /= Void last_query_not_void: last_query /= Void @@ -29,71 +28,52 @@ feature {NONE} -- Initialization feature -- Functionality - execute_reader + execute_store_reader -- Execute stored procedure that returns data. local l_db_selection: DB_SELECTION l_retried: BOOLEAN do if not l_retried then - if not keep_connection then - connect - end - if attached store as l_store then create l_db_selection.make db_selection := l_db_selection items := l_store.execute_reader (l_db_selection) + check_database_selection_error end - - if not keep_connection then - disconnect - end - set_successful log.write_debug ( generator+".execute_reader Successful") end rescue - set_last_error_from_exception ("Store procedure execution") - log.write_critical (generator+ ".execute_reader " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_selection as l_selection then + l_selection.reset + end retry end - execute_writer + execute_store_writer -- Execute stored procedure that update/add data. local l_db_change: DB_CHANGE l_retried : BOOLEAN do if not l_retried then - if not keep_connection and not is_connected then - connect - end if attached store as l_store then create l_db_change.make - db_update := l_db_change + db_change := l_db_change l_store.execute_writer (l_db_change) - if not l_store.has_error then - db_control.commit - end + check_database_change_error end - if not keep_connection then - disconnect - end - set_successful log.write_debug ( generator+".execute_writer Successful") end rescue - set_last_error_from_exception ("Store procedure execution") - log.write_critical (generator+ ".execute_writer " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_change as l_change then + l_change.reset + end retry end @@ -106,60 +86,43 @@ feature -- SQL Queries l_retried: BOOLEAN do if not l_retried then - if not keep_connection then - connect - end - if attached query as l_query then create l_db_selection.make db_selection := l_db_selection items := l_query.execute_reader (l_db_selection) + check_database_selection_error end - if not keep_connection then - disconnect - end - set_successful end rescue - set_last_error_from_exception ("execute_query") - log.write_critical (generator+ ".execute_query " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_selection as l_selection then + l_selection.reset + end retry end execute_change - -- Execute sqlquery that update/add data. + -- Execute sql_query that update/add data. local l_db_change: DB_CHANGE l_retried : BOOLEAN do if not l_retried then - if not keep_connection and not is_connected then - connect - end - if attached query as l_query then create l_db_change.make - db_update := l_db_change + db_change := l_db_change l_query.execute_change (l_db_change) - db_control.commit + check_database_change_error end - if not keep_connection then - disconnect - end - set_successful end rescue - set_last_error_from_exception ("Store procedure execution") - log.write_critical (generator+ ".execute_writer " + last_error_message) - if is_connected then - disconnect - end l_retried := True + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + if attached db_change as l_change then + l_change.reset + end retry end @@ -207,24 +170,6 @@ feature -- Iteration feature {NONE} -- Implementation - connection: DATABASE_CONNECTION - -- Database connection. - - db_control: DB_CONTROL - -- Database control. - do - Result := connection.db_control - end - - db_result: detachable DB_RESULT - -- Database query result. - - db_selection: detachable DB_SELECTION - -- Database selection. - - db_update: detachable DB_CHANGE - -- Database modification. - last_query: DATE_TIME -- Last time when a query was executed. @@ -263,7 +208,7 @@ feature {NONE} -- Implementation affected_row_count: INTEGER -- The number of rows changed, deleted, or inserted by the last statement. do - if attached db_update as l_update then + if attached db_change as l_update then Result := l_update.affected_row_count end end diff --git a/persistence/implementation/common/database/database_iteration_cursor.e b/persistence/implementation/common/database/database_iteration_cursor.e index f38d844..1688b76 100644 --- a/persistence/implementation/common/database/database_iteration_cursor.e +++ b/persistence/implementation/common/database/database_iteration_cursor.e @@ -1,5 +1,5 @@ note - description: "External iteration cursor for {ESA_DATABASE_HANDLER}" + description: "External iteration cursor for {DATABASE_HANDLER}" date: "$Date: 2014-08-20 15:21:15 -0300 (mi., 20 ago. 2014) $" revision: "$Revision: 95678 $" diff --git a/persistence/implementation/common/database/database_no_change_error.e b/persistence/implementation/common/database/database_no_change_error.e new file mode 100644 index 0000000..6855279 --- /dev/null +++ b/persistence/implementation/common/database/database_no_change_error.e @@ -0,0 +1,27 @@ +note + description: "Summary description for {DATABASE_NO_CHANGE_ERROR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + DATABASE_NO_CHANGE_ERROR + +inherit + DATABASE_ERROR + redefine + make_from_message + end + +create + make_from_message + +feature {NONE} -- Init + + make_from_message (a_m: like message; a_code: like code) + -- Create from `a_m' + do + make (a_code, once "Database No Change Error", a_m) + end + +end diff --git a/persistence/implementation/common/database/database_query.e b/persistence/implementation/common/database/database_query.e index 1a5f8cd..4acec0c 100644 --- a/persistence/implementation/common/database/database_query.e +++ b/persistence/implementation/common/database/database_query.e @@ -8,14 +8,14 @@ class inherit - SHARED_ERROR + SHARED_ERROR_HANDLER REFACTORING_HELPER create data_reader -feature -- Intialization +feature {NONE} -- Intialization data_reader (a_query: STRING; a_parameters: STRING_TABLE [detachable ANY]) -- SQL data reader for the query `a_query' with arguments `a_parameters' @@ -29,6 +29,8 @@ feature -- Intialization parameters_set: parameters = a_parameters end +feature -- Execution + execute_reader (a_base_selection: DB_SELECTION): detachable LIST [DB_RESULT] -- Execute the Current sql query. do @@ -42,7 +44,6 @@ feature -- Intialization a_base_selection.load_result Result := a_base_selection.container else - set_last_error (a_base_selection.error_message_32, generator + ".execute_reader" ) log.write_error (generator + "." + a_base_selection.error_message_32) end unset_map_name (a_base_selection) @@ -50,17 +51,12 @@ feature -- Intialization end execute_change (a_base_change: DB_CHANGE) - -- Execute the Current sql query . + -- Execute the Current sql query to change/update data in the database. do to_implement ("Check test dynamic sequel. to redesign.") set_map_name (a_base_change) a_base_change.set_query (query) a_base_change.execute_query - if a_base_change.is_ok then - else - set_last_error (a_base_change.error_message_32, generator + ".execute_reader" ) - log.write_error (generator + "." + a_base_change.error_message_32) - end unset_map_name (a_base_change) end @@ -72,17 +68,6 @@ feature -- Access parameters: STRING_TABLE [detachable ANY] -- query parameters. -feature -- Status Report - - has_error: BOOLEAN - -- is there an error? - - error_message: detachable STRING_32 - -- Error message if any. - - error_code: INTEGER - -- Error code. - feature {NONE} -- Implementation set_map_name (a_base_selection: DB_EXPRESSION) @@ -140,4 +125,4 @@ feature {NONE} -- Implementation end -end -- ESA_DATABASE_QUERY +end -- DATABASE_QUERY diff --git a/persistence/implementation/common/database/error/database_error.e b/persistence/implementation/common/database/error/database_error.e new file mode 100644 index 0000000..2dcd646 --- /dev/null +++ b/persistence/implementation/common/database/error/database_error.e @@ -0,0 +1,23 @@ +note + description: "Error from database" + date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $" + revision: "$Revision: 195 $" + +class + DATABASE_ERROR + +inherit + ERROR_CUSTOM + +create + make_from_message + +feature {NONE} -- Init + + make_from_message (a_m: like message; a_code: like code) + -- Create from `a_m' + do + make (a_code, once "Database Error", a_m) + end + +end diff --git a/persistence/implementation/common/database/database_error_handler.e b/persistence/implementation/common/database/error/database_error_handler.e similarity index 60% rename from persistence/implementation/common/database/database_error_handler.e rename to persistence/implementation/common/database/error/database_error_handler.e index a2718fe..5af3545 100644 --- a/persistence/implementation/common/database/database_error_handler.e +++ b/persistence/implementation/common/database/error/database_error_handler.e @@ -15,21 +15,21 @@ create feature -- Error operation add_database_error (a_message: READABLE_STRING_32; a_code: INTEGER) - -- Add a database error + -- Add a database error. local --- l_error: DATABASE_ERROR + l_error: DATABASE_ERROR do --- create l_error.make_from_message (a_message, a_code) --- add_error (l_error) + create l_error.make_from_message (a_message, a_code) + add_error (l_error) end add_database_no_change_error (a_message: READABLE_STRING_32; a_code: INTEGER) - -- Add a database error + -- Add a database error. local --- l_error: DATABASE_NO_CHANGE_ERROR + l_error: DATABASE_NO_CHANGE_ERROR do --- create l_error.make_from_message (a_message, a_code) --- add_error (l_error) + create l_error.make_from_message (a_message, a_code) + add_error (l_error) end end diff --git a/persistence/implementation/common/database/shared_error_handler.e b/persistence/implementation/common/database/error/shared_error_handler.e similarity index 78% rename from persistence/implementation/common/database/shared_error_handler.e rename to persistence/implementation/common/database/error/shared_error_handler.e index 9c26bfd..15c06c1 100644 --- a/persistence/implementation/common/database/shared_error_handler.e +++ b/persistence/implementation/common/database/error/shared_error_handler.e @@ -6,14 +6,26 @@ note class SHARED_ERROR_HANDLER +inherit + + SHARED_LOGGER + feature -- Access database_error_handler: DATABASE_ERROR_HANDLER - -- Error handler + -- Error handler. once create Result.make end +feature -- Status Report + + has_error: BOOLEAN + -- Has error? + do + Result := database_error_handler.has_error + end + feature -- Helper exception_as_error (a_e: like {EXCEPTION_MANAGER}.last_exception) diff --git a/persistence/implementation/mysql/src/cms_storage_mysql.e b/persistence/implementation/mysql/src/cms_storage_mysql.e index 74a72ce..47b74ca 100644 --- a/persistence/implementation/mysql/src/cms_storage_mysql.e +++ b/persistence/implementation/mysql/src/cms_storage_mysql.e @@ -21,6 +21,7 @@ feature {NONE} -- Initialization require is_connected: a_connection.is_connected do + connection := a_connection log.write_information (generator+".make_with_database is database connected? "+ a_connection.is_connected.out ) create node_provider.make (a_connection) create user_provider.make (a_connection) @@ -125,11 +126,14 @@ feature -- Change: user save_user (a_user: CMS_USER) -- Add a new user `a_user'. do + if attached a_user.password as l_password and then attached a_user.email as l_email then + connection.begin_transaction user_provider.new_user (a_user.name, l_password, l_email) + connection.commit else set_last_error ("User or Password not attached", generator + ".save_user") end @@ -282,4 +286,6 @@ feature {NONE} -- Post process user_provider: USER_DATA_PROVIDER -- User Data provider. + connection: DATABASE_CONNECTION + -- Current database connection. end diff --git a/persistence/implementation/mysql/src/database/database_connection_mysql.e b/persistence/implementation/mysql/src/database/database_connection_mysql.e index bc5f890..b9d173f 100644 --- a/persistence/implementation/mysql/src/database/database_connection_mysql.e +++ b/persistence/implementation/mysql/src/database/database_connection_mysql.e @@ -34,17 +34,11 @@ feature -- Initialization if keep_connection then connect end - set_successful else create db_control.make end rescue - create db_control.make - set_last_error_from_exception ("Connection execution") - log.write_critical (generator + ".make_common:" + last_error_message) - if is_connected then - disconnect - end + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) l_retried := True retry end @@ -66,17 +60,11 @@ feature -- Initialization if keep_connection then connect end - set_successful else create db_control.make end rescue - create db_control.make - set_last_error_from_exception ("Connection execution") - log.write_critical (generator + ".make_common:" + last_error_message) - if is_connected then - disconnect - end + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) l_retried := True retry end diff --git a/persistence/implementation/mysql/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e index 3bcfc84..59c9bae 100644 --- a/persistence/implementation/mysql/src/provider/node_data_provider.e +++ b/persistence/implementation/mysql/src/provider/node_data_provider.e @@ -34,7 +34,6 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do - Result := db_handler.successful end feature -- Access @@ -481,13 +480,6 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do - if db_handler.successful then - set_successful - else - if attached db_handler.last_error then - set_last_error_from_handler (db_handler.last_error) - end - end end end diff --git a/persistence/implementation/mysql/src/provider/role_data_provider.e b/persistence/implementation/mysql/src/provider/role_data_provider.e index 61590a0..8e82a64 100644 --- a/persistence/implementation/mysql/src/provider/role_data_provider.e +++ b/persistence/implementation/mysql/src/provider/role_data_provider.e @@ -35,7 +35,7 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do - Result := db_handler.successful +-- Result := db_handler.successful end has_roles: BOOLEAN @@ -210,13 +210,7 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do - if db_handler.successful then - set_successful - else - if attached db_handler.last_error then - set_last_error_from_handler (db_handler.last_error) - end - end + end end diff --git a/persistence/implementation/mysql/src/provider/user_data_provider.e b/persistence/implementation/mysql/src/provider/user_data_provider.e index a1ac47b..0d9b0bf 100644 --- a/persistence/implementation/mysql/src/provider/user_data_provider.e +++ b/persistence/implementation/mysql/src/provider/user_data_provider.e @@ -34,7 +34,6 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do - Result := db_handler.successful end has_user: BOOLEAN @@ -311,13 +310,6 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do - if db_handler.successful then - set_successful - else - if attached db_handler.last_error then - set_last_error_from_handler (db_handler.last_error) - end - end end end diff --git a/persistence/implementation/mysql/tests/util/clean_db.e b/persistence/implementation/mysql/tests/util/clean_db.e index 965465f..dce7176 100644 --- a/persistence/implementation/mysql/tests/util/clean_db.e +++ b/persistence/implementation/mysql/tests/util/clean_db.e @@ -24,6 +24,7 @@ feature do create l_parameters.make (0) + a_connection.begin_transaction -- Clean Profiles db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Sql_delete_user_profiles, l_parameters)) @@ -72,6 +73,7 @@ feature db_handler(a_connection).set_query (create {DATABASE_QUERY}.data_reader (Rest_profiles_autoincrement, l_parameters)) db_handler(a_connection).execute_change + a_connection.commit end From ab76a752d787f1b2f75879251a027a7fbf63f44d Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 13 Oct 2014 18:45:45 -0300 Subject: [PATCH 033/386] Added nested transaction support. Added test cases showing transaction support scenarios. --- .../common/database/database_connection.e | 136 ++++++++++++++ .../tests/transactions/transaction_test_set.e | 168 ++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 persistence/implementation/mysql/tests/transactions/transaction_test_set.e diff --git a/persistence/implementation/common/database/database_connection.e b/persistence/implementation/common/database/database_connection.e index cfb9fd9..a6c2d2c 100644 --- a/persistence/implementation/common/database/database_connection.e +++ b/persistence/implementation/common/database/database_connection.e @@ -131,6 +131,142 @@ feature -- Transactions retry end + +feature -- + + is_connected_to_storage: BOOLEAN + -- Is connected to the database + do + Result := db_control.is_connected + end + +feature -- Transaction Status + + in_transaction_session: BOOLEAN + -- Is session started? + + transaction_session_depth: INTEGER + -- Depth in the transaction session + +feature -- Transaction Operation + + begin_transaction2 + -- Start session + -- if already started, increase the `transaction_session_depth' + require + in_transaction_session implies transaction_session_depth > 0 + not in_transaction_session implies transaction_session_depth = 0 + local + l_session_control: like db_control + l_retried: INTEGER + do + if l_retried = 0 then + if not in_transaction_session then + database_error_handler.reset + + check transaction_session_depth = 0 end + + debug ("database_session") + print ("..Start session%N") + end + connect -- connect the DB + if is_connected_to_storage then + in_transaction_session := True + db_control.begin -- start transaction + else + l_session_control := db_control + if not l_session_control.is_ok then + database_error_handler.add_database_error (l_session_control.error_message_32, l_session_control.error_code) + else + database_error_handler.add_database_error ("Session_not_started_error_message", 0) + end + l_session_control.reset + end + end + transaction_session_depth := transaction_session_depth + 1 + else + if l_retried = 1 then + transaction_session_depth := transaction_session_depth + 1 + if attached (create {EXCEPTION_MANAGER}).last_exception as e then + if attached {ASSERTION_VIOLATION} e then + --| Ignore for now with MYSQL ... + else + exception_as_error (e) + end + end + + in_transaction_session := False + db_control.reset + else + in_transaction_session := False + end + end + ensure + transaction_session_depth = (old transaction_session_depth) + 1 + rescue + l_retried := l_retried + 1 + retry + end + + commit2 + -- End session + local + l_retried: BOOLEAN + do + if not l_retried then + transaction_session_depth := transaction_session_depth - 1 + if transaction_session_depth = 0 then + debug ("database_session") + print ("..End session%N") + end + if is_connected_to_storage then + if not database_error_handler.has_error then + db_control.commit -- Commit transaction + else + db_control.rollback -- Rollback transaction + end + end + in_transaction_session := False + end + else + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + in_transaction_session := False + transaction_session_depth := transaction_session_depth - 1 + db_control.reset + end + rescue + l_retried := True + retry + end + + + + rollback2 + -- End session + local + l_retried: BOOLEAN + do + if not l_retried then + transaction_session_depth := transaction_session_depth - 1 + if transaction_session_depth = 0 then + debug ("database_session") + print ("..End session%N") + end + if is_connected_to_storage then + db_control.rollback -- Rollback transaction + end + in_transaction_session := False + end + else + exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) + in_transaction_session := False + transaction_session_depth := transaction_session_depth - 1 + db_control.reset + end + rescue + l_retried := True + retry + end feature -- Change Element not_keep_connection diff --git a/persistence/implementation/mysql/tests/transactions/transaction_test_set.e b/persistence/implementation/mysql/tests/transactions/transaction_test_set.e new file mode 100644 index 0000000..3199f71 --- /dev/null +++ b/persistence/implementation/mysql/tests/transactions/transaction_test_set.e @@ -0,0 +1,168 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + TRANSACTION_TEST_SET + +inherit + + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + end + + on_clean + -- + do + end + +feature -- Test routines + + test_user_rollback + note + testing: "execution/isolated" + do + connection.begin_transaction + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + connection.rollback + assert ("Not has user:", not user_provider.has_user) + end + + test_user_node_rollback + note + testing: "execution/isolated" + do + connection.begin_transaction + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + node_provider.add_author (1, 1) + assert ("Has one node:", node_provider.count = 1) + connection.rollback + assert ("Not has user:", not user_provider.has_user) + assert ("Not has nodes:", node_provider.count = 0) + end + + + + test_user_node_nested_transaction_with_rollback_not_supported + note + testing: "execution/isolated" + do + connection.begin_transaction2 + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + assert ("Has one node:", node_provider.count = 1) + + connection.begin_transaction2 + user_provider.new_user ("test1", "test1","test1@admin.com") + assert ("Has user: test1", attached user_provider.user_by_name ("test1")) + connection.rollback2 + + connection.commit2 + assert ("Has user test:", attached user_provider.user_by_name ("test")) + assert ("Has nodes:", node_provider.count = 1) + assert ("Not has user: test1", user_provider.user_by_name ("test1") = Void) + end + + + test_user_node_nested_transaction_with_commit + note + testing: "execution/isolated" + do + connection.begin_transaction2 + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + assert ("Has one node:", node_provider.count = 1) + + connection.begin_transaction2 + user_provider.new_user ("test1", "test1","test1@admin.com") + assert ("Has user: test1", attached user_provider.user_by_name ("test1")) + connection.commit2 + + connection.commit2 + assert ("Has user test:", attached user_provider.user_by_name ("test")) + assert ("Has user test1:", attached user_provider.user_by_name ("test1")) + assert ("Has nodes:", node_provider.count = 1) + end + + + test_user_node_nested_transaction_with_rollback + note + testing: "execution/isolated" + do + connection.begin_transaction2 + user_provider.new_user ("test", "test","test@admin.com") + assert ("Has user:", user_provider.has_user) + node_provider.new_node (default_node) + assert ("Has one node:", node_provider.count = 1) + + connection.begin_transaction2 + user_provider.new_user ("test1", "test1","test1@admin.com") + assert ("Has user: test1", attached user_provider.user_by_name ("test1")) + connection.commit2 + + connection.rollback2 + assert ("Not Has user test:", user_provider.user_by_name ("test") = void) + assert ("Not Has user test1:", user_provider.user_by_name ("test1") = void) + assert ("Has 0 nodes:", node_provider.count = 0) + end + + + + + +feature {NONE} -- Implementation + + node_provider: NODE_DATA_PROVIDER + -- node provider. + once + create Result.make (connection) + end + + user_provider: USER_DATA_PROVIDER + -- user provider. + once + create Result.make (connection) + end + + +feature {NONE} -- Implementation Fixture Factories + + default_node: CMS_NODE + do + Result := custom_node ("Default content", "default summary", "Default") + end + + custom_node (a_content, a_summary, a_title: READABLE_STRING_32): CMS_NODE + do + create Result.make (a_content, a_summary, a_title) + end +end + + From 3c73202a0de7037152f5bae7d8fe6051a99480b5 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 15 Oct 2014 19:41:47 -0300 Subject: [PATCH 034/386] Initial Import Model Library --- cms/cms-safe.ecf | 2 + model/model-safe.ecf | 19 +++ model/src/cms_content_type.e | 10 ++ model/src/cms_link.e | 71 ++++++++++++ model/src/cms_link_composite.e | 27 +++++ model/src/cms_local_link.e | 109 ++++++++++++++++++ model/src/cms_menu.e | 72 ++++++++++++ .../common/model => model/src}/cms_node.e | 0 .../common/model => model/src}/cms_user.e | 0 .../model => model/src}/cms_user_profile.e | 0 .../model => model/src}/cms_user_role.e | 0 .../database/error/shared_error_handler.e | 2 +- .../mysql/persistence_mysql-safe.ecf | 1 + 13 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 model/model-safe.ecf create mode 100644 model/src/cms_content_type.e create mode 100644 model/src/cms_link.e create mode 100644 model/src/cms_link_composite.e create mode 100644 model/src/cms_local_link.e create mode 100644 model/src/cms_menu.e rename {persistence/implementation/common/model => model/src}/cms_node.e (100%) rename {persistence/implementation/common/model => model/src}/cms_user.e (100%) rename {persistence/implementation/common/model => model/src}/cms_user_profile.e (100%) rename {persistence/implementation/common/model => model/src}/cms_user_role.e (100%) diff --git a/cms/cms-safe.ecf b/cms/cms-safe.ecf index 5da317c..3a5945e 100644 --- a/cms/cms-safe.ecf +++ b/cms/cms-safe.ecf @@ -10,6 +10,8 @@ + + diff --git a/model/model-safe.ecf b/model/model-safe.ecf new file mode 100644 index 0000000..cc50b08 --- /dev/null +++ b/model/model-safe.ecf @@ -0,0 +1,19 @@ + + + + + + + + + + + /EIFGENs$ + /.svn$ + /CVS$ + + + + diff --git a/model/src/cms_content_type.e b/model/src/cms_content_type.e new file mode 100644 index 0000000..fc6cec2 --- /dev/null +++ b/model/src/cms_content_type.e @@ -0,0 +1,10 @@ +note + description: "Summary description for {CMS_CONTENT_TYPE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_CONTENT_TYPE + +end diff --git a/model/src/cms_link.e b/model/src/cms_link.e new file mode 100644 index 0000000..7606366 --- /dev/null +++ b/model/src/cms_link.e @@ -0,0 +1,71 @@ +note + description: "Summary description for {CMS_MENU}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_LINK + +inherit + REFACTORING_HELPER + + DEBUG_OUTPUT + + ITERABLE [CMS_LINK] + +feature -- Access + + title: READABLE_STRING_32 + -- link's title. + + location: READABLE_STRING_8 + -- link's location. + +feature -- status report + + is_active: BOOLEAN + deferred + end + + is_expanded: BOOLEAN + deferred + end + + is_expandable: BOOLEAN + deferred + end + + has_children: BOOLEAN + deferred + end + +feature -- Query + + parent: detachable CMS_LINK + + children: detachable LIST [CMS_LINK] + deferred + end + +feature -- Access + + new_cursor: ITERATION_CURSOR [CMS_LINK] + -- Fresh cursor associated with current structure + do + if attached children as lst then + Result := lst.new_cursor + else + Result := (create {ARRAYED_LIST [CMS_LINK]}.make (0)).new_cursor + end + end + +feature -- Status report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + Result := title.as_string_8 + " -> " + location + end + +end diff --git a/model/src/cms_link_composite.e b/model/src/cms_link_composite.e new file mode 100644 index 0000000..3a9c7da --- /dev/null +++ b/model/src/cms_link_composite.e @@ -0,0 +1,27 @@ +note + description: "Summary description for {CMS_LINK_COMPOSITE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_LINK_COMPOSITE + +inherit + ITERABLE [CMS_LINK] + +feature -- Access + + items: detachable LIST [CMS_LINK] + deferred + end + + extend (lnk: CMS_LINK) + deferred + end + + remove (lnk: CMS_LINK) + deferred + end + +end diff --git a/model/src/cms_local_link.e b/model/src/cms_local_link.e new file mode 100644 index 0000000..9633fc0 --- /dev/null +++ b/model/src/cms_local_link.e @@ -0,0 +1,109 @@ +note + description: "Summary description for {CMS_LOCAL_MENU}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_LOCAL_LINK + +inherit + CMS_LINK + + CMS_LINK_COMPOSITE + rename + items as children, + extend as add_link, + remove as remove_link + end + +create + make + +feature {NONE} -- Initialization + + make (a_title: detachable like title; a_location: like location) + do + if a_title /= Void then + title := a_title + else + title := a_location + end + location := a_location + end + +feature -- Status report + + is_active: BOOLEAN + + is_expanded: BOOLEAN + do + Result := is_expandable and then internal_is_expanded + end + + is_expandable: BOOLEAN + do + Result := internal_is_expandable or internal_is_expanded or has_children + end + + has_children: BOOLEAN + do + Result := attached children as l_children and then not l_children.is_empty + end + + permission_arguments: detachable ITERABLE [READABLE_STRING_8] + + children: detachable LIST [CMS_LINK] + + internal_is_expandable: BOOLEAN + + internal_is_expanded: BOOLEAN + +feature -- Element change + + add_link (lnk: CMS_LINK) + local + lst: like children + do + lst := children + if lst = Void then + create {ARRAYED_LIST [CMS_LINK]} lst.make (1) + children := lst + end + lst.force (lnk) + end + + remove_link (lnk: CMS_LINK) + local + lst: like children + do + lst := children + if lst /= Void then + lst.prune_all (lnk) + if lst.is_empty then + children := Void + end + end + end + + set_children (lst: like children) + do + children := lst + end + + set_expanded (b: like is_expanded) + do + internal_is_expanded := b + end + + set_expandable (b: like is_expandable) + do + internal_is_expandable := b + end + + + set_permission_arguments (args: like permission_arguments) + do + permission_arguments := args + end + +end diff --git a/model/src/cms_menu.e b/model/src/cms_menu.e new file mode 100644 index 0000000..6cb332f --- /dev/null +++ b/model/src/cms_menu.e @@ -0,0 +1,72 @@ +note + description: "Summary description for {CMS_MENU}." + date: "$Date$" + revision: "$Revision$" + +class + CMS_MENU + +inherit + CMS_LINK_COMPOSITE + +create + make, + make_with_title + +feature {NONE} -- Initialization + + make (a_name: like name; n: INTEGER) + do + name := a_name + create items.make (n) + end + + make_with_title (a_name: like name; a_title: READABLE_STRING_32; n: INTEGER) + do + make (a_name, n) + set_title (a_title) + end + +feature -- Access + + name: READABLE_STRING_8 + + title: detachable READABLE_STRING_32 + + items: ARRAYED_LIST [CMS_LINK] + + extend (lnk: CMS_LINK) + do + items.extend (lnk) + end + + remove (lnk: CMS_LINK) + do + items.prune_all (lnk) + end + +feature -- status report + + is_empty: BOOLEAN + do + Result := items.is_empty + end + +feature -- Element change + + set_title (t: like title) + do + title := t + end + +feature -- Access + + new_cursor: ITERATION_CURSOR [CMS_LINK] + -- Fresh cursor associated with current structure + do + Result := items.new_cursor + end + +invariant + +end diff --git a/persistence/implementation/common/model/cms_node.e b/model/src/cms_node.e similarity index 100% rename from persistence/implementation/common/model/cms_node.e rename to model/src/cms_node.e diff --git a/persistence/implementation/common/model/cms_user.e b/model/src/cms_user.e similarity index 100% rename from persistence/implementation/common/model/cms_user.e rename to model/src/cms_user.e diff --git a/persistence/implementation/common/model/cms_user_profile.e b/model/src/cms_user_profile.e similarity index 100% rename from persistence/implementation/common/model/cms_user_profile.e rename to model/src/cms_user_profile.e diff --git a/persistence/implementation/common/model/cms_user_role.e b/model/src/cms_user_role.e similarity index 100% rename from persistence/implementation/common/model/cms_user_role.e rename to model/src/cms_user_role.e diff --git a/persistence/implementation/common/database/error/shared_error_handler.e b/persistence/implementation/common/database/error/shared_error_handler.e index 15c06c1..f5a80ca 100644 --- a/persistence/implementation/common/database/error/shared_error_handler.e +++ b/persistence/implementation/common/database/error/shared_error_handler.e @@ -31,7 +31,7 @@ feature -- Helper exception_as_error (a_e: like {EXCEPTION_MANAGER}.last_exception) -- Record exception as an error. do - if attached a_e as l_e and then attached l_e.exception_trace as l_trace then + if attached a_e as l_e and then attached l_e.trace as l_trace then database_error_handler.add_error_details (l_e.code, once "Exception", l_trace.as_string_32) end end diff --git a/persistence/implementation/mysql/persistence_mysql-safe.ecf b/persistence/implementation/mysql/persistence_mysql-safe.ecf index 6811629..9d99a01 100644 --- a/persistence/implementation/mysql/persistence_mysql-safe.ecf +++ b/persistence/implementation/mysql/persistence_mysql-safe.ecf @@ -13,6 +13,7 @@ + From 5cb26f0b24fdee9ff5d38b4fbe55632fce6d226a Mon Sep 17 00:00:00 2001 From: jvelilla Date: Fri, 17 Oct 2014 08:53:02 -0300 Subject: [PATCH 035/386] Updated Peristance layer to use the new error_hanler based on EWF error library. Remove once use from DATABASE_ERROR_HANDLER. Initial support transaction handling. --- .../implementation/common/cms_storage_null.e | 14 + .../common/database/database_connection.e | 162 ++------ .../database/database_connection_null.e | 1 + .../common/database/database_handler.e | 29 +- .../common/database/database_handler_impl.e | 7 +- .../common/database/database_query.e | 4 +- .../database/database_storage_manager.e | 371 ------------------ .../{ => error}/database_no_change_error.e | 0 .../database/error/shared_error_handler.e | 39 -- .../mysql/src/cms_storage_mysql.e | 54 +-- .../src/database/database_connection_mysql.e | 9 +- .../mysql/src/provider/node_data_provider.e | 38 +- .../mysql/src/provider/user_data_provider.e | 26 +- .../tests/handler/database_handler_test.e | 81 ++++ .../mysql/tests/nodes/node_test_set.e | 14 +- .../implementation/mysql/tests/tests.ecf | 1 + .../tests/transactions/transaction_test_set.e | 71 ---- persistence/interface/cms_storage.e | 7 +- 18 files changed, 254 insertions(+), 674 deletions(-) delete mode 100644 persistence/implementation/common/database/database_storage_manager.e rename persistence/implementation/common/database/{ => error}/database_no_change_error.e (100%) delete mode 100644 persistence/implementation/common/database/error/shared_error_handler.e create mode 100644 persistence/implementation/mysql/tests/handler/database_handler_test.e diff --git a/persistence/implementation/common/cms_storage_null.e b/persistence/implementation/common/cms_storage_null.e index c82e9b6..89eb3df 100644 --- a/persistence/implementation/common/cms_storage_null.e +++ b/persistence/implementation/common/cms_storage_null.e @@ -9,8 +9,22 @@ class inherit CMS_STORAGE + redefine + default_create + select + default_create + end REFACTORING_HELPER + rename + default_create as default_create_rh + end +feature -- Initialization + + default_create + do + create error_handler.make + end feature -- Access: user diff --git a/persistence/implementation/common/database/database_connection.e b/persistence/implementation/common/database/database_connection.e index a6c2d2c..5f0e9f7 100644 --- a/persistence/implementation/common/database/database_connection.e +++ b/persistence/implementation/common/database/database_connection.e @@ -10,8 +10,6 @@ inherit DATABASE_CONFIG - SHARED_ERROR_HANDLER - feature {NONE} -- Initialization make_common @@ -131,142 +129,6 @@ feature -- Transactions retry end - -feature -- - - is_connected_to_storage: BOOLEAN - -- Is connected to the database - do - Result := db_control.is_connected - end - -feature -- Transaction Status - - in_transaction_session: BOOLEAN - -- Is session started? - - transaction_session_depth: INTEGER - -- Depth in the transaction session - -feature -- Transaction Operation - - begin_transaction2 - -- Start session - -- if already started, increase the `transaction_session_depth' - require - in_transaction_session implies transaction_session_depth > 0 - not in_transaction_session implies transaction_session_depth = 0 - local - l_session_control: like db_control - l_retried: INTEGER - do - if l_retried = 0 then - if not in_transaction_session then - database_error_handler.reset - - check transaction_session_depth = 0 end - - debug ("database_session") - print ("..Start session%N") - end - connect -- connect the DB - if is_connected_to_storage then - in_transaction_session := True - db_control.begin -- start transaction - else - l_session_control := db_control - if not l_session_control.is_ok then - database_error_handler.add_database_error (l_session_control.error_message_32, l_session_control.error_code) - else - database_error_handler.add_database_error ("Session_not_started_error_message", 0) - end - l_session_control.reset - end - end - transaction_session_depth := transaction_session_depth + 1 - else - if l_retried = 1 then - transaction_session_depth := transaction_session_depth + 1 - if attached (create {EXCEPTION_MANAGER}).last_exception as e then - if attached {ASSERTION_VIOLATION} e then - --| Ignore for now with MYSQL ... - else - exception_as_error (e) - end - end - - in_transaction_session := False - db_control.reset - else - in_transaction_session := False - end - end - ensure - transaction_session_depth = (old transaction_session_depth) + 1 - rescue - l_retried := l_retried + 1 - retry - end - - commit2 - -- End session - local - l_retried: BOOLEAN - do - if not l_retried then - transaction_session_depth := transaction_session_depth - 1 - if transaction_session_depth = 0 then - debug ("database_session") - print ("..End session%N") - end - if is_connected_to_storage then - if not database_error_handler.has_error then - db_control.commit -- Commit transaction - else - db_control.rollback -- Rollback transaction - end - end - in_transaction_session := False - end - else - exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) - in_transaction_session := False - transaction_session_depth := transaction_session_depth - 1 - db_control.reset - end - rescue - l_retried := True - retry - end - - - - rollback2 - -- End session - local - l_retried: BOOLEAN - do - if not l_retried then - transaction_session_depth := transaction_session_depth - 1 - if transaction_session_depth = 0 then - debug ("database_session") - print ("..End session%N") - end - if is_connected_to_storage then - db_control.rollback -- Rollback transaction - end - in_transaction_session := False - end - else - exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) - in_transaction_session := False - transaction_session_depth := transaction_session_depth - 1 - db_control.reset - end - rescue - l_retried := True - retry - end feature -- Change Element not_keep_connection @@ -302,4 +164,28 @@ feature -- Conection Result := db_control.is_connected end +feature -- Error Handling + + database_error_handler: DATABASE_ERROR_HANDLER + -- Error handler. + +feature -- Status Report + + has_error: BOOLEAN + -- Has error? + do + Result := database_error_handler.has_error + end + +feature -- Helper + + exception_as_error (a_e: like {EXCEPTION_MANAGER}.last_exception) + -- Record exception as an error. + do + if attached a_e as l_e and then attached l_e.trace as l_trace then + database_error_handler.add_error_details (l_e.code, once "Exception", l_trace.as_string_32) + end + end + + end diff --git a/persistence/implementation/common/database/database_connection_null.e b/persistence/implementation/common/database/database_connection_null.e index d9257b3..ec54d13 100644 --- a/persistence/implementation/common/database/database_connection_null.e +++ b/persistence/implementation/common/database/database_connection_null.e @@ -22,6 +22,7 @@ feature -- Initialization make_common -- Create a database handler for ODBC with common settings. do + create database_error_handler.make create db_application.login (username, password) db_application.set_hostname (hostname) db_application.set_data_source (database_name) diff --git a/persistence/implementation/common/database/database_handler.e b/persistence/implementation/common/database/database_handler.e index 875b7a7..3517ff3 100644 --- a/persistence/implementation/common/database/database_handler.e +++ b/persistence/implementation/common/database/database_handler.e @@ -8,7 +8,7 @@ deferred class inherit - SHARED_ERROR_HANDLER + SHARED_LOGGER feature -- Access @@ -178,6 +178,7 @@ feature -- Error handling if attached db_change as l_change and then not l_change.is_ok then database_error_handler.add_database_error (l_change.error_message_32, l_change.error_code) log.write_error (generator + ".check_database_change_error: " + l_change.error_message_32) + l_change.reset end end @@ -187,10 +188,34 @@ feature -- Error handling if attached db_selection as l_selection and then not l_selection.is_ok then database_error_handler.add_database_error (l_selection.error_message_32, l_selection.error_code) log.write_error (generator + ".check_database_selection_error: " + l_selection.error_message_32) + l_selection.reset end end -feature {NODE_DATA_PROVIDER}-- Implementation +feature -- Error Handling + + database_error_handler: DATABASE_ERROR_HANDLER + -- Error handler. + +feature -- Status Report + + has_error: BOOLEAN + -- Has error? + do + Result := database_error_handler.has_error + end + +feature -- Helper + + exception_as_error (a_e: like {EXCEPTION_MANAGER}.last_exception) + -- Record exception as an error. + do + if attached a_e as l_e and then attached l_e.trace as l_trace then + database_error_handler.add_error_details (l_e.code, once "Exception", l_trace.as_string_32) + end + end + +feature -- Connection Handling connect -- Connect to the database. diff --git a/persistence/implementation/common/database/database_handler_impl.e b/persistence/implementation/common/database/database_handler_impl.e index 55bde06..1de28a1 100644 --- a/persistence/implementation/common/database/database_handler_impl.e +++ b/persistence/implementation/common/database/database_handler_impl.e @@ -21,9 +21,11 @@ feature {NONE} -- Initialization do connection := a_connection create last_query.make_now + create database_error_handler.make ensure connection_not_void: connection /= Void last_query_not_void: last_query /= Void + database_error_handler_set: attached database_error_handler end feature -- Functionality @@ -35,6 +37,7 @@ feature -- Functionality l_retried: BOOLEAN do if not l_retried then + database_error_handler.reset if attached store as l_store then create l_db_selection.make db_selection := l_db_selection @@ -59,7 +62,7 @@ feature -- Functionality l_retried : BOOLEAN do if not l_retried then - + database_error_handler.reset if attached store as l_store then create l_db_change.make db_change := l_db_change @@ -86,6 +89,7 @@ feature -- SQL Queries l_retried: BOOLEAN do if not l_retried then + database_error_handler.reset if attached query as l_query then create l_db_selection.make db_selection := l_db_selection @@ -110,6 +114,7 @@ feature -- SQL Queries l_retried : BOOLEAN do if not l_retried then + database_error_handler.reset if attached query as l_query then create l_db_change.make db_change := l_db_change diff --git a/persistence/implementation/common/database/database_query.e b/persistence/implementation/common/database/database_query.e index 4acec0c..25088b3 100644 --- a/persistence/implementation/common/database/database_query.e +++ b/persistence/implementation/common/database/database_query.e @@ -8,10 +8,10 @@ class inherit - SHARED_ERROR_HANDLER - REFACTORING_HELPER + SHARED_LOGGER + create data_reader diff --git a/persistence/implementation/common/database/database_storage_manager.e b/persistence/implementation/common/database/database_storage_manager.e deleted file mode 100644 index e59ef5a..0000000 --- a/persistence/implementation/common/database/database_storage_manager.e +++ /dev/null @@ -1,371 +0,0 @@ -note - description: "[ - Manager to initialize data api for database access, - create database connection and so on - ]" - date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $" - revision: "$Revision: 195 $" - -class - DATABASE_STORAGE_MANAGER - -inherit - GLOBAL_SETTINGS - - REFACTORING_HELPER - - SHARED_ERROR_HANDLER - -create - make - -feature -- Initialization - - make (a_data_app: like data_app; a_database_name: like database_name; a_name: like name; a_password: like password; - a_host_name: like host_name; a_role_id: like role_id; a_role_password: like role_password - a_data_source: like data_source; a_group: like group) - -- Initialize with login info. - -- - -- `a_database_name' is used for MySQL - -- `a_name', the login user name - -- `a_password', the login user password, unencrypted. - local - l_storage: STRING_8 - do - create l_storage.make (64) - storage_url := l_storage - l_storage.append (a_data_app.db_spec.database_handle_name.as_lower) - l_storage.append ("://") - - data_app := a_data_app - database_name := a_database_name - name := a_name - password := a_password - host_name := a_host_name - role_id := a_role_id - role_password := a_role_password - data_source := a_data_source - group := a_group - - set_use_extended_types (True) - set_map_zero_null_value (False) - - l_storage.append (a_name.as_string_8) - l_storage.append (":********@") - if a_host_name /= Void then - a_data_app.set_hostname (a_host_name) - l_storage.append (a_host_name) - end - - if a_database_name /= Void then - a_data_app.set_application (a_database_name.as_string_8) - l_storage.append ("/" + a_database_name.as_string_8) - end - - if a_data_source /= Void then - a_data_app.set_data_source (a_data_source) - end - if a_role_id /= Void and then a_role_password /= Void then - a_data_app.set_role (a_role_id, a_role_password) - end - if a_group /= Void then - a_data_app.set_group (a_group) - end - - a_data_app.login (a_name.as_string_8, a_password.as_string_8) - a_data_app.set_base - - create session_control.make - - end - - report_database_schema_incompatibility (a_output: BOOLEAN) - -- Report the application code is not compatible with database schema version - -- if `a_output' is True, write error in io.error as well - require --- incompatible_database_schema_version: not is_database_schema_version_compatible - local - db_v: READABLE_STRING_8 - do --- if attached database_schema_version as v then --- db_v := v.version --- else --- db_v := "?.?.?.?" --- end --- database_error_handler.add_error_details (0, "MISC Error", "Schema version incompatible (application=" --- + database_storage_version.version + " database=" + db_v + ")." --- ) - if a_output then - io.error.put_string (database_error_handler.as_string_representation) - io.error.put_new_line - end - end - -feature -- System Update - - update_system - do --- if is_database_schema_version_compatible then --- misc_manager.initialize_reference_types --- history_manager.initialize_data --- user_role_permission_manager.initialize_built_in_user_role_permission --- user_role_permission_manager.initialize_extra_user_role_permission --- task_manager.initialize_data --- else --- -- If schema incompatible, report it and exit --- report_database_schema_incompatibility (True) --- (create {EXCEPTIONS}).die (-1) --- end - --- [2012-Mars-21] Idea about update system implementation --- if update_version < 01.00.0012 then --- if update_version < 01.00.0005 then --- update_version_01_00_0005 --- end --- update_version_01_00_0012 --- end - end - - reset_storage_manager - do --- initialize_managers (Current) - end - -feature -- Storage - - storage_url: READABLE_STRING_8 - -- Associated storage URL - - storage_connection_kept_alive: BOOLEAN - -- Keep storage connection alive? - -- i.e: never disconnect between 2 transactions. - - keep_storage_connection_alive (b: BOOLEAN) - do - storage_connection_kept_alive := b - end - - connect_storage - -- Connect the database - do - if not session_control.is_connected then - session_control.connect - end - end - - disconnect_from_storage - -- Disconnect from the storage - require - is_connected_to_storage: is_connected_to_storage - do - if not storage_connection_kept_alive then - session_control.disconnect - end - end - - force_disconnect_from_storage - -- Force disconnection from the storage - -- i.e ignore any `storage_connection_kept_alive' - do - if session_control.is_connected then - session_control.disconnect - end - end - - is_connected_to_storage: BOOLEAN - -- Is connected to the database - do - Result := session_control.is_connected - end - -feature -- Transaction Status - - in_transaction_session: BOOLEAN - -- Is session started? - - transaction_session_depth: INTEGER - -- Depth in the transaction session - -feature -- Transaction Operation - - start_transaction_session - -- Start session - -- if already started, increase the `transaction_session_depth' - require - in_transaction_session implies transaction_session_depth > 0 - not in_transaction_session implies transaction_session_depth = 0 - local - l_session_control: like session_control - l_retried: INTEGER - do - if l_retried = 0 then - if not in_transaction_session then - database_error_handler.reset - - check transaction_session_depth = 0 end - - debug ("database_session") - print ("..Start session%N") - end - connect_storage -- connect the DB - if is_connected_to_storage then - in_transaction_session := True - session_control.begin -- start transaction - else - l_session_control := session_control - if not l_session_control.is_ok then - database_error_handler.add_database_error (l_session_control.error_message_32, l_session_control.error_code) - else - database_error_handler.add_database_error (Session_not_started_error_message, 0) - end - l_session_control.reset - end - end - transaction_session_depth := transaction_session_depth + 1 - else - if l_retried = 1 then - transaction_session_depth := transaction_session_depth + 1 - if attached (create {EXCEPTION_MANAGER}).last_exception as e then - if attached {ASSERTION_VIOLATION} e then - --| Ignore for now with MYSQL ... - else - exception_as_error (e) - end - end - - in_transaction_session := False - session_control.reset - else - in_transaction_session := False - end - end - ensure - transaction_session_depth = (old transaction_session_depth) + 1 - rescue - l_retried := l_retried + 1 - retry - end - - end_transaction_session - -- End session - local - l_retried: BOOLEAN - do - if not l_retried then - transaction_session_depth := transaction_session_depth - 1 - if transaction_session_depth = 0 then - debug ("database_session") - print ("..End session%N") - end - if is_connected_to_storage then - if not database_error_handler.has_error then - session_control.commit -- Commit transaction - else - session_control.rollback -- Rollback transaction - end - disconnect_from_storage - end - in_transaction_session := False - end - else - exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) - in_transaction_session := False - transaction_session_depth := transaction_session_depth - 1 - session_control.reset - end - rescue - l_retried := True - retry - end - - execute_query (a_query: STRING_32) - -- Execute `a_q' - require - is_session_started: in_transaction_session - local - rescued: BOOLEAN - l_change: like new_database_change - do - if not rescued then - session_control.reset - l_change := new_database_change - l_change.set_query (a_query) - l_change.execute_query - else - database_error_handler.add_error_details (0, "Unexpected Error", "Unexpected Error when executing query") - end - rescue - rescued := True - retry - end - -feature -- Element Change - - set_last_inserted_id_function (a_f: like last_inserted_id_function) - -- Set `last_inserted_id_function' with `a_f' - do - last_inserted_id_function := a_f - ensure - last_inserted_id_function_set: last_inserted_id_function = a_f - end - -feature -- Access - - database_name: detachable READABLE_STRING_GENERAL - -- Database to access - - name: READABLE_STRING_GENERAL - -- Login user name - - password: READABLE_STRING_8 - -- Password - - host_name: detachable READABLE_STRING_8 - -- Host name, and port if needed - - role_id: detachable READABLE_STRING_8 - -- Role id - - role_password: detachable READABLE_STRING_8 - -- Role password - - data_source: detachable READABLE_STRING_8 - -- Data source - - group: detachable READABLE_STRING_8 - -- Group - - data_app: DATABASE_APPL [DATABASE] - -- Database application - - last_inserted_id_function: detachable FUNCTION [ANY, TUPLE, NATURAL_64] - -- Function to get last inserted id. - -feature -- Factory - - new_database_change: DB_CHANGE - -- Database change - do - create Result.make - end - - new_database_selection: DB_SELECTION - -- Database selection - do - create Result.make - end - - new_procedure (a_name: like {DB_PROC}.name): DB_PROC - -- Database procedure - do - create Result.make (a_name) - end - -feature {NONE} -- Implementation - - session_not_started_error_message: STRING_32 = "Session could not be started for unknown reason" - - session_control: DB_CONTROL - -- Session - -end diff --git a/persistence/implementation/common/database/database_no_change_error.e b/persistence/implementation/common/database/error/database_no_change_error.e similarity index 100% rename from persistence/implementation/common/database/database_no_change_error.e rename to persistence/implementation/common/database/error/database_no_change_error.e diff --git a/persistence/implementation/common/database/error/shared_error_handler.e b/persistence/implementation/common/database/error/shared_error_handler.e deleted file mode 100644 index f5a80ca..0000000 --- a/persistence/implementation/common/database/error/shared_error_handler.e +++ /dev/null @@ -1,39 +0,0 @@ -note - description: "Shared error handler for database" - date: "$Date: 2013-08-08 16:39:49 -0300 (ju. 08 de ago. de 2013) $" - revision: "$Revision: 195 $" - -class - SHARED_ERROR_HANDLER - -inherit - - SHARED_LOGGER - -feature -- Access - - database_error_handler: DATABASE_ERROR_HANDLER - -- Error handler. - once - create Result.make - end - -feature -- Status Report - - has_error: BOOLEAN - -- Has error? - do - Result := database_error_handler.has_error - end - -feature -- Helper - - exception_as_error (a_e: like {EXCEPTION_MANAGER}.last_exception) - -- Record exception as an error. - do - if attached a_e as l_e and then attached l_e.trace as l_trace then - database_error_handler.add_error_details (l_e.code, once "Exception", l_trace.as_string_32) - end - end - -end diff --git a/persistence/implementation/mysql/src/cms_storage_mysql.e b/persistence/implementation/mysql/src/cms_storage_mysql.e index 47b74ca..62422aa 100644 --- a/persistence/implementation/mysql/src/cms_storage_mysql.e +++ b/persistence/implementation/mysql/src/cms_storage_mysql.e @@ -25,8 +25,7 @@ feature {NONE} -- Initialization log.write_information (generator+".make_with_database is database connected? "+ a_connection.is_connected.out ) create node_provider.make (a_connection) create user_provider.make (a_connection) - post_node_provider_execution - post_user_provider_execution + create error_handler.make end @@ -36,7 +35,6 @@ feature -- Access: user -- Has any user? do Result := user_provider.has_user - post_user_provider_execution end all_users: LIST [CMS_USER] @@ -48,19 +46,19 @@ feature -- Access: user user_by_id (a_id: like {CMS_USER}.id): detachable CMS_USER do Result := user_provider.user (a_id) - post_user_provider_execution + end user_by_name (a_name: like {CMS_USER}.name): detachable CMS_USER do Result := user_provider.user_by_name (a_name) - post_user_provider_execution + end user_by_email (a_email: like {CMS_USER}.email): detachable CMS_USER do Result := user_provider.user_by_email (a_email) - post_user_provider_execution + end is_valid_credential (l_auth_login, l_auth_password: READABLE_STRING_32): BOOLEAN @@ -82,7 +80,7 @@ feature -- Access: user log.write_information (generator + ".login_valid User:" + l_auth_login + "does not exist" ) end end - post_user_provider_execution + end feature -- User Nodes @@ -135,7 +133,6 @@ feature -- Change: user user_provider.new_user (a_user.name, l_password, l_email) connection.commit else - set_last_error ("User or Password not attached", generator + ".save_user") end end @@ -148,7 +145,7 @@ feature -- Access: node across node_provider.nodes as c loop Result.force (c.item) end - post_node_provider_execution + end recent_nodes (a_lower: INTEGER; a_count: INTEGER): LIST [CMS_NODE] @@ -158,21 +155,21 @@ feature -- Access: node across node_provider.recent_nodes (a_lower,a_count) as c loop Result.force (c.item) end - post_node_provider_execution + end node (a_id: INTEGER_64): detachable CMS_NODE -- do Result := node_provider.node (a_id) - post_node_provider_execution + end node_author (a_id: like {CMS_NODE}.id): detachable CMS_USER -- do Result := node_provider.node_author (a_id) - post_node_provider_execution + end node_collaborators (a_id: like {CMS_NODE}.id): LIST [CMS_USER] @@ -188,21 +185,21 @@ feature -- Node -- do node_provider.new_node (a_node) - post_node_provider_execution + end delete_node (a_id: INTEGER_64) do node_provider.delete_from_user_nodes(a_id) node_provider.delete_node (a_id) - post_node_provider_execution + end update_node (a_id: like {CMS_USER}.id; a_node: CMS_NODE) -- do node_provider.update_node (a_id, a_node) - post_node_provider_execution + end update_node_title (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_title: READABLE_STRING_32) @@ -210,7 +207,7 @@ feature -- Node do node_provider.update_node_title (a_node_id, a_title) internal_node_update (a_id, a_node_id) - post_node_provider_execution + end update_node_summary (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_summary: READABLE_STRING_32) @@ -218,7 +215,7 @@ feature -- Node do node_provider.update_node_summary (a_node_id, a_summary) internal_node_update (a_id, a_node_id) - post_node_provider_execution + end update_node_content (a_id: like {CMS_USER}.id; a_node_id: like {CMS_NODE}.id; a_content: READABLE_STRING_32) @@ -226,7 +223,7 @@ feature -- Node do node_provider.update_node_content (a_node_id, a_content) internal_node_update (a_id, a_node_id) - post_node_provider_execution + end feature {NONE} -- NODE Implemenation @@ -258,27 +255,6 @@ feature -- User feature {NONE} -- Post process - post_node_provider_execution - do - if node_provider.successful then - set_successful - else - if attached node_provider.last_error then - set_last_error_from_handler (node_provider.last_error) - end - end - end - - post_user_provider_execution - do - if user_provider.successful then - set_successful - else - if attached user_provider.last_error then - set_last_error_from_handler (user_provider.last_error) - end - end - end node_provider: NODE_DATA_PROVIDER -- Node Data provider. diff --git a/persistence/implementation/mysql/src/database/database_connection_mysql.e b/persistence/implementation/mysql/src/database/database_connection_mysql.e index b9d173f..05776f6 100644 --- a/persistence/implementation/mysql/src/database/database_connection_mysql.e +++ b/persistence/implementation/mysql/src/database/database_connection_mysql.e @@ -23,6 +23,7 @@ feature -- Initialization local l_retried: BOOLEAN do + create database_error_handler.make create db_application.login (username, password) if not l_retried then @@ -38,6 +39,7 @@ feature -- Initialization create db_control.make end rescue + create database_error_handler.make exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) l_retried := True retry @@ -49,6 +51,7 @@ feature -- Initialization local l_retried: BOOLEAN do + create database_error_handler.make create db_application.login (username, password) if not l_retried then @@ -64,6 +67,7 @@ feature -- Initialization create db_control.make end rescue + create database_error_handler.make exception_as_error ((create {EXCEPTION_MANAGER}).last_exception) l_retried := True retry @@ -76,6 +80,7 @@ feature -- Initialization -- `database_name' to `a_database_name' -- `connection' to `a_connection' do + create database_error_handler.make create db_application.login (a_username, a_password) db_application.set_hostname (a_hostname) db_application.set_data_source (a_database_name) @@ -97,6 +102,8 @@ feature -- Initialization l_user: STRING l_password: STRING do + create database_error_handler.make + l_string := a_string.split (';') l_server := l_string.at (2).split ('=').at (2) l_port := l_string.at (3).split ('=').at (2) @@ -111,12 +118,12 @@ feature -- Initialization db_application.set_base create db_control.make keep_connection := is_keep_connection - end login_with_schema (a_schema: STRING; a_username: STRING; a_password: STRING) -- Login with `a_connection_string'and immediately connect to database. do + create database_error_handler.make create db_application db_application.set_application (a_schema) db_application.login_and_connect (a_username, a_password) diff --git a/persistence/implementation/mysql/src/provider/node_data_provider.e b/persistence/implementation/mysql/src/provider/node_data_provider.e index 59c9bae..5951cc5 100644 --- a/persistence/implementation/mysql/src/provider/node_data_provider.e +++ b/persistence/implementation/mysql/src/provider/node_data_provider.e @@ -10,10 +10,10 @@ inherit PARAMETER_NAME_HELPER - SHARED_ERROR - REFACTORING_HELPER + SHARED_LOGGER + create make @@ -23,6 +23,7 @@ feature -- Initialization -- Create a data provider. do create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) + create error_handler.make post_execution end @@ -34,8 +35,13 @@ feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do + Result := not error_handler.has_error end +feature -- Error Handler + + error_handler: ERROR_HANDLER + feature -- Access nodes: DATABASE_ITERATION_CURSOR [CMS_NODE] @@ -43,6 +49,7 @@ feature -- Access local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".nodes") create l_parameters.make (0) db_handler.set_query (create {DATABASE_QUERY}.data_reader (Select_nodes, l_parameters)) @@ -57,6 +64,7 @@ feature -- Access l_parameters: STRING_TABLE [ANY] l_query: STRING do + error_handler.reset log.write_information (generator + ".recent_nodes") create l_parameters.make (2) l_parameters.put (a_rows, "rows") @@ -73,6 +81,7 @@ feature -- Access local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".node") create l_parameters.make (1) l_parameters.put (a_id,"id") @@ -89,6 +98,7 @@ feature -- Access local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".count") create l_parameters.make (0) db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_count, l_parameters)) @@ -105,6 +115,7 @@ feature -- Access local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".last_inserted_node_id") create l_parameters.make (0) db_handler.set_query (create {DATABASE_QUERY}.data_reader (Sql_last_insert_node_id, l_parameters)) @@ -122,6 +133,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".new_node") create l_parameters.make (7) l_parameters.put (a_node.title, "title") @@ -140,10 +152,7 @@ feature -- Basic operations db_handler.execute_change a_node.set_id (last_inserted_node_id) - post_execution - - end update_node_title (a_id: INTEGER_64; a_title: READABLE_STRING_32) @@ -151,6 +160,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".update_node_title") create l_parameters.make (3) l_parameters.put (a_title, "title") @@ -166,6 +176,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".update_node_summary") create l_parameters.make (3) l_parameters.put (a_summary, "summary") @@ -181,6 +192,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".update_node_content") create l_parameters.make (3) l_parameters.put (a_content, "content") @@ -196,6 +208,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".update_node") create l_parameters.make (7) l_parameters.put (a_node.title, "title") @@ -215,6 +228,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".delete_node") create l_parameters.make (1) l_parameters.put (a_id, "id") @@ -227,6 +241,7 @@ feature -- Basic operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".delete_from_user_nodes") create l_parameters.make (1) l_parameters.put (a_id, "id") @@ -242,6 +257,7 @@ feature -- Basic Operations: User_Nodes local l_parameters: STRING_TABLE [detachable ANY] do + error_handler.reset log.write_information (generator + ".add_author") create l_parameters.make (2) l_parameters.put (a_user_id,"user_id") @@ -256,6 +272,7 @@ feature -- Basic Operations: User_Nodes local l_parameters: STRING_TABLE [detachable ANY] do + error_handler.reset log.write_information (generator + ".add_collaborator") create l_parameters.make (2) l_parameters.put (a_user_id,"users_id") @@ -270,6 +287,7 @@ feature -- Basic Operations: User_Nodes local l_parameters: STRING_TABLE [detachable ANY] do + error_handler.reset log.write_information (generator + ".add_collaborator") create l_parameters.make (2) l_parameters.put (a_user_id,"users_id") @@ -285,6 +303,7 @@ feature -- Basic Operations: User_Nodes local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".author_nodes") create l_parameters.make (1) l_parameters.put (a_id, "user_id") @@ -299,6 +318,7 @@ feature -- Basic Operations: User_Nodes local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".collaborator_nodes") create l_parameters.make (1) l_parameters.put (a_id, "user_id") @@ -314,6 +334,7 @@ feature -- Basic Operations: User_Nodes local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".node_author") create l_parameters.make (1) l_parameters.put (a_id, "node_id") @@ -330,6 +351,7 @@ feature -- Basic Operations: User_Nodes local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".node_collaborators") create l_parameters.make (1) l_parameters.put (a_id, "node_id") @@ -344,6 +366,7 @@ feature -- Basic Operations: User_Nodes local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".node_collaborators") create l_parameters.make (2) l_parameters.put (a_user_id, "user_id") @@ -353,7 +376,6 @@ feature -- Basic Operations: User_Nodes if db_handler.count = 1 then Result := db_handler.read_integer_32 (1) = 1 end - post_execution end @@ -480,6 +502,10 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do + error_handler.add_synchronization (db_handler.database_error_handler) + if error_handler.has_error then + log.write_critical (generator + ".post_execution " + error_handler.as_string_representation) + end end end diff --git a/persistence/implementation/mysql/src/provider/user_data_provider.e b/persistence/implementation/mysql/src/provider/user_data_provider.e index 0d9b0bf..8660b8e 100644 --- a/persistence/implementation/mysql/src/provider/user_data_provider.e +++ b/persistence/implementation/mysql/src/provider/user_data_provider.e @@ -10,10 +10,10 @@ inherit PARAMETER_NAME_HELPER - SHARED_ERROR - REFACTORING_HELPER + SHARED_LOGGER + create make @@ -23,17 +23,24 @@ feature -- Initialization -- Create a data provider. do create {DATABASE_HANDLER_IMPL} db_handler.make (a_connection) + create error_handler.make post_execution end db_handler: DATABASE_HANDLER -- Db handler. +feature -- Error Handler + + error_handler: ERROR_HANDLER + + feature -- Status Report is_successful: BOOLEAN -- Is the last execution sucessful? do + Result := not error_handler.has_error end has_user: BOOLEAN @@ -51,6 +58,7 @@ feature -- Basic Operations l_password_salt, l_password_hash: STRING l_security: SECURITY_PROVIDER do + error_handler.reset create l_security l_password_salt := l_security.salt l_password_hash := l_security.password_hash (a_password, l_password_salt) @@ -71,6 +79,7 @@ feature -- Basic Operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".user") create l_parameters.make (1) l_parameters.put (a_id,"id") @@ -87,6 +96,7 @@ feature -- Basic Operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".user_by_name") create l_parameters.make (1) l_parameters.put (a_name,"name") @@ -104,6 +114,7 @@ feature -- Basic Operations local l_parameters: STRING_TABLE [detachable ANY] do + error_handler.reset log.write_information (generator + ".user_by_email") create l_parameters.make (1) l_parameters.put (a_email,"email") @@ -120,6 +131,7 @@ feature -- Basic Operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".user_salt") create l_parameters.make (1) l_parameters.put (a_username,"name") @@ -138,6 +150,7 @@ feature -- Basic Operations local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".count") create l_parameters.make (0) db_handler.set_query (create {DATABASE_QUERY}.data_reader (select_count, l_parameters)) @@ -155,6 +168,7 @@ feature -- Basic operations: User Roles local l_parameters: STRING_TABLE [detachable ANY] do + error_handler.reset log.write_information (generator + ".add_role") create l_parameters.make (2) l_parameters.put (a_user_id,"users_id") @@ -169,6 +183,7 @@ feature -- Basic operations: User Roles local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".user_roles") create l_parameters.make (1) l_parameters.put (a_id, "user_id") @@ -186,6 +201,7 @@ feature -- Basic operations: User Profiles local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".save_profile_item") create l_parameters.make (3) l_parameters.put (a_key, "key") @@ -201,6 +217,7 @@ feature -- Basic operations: User Profiles local l_cursor: TABLE_ITERATION_CURSOR [READABLE_STRING_8, READABLE_STRING_8] do + error_handler.reset log.write_information (generator + ".save_profile") from l_cursor := a_user_profile.new_cursor @@ -219,6 +236,7 @@ feature -- Basic operations: User Profiles local l_parameters: STRING_TABLE [ANY] do + error_handler.reset log.write_information (generator + ".user_profile") create l_parameters.make (1) l_parameters.put (a_user_id, "users_id") @@ -310,6 +328,10 @@ feature {NONE} -- Implementation post_execution -- Post database execution. do + error_handler.add_synchronization (db_handler.database_error_handler) + if error_handler.has_error then + log.write_critical (generator + ".post_execution " + error_handler.as_string_representation) + end end end diff --git a/persistence/implementation/mysql/tests/handler/database_handler_test.e b/persistence/implementation/mysql/tests/handler/database_handler_test.e new file mode 100644 index 0000000..6ae64b4 --- /dev/null +++ b/persistence/implementation/mysql/tests/handler/database_handler_test.e @@ -0,0 +1,81 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + DATABASE_HANDLER_TEST + +inherit + EQA_TEST_SET + redefine + on_prepare, + on_clean + select + default_create + end + ABSTRACT_DB_TEST + rename + default_create as default_db_test + end + + +feature {NONE} -- Events + + on_prepare + -- + do + (create {CLEAN_DB}).clean_db(connection) + end + + on_clean + -- + do + end + + +feature -- Test routines + + test_wrong_database_query + -- New test routine + local + l_parameters: STRING_TABLE[detachable ANY] + do + create l_parameters.make (0) + handler.set_query (create {DATABASE_QUERY}.data_reader ("Sellect from users", l_parameters)) + handler.execute_query + assert ("Has error:", handler.has_error) + end + + + + test_sequences_of_wrong_and_correct_queries + -- New test routine + local + l_parameters: STRING_TABLE[detachable ANY] + do + create l_parameters.make (0) + handler.set_query (create {DATABASE_QUERY}.data_reader ("Sellect from users;", l_parameters)) + handler.execute_query + assert ("Has error:", handler.has_error) + + handler.set_query (create {DATABASE_QUERY}.data_reader ("Select * from users;", l_parameters)) + handler.execute_query + assert ("Not Has error:",not handler.has_error) + end + + +feature -- Handler + + handler: DATABASE_HANDLER + once + create {DATABASE_HANDLER_IMPL} Result.make (connection ) + end + +end + + diff --git a/persistence/implementation/mysql/tests/nodes/node_test_set.e b/persistence/implementation/mysql/tests/nodes/node_test_set.e index 7993a72..9b3b442 100644 --- a/persistence/implementation/mysql/tests/nodes/node_test_set.e +++ b/persistence/implementation/mysql/tests/nodes/node_test_set.e @@ -41,7 +41,9 @@ feature {NONE} -- Events feature -- Test routines test_new_node - do + note + testing: "execution/isolated" + do assert ("Empty Nodes", node_provider.nodes.after) node_provider.new_node (default_node) assert ("Not empty Nodes after new_node", not node_provider.nodes.after) @@ -53,6 +55,8 @@ feature -- Test routines test_update_node + note + testing: "execution/isolated" local l_node: CMS_NODE do @@ -85,6 +89,8 @@ feature -- Test routines end test_update_title + note + testing: "execution/isolated" local l_node: CMS_NODE do @@ -104,6 +110,8 @@ feature -- Test routines end test_update_summary + note + testing: "execution/isolated" local l_node: CMS_NODE do @@ -123,11 +131,14 @@ feature -- Test routines end test_update_content + note + testing: "execution/isolated" local l_node: CMS_NODE do assert ("Empty Nodes", node_provider.nodes.after) l_node := custom_node ("

    test node udpate

    ", "Update node", "Test case update") + connection.begin_transaction node_provider.new_node (l_node) assert ("Not empty Nodes after new_node", not node_provider.nodes.after) -- Exist node with id 1 @@ -139,6 +150,7 @@ feature -- Test routines node_provider.update_node_content (l_un.id,"New Content") assert ("Exist node with id 1", attached {CMS_NODE} node_provider.node (1) as ll_node and then not (ll_node.content ~ l_un.content) and then ll_node.content ~ "New Content" and then ll_node.summary ~ l_un.summary and then ll_node.title ~ l_un.title) end + connection.commit end diff --git a/persistence/implementation/mysql/tests/tests.ecf b/persistence/implementation/mysql/tests/tests.ecf index 363c33c..abe2c75 100644 --- a/persistence/implementation/mysql/tests/tests.ecf +++ b/persistence/implementation/mysql/tests/tests.ecf @@ -7,6 +7,7 @@ + diff --git a/persistence/implementation/mysql/tests/transactions/transaction_test_set.e b/persistence/implementation/mysql/tests/transactions/transaction_test_set.e index 3199f71..4abdd8c 100644 --- a/persistence/implementation/mysql/tests/transactions/transaction_test_set.e +++ b/persistence/implementation/mysql/tests/transactions/transaction_test_set.e @@ -66,77 +66,6 @@ feature -- Test routines assert ("Not has nodes:", node_provider.count = 0) end - - - test_user_node_nested_transaction_with_rollback_not_supported - note - testing: "execution/isolated" - do - connection.begin_transaction2 - user_provider.new_user ("test", "test","test@admin.com") - assert ("Has user:", user_provider.has_user) - node_provider.new_node (default_node) - assert ("Has one node:", node_provider.count = 1) - - connection.begin_transaction2 - user_provider.new_user ("test1", "test1","test1@admin.com") - assert ("Has user: test1", attached user_provider.user_by_name ("test1")) - connection.rollback2 - - connection.commit2 - assert ("Has user test:", attached user_provider.user_by_name ("test")) - assert ("Has nodes:", node_provider.count = 1) - assert ("Not has user: test1", user_provider.user_by_name ("test1") = Void) - end - - - test_user_node_nested_transaction_with_commit - note - testing: "execution/isolated" - do - connection.begin_transaction2 - user_provider.new_user ("test", "test","test@admin.com") - assert ("Has user:", user_provider.has_user) - node_provider.new_node (default_node) - assert ("Has one node:", node_provider.count = 1) - - connection.begin_transaction2 - user_provider.new_user ("test1", "test1","test1@admin.com") - assert ("Has user: test1", attached user_provider.user_by_name ("test1")) - connection.commit2 - - connection.commit2 - assert ("Has user test:", attached user_provider.user_by_name ("test")) - assert ("Has user test1:", attached user_provider.user_by_name ("test1")) - assert ("Has nodes:", node_provider.count = 1) - end - - - test_user_node_nested_transaction_with_rollback - note - testing: "execution/isolated" - do - connection.begin_transaction2 - user_provider.new_user ("test", "test","test@admin.com") - assert ("Has user:", user_provider.has_user) - node_provider.new_node (default_node) - assert ("Has one node:", node_provider.count = 1) - - connection.begin_transaction2 - user_provider.new_user ("test1", "test1","test1@admin.com") - assert ("Has user: test1", attached user_provider.user_by_name ("test1")) - connection.commit2 - - connection.rollback2 - assert ("Not Has user test:", user_provider.user_by_name ("test") = void) - assert ("Not Has user test1:", user_provider.user_by_name ("test1") = void) - assert ("Has 0 nodes:", node_provider.count = 0) - end - - - - - feature {NONE} -- Implementation node_provider: NODE_DATA_PROVIDER diff --git a/persistence/interface/cms_storage.e b/persistence/interface/cms_storage.e index eb5a44a..234613d 100644 --- a/persistence/interface/cms_storage.e +++ b/persistence/interface/cms_storage.e @@ -11,7 +11,7 @@ deferred class inherit - SHARED_ERROR + SHARED_LOGGER feature {NONE} -- Initialization @@ -19,6 +19,11 @@ feature {NONE} -- Initialization do end +feature -- Error Handling + + error_handler: ERROR_HANDLER + -- Error handler. + feature -- Access: user has_user: BOOLEAN From b11462105ae35add2fb328fcf2c5ac3cd2c36403 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 3 Nov 2014 12:40:54 -0300 Subject: [PATCH 036/386] Initial implementation with Hooks, Regions and Templates Updated Modules to support hooks, still work in progress. --- cms/cms-safe.ecf | 6 +- cms/src/hooks/cms_hook.e | 9 + cms/src/hooks/cms_hook_auto_register.e | 33 ++ cms/src/hooks/cms_hook_block.e | 23 + cms/src/hooks/cms_hook_form_alter.e | 17 + cms/src/hooks/cms_hook_menu_alter.e | 17 + cms/src/hooks/cms_hook_value_alter.e | 28 + cms/src/kernel/cms_common_api.e | 97 +++ cms/src/kernel/content/cms_block.e | 28 + cms/src/kernel/content/cms_block_region.e | 42 ++ cms/src/kernel/content/cms_content_block.e | 74 +++ cms/src/kernel/content/cms_menu_block.e | 41 ++ cms/src/kernel/content/cms_value_table.e | 89 +++ cms/src/kernel/content/format/cms_formats.e | 64 ++ cms/src/kernel/form/cms_form.e | 42 ++ cms/src/kernel/link/cms_menu_system.e | 88 +++ .../modules/basic_auth/basic_auth_module.e | 7 + .../handler/basic_auth_logoff_handler.e | 2 +- cms/src/modules/cms_module.e | 11 + .../node/handler/node_content_handler.e | 12 +- cms/src/modules/node/handler/node_handler.e | 12 +- .../node/handler/node_summary_handler.e | 12 +- .../modules/node/handler/node_title_handler.e | 12 +- cms/src/modules/node/handler/nodes_handler.e | 2 +- cms/src/modules/node/node_module.e | 41 ++ cms/src/service/filter/cms_error_filter.e | 2 +- cms/src/service/handler/cms_root_handler.e | 2 +- cms/src/service/response/cms_response.e | 557 +++++++++++++++++- cms/src/theme/cms_theme.e | 72 ++- .../theme/default_theme/default_cms_theme.e | 41 +- cms/src/theme/smarty_theme/smarty_cms_theme.e | 27 +- .../roc_api/modules/demo/cms_demo_module.e | 7 + examples/roc_api/site/config/cms.ini | 2 +- .../roc_api/site/www/themes/api/layout.tpl | 36 ++ examples/roc_api/site/www/themes/bootstrap.7z | Bin 0 -> 2576 bytes .../site/www/themes/bootstrap/layout.tpl | 64 ++ .../site/www/themes/bootstrap/page.tpl | 64 ++ .../www/themes/bootstrap/roc_template.html | 86 +++ .../site/www/themes/bootstrap/theme.info | 13 + .../www/themes/bootstrap/tpl/help_section.tpl | 1 + .../bootstrap/tpl/highlighted_section.tpl | 1 + .../www/themes/bootstrap/tpl/left_sidebar.tpl | 8 + .../www/themes/bootstrap/tpl/main_content.tpl | 4 + .../www/themes/bootstrap/tpl/page_bottom.tpl | 0 .../www/themes/bootstrap/tpl/page_footer.tpl | 9 + .../www/themes/bootstrap/tpl/page_header.tpl | 10 + .../www/themes/bootstrap/tpl/page_top.tpl | 0 .../themes/bootstrap/tpl/right_sidebar.tpl | 8 + model/src/cms_link.e | 11 +- model/src/cms_local_link.e | 21 + 50 files changed, 1810 insertions(+), 45 deletions(-) create mode 100644 cms/src/hooks/cms_hook.e create mode 100644 cms/src/hooks/cms_hook_auto_register.e create mode 100644 cms/src/hooks/cms_hook_block.e create mode 100644 cms/src/hooks/cms_hook_form_alter.e create mode 100644 cms/src/hooks/cms_hook_menu_alter.e create mode 100644 cms/src/hooks/cms_hook_value_alter.e create mode 100644 cms/src/kernel/cms_common_api.e create mode 100644 cms/src/kernel/content/cms_block.e create mode 100644 cms/src/kernel/content/cms_block_region.e create mode 100644 cms/src/kernel/content/cms_content_block.e create mode 100644 cms/src/kernel/content/cms_menu_block.e create mode 100644 cms/src/kernel/content/cms_value_table.e create mode 100644 cms/src/kernel/content/format/cms_formats.e create mode 100644 cms/src/kernel/form/cms_form.e create mode 100644 cms/src/kernel/link/cms_menu_system.e create mode 100644 examples/roc_api/site/www/themes/api/layout.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap.7z create mode 100644 examples/roc_api/site/www/themes/bootstrap/layout.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/page.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/roc_template.html create mode 100644 examples/roc_api/site/www/themes/bootstrap/theme.info create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/help_section.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/highlighted_section.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/left_sidebar.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/main_content.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/page_bottom.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/page_footer.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/page_header.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/page_top.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/right_sidebar.tpl diff --git a/cms/cms-safe.ecf b/cms/cms-safe.ecf index 3a5945e..e8f43fa 100644 --- a/cms/cms-safe.ecf +++ b/cms/cms-safe.ecf @@ -10,7 +10,9 @@ - + + + @@ -18,6 +20,8 @@ + + /EIFGENs$ diff --git a/cms/src/hooks/cms_hook.e b/cms/src/hooks/cms_hook.e new file mode 100644 index 0000000..83f9ac3 --- /dev/null +++ b/cms/src/hooks/cms_hook.e @@ -0,0 +1,9 @@ +note + description: "Marker interface" + date: "$Date$" + revision: "$Revision$" + +deferred class + CMS_HOOK + +end diff --git a/cms/src/hooks/cms_hook_auto_register.e b/cms/src/hooks/cms_hook_auto_register.e new file mode 100644 index 0000000..d960b6d --- /dev/null +++ b/cms/src/hooks/cms_hook_auto_register.e @@ -0,0 +1,33 @@ +note + description: "[ + Summary description for {CMS_HOOK_AUTO_REGISTER}. + When inheriting from this class, the declared hooks are automatically + registered, otherwise, each descendant has to add it to the cms service + itself. + ]" + date: "$Date: 2014-08-28 08:21:49 -0300 (ju. 28 de ago. de 2014) $" + revision: "$Revision: 95708 $" + +deferred class + CMS_HOOK_AUTO_REGISTER + +inherit + CMS_HOOK + +feature -- Hook + + hook_auto_register (a_response: CMS_RESPONSE) + do + if attached {CMS_HOOK_MENU_ALTER} Current as h_menu_alter then + a_response.add_menu_alter_hook (h_menu_alter) + end + if attached {CMS_HOOK_BLOCK} Current as h_block then + a_response.add_block_hook (h_block) + end + if attached {CMS_HOOK_FORM_ALTER} Current as h_block then + a_response.add_form_alter_hook (h_block) + end + + end + +end diff --git a/cms/src/hooks/cms_hook_block.e b/cms/src/hooks/cms_hook_block.e new file mode 100644 index 0000000..54b493a --- /dev/null +++ b/cms/src/hooks/cms_hook_block.e @@ -0,0 +1,23 @@ +note + description: "Summary description for {CMS_HOOK_BLOCK}." + author: "" + date: "$Date: 2014-08-28 08:21:49 -0300 (ju. 28 de ago. de 2014) $" + revision: "$Revision: 95708 $" + +deferred class + CMS_HOOK_BLOCK + +inherit + CMS_HOOK + +feature -- Hook + + block_list: ITERABLE [like {CMS_BLOCK}.name] + deferred + end + + get_block_view (a_block_id: detachable READABLE_STRING_8; a_response: CMS_RESPONSE) + deferred + end + +end diff --git a/cms/src/hooks/cms_hook_form_alter.e b/cms/src/hooks/cms_hook_form_alter.e new file mode 100644 index 0000000..472979c --- /dev/null +++ b/cms/src/hooks/cms_hook_form_alter.e @@ -0,0 +1,17 @@ +note + description: "Describe how to alter a form before it's rendered" + date: "$Date: 2014-08-28 08:21:49 -0300 (ju. 28 de ago. de 2014) $" + +deferred class + CMS_HOOK_FORM_ALTER + +inherit + CMS_HOOK + +feature -- Hook + + form_alter (a_form: CMS_FORM; a_form_data: detachable WSF_FORM_DATA; a_response: CMS_RESPONSE) + deferred + end + +end diff --git a/cms/src/hooks/cms_hook_menu_alter.e b/cms/src/hooks/cms_hook_menu_alter.e new file mode 100644 index 0000000..d409662 --- /dev/null +++ b/cms/src/hooks/cms_hook_menu_alter.e @@ -0,0 +1,17 @@ +note + description: "Describe how to alter a menu before it's rendered." + date: "$Date: 2014-08-28 08:21:49 -0300 (ju. 28 de ago. de 2014) $" + +deferred class + CMS_HOOK_MENU_ALTER + +inherit + CMS_HOOK + +feature -- Hook + + menu_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) + deferred + end + +end diff --git a/cms/src/hooks/cms_hook_value_alter.e b/cms/src/hooks/cms_hook_value_alter.e new file mode 100644 index 0000000..0ec43b9 --- /dev/null +++ b/cms/src/hooks/cms_hook_value_alter.e @@ -0,0 +1,28 @@ +note + description: "Describe how to alter generic values before they are rendered." + date: "$Date: 2014-10-23 08:30:11 -0300 (ju. 23 de oct. de 2014) $" + revision: "$Revision: 95980 $" + +deferred class + CMS_HOOK_VALUE_ALTER + +inherit + + CMS_HOOK + +feature -- Hook + + value_alter (a_value: CMS_VALUE_TABLE; a_response: CMS_RESPONSE) + deferred + end +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/kernel/cms_common_api.e b/cms/src/kernel/cms_common_api.e new file mode 100644 index 0000000..9301ddf --- /dev/null +++ b/cms/src/kernel/cms_common_api.e @@ -0,0 +1,97 @@ +note + description: "Summary description for {WSF_CMS_COMMON_API}." + author: "" + date: "$Date: 2014-08-28 08:21:49 -0300 (ju. 28 de ago. de 2014) $" + revision: "$Revision: 95708 $" + +deferred class + CMS_COMMON_API + +inherit + WSF_API_UTILITIES + +feature {NONE} -- Access + + site_url: READABLE_STRING_8 + do + Result := "" + end + + base_url: detachable READABLE_STRING_8 + -- Base url if any. + do + end + +feature -- Access + + user_link (u: CMS_USER): like link + do + Result := link (u.name, "/user/" + u.id.out, Void) + end + + node_link (n: CMS_NODE): like link + do + Result := link (n.title, "/node/" + n.id.out, Void) + end + + user_url (u: CMS_USER): like url + do + Result := url ("/user/" + u.id.out, Void) + end + + node_url (n: CMS_NODE): like url + do + Result := url ("/node/" + n.id.out, Void) + end + +feature -- Helper + + is_empty (s: detachable READABLE_STRING_GENERAL): BOOLEAN + -- Is `s' is Void or empty ? + do + Result := s = Void or else s.is_empty + end + + unix_timestamp (dt: DATE_TIME): INTEGER_64 + do + Result := (create {HTTP_DATE_TIME_UTILITIES}).unix_time_stamp (dt) + end + + unix_timestamp_to_date_time (t: INTEGER_64): DATE_TIME + do + Result := (create {HTTP_DATE_TIME_UTILITIES}).unix_time_stamp_to_date_time (t) + end + + string_unix_timestamp_to_date_time (s: READABLE_STRING_8): DATE_TIME + do + if s.is_integer_64 then + Result := (create {HTTP_DATE_TIME_UTILITIES}).unix_time_stamp_to_date_time (s.to_integer_64) + else + Result := (create {HTTP_DATE_TIME_UTILITIES}).unix_time_stamp_to_date_time (0) + end + end + +feature {NONE} -- Implementation + + options_boolean (opts: HASH_TABLE [detachable ANY, STRING]; k: STRING; dft: BOOLEAN): BOOLEAN + do + if attached {BOOLEAN} opts.item (k) as h then + Result := h + else + Result := dft + end + end + + options_string (opts: HASH_TABLE [detachable ANY, STRING]; k: STRING): detachable STRING + do + if attached {STRING} opts.item (k) as s then + Result := s + end + end + +-- html_encoder: HTML_ENCODER +-- once ("thread") +-- create Result +-- end + +end diff --git a/cms/src/kernel/content/cms_block.e b/cms/src/kernel/content/cms_block.e new file mode 100644 index 0000000..0b67db7 --- /dev/null +++ b/cms/src/kernel/content/cms_block.e @@ -0,0 +1,28 @@ +note + description: "Summary description for {CMS_BLOCK}." + date: "$Date: 2014-08-28 08:21:49 -0300 (ju. 28 de ago. de 2014) $" + +deferred class + CMS_BLOCK + +feature -- Access + + name: READABLE_STRING_8 + deferred + end + + title: detachable READABLE_STRING_32 + deferred + end + +feature -- status report + + is_enabled: BOOLEAN + +feature -- Conversion + + to_html (a_theme: CMS_THEME): STRING_8 + deferred + end + +end diff --git a/cms/src/kernel/content/cms_block_region.e b/cms/src/kernel/content/cms_block_region.e new file mode 100644 index 0000000..baef14f --- /dev/null +++ b/cms/src/kernel/content/cms_block_region.e @@ -0,0 +1,42 @@ +note + description: "Summary description for {CMS_BLOCK_REGION}." + date: "$Date: 2014-10-30 12:55:33 -0300 (ju. 30 de oct. de 2014) $" + +class + CMS_BLOCK_REGION + +create + make + +feature {NONE} -- Initialization + + make (a_name: like name) + do + name := a_name + create blocks.make (1) + end + +feature -- Access + + name: READABLE_STRING_8 + + blocks: ARRAYED_LIST [CMS_BLOCK] + +feature -- Element change + + extend (b: CMS_BLOCK) + do + blocks.force (b) + end + +;note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/kernel/content/cms_content_block.e b/cms/src/kernel/content/cms_content_block.e new file mode 100644 index 0000000..8a5f7c1 --- /dev/null +++ b/cms/src/kernel/content/cms_content_block.e @@ -0,0 +1,74 @@ +note + description: "Summary description for {CMS_CONTENT_BLOCK}." + author: "" + date: "$Date: 2014-10-30 12:55:33 -0300 (ju. 30 de oct. de 2014) $" + revision: "$Revision: 96018 $" + +class + CMS_CONTENT_BLOCK + +inherit + CMS_BLOCK + +create + make, + make_raw + +feature {NONE} -- Initialization + + make (a_name: like name; a_title: like title; a_content: like content; a_format: like format) + do + is_enabled := True + name := a_name + title := a_title + content := a_content + format := a_format + end + + make_raw (a_name: like name; a_title: like title; a_content: like content; a_format: like format) + do + make (a_name, a_title, a_content, a_format) + set_is_raw (True) + end + +feature -- Access + + name: READABLE_STRING_8 + + title: detachable READABLE_STRING_32 + + content: READABLE_STRING_8 + + format: CONTENT_FORMAT + +feature -- Status report + + is_raw: BOOLEAN + -- Is raw? + -- If True, do not get wrapped it with block specific div + +feature -- Element change + + set_is_raw (b: BOOLEAN) + do + is_raw := b + end + +feature -- Conversion + + to_html (a_theme: CMS_THEME): STRING_8 + do + Result := content + end + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/kernel/content/cms_menu_block.e b/cms/src/kernel/content/cms_menu_block.e new file mode 100644 index 0000000..a7a0797 --- /dev/null +++ b/cms/src/kernel/content/cms_menu_block.e @@ -0,0 +1,41 @@ +note + description: "Summary description for {CMS_MENU_BLOCK}." + date: "$Date: 2014-08-28 08:21:49 -0300 (ju. 28 de ago. de 2014) $" + +class + CMS_MENU_BLOCK + +inherit + CMS_BLOCK + +create + make + +feature {NONE} -- Initialization + + make (a_menu: like menu) + do + is_enabled := True + menu := a_menu + name := a_menu.name + title := a_menu.title + end + +feature -- Access + + menu: CMS_MENU + + name: READABLE_STRING_8 + + title: detachable READABLE_STRING_32 + + is_horizontal: BOOLEAN + +feature -- Conversion + + to_html (a_theme: CMS_THEME): STRING_8 + do + Result := a_theme.menu_html (menu, is_horizontal) + end + +end diff --git a/cms/src/kernel/content/cms_value_table.e b/cms/src/kernel/content/cms_value_table.e new file mode 100644 index 0000000..2e5ccc9 --- /dev/null +++ b/cms/src/kernel/content/cms_value_table.e @@ -0,0 +1,89 @@ +note + description: "Summary description for {CMS_VALUE_TABLE}." + date: "$Date: 2014-10-23 08:30:11 -0300 (ju. 23 de oct. de 2014) $" + revision: "$Revision: 95980 $" + +class + CMS_VALUE_TABLE + +inherit + TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL] + +create + make + +feature {NONE} -- Initialization + + make (nb: INTEGER) + do + create table.make (nb) + end + +feature -- Access + + count: INTEGER + -- Number of items. + do + Result := table.count + end + + item (key: READABLE_STRING_GENERAL): detachable ANY + -- Item associated with `key', if present + -- otherwise default value of type `G'. + note + option: stable + do + Result := table.item (key) + end + + has (key: READABLE_STRING_GENERAL): BOOLEAN + -- Has item associated with key `key'? + do + Result := table.has (key) + end + + new_cursor: TABLE_ITERATION_CURSOR [detachable ANY, READABLE_STRING_GENERAL] + -- + do + Result := table.new_cursor + end + +feature -- Element change + + put (new: detachable ANY; key: READABLE_STRING_GENERAL) + -- Insert `new' with `key' if there is no other item + -- associated with the same key. + do + table.put (new, key) + end + + force (new: detachable ANY; key: READABLE_STRING_GENERAL) + -- Update table so that `new' will be the item associated + -- with `key'. + do + table.force (new, key) + end + + remove (key: READABLE_STRING_GENERAL) + do + table.remove (key) + end + +feature {NONE} -- Duplication + + table: STRING_TABLE [detachable ANY] + +invariant + table_set: table /= Void + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/kernel/content/format/cms_formats.e b/cms/src/kernel/content/format/cms_formats.e new file mode 100644 index 0000000..11823ae --- /dev/null +++ b/cms/src/kernel/content/format/cms_formats.e @@ -0,0 +1,64 @@ +note + description: "Summary description for {CMS_FORMATS}." + author: "" + date: "$Date: 2014-10-16 04:45:23 -0300 (ju. 16 de oct. de 2014) $" + revision: "$Revision: 95932 $" + +class + CMS_FORMATS + +feature -- Access + + format (a_name: like {CONTENT_FORMAT}.name): detachable CONTENT_FORMAT + do + across + all_formats as c + until + Result /= Void + loop + if c.item.name.same_string (a_name) then + Result := c.item + end + end + end + + all_formats: LIST [CONTENT_FORMAT] + once + create {ARRAYED_LIST [CONTENT_FORMAT]} Result.make (3) + Result.force (plain_text) + Result.force (full_html) + Result.force (filtered_html) + end + + default_format: CONTENT_FORMAT + do + Result := plain_text --FIXME + end + + plain_text: PLAIN_TEXT_CONTENT_FORMAT + once + create Result + end + + full_html: FULL_HTML_CONTENT_FORMAT + once + create Result + end + + filtered_html: FILTERED_HTML_CONTENT_FORMAT + once + create Result + end + + +note + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/cms/src/kernel/form/cms_form.e b/cms/src/kernel/form/cms_form.e new file mode 100644 index 0000000..2038a14 --- /dev/null +++ b/cms/src/kernel/form/cms_form.e @@ -0,0 +1,42 @@ +note + description: "Summary description for {CMS_FORM}." + date: "$Date: 2014-08-28 08:21:49 -0300 (ju. 28 de ago. de 2014) $" + revision: "$Revision: 95708 $" + +class + CMS_FORM + +inherit + WSF_FORM + rename + process as process_form + end + +create + make + +feature -- Basic operation + + prepare (a_response: CMS_RESPONSE) + do + a_response.call_form_alter_hooks (Current, Void) + end + + process (a_response: CMS_RESPONSE) + do + process_form (a_response.request, agent on_prepared (a_response, ?), agent on_processed (a_response, ?)) + end + + on_prepared (a_response: CMS_RESPONSE; fd: WSF_FORM_DATA) + do + a_response.call_form_alter_hooks (Current, fd) + end + + on_processed (a_response: CMS_RESPONSE; fd: WSF_FORM_DATA) + do + if not fd.is_valid or fd.has_error then + a_response.report_form_errors (fd) + end + end + +end diff --git a/cms/src/kernel/link/cms_menu_system.e b/cms/src/kernel/link/cms_menu_system.e new file mode 100644 index 0000000..15978bd --- /dev/null +++ b/cms/src/kernel/link/cms_menu_system.e @@ -0,0 +1,88 @@ +note + description: "Describe the navigation menus." + date: "$Date: 2014-08-28 08:21:49 -0300 (ju. 28 de ago. de 2014) $" + revision: "$Revision: 95708 $" + +class + CMS_MENU_SYSTEM + +inherit + ITERABLE [CMS_MENU] + + REFACTORING_HELPER +create + make + +feature {NONE} -- Initialization + + make + -- Create a predefined manu system + do + to_implement ("Refactor, take the info from a Database or Configuration file.") + create items.make (5) + force (create {CMS_MENU}.make ("main-menu", 3)) + force (create {CMS_MENU}.make_with_title ("management", "Management", 3)) + force (create {CMS_MENU}.make_with_title ("navigation", "Navigation", 3)) + force (create {CMS_MENU}.make_with_title ("user", "User", 3)) + end + +feature -- Access + + item (n: like {CMS_MENU}.name): CMS_MENU + local + m: detachable CMS_MENU + do + m := items.item (n) + if m = Void then + create m.make (n, 3) + force (m) + end + Result := m + end + + main_menu: CMS_MENU + do + Result := item ("main-menu") + end + + management_menu: CMS_MENU + do + Result := item ("management") + end + + navigation_menu: CMS_MENU + do + Result := item ("navigation") + end + + user_menu: CMS_MENU + do + Result := item ("user") + end + + primary_tabs: CMS_MENU + do + Result := item ("primary-tabs") + end + +feature -- Change + + force (m: CMS_MENU) + do + items.force (m, m.name) + end + +feature -- Access + + new_cursor: ITERATION_CURSOR [CMS_MENU] + -- Fresh cursor associated with current structure. + do + Result := items.new_cursor + end + +feature {NONE} -- Implementation + + items: HASH_TABLE [CMS_MENU, like {CMS_MENU}.name] +-- items: ARRAYED_LIST [CMS_MENU] + +end diff --git a/cms/src/modules/basic_auth/basic_auth_module.e b/cms/src/modules/basic_auth/basic_auth_module.e index 2c589d1..1afac3e 100644 --- a/cms/src/modules/basic_auth/basic_auth_module.e +++ b/cms/src/modules/basic_auth/basic_auth_module.e @@ -74,4 +74,11 @@ feature {NONE} -- Implementation: routes a_router.handle_with_request_methods ("/basic_auth_logoff", l_bal_handler, l_methods) end + +feature -- Hooks + + register_hooks (a_response: CMS_RESPONSE) + do + end + end diff --git a/cms/src/modules/basic_auth/handler/basic_auth_logoff_handler.e b/cms/src/modules/basic_auth/handler/basic_auth_logoff_handler.e index b274ab4..ecf3e7b 100644 --- a/cms/src/modules/basic_auth/handler/basic_auth_logoff_handler.e +++ b/cms/src/modules/basic_auth/handler/basic_auth_logoff_handler.e @@ -50,7 +50,7 @@ feature -- HTTP Methods if attached req.query_parameter ("prompt") as l_prompt then (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) else - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/basic_auth/logoff") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) l_page.execute end diff --git a/cms/src/modules/cms_module.e b/cms/src/modules/cms_module.e index a58805e..f7d1eac 100644 --- a/cms/src/modules/cms_module.e +++ b/cms/src/modules/cms_module.e @@ -27,6 +27,17 @@ feature -- Router deferred end + + +feature -- Hooks configuration + + register_hooks (a_response: CMS_RESPONSE) + -- Module hooks configuration. + require + is_enabled: is_enabled + deferred + end + feature -- Filter filters: detachable LIST [WSF_FILTER] diff --git a/cms/src/modules/node/handler/node_content_handler.e b/cms/src/modules/node/handler/node_content_handler.e index 31b911a..4232053 100644 --- a/cms/src/modules/node/handler/node_content_handler.e +++ b/cms/src/modules/node/handler/node_content_handler.e @@ -70,7 +70,7 @@ feature -- HTTP Methods -- Existing node if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_content") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (l_node.content, "node_content") l_page.add_variable (l_id.value, "id") l_page.execute @@ -78,7 +78,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -96,14 +96,14 @@ feature -- HTTP Methods if l_method.is_case_insensitive_equal ("PUT") then do_put (req, res) else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end end else do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -127,7 +127,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -140,7 +140,7 @@ feature -- Error local l_page: CMS_RESPONSE do - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (req.absolute_script_url (req.path_info), "request") if a_id.is_integer then -- resource not found diff --git a/cms/src/modules/node/handler/node_handler.e b/cms/src/modules/node/handler/node_handler.e index e4257b6..c4e5bf9 100644 --- a/cms/src/modules/node/handler/node_handler.e +++ b/cms/src/modules/node/handler/node_handler.e @@ -69,7 +69,7 @@ feature -- HTTP Methods -- Existing node if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup,"modules/node") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (l_node, "node") l_page.execute else @@ -96,7 +96,7 @@ feature -- HTTP Methods elseif l_method.is_case_insensitive_equal ("PUT") then do_put (req, res) else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end end else @@ -131,7 +131,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -151,7 +151,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -165,7 +165,7 @@ feature -- Error local l_page: CMS_RESPONSE do - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (req.absolute_script_url (req.path_info), "request") if a_id.is_integer then -- resource not found @@ -186,7 +186,7 @@ feature {NONE} -- Node l_page: CMS_RESPONSE do if attached current_user_name (req) then - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (setup.is_html, "html") l_page.add_variable (setup.is_web, "web") l_page.execute diff --git a/cms/src/modules/node/handler/node_summary_handler.e b/cms/src/modules/node/handler/node_summary_handler.e index 96f3c36..585a050 100644 --- a/cms/src/modules/node/handler/node_summary_handler.e +++ b/cms/src/modules/node/handler/node_summary_handler.e @@ -69,7 +69,7 @@ feature -- HTTP Methods -- Existing node if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_summary") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (l_id.value, "id") l_page.add_variable (l_node.summary, "node_summary") l_page.execute @@ -77,7 +77,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -95,14 +95,14 @@ feature -- HTTP Methods if l_method.is_case_insensitive_equal ("PUT") then do_put (req, res) else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end end else do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -125,7 +125,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -140,7 +140,7 @@ feature -- Error local l_page: CMS_RESPONSE do - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (req.absolute_script_url (req.path_info), "request") if a_id.is_integer then -- resource not found diff --git a/cms/src/modules/node/handler/node_title_handler.e b/cms/src/modules/node/handler/node_title_handler.e index a5dfe9d..e1baa7b 100644 --- a/cms/src/modules/node/handler/node_title_handler.e +++ b/cms/src/modules/node/handler/node_title_handler.e @@ -69,7 +69,7 @@ feature -- HTTP Methods -- Existing node if attached {WSF_STRING} req.path_parameter ("id") as l_id then if l_id.is_integer and then attached {CMS_NODE} api_service.node (l_id.integer_value) as l_node then - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/node_title") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (l_node.title, "node_title") l_page.add_variable (l_id.value, "id") l_page.execute @@ -77,7 +77,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -94,14 +94,14 @@ feature -- HTTP Methods if l_method.is_case_insensitive_equal ("PUT") then do_put (req, res) else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end end else do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -125,7 +125,7 @@ feature -- HTTP Methods do_error (req, res, l_id) end else - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute end else (create {CMS_GENERIC_RESPONSE}).new_response_unauthorized (req, res) @@ -139,7 +139,7 @@ feature -- Error local l_page: CMS_RESPONSE do - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "master2/error") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (req.absolute_script_url (req.path_info), "request") if a_id.is_integer then -- resource not found diff --git a/cms/src/modules/node/handler/nodes_handler.e b/cms/src/modules/node/handler/nodes_handler.e index f1097db..4251f70 100644 --- a/cms/src/modules/node/handler/nodes_handler.e +++ b/cms/src/modules/node/handler/nodes_handler.e @@ -52,7 +52,7 @@ feature -- HTTP Methods -- At the moment the template is hardcoded, but we can -- get them from the configuration file and load them into -- the setup class. - create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup, "modules/nodes") + create {GENERIC_VIEW_CMS_RESPONSE} l_page.make (req, res, setup) l_page.add_variable (api_service.nodes, "nodes") l_page.execute end diff --git a/cms/src/modules/node/node_module.e b/cms/src/modules/node/node_module.e index 1ecaea3..f435b04 100644 --- a/cms/src/modules/node/node_module.e +++ b/cms/src/modules/node/node_module.e @@ -10,6 +10,11 @@ inherit CMS_MODULE + CMS_HOOK_MENU_ALTER + + CMS_HOOK_BLOCK + + create make @@ -115,4 +120,40 @@ feature {NONE} -- Implementation: routes a_router.handle_with_request_methods ("/node/{id}/content", l_report_handler, l_methods) end + +feature -- Hooks + + register_hooks (a_response: CMS_RESPONSE) + do + a_response.add_menu_alter_hook (Current) + a_response.add_block_hook (Current) + end + + block_list: ITERABLE [like {CMS_BLOCK}.name] + do + Result := <<"node-info">> + end + + get_block_view (a_block_id: detachable READABLE_STRING_8; a_response: CMS_RESPONSE) +-- local +-- b: CMS_CONTENT_BLOCK + do +-- if +-- a_execution.is_front and then +-- attached a_execution.user as u +-- then +-- create b.make ("node-info", "Node", "Node ...", a_execution.formats.plain_text) +-- a_execution.add_block (b, Void) +-- end + end + + menu_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) + local + lnk: CMS_LOCAL_LINK + perms: detachable ARRAYED_LIST [READABLE_STRING_8] + do + create lnk.make ("node", "/node") + a_menu_system.navigation_menu.extend (lnk) + end + end diff --git a/cms/src/service/filter/cms_error_filter.e b/cms/src/service/filter/cms_error_filter.e index 8a2f154..6a205e1 100644 --- a/cms/src/service/filter/cms_error_filter.e +++ b/cms/src/service/filter/cms_error_filter.e @@ -25,7 +25,7 @@ feature -- Basic operations execute_next (req, res) else log.write_critical (generator + ".execute" + setup.error_handler.as_string_representation ) - (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup, "master2/error")).execute + (create {ERROR_500_CMS_RESPONSE}.make (req, res, setup)).execute setup.error_handler.reset end end diff --git a/cms/src/service/handler/cms_root_handler.e b/cms/src/service/handler/cms_root_handler.e index fdb2783..785629f 100644 --- a/cms/src/service/handler/cms_root_handler.e +++ b/cms/src/service/handler/cms_root_handler.e @@ -48,7 +48,7 @@ feature -- HTTP Methods do_get (req: WSF_REQUEST; res: WSF_RESPONSE) -- do - (create {HOME_CMS_RESPONSE}.make (req, res, setup,"layout2")).execute + (create {HOME_CMS_RESPONSE}.make (req, res, setup)).execute end end diff --git a/cms/src/service/response/cms_response.e b/cms/src/service/response/cms_response.e index 671c031..97a5a65 100644 --- a/cms/src/service/response/cms_response.e +++ b/cms/src/service/response/cms_response.e @@ -14,22 +14,44 @@ inherit feature {NONE} -- Initialization - make (req: WSF_REQUEST; res: WSF_RESPONSE; a_setup: like setup; a_template: like template) + make(req: WSF_REQUEST; res: WSF_RESPONSE; a_setup: like setup) do status_code := {HTTP_STATUS_CODE}.ok setup := a_setup request := req response := res - template := a_template create header.make + create values.make (3) initialize end initialize do get_theme + create menu_system.make + initialize_block_region_settings + register_hooks end + + register_hooks + local + l_module: CMS_MODULE + l_available_modules: CMS_MODULE_COLLECTION + do +-- log.write_debug (generator + ".register_hooks") + l_available_modules := setup.modules + across + l_available_modules as ic + loop + l_module := ic.item + if l_module.is_enabled then + l_module.register_hooks (Current) + end + end + end + + feature -- Access request: WSF_REQUEST @@ -50,8 +72,9 @@ feature -- Access main_content: detachable STRING_8 - template: READABLE_STRING_32 - -- Current template. + values: CMS_VALUE_TABLE + -- Associated values indexed by string name. + feature -- Element change @@ -71,6 +94,423 @@ feature -- Element change main_content := s end + +feature -- Formats + + formats: CMS_FORMATS + once + create Result + end + +feature -- Menu + + menu_system: CMS_MENU_SYSTEM + + main_menu: CMS_MENU + do + Result := menu_system.main_menu + end + + management_menu: CMS_MENU + do + Result := menu_system.management_menu + end + + navigation_menu: CMS_MENU + do + Result := menu_system.navigation_menu + end + + user_menu: CMS_MENU + do + Result := menu_system.user_menu + end + + primary_tabs: CMS_MENU + do + Result := menu_system.primary_tabs + debug + end + end + +feature -- Blocks initialization + + initialize_block_region_settings + local + l_table: like block_region_settings + do + create regions.make_caseless (5) + + -- FIXME: let the user choose ... + create l_table.make_caseless (10) + l_table["page_top"] := "top" + l_table["header"] := "header" + l_table["highlighted"] := "highlighted" + l_table["help"] := "help" + l_table["content"] := "content" + l_table["footer"] := "footer" + l_table["management"] := "first_sidebar" + l_table["navigation"] := "first_sidebar" + l_table["user"] := "first_sidebar" + l_table["page_bottom"] := "page_bottom" + block_region_settings := l_table + end + +feature -- Blocks regions + + regions: STRING_TABLE [CMS_BLOCK_REGION] + -- Layout regions, that contains blocks. + + block_region_settings: STRING_TABLE [STRING] + + block_region (b: CMS_BLOCK; a_default_region: detachable READABLE_STRING_8): CMS_BLOCK_REGION + -- Region associated with block `b', or else `a_default_region' if provided. + local + l_region_name: detachable READABLE_STRING_8 + do + l_region_name := block_region_settings.item (b.name) + if l_region_name = Void then + if a_default_region /= Void then + l_region_name := a_default_region + else + -- Default .. put it in same named region + -- Maybe a bad idea + + l_region_name := b.name.as_lower + end + end + if attached regions.item (l_region_name) as res then + Result := res + else + create Result.make (l_region_name) + regions.force (Result, l_region_name) + end + end + + +feature -- Blocks + + add_block (b: CMS_BLOCK; a_default_region: detachable READABLE_STRING_8) + -- Add block `b' to associated region or `a_default_region' if provided. + local + l_region: detachable like block_region + do + l_region := block_region (b, a_default_region) + l_region.extend (b) + end + + get_blocks + do + fixme ("find a way to have this in configuration or database, and allow different order") + add_block (top_header_block, "header") + add_block (header_block, "header") + if attached message_block as m then + add_block (m, "content") + end + -- FIXME: avoid hardcoded html! should be only in theme. + add_block (create {CMS_CONTENT_BLOCK}.make_raw ("top_content_anchor", Void, "%N", formats.full_html), "content") + if attached page_title as l_page_title then + -- FIXME: avoid hardcoded html! should be only in theme. + add_block (create {CMS_CONTENT_BLOCK}.make_raw ("page_title", Void, "

    "+ l_page_title +"

    %N", formats.full_html), "content") + end + if attached primary_tabs_block as m then + add_block (m, "content") + end + add_block (content_block, "content") + + if attached management_menu_block as l_block then + add_block (l_block, "first_sidebar") + end + if attached navigation_menu_block as l_block then + add_block (l_block, "first_sidebar") + end + + if attached user_menu_block as l_block then + add_block (l_block, "first_sidebar") + end + + if attached footer_block as l_block then + add_block (l_block, "footer") + end + + hook_block_view + end + + main_menu_block: detachable CMS_MENU_BLOCK + do + if attached main_menu as m and then not m.is_empty then + create Result.make (m) + end + end + + management_menu_block: detachable CMS_MENU_BLOCK + do + if attached management_menu as m and then not m.is_empty then + create Result.make (m) + end + end + + navigation_menu_block: detachable CMS_MENU_BLOCK + do + if attached navigation_menu as m and then not m.is_empty then + create Result.make (m) + end + end + + user_menu_block: detachable CMS_MENU_BLOCK + do + if attached user_menu as m and then not m.is_empty then + create Result.make (m) + end + end + + primary_tabs_block: detachable CMS_MENU_BLOCK + do + if attached primary_tabs as m and then not m.is_empty then + create Result.make (m) + end + end + + top_header_block: CMS_CONTENT_BLOCK + local + s: STRING + do + fixme ("Avoid Hardcoded HTML") + -- create s.make_from_string ("
    " + html_encoded (site_name) + "
    ") + create s.make_empty + s.append ("
    ") + s.append (theme.menu_html (main_menu, True)) + s.append ("
    ") + create Result.make ("top_header", Void, s, formats.full_html) + Result.set_is_raw (True) + end + + header_block: CMS_CONTENT_BLOCK + local + s: STRING + do + create s.make_empty + create Result.make ("header", Void, s, formats.full_html) + Result.set_is_raw (True) + end + + message_block: detachable CMS_CONTENT_BLOCK + do + if attached message as m and then not m.is_empty then + create Result.make ("message", Void, "
    " + m + "
    ", formats.full_html) + Result.set_is_raw (True) + end + end + + content_block: CMS_CONTENT_BLOCK + local + s: STRING + do + if attached main_content as l_content then + s := l_content + else + s := "" + debug + s := "No Content" + end + end + create Result.make ("content", Void, s, formats.full_html) + Result.set_is_raw (True) + end + + footer_block: CMS_CONTENT_BLOCK + local + s: STRING + do + create s.make_empty + s.append ("Made with EWF") + create Result.make ("made_with", Void, s, formats.full_html) + end + +feature -- Hook: value alter + + add_value_alter_hook (h: like value_alter_hooks.item) + local + lst: like value_alter_hooks + do + lst := value_alter_hooks + if lst = Void then + create lst.make (1) + value_alter_hooks := lst + end + if not lst.has (h) then + lst.force (h) + end + end + + value_alter_hooks: detachable ARRAYED_LIST [CMS_HOOK_VALUE_ALTER] + + call_value_alter_hooks (m: CMS_VALUE_TABLE) + do + if attached value_alter_hooks as lst then + across + lst as c + loop + c.item.value_alter (m, Current) + end + end + end + +feature -- Hook: menu_alter + + add_menu_alter_hook (h: like menu_alter_hooks.item) + local + lst: like menu_alter_hooks + do + lst := menu_alter_hooks + if lst = Void then + create lst.make (1) + menu_alter_hooks := lst + end + if not lst.has (h) then + lst.force (h) + end + end + + menu_alter_hooks: detachable ARRAYED_LIST [CMS_HOOK_MENU_ALTER] + + call_menu_alter_hooks (m: CMS_MENU_SYSTEM ) + do + if attached menu_alter_hooks as lst then + across + lst as c + loop + c.item.menu_alter (m, Current) + end + end + end + +feature -- Hook: form_alter + + add_form_alter_hook (h: like form_alter_hooks.item) + local + lst: like form_alter_hooks + do + lst := form_alter_hooks + if lst = Void then + create lst.make (1) + form_alter_hooks := lst + end + if not lst.has (h) then + lst.force (h) + end + end + + form_alter_hooks: detachable ARRAYED_LIST [CMS_HOOK_FORM_ALTER] + + call_form_alter_hooks (f: CMS_FORM; a_form_data: detachable WSF_FORM_DATA; ) + do + if attached form_alter_hooks as lst then + across + lst as c + loop + c.item.form_alter (f, a_form_data, Current) + end + end + end + +feature -- Hook: block + + add_block_hook (h: like block_hooks.item) + local + lst: like block_hooks + do + lst := block_hooks + if lst = Void then + create lst.make (1) + block_hooks := lst + end + if not lst.has (h) then + lst.force (h) + end + end + + block_hooks: detachable ARRAYED_LIST [CMS_HOOK_BLOCK] + + hook_block_view + do + if attached block_hooks as lst then + across + lst as c + loop + across + c.item.block_list as blst + loop + c.item.get_block_view (blst.item, Current) + end + end + end + end + +feature -- Message + + add_message (a_msg: READABLE_STRING_8; a_category: detachable READABLE_STRING_8) + local + m: like message + do + m := message + if m = Void then + create m.make (a_msg.count + 9) + message := m + end + if a_category /= Void then + m.append ("
  • ") + else + m.append ("
  • ") + end + m.append (a_msg + "
  • ") + end + + add_notice_message (a_msg: READABLE_STRING_8) + do + add_message (a_msg, "notice") + end + + add_warning_message (a_msg: READABLE_STRING_8) + do + add_message (a_msg, "warning") + end + + add_error_message (a_msg: READABLE_STRING_8) + do + add_message (a_msg, "error") + end + + add_success_message (a_msg: READABLE_STRING_8) + do + add_message (a_msg, "success") + end + + report_form_errors (fd: WSF_FORM_DATA) + require + has_error: not fd.is_valid + do + if attached fd.errors as errs then + across + errs as err + loop + if attached err.item as e then + if attached e.field as l_field then + if attached e.message as e_msg then + add_error_message (e_msg) --"Field [" + l_field.name + "] is invalid. " + e_msg) + else + add_error_message ("Field [" + l_field.name + "] is invalid.") + end + elseif attached e.message as e_msg then + add_error_message (e_msg) + end + end + end + end + end + + message: detachable STRING_8 + feature -- Theme theme: CMS_THEME @@ -86,7 +526,7 @@ feature -- Theme create l_info.make_default end if l_info.engine.is_case_insensitive_equal_general ("smarty") then - create {SMARTY_CMS_THEME} theme.make (setup, l_info, template) + create {SMARTY_CMS_THEME} theme.make (setup, l_info) else create {DEFAULT_CMS_THEME} theme.make (setup, l_info) end @@ -111,6 +551,54 @@ feature -- Generation do common_prepare (page) custom_prepare (page) + + -- Cms values + call_value_alter_hooks (values) + + -- Values Associated with current Execution object. + across + values as ic + loop + page.register_variable (ic.item, ic.key) + end + + -- Specific values + page.register_variable (request.absolute_script_url (""), "site_url") + + -- Additional lines in + + call_menu_alter_hooks (menu_system) + prepare_menu_system (menu_system) + + get_blocks + across + regions as reg_ic + loop + across + reg_ic.item.blocks as ic + loop + if attached {CMS_MENU_BLOCK} ic.item as l_menu_block then + recursive_get_active (l_menu_block.menu, request) + end + end + end + + if attached title as l_title then + page.set_title (l_title) + else + page.set_title ("CMS::" + request.path_info) + end + + -- blocks + across + regions as reg_ic + loop + across + reg_ic.item.blocks as ic + loop + page.add_to_region (theme.block_html (ic.item), reg_ic.item.name) + end + end end common_prepare (page: CMS_HTML_PAGE) @@ -129,6 +617,65 @@ feature -- Generation end + prepare_menu_system (a_menu_system: CMS_MENU_SYSTEM) + do + across + a_menu_system as c + loop + prepare_links (c.item) + end + end + + prepare_links (a_menu: CMS_LINK_COMPOSITE) + local + to_remove: ARRAYED_LIST [CMS_LINK] + do + create to_remove.make (0) + across + a_menu as c + loop + if attached {CMS_LOCAL_LINK} c.item as lm then +-- if attached lm.permission_arguments as perms and then not has_permissions (perms) then +-- to_remove.force (lm) +-- else + -- if lm.permission_arguments is Void , this is permitted +-- lm.get_is_active (request) + if attached {CMS_LINK_COMPOSITE} lm as comp then + prepare_links (comp) + end +-- end + elseif attached {CMS_LINK_COMPOSITE} c.item as comp then + prepare_links (comp) + end + end + across + to_remove as c + loop + a_menu.remove (c.item) + end + end + + recursive_get_active (a_comp: CMS_LINK_COMPOSITE; req: WSF_REQUEST) + -- Update the active status recursively on `a_comp'. + local + ln: CMS_LINK + do + if attached a_comp.items as l_items then + across + l_items as ic + loop + ln := ic.item + if attached {CMS_LOCAL_LINK} ln as l_local then +-- l_local.get_is_active (request) + end + if (ln.is_expanded or ln.is_collapsed) and then attached {CMS_LINK_COMPOSITE} ln as l_comp then + recursive_get_active (l_comp, req) + end + end + end + end + + feature -- Custom Variables variables: detachable STRING_TABLE[ANY] diff --git a/cms/src/theme/cms_theme.e b/cms/src/theme/cms_theme.e index 4557a61..c970bba 100644 --- a/cms/src/theme/cms_theme.e +++ b/cms/src/theme/cms_theme.e @@ -24,9 +24,18 @@ feature -- Access page_template: CMS_TEMPLATE deferred end - feature -- Conversion + menu_html (a_menu: CMS_MENU; is_horizontal: BOOLEAN): STRING_8 + -- Render Menu as HTML. + -- A theme will define a menu.tpl + deferred + end + + block_html (a_block: CMS_BLOCK): STRING_8 + deferred + end + page_html (page: CMS_HTML_PAGE): STRING_8 -- Render `page' as html. deferred @@ -34,6 +43,67 @@ feature -- Conversion feature {NONE} -- Implementation + append_cms_link_to (lnk: CMS_LINK; s: STRING_8) + local + cl: STRING + do + create cl.make_empty + if lnk.is_active then + cl.append ("active ") + end + if lnk.is_expandable then + cl.append ("expandable ") + end + if lnk.is_expanded then + cl.append ("expanded ") + end + if cl.is_empty then + s.append ("
  • ") + else + s.append ("
  • ") + end +-- s.append ("" + html_encoded (lnk.title) + "") + if +-- (lnk.is_expanded or lnk.is_collapsed) and then + attached lnk.children as l_children + then + s.append ("
      %N") + across + l_children as c + loop + append_cms_link_to (c.item, s) + end + s.append ("
    ") + end + s.append ("
  • ") + end + + +feature -- Encoders + + url_encoded (s: detachable READABLE_STRING_GENERAL): STRING_8 + local + enc: URL_ENCODER + do + create enc + if s /= Void then + Result := enc.general_encoded_string (s) + else + create Result.make_empty + end + end + + html_encoded (s: detachable READABLE_STRING_GENERAL): STRING_8 + local + enc: HTML_ENCODER + do + create enc + if s /= Void then + Result := enc.general_encoded_string (s) + else + create Result.make_empty + end + end note copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" diff --git a/cms/src/theme/default_theme/default_cms_theme.e b/cms/src/theme/default_theme/default_cms_theme.e index dcf4950..a15cc0d 100644 --- a/cms/src/theme/default_theme/default_cms_theme.e +++ b/cms/src/theme/default_theme/default_cms_theme.e @@ -60,7 +60,46 @@ feature -- Conversion prepare (page: CMS_HTML_PAGE) do --- page.add_style (url ("/theme/style.css", Void), Void) + end + + menu_html (a_menu: CMS_MENU; is_horizontal: BOOLEAN): STRING_8 + do + create Result.make_from_string ("
    ") + if is_horizontal then + Result.append ("
      %N") + else + Result.append ("
        %N") + end + across + a_menu as c + loop + append_cms_link_to (c.item, Result) + end + Result.append ("
      %N") + Result.append ("
    ") + end + + block_html (a_block: CMS_BLOCK): STRING_8 + local + s: STRING + do + if attached {CMS_CONTENT_BLOCK} a_block as l_content_block and then l_content_block.is_raw then + create s.make_empty + if attached l_content_block.title as l_title then + s.append ("
    " + html_encoded (l_title) + "
    ") + end + s.append (l_content_block.to_html (Current)) + else + create s.make_from_string ("
    ") + if attached a_block.title as l_title then + s.append ("
    " + html_encoded (l_title) + "
    ") + end + s.append ("
    ") + s.append (a_block.to_html (Current)) + s.append ("
    ") + s.append ("
    ") + end + Result := s end page_html (page: CMS_HTML_PAGE): STRING_8 diff --git a/cms/src/theme/smarty_theme/smarty_cms_theme.e b/cms/src/theme/smarty_theme/smarty_cms_theme.e index 2b2e6a4..1debe62 100644 --- a/cms/src/theme/smarty_theme/smarty_cms_theme.e +++ b/cms/src/theme/smarty_theme/smarty_cms_theme.e @@ -10,16 +10,17 @@ class inherit CMS_THEME + REFACTORING_HELPER + create make feature {NONE} -- Initialization - make (a_setup: like setup; a_info: like information; a_template: like template) + make (a_setup: like setup; a_info: like information;) do setup := a_setup information := a_info - template := a_template if attached a_info.item ("template_dir") as s then templates_directory := a_setup.theme_location.extended (s) else @@ -28,15 +29,12 @@ feature {NONE} -- Initialization ensure setup_set: setup = a_setup information_set: information = a_info - template_set: template = a_template end feature -- Access name: STRING = "smarty-CMS" - template: STRING; - templates_directory: PATH information: CMS_THEME_INFORMATION @@ -59,7 +57,7 @@ feature -- Access i := i + 1 end else - l_regions := <<"header", "content", "footer", "first_sidebar", "second_sidebar">> + l_regions := <<"top","header", "content", "footer", "first_sidebar", "second_sidebar","bottom">> end internaL_regions := l_regions end @@ -72,7 +70,7 @@ feature -- Access do tpl := internal_page_template if tpl = Void then - create tpl.make (template, Current) + create tpl.make ("page", Current) internal_page_template := tpl end Result := tpl @@ -80,6 +78,21 @@ feature -- Access feature -- Conversion + menu_html (a_menu: CMS_MENU; is_horizontal: BOOLEAN): STRING_8 + -- Render Menu as HTML. + -- A theme will define a menu.tpl + do + to_implement ("Add implementation") + Result := "to be implemented" + end + + + block_html (a_block: CMS_BLOCK): STRING_8 + do + to_implement ("Add implementation") + Result := "to be implemented" + end + prepare (page: CMS_HTML_PAGE) do end diff --git a/examples/roc_api/modules/demo/cms_demo_module.e b/examples/roc_api/modules/demo/cms_demo_module.e index aeabe7b..bdbe811 100644 --- a/examples/roc_api/modules/demo/cms_demo_module.e +++ b/examples/roc_api/modules/demo/cms_demo_module.e @@ -90,4 +90,11 @@ feature -- Mapping helper: uri template agent map_uri_template_with_request_methods (a_router, a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods) end + +feature -- Hooks + + register_hooks (a_response: CMS_RESPONSE) + do + end + end diff --git a/examples/roc_api/site/config/cms.ini b/examples/roc_api/site/config/cms.ini index 27ebf1f..05a3774 100644 --- a/examples/roc_api/site/config/cms.ini +++ b/examples/roc_api/site/config/cms.ini @@ -3,4 +3,4 @@ site.name=EWF Web CMS site.email=your@email.com var-dir=var files-dir=files -theme=api +theme=bootstrap diff --git a/examples/roc_api/site/www/themes/api/layout.tpl b/examples/roc_api/site/www/themes/api/layout.tpl new file mode 100644 index 0000000..6025408 --- /dev/null +++ b/examples/roc_api/site/www/themes/api/layout.tpl @@ -0,0 +1,36 @@ + + + + {include file="master2/head.tpl"/} + + + + + {include file="master2/site_navigation.tpl"/} + +
    +
    +
    +
    + {include file="master2/content.tpl"/} +
    +
    +
    + + + + {if condition="$web"} + {include file="master2/optional_enhancement_js.tpl"/} + {/if} + + {if condition="$html"} + {include file="master2/optional_enhancement_js.tpl"/} + {/if} + + + + \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/bootstrap.7z b/examples/roc_api/site/www/themes/bootstrap.7z new file mode 100644 index 0000000000000000000000000000000000000000..45c941decd6106e4b114e1d7b783924de174214d GIT binary patch literal 2576 zcmV+r3h(tddc3bE8~_8R1VBj52><{90000Z0000000002DVy5>9tcGS(C?@y7g^b< z_3Ec3KE-SU+(51|DEj{Jb*Xm>EK3AK#$20c(M9hS4eBCS$#-k2m@!U0u)?St?(5l? z+J#Qq(R0L!^oXpcSq(LVJ2>W+I%noD=J}g@+KVUX-oB)0#YL*xY7M6Y8y@#j+lS8J zuQ_u<%)3{4^zTxPUcfXA&9na84S!S*mH9R3Pbn9zfC7_T;bHyBM5BXrb+JQvyQSP- zvJmD{7XiIlwe!$*9RO@KTl_d8DhnY2&zeN%F?$2tz2c-$@gHfJFWRqToojIi$1hh0 z($Eh&<=G${%Gk)p6At9TQ}~j^b$&peJ1K?k;S_yX{4?uP5MaAWf|Q1v+0j%~7wKGg zMv(Rr2(z{BoWa-dCdz)JmPk6MK^;SkUVRaYz-})p>|%;FkpyLskBaniTjlO`j}2`k z9m_Y987gMNtR&_^445k+Cr$kP&x6m)y96Q2p#5TBnfjcNj*>_Wkne!iJmI2?b9&6iXtg+mU6Q=BYXGwHitJY_Q6 zF=s%r%Za15gSU3fr(01CYuhrD<54l+jeprX)GY{BSN#3~Xc_Nqy&ndZI0n|WL`0WI z_ExbN2!?_dt-Tom&!Vy%QZ7#r7c&a!yESMPk%}sJwOrjt%gAp;fv-C>Rq$RQ0+Qmg0X4%i!~Zg)Tx5Wk_};uyp0H_IhGAfm^4t? zzk{Kl!#U<}J!}sS33yyyLJWQ`eYIOkcxW&kDn?9-Z@YtAh&&qvNt(4HPAasE`t4Tt zO51jPn6fK~9)#RKGmf!Q5~I=BH!#>TMqR}}D-18`-tSx^lhPqRWr1Uq!wa_7L7t3g zi%sGU=1;4#_hhtTcb$2BFk8mId>A! zZtS8edApwPczvAXv(Uq%26{bY`tCJY-o#%FFYHK>&2;s@U*RgIfmBLw_07ASn3Vk-LQ6GlDOit{9Bs8ve7w$nGg z+06rSY(bWq2|JSp)s5o1bz&#Ige8f+*m=7htIi`e=0VHrMjLV_6$YN!!i&zIGrD=e znNWFLAk<}(l%a{^oAev3$y_i3cJm{jPIuUmZX{X8I26I>UK9;=H<@2H?_1}epO}Zy z1Hh!>FX7i=*S;zCqI(MVPb+0oHZIj$45Wtow(c|b7405Ig&5GIo!QK#qNZx+HdrW3 z-d0Y8#>0f){eK#3jtJx89}y9-D>{vul^H7A1Mw8kxn-@(<+Gi3tHeCK6}k`WOG*wz zB;}al36%cYEO(^0s18z27ZOJ~3!uNQTors?EwSm((|RY+ysUbqBw>{^1jAbVvpbaW zr8~9uX%TBn%U>n1a@rAes`-KnU@- zPKbM)Iv%uTQMn+Vt;0We5P2M7cAUjvg-;-H3#NkZ&9q(fVN$n?v|=8N`XH9O&v79x z!e)>z4Hfuw_sj>8d~*vakZd}rAuT2KILntbZ*PL^t1RA58_|D-rH{q1$(G46kH<28 z?S8MpAQI`uwK~OYpyHS^QIPYs#dxHRZ#(D2U?oo3;W4+$_Mt^nxmAcfCc=Rx9ZTty zb0m&Z#+Zk@sXNi&;(^tDbRM(~RzLH{BoTVbj_$(3)()znx2Ogp61R=com%-@DRlX> zmm!IwR~PiOz(6%-YQ#NmiH$$xWb+HVs{GvAQP?xH_r&d?hK7~{9qh}r@I%v6v)`oNUOvW;}Xy{H7e=_furW6bm!L8U^OCydyt*3m1)Ct?fX6HXL zs{4e;w0e(Oq;klui%08`K5j~Cn1!d`wi_I?I9JLUZei6yA%upS2Twyp(1C1P zq!GRjXNNT8h6N&6PUDq6){*ZsK)VDfnH-8^;zInzYZdkw+b*=5!o7jhi>f}0M9!(u z`bhgi1U3^L2A$^FIzPnt_4WANUW&pS|KF)mXpmHf=Ttb{!xK2%-&F}(%=1D%RJGsS z3gVVa@C8r2X@;w(s7W9j36^5!E6CFKegDOKEJ7(=v{ENfQ8tivcZ(Bkh&6qfwB}NbVUh`gdU7=R$>F+}% zjqnqs2|ESrfTC7pVcNiRz(Y2EpCs1%tFp}9n4C8EV;c`kB5u#TzH4XhL+U0{iJ3F; zG01kCi9Jr+5F8zTAGrOP0yL!SepAgqP9@P2%a|cl-q&cW^JiQxz6V-3S*IR_E8PRO z{pd28_!6_GS#3L6VTvdqxC>5#mZP(x=GP_%P@>k4QFir;Nlq8%w&ulFbKdRlH19-mOASNM$okJeo_q0u_#K6`;ZAD({SK&z?CQ`p0p= zk-q#b5~`&B!jSSI$=Xth7oC5NEd4z>-^>t4lPNR}8nw-~K{Pd*25BHIXgS`#cr(y7 zxR1hG-pwq}12jZE1{doT(3X;x5a%PiFMj8{?YHiT$^v^Qa{0MTZKgmjn>Hq=GTi9k z8}Fq#qUbsWQv*Mf%=-X*si1{Zs`Ba*TwnjNlKT~=kof6TDRWZoM^xGrgQCJwp?dZj z1oAvUq)|NAy>queF?Vbw(+FUFTbB?=ccjbhhRLLzCQgy3+IEGy305p6qe?5zXqoo8 zotTxAh36SEeGT*iE^I!`!{wRlrO85ME8yodrqgs|^hO19UR!Ay;}nhbWmli$a!fps zMo`?!0i2*V?mt=$ByAiHciIIvp)UFuYtbzFQg>(&I_GXA-K9}c74s)6WAbsJqCQ6H m7Y2w`0SSS400#>J00AQd0RaVF01yBG41@;?0cX3}!2kek%Il&4 literal 0 HcmV?d00001 diff --git a/examples/roc_api/site/www/themes/bootstrap/layout.tpl b/examples/roc_api/site/www/themes/bootstrap/layout.tpl new file mode 100644 index 0000000..c2ed53f --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/layout.tpl @@ -0,0 +1,64 @@ + + + + + ROC- Layout with defualt Regions + + + + + + + + +ROC CMS - A responsive layout + + + + {include file="tpl/page_top.tpl"/} + + +
    + + + {include file="tpl/page_header.tpl"/} + + +
    + + + + {include file="tpl/left_sidebar.tpl"/} + + +
    + + {include file="tpl/highlighted_section.tpl"/} + + + + {include file="tpl/help_section.tpl"/} + + + + {include file="tpl/main_content.tpl"/} +
    + + + {include file="tpl/right_sidebar.tpl"/} +
    +
    + + + {include file="tpl/page_footer.tpl"/} + + + {include file="tpl/page_bottom.tpl"/} + + + + diff --git a/examples/roc_api/site/www/themes/bootstrap/page.tpl b/examples/roc_api/site/www/themes/bootstrap/page.tpl new file mode 100644 index 0000000..36952f5 --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/page.tpl @@ -0,0 +1,64 @@ + + + + + ROC- Layout with defualt Regions + + + + + + + + +{$title/} + + + + {$region_top/} + + +
    + + + {$region_header/} + + +
    + + + + {$region_sidebar_first/} + + +
    + + {$region_highlighted/} + + + + {$region_help/} + + + + {$region_main/} +
    + + + {$region_sidebar_second/} +
    +
    + + + {$region_footer/} + + + {$region_bottom/} + + + + diff --git a/examples/roc_api/site/www/themes/bootstrap/roc_template.html b/examples/roc_api/site/www/themes/bootstrap/roc_template.html new file mode 100644 index 0000000..83d6149 --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/roc_template.html @@ -0,0 +1,86 @@ + + + + + ROC- Layout with defualt Regions + + + + + + + + +ROC CMS - A responsive layout + + + + +
    +

    ROC Layout with Defaul Regions

    + +
    + + + + + + + +
    +

    Highlighted Section

    +

    Help Section

    + +

    Main Content Section

    +

    Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.

    + +

    Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.

    +
    + + + +
    +
    + + + + + + + + + diff --git a/examples/roc_api/site/www/themes/bootstrap/theme.info b/examples/roc_api/site/www/themes/bootstrap/theme.info new file mode 100644 index 0000000..8812944 --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/theme.info @@ -0,0 +1,13 @@ +name=bootstrap +engine=smarty +author=jvelilla +version=0.1 +regions[page_top] = Top +regions[header] = Header +regions[content] = Content +regions[highlighted] = Highlighted +regions[help] = Help +regions[footer] = Footer +regions[first_sidebar] = first sidebar +regions[second_sidebar] = second sidebar +regions[page_bottom] = Bottom \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/help_section.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/help_section.tpl new file mode 100644 index 0000000..b20cb47 --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/tpl/help_section.tpl @@ -0,0 +1 @@ +

    Help Section

    \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/highlighted_section.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/highlighted_section.tpl new file mode 100644 index 0000000..fc75c0c --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/tpl/highlighted_section.tpl @@ -0,0 +1 @@ +

    Highlighted Section

    diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/left_sidebar.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/left_sidebar.tpl new file mode 100644 index 0000000..ee2ee4c --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/tpl/left_sidebar.tpl @@ -0,0 +1,8 @@ + diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/main_content.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/main_content.tpl new file mode 100644 index 0000000..0df8def --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/tpl/main_content.tpl @@ -0,0 +1,4 @@ +

    Main Content Section

    +

    Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.

    + +

    Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.

    \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/page_bottom.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/page_bottom.tpl new file mode 100644 index 0000000..e69de29 diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/page_footer.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/page_footer.tpl new file mode 100644 index 0000000..9c7ee8e --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/tpl/page_footer.tpl @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/page_header.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/page_header.tpl new file mode 100644 index 0000000..4cd6c4b --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/tpl/page_header.tpl @@ -0,0 +1,10 @@ +

    ROC Layout with Default Regions

    + \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/page_top.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/page_top.tpl new file mode 100644 index 0000000..e69de29 diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/right_sidebar.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/right_sidebar.tpl new file mode 100644 index 0000000..b0f6904 --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/tpl/right_sidebar.tpl @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/model/src/cms_link.e b/model/src/cms_link.e index 7606366..70aca33 100644 --- a/model/src/cms_link.e +++ b/model/src/cms_link.e @@ -29,13 +29,22 @@ feature -- status report end is_expanded: BOOLEAN + -- Is expanded and visually expanded? deferred end + is_collapsed: BOOLEAN + -- Is expanded, but visually collapsed? + deferred + ensure + Result implies is_expandable + end + is_expandable: BOOLEAN + -- Is expandable? deferred end - + has_children: BOOLEAN deferred end diff --git a/model/src/cms_local_link.e b/model/src/cms_local_link.e index 9633fc0..f6bd165 100644 --- a/model/src/cms_local_link.e +++ b/model/src/cms_local_link.e @@ -40,6 +40,12 @@ feature -- Status report Result := is_expandable and then internal_is_expanded end + is_collapsed: BOOLEAN + -- Is expanded, but visually collapsed? + do + Result := is_expandable and then internal_is_collapsed + end + is_expandable: BOOLEAN do Result := internal_is_expandable or internal_is_expanded or has_children @@ -58,6 +64,8 @@ feature -- Status report internal_is_expanded: BOOLEAN + internal_is_collapsed: BOOLEAN + feature -- Element change add_link (lnk: CMS_LINK) @@ -90,11 +98,24 @@ feature -- Element change children := lst end + set_expanded (b: like is_expanded) do + if b then + set_expandable (True) + set_collapsed (False) + end internal_is_expanded := b end + set_collapsed (b: like is_collapsed) + do + if b then + set_expanded (False) + end + internal_is_collapsed := b + end + set_expandable (b: like is_expandable) do internal_is_expandable := b From bafdc085d9d7f2e43121f0e28b5489dfe1ce0d1d Mon Sep 17 00:00:00 2001 From: jvelilla Date: Tue, 4 Nov 2014 11:31:43 -0300 Subject: [PATCH 037/386] Added refactoring helper class to add comments. --- cms/src/service/response/cms_response.e | 8 +- cms/src/theme/cms_html_page.e | 97 ++++++++++--------- cms/src/theme/cms_theme.e | 12 ++- cms/src/theme/cms_theme_information.e | 2 +- .../smarty_theme/smarty_cms_page_template.e | 2 +- cms/src/theme/smarty_theme/smarty_cms_theme.e | 38 ++++++-- .../site/www/themes/bootstrap/page.tpl | 2 +- model/src/cms_local_link.e | 2 - 8 files changed, 99 insertions(+), 64 deletions(-) diff --git a/cms/src/service/response/cms_response.e b/cms/src/service/response/cms_response.e index 97a5a65..56500a0 100644 --- a/cms/src/service/response/cms_response.e +++ b/cms/src/service/response/cms_response.e @@ -143,7 +143,7 @@ feature -- Blocks initialization -- FIXME: let the user choose ... create l_table.make_caseless (10) - l_table["page_top"] := "top" + l_table["top"] := "top" l_table["header"] := "header" l_table["highlighted"] := "highlighted" l_table["help"] := "help" @@ -152,7 +152,7 @@ feature -- Blocks initialization l_table["management"] := "first_sidebar" l_table["navigation"] := "first_sidebar" l_table["user"] := "first_sidebar" - l_table["page_bottom"] := "page_bottom" + l_table["bottom"] := "page_bottom" block_region_settings := l_table end @@ -275,13 +275,13 @@ feature -- Blocks local s: STRING do - fixme ("Avoid Hardcoded HTML") + fixme ("Avoid Hardcoded HTML!!!") -- create s.make_from_string ("
    " + html_encoded (site_name) + "
    ") create s.make_empty s.append ("
    ") s.append (theme.menu_html (main_menu, True)) s.append ("
    ") - create Result.make ("top_header", Void, s, formats.full_html) + create Result.make ("header", Void, s, formats.full_html) Result.set_is_raw (True) end diff --git a/cms/src/theme/cms_html_page.e b/cms/src/theme/cms_html_page.e index 5c6a5c0..1565d07 100644 --- a/cms/src/theme/cms_html_page.e +++ b/cms/src/theme/cms_html_page.e @@ -1,7 +1,8 @@ note description: "Summary description for {CMS_HTML_PAGE}." - date: "$Date$" - revision: "$Revision$" + author: "" + date: "$Date: 2014-11-04 09:57:24 -0300 (ma. 04 de nov. de 2014) $" + revision: "$Revision: 96034 $" class CMS_HTML_PAGE @@ -23,6 +24,7 @@ feature {NONE} -- Initialization do create regions.make (5) language := "en" + status_code := {HTTP_STATUS_CODE}.ok create header.make create {ARRAYED_LIST [STRING]} head_lines.make (5) @@ -65,7 +67,7 @@ feature -- Header feature -- Region - regions: HASH_TABLE [STRING_8, STRING_8] + regions: STRING_TABLE [STRING_8] -- header -- content -- footer @@ -83,21 +85,6 @@ feature -- Region end end - header_region: STRING_8 - do - Result := region ("header") - end - - content_region: STRING_8 - do - Result := region ("content") - end - - footer_region: STRING_8 - do - Result := region ("content") - end - feature -- Element change register_variable (a_value: detachable ANY; k: READABLE_STRING_GENERAL) @@ -118,41 +105,11 @@ feature -- Element change end end - add_to_header_region (s: STRING) - do - add_to_region (s, "header") - end - - add_to_content_region (s: STRING) - do - add_to_region (s, "content") - end - - add_to_footer_region (s: STRING) - do - add_to_region (s, "footer") - end - set_region (s: STRING; k: STRING) do regions.force (s, k) end - set_header_region (s: STRING) - do - set_region (s, "header") - end - - set_content_region (s: STRING) - do - set_region (s, "content") - end - - set_footer_region (s: STRING) - do - set_region (s, "footer") - end - feature -- Element change set_status_code (c: like status_code) @@ -170,6 +127,50 @@ feature -- Element change title := s end + add_meta_name_content (a_name: STRING; a_content: STRING) + local + s: STRING_8 + do + s := "" + head_lines.extend (s) + end + + add_meta_http_equiv (a_http_equiv: STRING; a_content: STRING) + local + s: STRING_8 + do + s := "" + head_lines.extend (s) + end + + add_style (a_href: STRING; a_media: detachable STRING) + local + s: STRING_8 + do + s := "") + head_lines.extend (s) + end + + add_javascript_url (a_src: STRING) + local + s: STRING_8 + do + s := "" + head_lines.extend (s) + end + + add_javascript_content (a_script: STRING) + local + s: STRING_8 + do + s := "" + head_lines.extend (s) + end + note copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/cms/src/theme/cms_theme.e b/cms/src/theme/cms_theme.e index c970bba..441f925 100644 --- a/cms/src/theme/cms_theme.e +++ b/cms/src/theme/cms_theme.e @@ -1,12 +1,16 @@ note description: "Summary description for {WSF_CMS_THEME}." - author: "" date: "$Date$" revision: "$Revision$" deferred class CMS_THEME +inherit + + REFACTORING_HELPER + + feature {NONE} -- Access setup: CMS_SETUP @@ -14,16 +18,20 @@ feature {NONE} -- Access feature -- Access name: STRING + -- theme name. deferred end regions: ARRAY [STRING] + -- theme's regions. deferred end page_template: CMS_TEMPLATE + -- theme template page. deferred end + feature -- Conversion menu_html (a_menu: CMS_MENU; is_horizontal: BOOLEAN): STRING_8 @@ -33,6 +41,7 @@ feature -- Conversion end block_html (a_block: CMS_BLOCK): STRING_8 + -- Render a block as HTML. deferred end @@ -47,6 +56,7 @@ feature {NONE} -- Implementation local cl: STRING do + fixme ("Remove HTML from Eiffel") create cl.make_empty if lnk.is_active then cl.append ("active ") diff --git a/cms/src/theme/cms_theme_information.e b/cms/src/theme/cms_theme_information.e index ee0800a..719b3c1 100644 --- a/cms/src/theme/cms_theme_information.e +++ b/cms/src/theme/cms_theme_information.e @@ -18,7 +18,7 @@ feature {NONE} -- Initialization create items.make_caseless (1) create regions.make (5) across - (<<"header", "content", "footer", "first_sidebar", "second_sidebar">>) as ic + (<<"top","header", "highlighted","help", "content", "footer", "first_sidebar", "second_sidebar", "bottom">>) as ic loop regions.force (ic.item, ic.item) end diff --git a/cms/src/theme/smarty_theme/smarty_cms_page_template.e b/cms/src/theme/smarty_theme/smarty_cms_page_template.e index b02fe15..188a743 100644 --- a/cms/src/theme/smarty_theme/smarty_cms_page_template.e +++ b/cms/src/theme/smarty_theme/smarty_cms_page_template.e @@ -55,7 +55,7 @@ feature -- Access across theme.regions as r loop - variables.force (page.region (r.item), r.item) + variables.force (page.region (r.item), "region_" + r.item) end end diff --git a/cms/src/theme/smarty_theme/smarty_cms_theme.e b/cms/src/theme/smarty_theme/smarty_cms_theme.e index 1debe62..88ee5aa 100644 --- a/cms/src/theme/smarty_theme/smarty_cms_theme.e +++ b/cms/src/theme/smarty_theme/smarty_cms_theme.e @@ -10,8 +10,6 @@ class inherit CMS_THEME - REFACTORING_HELPER - create make @@ -82,15 +80,43 @@ feature -- Conversion -- Render Menu as HTML. -- A theme will define a menu.tpl do - to_implement ("Add implementation") - Result := "to be implemented" + create Result.make_from_string ("
    ") + if is_horizontal then + Result.append ("
      %N") + else + Result.append ("
        %N") + end + across + a_menu as c + loop + append_cms_link_to (c.item, Result) + end + Result.append ("
      %N") + Result.append ("
    ") end block_html (a_block: CMS_BLOCK): STRING_8 + local + s: STRING do - to_implement ("Add implementation") - Result := "to be implemented" + if attached {CMS_CONTENT_BLOCK} a_block as l_content_block and then l_content_block.is_raw then + create s.make_empty + if attached l_content_block.title as l_title then + s.append ("
    " + html_encoded (l_title) + "
    ") + end + s.append (l_content_block.to_html (Current)) + else + create s.make_from_string ("
    ") + if attached a_block.title as l_title then + s.append ("
    " + html_encoded (l_title) + "
    ") + end + s.append ("
    ") + s.append (a_block.to_html (Current)) + s.append ("
    ") + s.append ("
    ") + end + Result := s end prepare (page: CMS_HTML_PAGE) diff --git a/examples/roc_api/site/www/themes/bootstrap/page.tpl b/examples/roc_api/site/www/themes/bootstrap/page.tpl index 36952f5..54e6aaa 100644 --- a/examples/roc_api/site/www/themes/bootstrap/page.tpl +++ b/examples/roc_api/site/www/themes/bootstrap/page.tpl @@ -13,7 +13,7 @@ -{$title/} +{$page_title/} diff --git a/model/src/cms_local_link.e b/model/src/cms_local_link.e index f6bd165..876b47e 100644 --- a/model/src/cms_local_link.e +++ b/model/src/cms_local_link.e @@ -98,7 +98,6 @@ feature -- Element change children := lst end - set_expanded (b: like is_expanded) do if b then @@ -121,7 +120,6 @@ feature -- Element change internal_is_expandable := b end - set_permission_arguments (args: like permission_arguments) do permission_arguments := args From b90e3b3df0a9eff7fd156b4781ef7f7a16209343 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Wed, 5 Nov 2014 07:45:36 -0300 Subject: [PATCH 038/386] Added a CMS_ENCODER utility class. Added a block header template: smarty engine. Added a menu template: smarty engine. --- cms/src/kernel/content/cms_encoders.e | 35 +++++++++ cms/src/modules/node/node_module.e | 4 +- cms/src/service/response/cms_response.e | 76 +++++++++++++++---- cms/src/theme/cms_theme.e | 28 +------ cms/src/theme/smarty_theme/smarty_cms_theme.e | 27 +++---- .../themes/bootstrap/block/header_block.tpl | 7 ++ .../site/www/themes/bootstrap/page.tpl | 4 +- .../site/www/themes/bootstrap/tpl/menu.tpl | 3 + .../www/themes/bootstrap/tpl/page_header.tpl | 2 +- 9 files changed, 126 insertions(+), 60 deletions(-) create mode 100644 cms/src/kernel/content/cms_encoders.e create mode 100644 examples/roc_api/site/www/themes/bootstrap/block/header_block.tpl create mode 100644 examples/roc_api/site/www/themes/bootstrap/tpl/menu.tpl diff --git a/cms/src/kernel/content/cms_encoders.e b/cms/src/kernel/content/cms_encoders.e new file mode 100644 index 0000000..36b60fc --- /dev/null +++ b/cms/src/kernel/content/cms_encoders.e @@ -0,0 +1,35 @@ +note + description: "Summary description for {CMS_ENCODERS}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CMS_ENCODERS + +feature -- Encoders + + url_encoded (s: detachable READABLE_STRING_GENERAL): STRING_8 + local + enc: URL_ENCODER + do + create enc + if s /= Void then + Result := enc.general_encoded_string (s) + else + create Result.make_empty + end + end + + html_encoded (s: detachable READABLE_STRING_GENERAL): STRING_8 + local + enc: HTML_ENCODER + do + create enc + if s /= Void then + Result := enc.general_encoded_string (s) + else + create Result.make_empty + end + end +end diff --git a/cms/src/modules/node/node_module.e b/cms/src/modules/node/node_module.e index f435b04..e1e59a5 100644 --- a/cms/src/modules/node/node_module.e +++ b/cms/src/modules/node/node_module.e @@ -152,8 +152,8 @@ feature -- Hooks lnk: CMS_LOCAL_LINK perms: detachable ARRAYED_LIST [READABLE_STRING_8] do - create lnk.make ("node", "/node") - a_menu_system.navigation_menu.extend (lnk) + create lnk.make ("List of nodes", "/nodes") + a_menu_system.main_menu.extend (lnk) end end diff --git a/cms/src/service/response/cms_response.e b/cms/src/service/response/cms_response.e index 56500a0..8927719 100644 --- a/cms/src/service/response/cms_response.e +++ b/cms/src/service/response/cms_response.e @@ -7,6 +7,7 @@ deferred class CMS_RESPONSE inherit + CMS_ENCODERS CMS_REQUEST_UTIL @@ -139,9 +140,10 @@ feature -- Blocks initialization local l_table: like block_region_settings do + fixme ("CHECK:Can we use the same structure as in theme.info?") create regions.make_caseless (5) - -- FIXME: let the user choose ... + fixme ("let the user choose ...") create l_table.make_caseless (10) l_table["top"] := "top" l_table["header"] := "header" @@ -202,7 +204,7 @@ feature -- Blocks get_blocks do fixme ("find a way to have this in configuration or database, and allow different order") - add_block (top_header_block, "header") + add_block (top_header_block, "top") add_block (header_block, "header") if attached message_block as m then add_block (m, "content") @@ -275,22 +277,46 @@ feature -- Blocks local s: STRING do - fixme ("Avoid Hardcoded HTML!!!") - -- create s.make_from_string ("
    " + html_encoded (site_name) + "
    ") create s.make_empty - s.append ("
    ") - s.append (theme.menu_html (main_menu, True)) - s.append ("
    ") - create Result.make ("header", Void, s, formats.full_html) + create Result.make ("page_top", Void, s, formats.full_html) Result.set_is_raw (True) end header_block: CMS_CONTENT_BLOCK local s: STRING + tpl: SMARTY_CMS_PAGE_TEMPLATE + l_page: CMS_HTML_PAGE + l_hb: STRING do - create s.make_empty - create Result.make ("header", Void, s, formats.full_html) + create s.make_from_string (theme.menu_html (main_menu, True)) + create l_hb.make_empty + to_implement ("Check if the tpl does not exist, provide a default implementation") + if attached {SMARTY_CMS_THEME} theme as l_theme then + create tpl.make ("block/header_test", l_theme) + -- Change to "block/header_block" to use the template code. + create l_page.make + l_page.register_variable (s, "header_block") + tpl.prepare (l_page) + l_hb := tpl.to_html (l_page) + end + if l_hb.starts_with ("ERROR") then + l_hb.wipe_out + to_implement ("Hardcoded HTML with CSS, find a better way to define defaults!!!.") + l_hb := "[ + + ]") + + end + create Result.make ("header", Void, l_hb, formats.full_html) Result.set_is_raw (True) end @@ -447,6 +473,25 @@ feature -- Hook: block end end + +feature -- Menu: change + + add_to_main_menu (lnk: CMS_LINK) + do +-- if attached {CMS_LOCAL_LINK} lnk as l_local then +-- l_local.get_is_active (request) +-- end + main_menu.extend (lnk) + end + + add_to_menu (lnk: CMS_LINK; m: CMS_MENU) + do +-- if attached {CMS_LOCAL_LINK} lnk as l_local then +-- l_local.get_is_active (request) +-- end + m.extend (lnk) + end + feature -- Message add_message (a_msg: READABLE_STRING_8; a_category: detachable READABLE_STRING_8) @@ -567,6 +612,7 @@ feature -- Generation -- Additional lines in + add_to_main_menu (create {CMS_LOCAL_LINK}.make ("Home", "/")) call_menu_alter_hooks (menu_system) prepare_menu_system (menu_system) @@ -619,11 +665,11 @@ feature -- Generation prepare_menu_system (a_menu_system: CMS_MENU_SYSTEM) do - across - a_menu_system as c - loop - prepare_links (c.item) - end +-- across +-- a_menu_system as c +-- loop +-- prepare_links (c.item) +-- end end prepare_links (a_menu: CMS_LINK_COMPOSITE) diff --git a/cms/src/theme/cms_theme.e b/cms/src/theme/cms_theme.e index 441f925..14b1d9d 100644 --- a/cms/src/theme/cms_theme.e +++ b/cms/src/theme/cms_theme.e @@ -7,6 +7,7 @@ deferred class CMS_THEME inherit + CMS_ENCODERS REFACTORING_HELPER @@ -88,33 +89,6 @@ feature {NONE} -- Implementation s.append ("") end - -feature -- Encoders - - url_encoded (s: detachable READABLE_STRING_GENERAL): STRING_8 - local - enc: URL_ENCODER - do - create enc - if s /= Void then - Result := enc.general_encoded_string (s) - else - create Result.make_empty - end - end - - html_encoded (s: detachable READABLE_STRING_GENERAL): STRING_8 - local - enc: HTML_ENCODER - do - create enc - if s /= Void then - Result := enc.general_encoded_string (s) - else - create Result.make_empty - end - end - note copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/cms/src/theme/smarty_theme/smarty_cms_theme.e b/cms/src/theme/smarty_theme/smarty_cms_theme.e index 88ee5aa..39ea5df 100644 --- a/cms/src/theme/smarty_theme/smarty_cms_theme.e +++ b/cms/src/theme/smarty_theme/smarty_cms_theme.e @@ -79,20 +79,21 @@ feature -- Conversion menu_html (a_menu: CMS_MENU; is_horizontal: BOOLEAN): STRING_8 -- Render Menu as HTML. -- A theme will define a menu.tpl + local + tpl: SMARTY_CMS_PAGE_TEMPLATE + l_page: CMS_HTML_PAGE do - create Result.make_from_string ("
    ") - if is_horizontal then - Result.append ("
      %N") - else - Result.append ("
        %N") - end - across - a_menu as c - loop - append_cms_link_to (c.item, Result) - end - Result.append ("
      %N") - Result.append ("
    ") + to_implement ("Proof of concept to load a Menu from a tpl/menu.tpl.") + to_implement ("In this case the template only take care of links.") + to_implement ("Maybe we need a SMARTY_CMS_REGION_TEMPLATE") + to_implement ("Provide a default Menu using HTML hardcoded, maybe using the Default or providing a default implementation in CMS_THEME.menu_html") + -- Use the similar pattern to SMARTY_CMS_PAGE_TEMPLATE, with a different prepare + -- feature. + create tpl.make ("tpl/menu", Current) + create l_page.make + l_page.register_variable (a_menu, "menu") + tpl.prepare (l_page) + Result := tpl.to_html (l_page) end diff --git a/examples/roc_api/site/www/themes/bootstrap/block/header_block.tpl b/examples/roc_api/site/www/themes/bootstrap/block/header_block.tpl new file mode 100644 index 0000000..4a3b24c --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/block/header_block.tpl @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/bootstrap/page.tpl b/examples/roc_api/site/www/themes/bootstrap/page.tpl index 54e6aaa..8ee484d 100644 --- a/examples/roc_api/site/www/themes/bootstrap/page.tpl +++ b/examples/roc_api/site/www/themes/bootstrap/page.tpl @@ -23,8 +23,8 @@
    - {$region_header/} - + {$region_header/} +
    diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/menu.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/menu.tpl new file mode 100644 index 0000000..c91d22c --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/tpl/menu.tpl @@ -0,0 +1,3 @@ + {foreach item="item" from="$menu.items"} +
  • {$item.title/}
  • + {/foreach} diff --git a/examples/roc_api/site/www/themes/bootstrap/tpl/page_header.tpl b/examples/roc_api/site/www/themes/bootstrap/tpl/page_header.tpl index 4cd6c4b..b0c3d8f 100644 --- a/examples/roc_api/site/www/themes/bootstrap/tpl/page_header.tpl +++ b/examples/roc_api/site/www/themes/bootstrap/tpl/page_header.tpl @@ -1,4 +1,4 @@ -

    ROC Layout with Default Regions

    +

    {$page_title/}

    +
    ]") end @@ -616,18 +639,19 @@ feature -- Generation call_menu_alter_hooks (menu_system) prepare_menu_system (menu_system) - get_blocks - across - regions as reg_ic - loop - across - reg_ic.item.blocks as ic - loop - if attached {CMS_MENU_BLOCK} ic.item as l_menu_block then - recursive_get_active (l_menu_block.menu, request) - end - end - end + set_blocks (page) +-- get_blocks +-- across +-- regions as reg_ic +-- loop +-- across +-- reg_ic.item.blocks as ic +-- loop +-- if attached {CMS_MENU_BLOCK} ic.item as l_menu_block then +-- recursive_get_active (l_menu_block.menu, request) +-- end +-- end +-- end if attached title as l_title then page.set_title (l_title) @@ -635,16 +659,16 @@ feature -- Generation page.set_title ("CMS::" + request.path_info) end - -- blocks - across - regions as reg_ic - loop - across - reg_ic.item.blocks as ic - loop - page.add_to_region (theme.block_html (ic.item), reg_ic.item.name) - end - end +-- -- blocks +-- across +-- regions as reg_ic +-- loop +-- across +-- reg_ic.item.blocks as ic +-- loop +-- page.add_to_region (theme.block_html (ic.item), reg_ic.item.name) +-- end +-- end end common_prepare (page: CMS_HTML_PAGE) diff --git a/cms/src/theme/smarty_theme/smarty_cms_theme.e b/cms/src/theme/smarty_theme/smarty_cms_theme.e index 39ea5df..1eb14f1 100644 --- a/cms/src/theme/smarty_theme/smarty_cms_theme.e +++ b/cms/src/theme/smarty_theme/smarty_cms_theme.e @@ -88,7 +88,7 @@ feature -- Conversion to_implement ("Maybe we need a SMARTY_CMS_REGION_TEMPLATE") to_implement ("Provide a default Menu using HTML hardcoded, maybe using the Default or providing a default implementation in CMS_THEME.menu_html") -- Use the similar pattern to SMARTY_CMS_PAGE_TEMPLATE, with a different prepare - -- feature. + -- feature create tpl.make ("tpl/menu", Current) create l_page.make l_page.register_variable (a_menu, "menu") diff --git a/examples/roc_api/site/www/template/html/layout2.tpl b/examples/roc_api/site/www/template/html/layout2.tpl deleted file mode 100644 index 34a5659..0000000 --- a/examples/roc_api/site/www/template/html/layout2.tpl +++ /dev/null @@ -1,40 +0,0 @@ - - - - {include file="master2/head.tpl"/} - - - - - {include file="master2/site_navigation.tpl"/} - - - -
    -
    -
    -
    - {include file="master2/content.tpl"/} -
    -
    -
    - - - - {if condition="$web"} - {include file="master2/optional_enhancement_js.tpl"/} - {/if} - - {if condition="$html"} - {include file="master2/optional_enhancement_js.tpl"/} - {/if} - - - - \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/content.tpl b/examples/roc_api/site/www/template/html/master2/content.tpl deleted file mode 100644 index b18c5dd..0000000 --- a/examples/roc_api/site/www/template/html/master2/content.tpl +++ /dev/null @@ -1,11 +0,0 @@ - -
    -

    Top most recent nodes


    - - - {foreach from="$nodes" item="item"} - - {/foreach} -
    diff --git a/examples/roc_api/site/www/template/html/master2/error.tpl b/examples/roc_api/site/www/template/html/master2/error.tpl deleted file mode 100644 index 0ca7ad0..0000000 --- a/examples/roc_api/site/www/template/html/master2/error.tpl +++ /dev/null @@ -1,18 +0,0 @@ -

    Error: {$code/}

    - -{assign name="status400" value="400"/} -{assign name="status404" value="404"/} -{assign name="status500" value="500"/} - -{if condition="$code ~ $status500"} -

    Internal server error, for the request {$request/}

    -{/if} - - -{if condition="$code ~ $status404"} -

    Resourse not found, for the request {$request/}

    -{/if} - -{if condition="$code ~ $status400"} -

    Bad request, the request {$request/} is not valid

    -{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/footer.tpl b/examples/roc_api/site/www/template/html/master2/footer.tpl deleted file mode 100644 index 11fe183..0000000 --- a/examples/roc_api/site/www/template/html/master2/footer.tpl +++ /dev/null @@ -1,7 +0,0 @@ - -
    -

    API Documentation     - Questions? Comments? Let us know!

    -

    © Copyright 2014 Eiffel Software -- Privacy Policy -

    -
    \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/head.tpl b/examples/roc_api/site/www/template/html/master2/head.tpl deleted file mode 100644 index 4ba4616..0000000 --- a/examples/roc_api/site/www/template/html/master2/head.tpl +++ /dev/null @@ -1,10 +0,0 @@ - - -Eiffel RESTonCMS -{if condition="$web"} - {include file="master2/optional_styling_css.tpl"/} -{/if} -{if condition="$html"} - {include file="master2/optional_styling_css.tpl"/} -{/if} - diff --git a/examples/roc_api/site/www/template/html/master2/header.tpl b/examples/roc_api/site/www/template/html/master2/header.tpl deleted file mode 100644 index ce8ec71..0000000 --- a/examples/roc_api/site/www/template/html/master2/header.tpl +++ /dev/null @@ -1,2 +0,0 @@ -

    RESTonCMS

    -

    Tagline

    \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/logoff.tpl b/examples/roc_api/site/www/template/html/master2/logoff.tpl deleted file mode 100644 index 8139dc7..0000000 --- a/examples/roc_api/site/www/template/html/master2/logoff.tpl +++ /dev/null @@ -1,5 +0,0 @@ -

    You have successfully signed out

    - -You may want to return - -Press this neat little button:Take Me Home diff --git a/examples/roc_api/site/www/template/html/master2/main_navigation.tpl b/examples/roc_api/site/www/template/html/master2/main_navigation.tpl deleted file mode 100644 index 88365d7..0000000 --- a/examples/roc_api/site/www/template/html/master2/main_navigation.tpl +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/optional_enhancement_js.tpl b/examples/roc_api/site/www/template/html/master2/optional_enhancement_js.tpl deleted file mode 100644 index 1353b83..0000000 --- a/examples/roc_api/site/www/template/html/master2/optional_enhancement_js.tpl +++ /dev/null @@ -1,5 +0,0 @@ - - -{if condition="$web"} - -{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/master2/optional_styling_css.tpl b/examples/roc_api/site/www/template/html/master2/optional_styling_css.tpl deleted file mode 100644 index f56d770..0000000 --- a/examples/roc_api/site/www/template/html/master2/optional_styling_css.tpl +++ /dev/null @@ -1,9 +0,0 @@ -{if condition="$html"} - - - -{/if} -{if condition="$web"} - - -{/if} diff --git a/examples/roc_api/site/www/template/html/master2/site_navigation.tpl b/examples/roc_api/site/www/template/html/master2/site_navigation.tpl deleted file mode 100644 index a75760c..0000000 --- a/examples/roc_api/site/www/template/html/master2/site_navigation.tpl +++ /dev/null @@ -1,27 +0,0 @@ - - - - \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/navigation.tpl b/examples/roc_api/site/www/template/html/modules/navigation.tpl deleted file mode 100644 index ee2d15e..0000000 --- a/examples/roc_api/site/www/template/html/modules/navigation.tpl +++ /dev/null @@ -1,8 +0,0 @@ -{if isset="$user"} - Logoff -{/if} -{unless isset="$user"} - Login - Register -{/unless} -List of Nodes diff --git a/examples/roc_api/site/www/template/html/modules/node.tpl b/examples/roc_api/site/www/template/html/modules/node.tpl deleted file mode 100644 index 0f6173f..0000000 --- a/examples/roc_api/site/www/template/html/modules/node.tpl +++ /dev/null @@ -1,174 +0,0 @@ -{if condition="html"} - - - - {include file="master2/head.tpl"/} - - -{/if} - -{unless condition="$web"} - - {include file="master2/site_navigation.tpl"/} -{/unless} - - -{if condition="html"} - -
    -
    -
    -
    -{/if} - - -
    - {if condition="$web"} - - {/if} - {if condition="$html"} - - {/if} -
    -
    - {if isset="$node"} -
    -
    -
    -
    -

    {$node.title/}

    -
    -
    -
    {$node.content/}
    -
    -
    -
    - {/if} -
    - -
    - {if isset="$user"} -
    -
    -
    - {if isset="$node"} - -
    - {/if} -
    -
    -
    - {if isset="$user"} -
    -
    - - {if isset="$node"} -
    - -
    - Delete Node - -
    - -
    -
    -
    - {/if} -
    - {/if} -
    -
    -
    - -{if condition="html"} -
    -
    -
    -{/if} - - -{if condition="html"} - - - - {include file="master2/optional_enhancement_js.tpl"/} - - - -{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/node_content.tpl b/examples/roc_api/site/www/template/html/modules/node_content.tpl deleted file mode 100644 index 4825500..0000000 --- a/examples/roc_api/site/www/template/html/modules/node_content.tpl +++ /dev/null @@ -1,70 +0,0 @@ -{if condition="html"} - - - - {include file="master2/head.tpl"/} - - -{/if} - -{unless condition="$web"} - - {include file="master2/site_navigation.tpl"/} -{/unless} - -{if condition="html"} - -
    -
    -
    -
    -{/if} - - -
    -
    -
    - -
    - Edit Node Content - -
    -
    - -
    -
    - -
    -
    - -
    - -
    -
    -
    -
    -
    -{if condition="html"} - -
    -
    -
    -{/if} - - -{if condition="html"} - - - - {include file="master2/optional_enhancement_js.tpl"/} - - - -{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/node_summary.tpl b/examples/roc_api/site/www/template/html/modules/node_summary.tpl deleted file mode 100644 index 24f69e6..0000000 --- a/examples/roc_api/site/www/template/html/modules/node_summary.tpl +++ /dev/null @@ -1,71 +0,0 @@ -{if condition="html"} - - - - {include file="master2/head.tpl"/} - - -{/if} - - - {unless condition="$web"} - - {include file="master2/site_navigation.tpl"/} - {/unless} - -{if condition="html"} - -
    -
    -
    -
    -{/if} - -
    -
    -
    - -
    - Edit Node Summary - -
    -
    - -
    -
    - -
    -
    - -
    - -
    -
    -
    -
    -
    - -{if condition="html"} - -
    -
    -
    -{/if} - - -{if condition="html"} - - - - {include file="master2/optional_enhancement_js.tpl"/} - - - -{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/node_title.tpl b/examples/roc_api/site/www/template/html/modules/node_title.tpl deleted file mode 100644 index 2e02d02..0000000 --- a/examples/roc_api/site/www/template/html/modules/node_title.tpl +++ /dev/null @@ -1,70 +0,0 @@ -{if condition="html"} - - - - {include file="master2/head.tpl"/} - - -{/if} - - {unless condition="$web"} - - {include file="master2/site_navigation.tpl"/} - {/unless} - - -{if condition="html"} - -
    -
    -
    -
    -{/if} - -
    -
    -
    - -
    - Edit Node Title - -
    -
    - -
    -
    - -
    -
    - -
    - -
    -
    -
    -
    -
    -{if condition="html"} - -
    -
    -
    -{/if} - - -{if condition="html"} - - - - {include file="master2/optional_enhancement_js.tpl"/} - - - -{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/nodes.tpl b/examples/roc_api/site/www/template/html/modules/nodes.tpl deleted file mode 100644 index 23b24c7..0000000 --- a/examples/roc_api/site/www/template/html/modules/nodes.tpl +++ /dev/null @@ -1,52 +0,0 @@ -{if condition="html"} - - - - {include file="master2/head.tpl"/} - - -{/if} - -{unless condition="$web"} - - {include file="master2/site_navigation.tpl"/} -{/unless} - -{if condition="html"} - -
    -
    -
    -
    -{/if} -
    -

    List nodes


    - - - {foreach from="$nodes" item="item"} - - {/foreach} -
    - -{if condition="html"} -
    -
    -
    -{/if} - - -{if condition="html"} - - - - {include file="master2/optional_enhancement_js.tpl"/} - - - -{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/template/html/modules/register.tpl b/examples/roc_api/site/www/template/html/modules/register.tpl deleted file mode 100644 index 3a14667..0000000 --- a/examples/roc_api/site/www/template/html/modules/register.tpl +++ /dev/null @@ -1,100 +0,0 @@ -{if condition="html"} - - - - {include file="master2/head.tpl"/} - - -{/if} - - -{unless condition="$web"} - - {include file="master2/site_navigation.tpl"/} -{/unless} - -{if condition="html"} - -
    -
    -
    -
    -{/if} - - -
    -
    -
    -
    - Register -
    -

    Register new user

    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    - s
    - -
    - -
    -
    -
    -
    -
    - -{if condition="html"} -
    -
    -
    -{/if} - - -{if condition="html"} - - - - {include file="master2/optional_enhancement_js.tpl"/} - - - -{/if} \ No newline at end of file diff --git a/examples/roc_api/site/www/themes/bootstrap/layout.tpl b/examples/roc_api/site/www/themes/bootstrap/layout.tpl deleted file mode 100644 index c2ed53f..0000000 --- a/examples/roc_api/site/www/themes/bootstrap/layout.tpl +++ /dev/null @@ -1,64 +0,0 @@ - - - - - ROC- Layout with defualt Regions - - - - - - - - -ROC CMS - A responsive layout - - - - {include file="tpl/page_top.tpl"/} - - -
    - - - {include file="tpl/page_header.tpl"/} - - -
    - - - - {include file="tpl/left_sidebar.tpl"/} - - -
    - - {include file="tpl/highlighted_section.tpl"/} - - - - {include file="tpl/help_section.tpl"/} - - - - {include file="tpl/main_content.tpl"/} -
    - - - {include file="tpl/right_sidebar.tpl"/} -
    -
    - - - {include file="tpl/page_footer.tpl"/} - - - {include file="tpl/page_bottom.tpl"/} - - - - diff --git a/examples/roc_api/site/www/themes/bootstrap/page.tpl b/examples/roc_api/site/www/themes/bootstrap/page.tpl index 8ee484d..7f32495 100644 --- a/examples/roc_api/site/www/themes/bootstrap/page.tpl +++ b/examples/roc_api/site/www/themes/bootstrap/page.tpl @@ -4,12 +4,20 @@ ROC- Layout with defualt Regions - + + + + + + + + + + - @@ -17,13 +25,16 @@ - {$region_top/} - + {if isset="$top"} + {$region_top/} + {/if}
    - {$region_header/} +
    diff --git a/examples/roc_api/site/www/themes/bootstrap/roc_template -2.html b/examples/roc_api/site/www/themes/bootstrap/roc_template -2.html new file mode 100644 index 0000000..17b8074 --- /dev/null +++ b/examples/roc_api/site/www/themes/bootstrap/roc_template -2.html @@ -0,0 +1,108 @@ + + + + + ROC- Layout with defualt Regions + + + + + + + + +ROC CMS - A responsive layout + + + + +
    +

    ROC Layout with Defaul Regions

    + + + +
    + + + + + + + +
    +

    Highlighted Section

    +

    Help Section

    + +

    Main Content Section

    +

    Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.

    + +

    Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.

    +
    + + + +
    +
    + + + + + + + + + diff --git a/examples/roc_api/site/www/themes/bootstrap/roc_template.html b/examples/roc_api/site/www/themes/bootstrap/roc_template.html index 83d6149..1472f38 100644 --- a/examples/roc_api/site/www/themes/bootstrap/roc_template.html +++ b/examples/roc_api/site/www/themes/bootstrap/roc_template.html @@ -4,12 +4,20 @@ ROC- Layout with defualt Regions - + + + + + + + + + + - @@ -18,33 +26,72 @@ -
    -

    ROC Layout with Defaul Regions

    -