From 930763e71e20c4423dc698d948de9b1ca1369e81 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 15 Sep 2014 14:46:34 -0300 Subject: [PATCH] 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$ + + + +