Compare commits

..

39 Commits

Author SHA1 Message Date
ed24eb7c94 Updated theme example, to have only 2 feeds. 2015-10-09 19:47:49 +02:00
43d6b4a197 Committed module files installed for demo example. 2015-10-09 19:45:21 +02:00
dffd06e331 Implemented a basic block caching system.
- for block {block_id}, to have a cache with 3600 seconds of expiration,
    declare in the cms.ini
     [blocks]
     {block_id}.expiration=3600

Added support for size in feed aggregation with new field "size"
2015-10-09 19:38:57 +02:00
463105f29f Added feed aggregation module.
Redesigned the CMS_BLOCK system,
   - added condition attribute. It can be set via configuration file
     with
     [blocks]
      {blockid}.region={region_name}
      {blockid}.conditions[]=is_front
      {blockid}.conditions[]=path:location-path/foo/bar
   - For backward compatibility, the CMS will check only conditions for block name prefixed by "?".
Improved the configuration library to support list and table properties.
Updated theme for now, to include the feed examples.
Added "cache" classes, to ease caching of html output for instance. (TODO: improve by providing a cache manager).
2015-10-08 13:56:31 +02:00
abebd00a4f Added feed aggregation module.
Improved the CMS Block system to support condition.
2015-10-05 16:04:24 +02:00
ec53a2682b Updated notification mailer, to always store output messages.
Fixed CMS_RESPONSE, and specific error response, to return expected status code.
2015-09-28 10:47:57 +02:00
jvelilla
7b2e6ab7b4 Merge branch 'jvelilla-roc_jv_issues' 2015-09-15 14:35:35 -03:00
jvelilla
87f4de1264 Merge branch 'roc_jv_issues' of https://github.com/jvelilla/ROC into roc_jv_issues 2015-09-15 14:30:02 -03:00
jvelilla
ed614a662c Updated CMS node and blog to remove extension data.
Comments: minor update.

Updated CMS_NODE_STORAGE_I API.
Delete a node using a node as formal parameter instead of node id.
Clean code and update log information.

Added precondition to delete node to accept nodes with a valid a id.

Added missing assertions tag names.
2015-09-15 14:28:33 -03:00
jvelilla
d54ad59e5f Added missing assertions tag names. 2015-09-15 14:10:51 -03:00
jvelilla
9173ef2ded Added precondition to delete node to accept nodes with a valid a id. 2015-09-15 11:40:13 -03:00
jvelilla
ad9e908dc2 Updated CMS_NODE_STORAGE_I API.
Delete a node using a node as formal parameter instead of node id.
Clean code and update log information.
2015-09-15 10:42:30 -03:00
jvelilla
4584917877 Comments: minor update. 2015-09-15 09:02:38 -03:00
jvelilla
f7d68d09e4 Updated CMS node and blog to remove extension data. 2015-09-15 08:54:43 -03:00
f9ecd4956f Keep the until date in the form data, so that new filter will remember the until date. 2015-09-09 23:12:52 +02:00
18e159ad3c Simplified the add child mechanism, by using a query parameter ?parent=nid
(instead of specific node/{nid}/add_child/page url)
Fixed implementation of `CMS_NODE_API.is_node_a_parent_of (..)',
  and improved parent validity checking against cycle.
2015-09-09 23:04:08 +02:00
438259033a Renamed link "Trash" to "Move to trash". 2015-09-09 22:10:13 +02:00
62e74ea6cd Refactor notion of trash and delete.
- Trash a node now does a soft delete (move to trash container).
 - Delete a node now remove it from the storage (no undo).

Signed-off-by: jvelilla <javier.hector@gmail.com>
2015-09-09 22:05:23 +02:00
32a409b7e9 Refactored add child feature, to have all the page specific code in CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER (and not in the general node code).
Note that ideally PAGE could be a separated page node module (as it is for blog).
Added listing of children for each "page".
2015-09-09 21:58:16 +02:00
jvelilla
eb70af6f19 CMS_NODE_API.available_parents_for_node
- Fixed comment.
     - added check to know if there are potencial cycles
     - added postcondition to ensure the list of potencial parent will not produce a cycle.

CMS_PAGE_NODE_TYPE_WEBFORM_MANAGER.update_node
     - Updated code to handle unassing parent from a node when the user
     - submit a -1 value.

NODE_FORM_RESPONSE.edit_form_validation
      - Added validation for node_parent, check if the input is valid and then
      - if exist a valid node in the list of available parents for the current node.

Signed-off-by: jvelilla <javier.hector@gmail.com>
2015-09-09 16:53:37 +02:00
5f4eb2cf10 Correct implementation of node extension save operation. 2015-09-08 20:44:00 +02:00
88bc52fffb Updated roc script, install module for the examples, and updated demo site folder. 2015-09-08 16:03:36 +02:00
jvelilla
544e6540ed Add child page support.
- Add support to create a new node as a child of an existing node.
   - Update or add a parent for a given node.
Fix delete and trash behavior: when a node is not published only the
   - author or admin see it.

Fix Node and Page node Revisions.
2015-09-08 15:57:17 +02:00
2431d7af6c Improved the recent changes modules:
- extracted from populate_recent_changes, the recent_changes_sources that enables to filter early.
Added author_name to the CMS_RECENT_CHANGE_ITEM to support author which is not related to any CMS_USER.
Implemented the simple filtering on source and add parameters size and date.
2015-09-03 14:47:17 +02:00
jvelilla
0d55bd67a2 Updated Basic Auth Module: remove the escaping from the logout message 2015-09-01 12:56:26 -03:00
jvelilla
e1727cc445 Merge branch 'jvelilla-roc_logout' 2015-09-01 11:50:37 -03:00
jvelilla
634a078282 Updated Loging form:
Display invalid credentials message inside the primary-tabs div.
Added support to submit the form using <Enter>.
2015-09-01 11:41:50 -03:00
jvelilla
2f65a084ac Updated logout message. Change message and added a div to make it easier style the site 2015-09-01 11:15:25 -03:00
jvelilla
13df6fd593 Added Logout message 2015-09-01 10:55:28 -03:00
ad4f020d0e The NULL storage may look into the CMS configuration file.
This allows to run the CMS without any database.
2015-08-21 19:16:31 +02:00
7a13b47131 Fixed cms library compilation.
CMS_FILE_BLOCK was missing `is_empty' implementation.
2015-08-19 12:34:35 +02:00
923089baa1 Better js code to apply CKEditor.replace, mainly to select only textarea.name=$name.
code cleaning.
2015-08-18 19:24:18 +02:00
cfec9dc7f8 Do not include empty blocks, this way, we avoid empty sidebars if not needed. 2015-08-18 10:58:31 +02:00
b5e7d5d201 Require "view recent changes" permission to see the recent changes. 2015-08-17 17:40:55 +02:00
e1bdcb965c Added permission arguments to "trash" and "Create" links.
Added blocks configuration settings via the cms.ini and "blocks" section
   ex: [blocks]
       navigation.region=sidebar_first
   This enables the site to change default block location, and even hides it easily, if the theme does not include associated region name.
2015-08-13 00:44:16 +02:00
0061afcbe8 use "deleted" instead of "trashed" . 2015-08-12 19:03:40 +02:00
6a9bc8aa42 Updated admin and recent_changes module permissions declaration. 2015-08-12 19:00:17 +02:00
1d7d79d69e Cleaned up hooks related code, and always go via CMS_RESPONSE.hooks 2015-08-12 17:50:23 +02:00
46014da3d8 Added recent_changes module.
Revisited hooks management, and added new CMS_HOOK_MANAGER.
Added admin, and other link into navigation menu that goes into first sidebar.
Fixed theme info, and template for sidebar ids.
Better css class name for cms node content.
2015-08-12 17:30:07 +02:00
87 changed files with 3229 additions and 701 deletions

View File

@@ -19,6 +19,7 @@
<library name="cms_blog_module" location="..\..\modules\blog\cms_blog_module-safe.ecf" readonly="false"/> <library name="cms_blog_module" location="..\..\modules\blog\cms_blog_module-safe.ecf" readonly="false"/>
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/> <library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_email_service" location="..\..\library\email\email-safe.ecf" readonly="false"/> <library name="cms_email_service" location="..\..\library\email\email-safe.ecf" readonly="false"/>
<library name="cms_feed_aggregator_module" location="..\..\modules\feed_aggregator\feed_aggregator-safe.ecf" readonly="false"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/> <library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/> <library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
<library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/> <library name="cms_oauth_20_module" location="..\..\modules\oauth20\oauth20-safe.ecf" readonly="false"/>

View File

@@ -1,9 +1,13 @@
setlocal setlocal
set ROC_CMD=%~dp0..\..\tools\roc.exe set ROC_CMD=call %~dp0..\..\tools\roc.bat
set ROC_CMS_DIR=%~dp0 set ROC_CMS_DIR=%~dp0
%ROC_CMD% install --module ..\..\modules\admin --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\auth --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\basic_auth --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\basic_auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\node --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\blog --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\blog --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\node --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\oauth20 --dir %ROC_CMS_DIR% %ROC_CMD% install --module ..\..\modules\oauth20 --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\openid --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\recent_changes --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\feed_aggregator --dir %ROC_CMS_DIR%

View File

@@ -93,7 +93,7 @@ feature -- Hooks
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
do do
Result := <<"demo-info">> Result := <<"?demo-info">>
end end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
@@ -103,8 +103,8 @@ feature -- Hooks
m: CMS_MENU m: CMS_MENU
lnk: CMS_LOCAL_LINK lnk: CMS_LOCAL_LINK
do do
if a_block_id.is_case_insensitive_equal_general ("demo-info") then if a_block_id.same_string ("demo-info") then
if a_response.request.request_uri.starts_with ("/demo/") then if a_response.location.starts_with_general ("demo/") then
create m.make_with_title (a_block_id, "Demo", 2) create m.make_with_title (a_block_id, "Demo", 2)
create lnk.make ("demo: abc", "demo/abc") create lnk.make ("demo: abc", "demo/abc")
m.extend (lnk) m.extend (lnk)

View File

@@ -20,7 +20,7 @@ smtp=localhost:25
# Default is "on" # Default is "on"
# for each module, this can be overwritten with # for each module, this can be overwritten with
# module_name= on or off # module_name= on or off
*=off *=all
admin=on admin=on
auth=on auth=on
basic_auth=on basic_auth=on
@@ -31,6 +31,20 @@ node=on
oauth20=on oauth20=on
openid=on openid=on
[blocks]
#navigation.region=sidebar_first
feed.eiffel.region=feed_eiffel
feed.eiffel.condition=is_front
feed.forum.region=feed_forum
feed.forum.condition=is_front
feed.stackoverflow.region=feed_stackoverflow
feed.stackoverflow.condition=is_front
#management.condition=is_front
#navigation.condition=is_front
[admin] [admin]
# CMS Installation, are accessible by "all", "none" or uppon "permission". (default is none) # CMS Installation, are accessible by "all", "none" or uppon "permission". (default is none)
installation_access=permission installation_access=permission

View File

@@ -0,0 +1,28 @@
{
"ids": ["eiffel", "forum"],
"feeds": {
"eiffel": {
"title": "Eiffel related posts.",
"expiration": "21600",
"size": 10,
"locations": [
"https://bertrandmeyer.com/feed/",
"https://room.eiffel.com/blog/feed",
"https://room.eiffel.com/article/feed",
"https://room.eiffel.com/library/feed"
]
, "categories": ["Eiffel"]
,"option_description": "enabled"
},
"forum": {
"title": "Eiffel Forum",
"expiration": "21600",
"size": 10,
"locations": [
"https://groups.google.com/forum/feed/eiffel-users/msgs/atom.xml?num=15".
"http://stackoverflow.com/feeds/tag?tagnames=eiffel&sort=newest"
]
,"option_description": "enabled"
}
}
}

View File

@@ -8,10 +8,10 @@
</head> </head>
<body> <body>
<p>You have required a new password at <a href="...">ROC CMS</a></p> <p>You have required a new password at <a href="$host">ROC CMS</a></p>
<p>To complete your request, please click on this link to genereate a new password:<p> <p>To complete your request, please click on this link to genereate a new password:<p>
<p><a href="$link">$link</a></p> <p><a href="$link">$link</a></p>
</body> </body>
</html> </html>

View File

@@ -8,11 +8,11 @@
</head> </head>
<body> <body>
<p>You have request a new activation token at<a href="...">ROC CMS</a></p> <p>You have request a new activation token at <a href="$host">ROC CMS</a></p>
<p>To complete your registration, please click on this link to activate your account:<p> <p>To complete your registration, please click on this link to activate your account:<p>
<p><a href="$link">$link</a></p> <p><a href="$link">$link</a></p>
<p>Thank you for joining us.</p> <p>Thank you for joining us.</p>
</body> </body>
</html> </html>

View File

@@ -7,7 +7,7 @@
<meta name="author" content="ROC CMS"> <meta name="author" content="ROC CMS">
</head> </head>
<body> <body>
<p>Welcome to<a href="...">ROC CMS</a></p> <p>Welcome to<a href="$host">ROC CMS</a></p>
<p>Thank you for joining us.</p> <p>Thank you for joining us.</p>
</body> </body>
</html> </html>

View File

@@ -25,5 +25,5 @@
</p> </p>
</div> </div>
</div> </div>
{/unless} {/unless}
</div> </div>

View File

@@ -25,7 +25,7 @@ ROC_AUTH.login = function() {
var newdiv = document.createElement('div'); var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>"; newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId'; newdiv.id = 'myModalFormId';
$("body").append(newdiv); $(".primary-tabs").append(newdiv);
} }
}else{ }else{
@@ -49,7 +49,7 @@ ROC_AUTH.login = function() {
var newdiv = document.createElement('div'); var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>"; newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId'; newdiv.id = 'myModalFormId';
$("body").append(newdiv); $(".primary-tabs").append(newdiv);
} }
} }
@@ -93,7 +93,7 @@ ROC_AUTH.login_with_redirect = function() {
var newdiv = document.createElement('div'); var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>"; newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId'; newdiv.id = 'myModalFormId';
$("body").append(newdiv); $(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide(); $("#imgProgressRedirect").hide();
} }
}else{ }else{
@@ -122,8 +122,8 @@ ROC_AUTH.login_with_redirect = function() {
var newdiv = document.createElement('div'); var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>"; newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId'; newdiv.id = 'myModalFormId';
$("body").append(newdiv); $(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide(); $("#imgProgressRedirect").hide();
} }
} }

View File

@@ -5,12 +5,12 @@
<div> <div>
<form name="cms_basic_auth" action method="POST"> <form name="cms_basic_auth" action method="POST">
<div> <div>
<input type="text" name="username" required> <input type="text" name="username" id="username" required>
<label>Username</label> <label>Username</label>
</div> </div>
<div> <div>
<input type="password" name="password" required> <input type="password" name="password" id="password" required>
<label>Password</label> <label>Password</label>
</div> </div>
@@ -25,10 +25,5 @@
</p> </p>
</div> </div>
</div> </div>
<div>
{foreach item="item" from="$oauth_consumers"}
<a href="{$site_url/}account/login-with-oauth/{$item/}">Login with {$item/}</a><br>
{/foreach}
</div>
{/unless} {/unless}
</div> </div>

View File

@@ -0,0 +1,53 @@
div.feed ul {
list-style: none;
position: relative;
padding: 0;
margin: 0;
width: 99%;
}
div.feed li {
/* border-top: solid 1px #ddd; */
padding: 0;
margin: 0 0 5px 0;
}
div.feed li a {
font-weight: bold;
}
div.feed li .date {
font-weight: bold;
font-size: small;
}
div.feed li .category {
margin-left: 20px;
font-size: 8px;
height: 9px;
overflow: hidden;
color: #999;
}
div.feed li .description {
margin-left: 20px;
font-size: small;
height: 18px;
overflow: hidden;
color: #999;
}
div.feed li:hover {
margin-bottom: 23px;
}
div.feed li:hover .description {
padding: 5px;
position: absolute;
height: auto;
overflow-y: scroll;
overflow-x: scroll;
color: #000;
background-color: #fff;
border: solid 1px #000;
z-index: 10;
}
div.feed li:hover:last-child {
margin-bottom: 28px;
}
div.feed li .description::after {
content: "...";
}

View File

@@ -0,0 +1,54 @@
div.feed {
ul {
list-style: none;
position: relative;
padding: 0;
margin: 0;
width: 99%;
}
li {
/* border-top: solid 1px #ddd; */
padding: 0;
margin: 0 0 5px 0;
a {
font-weight: bold;
}
.date {
font-weight: bold;
font-size: small;
}
.category {
margin-left: 20px;
font-size: 8px;
height: 9px;
overflow: hidden;
color: #999;
}
.description {
margin-left: 20px;
font-size: small;
height: 18px;
overflow: hidden;
color: #999;
}
&:hover {
margin-bottom: 23px;
.description {
padding: 5px;
position: absolute;
height: auto;
overflow-y: scroll;
overflow-x: scroll;
color: #000;
background-color: #fff;
border: solid 1px #000;
z-index: 10;
}
&:last-child {
margin-bottom: 28px;
}
}
.description::after { content: "..."; }
}
}

View File

@@ -62,9 +62,15 @@
<!-- Main Content Section --> <!-- Main Content Section -->
{unless isempty="$page_title"}<h1 class="page-title">{$page_title/}</h1>{/unless} {unless isempty="$page_title"}<h1 class="page-title">{$page_title/}</h1>{/unless}
{$page.region_content/} {$page.region_content/}
{if condition="$page.is_front"}
{if isset="$page.region_feed_eiffel"}
<div class="column" style="width: 45%; float: left">{$page.region_feed_eiffel/}</div>
{/if}
{if isset="$page.region_feed_forum"}
<div class="column" style="width: 45%; float: left">{$page.region_feed_forum/}</div>
{/if}
{/if}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -82,6 +82,9 @@ feature -- CMS setup
create {CMS_RECENT_CHANGES_MODULE} m.make create {CMS_RECENT_CHANGES_MODULE} m.make
a_setup.register_module (m) a_setup.register_module (m)
-- Recent changes
create {FEED_AGGREGATOR_MODULE} m.make
a_setup.register_module (m)
-- Miscellanious -- Miscellanious
create {CMS_DEBUG_MODULE} m.make create {CMS_DEBUG_MODULE} m.make

View File

@@ -33,11 +33,53 @@ feature -- Query
end end
end end
resolved_text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- List of String item associated with key `k',
-- and expanded values to resolved variables ${varname}.
do
if attached text_list_item (k) as lst then
from
lst.start
until
lst.after
loop
lst.replace (resolved_expression (lst.item))
lst.forth
end
end
end
resolved_text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k',
-- and expanded values to resolved variables ${varname}.
do
if attached text_table_item (k) as tb then
from
tb.start
until
tb.after
loop
tb.replace (resolved_expression (tb.item_for_iteration), tb.key_for_iteration)
tb.forth
end
end
end
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
-- String item associated with key `k'. -- String item associated with key `k'.
deferred deferred
end end
text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- List of String item associated with key `k'.
deferred
end
text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k'.
deferred
end
integer_item (k: READABLE_STRING_GENERAL): INTEGER integer_item (k: READABLE_STRING_GENERAL): INTEGER
-- Integer item associated with key `k'. -- Integer item associated with key `k'.
deferred deferred
@@ -109,7 +151,7 @@ feature -- Duplication
end end
note note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -119,14 +119,47 @@ feature -- Access: Config Reader
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
-- String item associated with key `k'. -- String item associated with key `k'.
local
obj: like item
do do
obj := item (k) Result := value_to_string_32 (item (k))
if attached {READABLE_STRING_32} obj as s32 then end
Result := s32
elseif attached {READABLE_STRING_8} obj as s then text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
Result := utf.utf_8_string_8_to_escaped_string_32 (s) -- List of String item associated with key `k'.
do
if attached {LIST [READABLE_STRING_8]} item (k) as l_list then
create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (l_list.count)
Result.compare_objects
across
l_list as ic
until
Result = Void
loop
if attached value_to_string_32 (ic.item) as s32 then
Result.force (s32)
else
Result := Void
end
end
end
end
text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k'.
do
if attached {STRING_TABLE [READABLE_STRING_8]} item (k) as l_list then
create {STRING_TABLE [READABLE_STRING_32]} Result.make (l_list.count)
Result.compare_objects
across
l_list as ic
until
Result = Void
loop
if attached value_to_string_32 (ic.item) as s32 then
Result.force (s32, ic.key)
else
Result := Void
end
end
end end
end end
@@ -226,6 +259,15 @@ feature -- Access
feature {NONE} -- Implementation feature {NONE} -- Implementation
value_to_string_32 (obj: detachable ANY): detachable STRING_32
do
if attached {READABLE_STRING_32} obj as s32 then
Result := s32
elseif attached {READABLE_STRING_8} obj as s then
Result := utf.utf_8_string_8_to_escaped_string_32 (s)
end
end
item_from_values (a_values: STRING_TABLE [ANY]; k: READABLE_STRING_GENERAL): detachable ANY item_from_values (a_values: STRING_TABLE [ANY]; k: READABLE_STRING_GENERAL): detachable ANY
local local
i,j: INTEGER i,j: INTEGER
@@ -460,7 +502,7 @@ feature {NONE} -- Implementation
invariant invariant
note note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -63,10 +63,46 @@ feature -- Access: Config Reader
text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 text_item (k: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
-- String item associated with query `k'. -- String item associated with query `k'.
do do
if attached {JSON_STRING} item (k) as l_string then Result := value_to_string_32 (item (k))
Result := l_string.unescaped_string_32 end
elseif attached {JSON_NUMBER} item (k) as l_number then
Result := l_number.item text_list_item (k: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- List of String item associated with key `k'.
do
if attached {JSON_ARRAY} item (k) as l_array then
create {ARRAYED_LIST [READABLE_STRING_32]} Result.make (l_array.count)
Result.compare_objects
across
l_array as ic
until
Result = Void
loop
if attached value_to_string_32 (ic.item) as s32 then
Result.force (s32)
else
Result := Void
end
end
end
end
text_table_item (k: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Table of String item associated with key `k'.
do
if attached {JSON_OBJECT} item (k) as obj then
create {STRING_TABLE [READABLE_STRING_32]} Result.make (obj.count)
Result.compare_objects
across
obj as ic
until
Result = Void
loop
if attached value_to_string_32 (ic.item) as s32 then
Result.force (s32, ic.key.item)
else
Result := Void
end
end
end end
end end
@@ -105,6 +141,15 @@ feature -- Access
feature {NONE} -- Implementation feature {NONE} -- Implementation
value_to_string_32 (v: detachable ANY): detachable STRING_32
do
if attached {JSON_STRING} v as l_string then
Result := l_string.unescaped_string_32
elseif attached {JSON_NUMBER} v as l_number then
Result := l_number.item
end
end
object_json_value (a_object: JSON_OBJECT; a_query: READABLE_STRING_32): detachable JSON_VALUE object_json_value (a_object: JSON_OBJECT; a_query: READABLE_STRING_32): detachable JSON_VALUE
-- Item associated with query `a_query' from object `a_object' if any. -- Item associated with query `a_query' from object `a_object' if any.
local local
@@ -163,7 +208,7 @@ feature {NONE} -- JSON
end end
note note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -21,6 +21,18 @@ feature -- Test
create {INI_CONFIG} cfg.make_from_string ("[ create {INI_CONFIG} cfg.make_from_string ("[
foo = bar foo = bar
collection[] = a
collection[] = b
collection[] = c
collection[] = 1
collection[] = 2
collection[] = 3
table[a] = 1
table[b] = 2
table[c] = 3
table[d] = test
[first] [first]
abc = 1 abc = 1
def = and so on def = and so on
@@ -58,6 +70,21 @@ feature -- Test
assert ("has_item (second.is)", cfg.has_item ("second.is")) assert ("has_item (second.is)", cfg.has_item ("second.is"))
assert ("item (second.is)", attached cfg.text_item ("second.is") as v and then v.same_string_general ("2")) assert ("item (second.is)", attached cfg.text_item ("second.is") as v and then v.same_string_general ("2"))
assert ("has_item (collection)", cfg.has_item ("collection"))
assert ("item (collection)", attached cfg.text_list_item ("collection") as lst and then (
lst.has ("a") and lst.has ("b") and lst.has ("c") and lst.has ("1") and lst.has ("2") and lst.has ("3")
)
)
assert ("has_item (table)", cfg.has_item ("table"))
assert ("item (table)", attached cfg.text_table_item ("table") as tb and then (
tb.item ("a") ~ {STRING_32} "1" and
tb.item ("b") ~ {STRING_32} "2" and
tb.item ("c") ~ {STRING_32} "3" and
tb.item ("d") ~ {STRING_32} "test"
)
)
if attached cfg.sub_config ("second") as cfg_second then if attached cfg.sub_config ("second") as cfg_second then
assert ("has_item (is)", cfg_second.has_item ("is")) assert ("has_item (is)", cfg_second.has_item ("is"))
assert ("item (is)", attached cfg_second.text_item ("is") as v and then v.same_string_general ("2")) assert ("item (is)", attached cfg_second.text_item ("is") as v and then v.same_string_general ("2"))
@@ -141,7 +168,9 @@ feature -- Test
"is": 2, "is": 2,
"the": 3, "the": 3,
"end": 4 "end": 4
} },
"collection": ["a", "b", "c", 1, 2, 3],
"table": { "a": 1, "b": 2, "c": 3, "d" : "test" }
} }
]") ]")
@@ -164,6 +193,21 @@ feature -- Test
assert ("item (second.the)", attached cfg.text_item ("second.the") as v and then v.same_string_general ("3")) assert ("item (second.the)", attached cfg.text_item ("second.the") as v and then v.same_string_general ("3"))
assert ("item (second.end)", attached cfg.text_item ("second.end") as v and then v.same_string_general ("4")) assert ("item (second.end)", attached cfg.text_item ("second.end") as v and then v.same_string_general ("4"))
assert ("has_item (collection)", cfg.has_item ("collection"))
assert ("item (collection)", attached cfg.text_list_item ("collection") as lst and then (
lst.has ("a") and lst.has ("b") and lst.has ("c") and lst.has ("1") and lst.has ("2") and lst.has ("3")
)
)
assert ("has_item (table)", cfg.has_item ("table"))
assert ("item (table)", attached cfg.text_table_item ("table") as tb and then (
tb.item ("a") ~ {STRING_32} "1" and
tb.item ("b") ~ {STRING_32} "2" and
tb.item ("c") ~ {STRING_32} "3" and
tb.item ("d") ~ {STRING_32} "test"
)
)
if attached cfg.sub_config ("second") as cfg_second then if attached cfg.sub_config ("second") as cfg_second then
assert ("has_item (is)", cfg_second.has_item ("is")) assert ("has_item (is)", cfg_second.has_item ("is"))
assert ("item (is)", attached cfg_second.text_item ("is") as v and then v.same_string_general ("2")) assert ("item (is)", attached cfg_second.text_item ("is") as v and then v.same_string_general ("2"))

View File

@@ -28,7 +28,6 @@ feature {NONE} -- Initialization
-- Initialize service. -- Initialize service.
do do
admin_email := parameters.admin_email admin_email := parameters.admin_email
create {NOTIFICATION_SMTP_MAILER} mailer.make (parameters.smtp_server) create {NOTIFICATION_SMTP_MAILER} mailer.make (parameters.smtp_server)
set_successful set_successful
end end

View File

@@ -90,6 +90,7 @@ feature -- Security
Result.force ("admin users") Result.force ("admin users")
Result.force ("admin roles") Result.force ("admin roles")
Result.force ("admin modules") Result.force ("admin modules")
Result.force ("install modules")
end end
feature -- Hooks feature -- Hooks

View File

@@ -84,7 +84,7 @@ feature -- Process Edit
do do
create b.make_empty create b.make_empty
f := new_edit_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user") f := new_edit_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user")
invoke_form_alter (f, fd) hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate(?,a_role, b)) f.validation_actions.extend (agent edit_form_validate(?,a_role, b))
f.submit_actions.extend (agent edit_form_submit(?, a_role, b)) f.submit_actions.extend (agent edit_form_submit(?, a_role, b))
@@ -117,7 +117,7 @@ feature -- Process Delete
do do
create b.make_empty create b.make_empty
f := new_delete_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user") f := new_delete_form (a_role, url (request.percent_encoded_path_info, Void), "edit-user")
invoke_form_alter (f, fd) hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then if request.is_post_request_method then
f.process (Current) f.process (Current)
fd := f.last_data fd := f.last_data
@@ -149,7 +149,7 @@ feature -- Process New
do do
create b.make_empty create b.make_empty
f := new_edit_form (l_role, url (request.percent_encoded_path_info, Void), "create-role") f := new_edit_form (l_role, url (request.percent_encoded_path_info, Void), "create-role")
invoke_form_alter (f, fd) hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then if request.is_post_request_method then
f.validation_actions.extend (agent new_form_validate(?, b)) f.validation_actions.extend (agent new_form_validate(?, b))
f.submit_actions.extend (agent edit_form_submit(?, l_role, b)) f.submit_actions.extend (agent edit_form_submit(?, l_role, b))

View File

@@ -86,7 +86,7 @@ feature -- Process Edit
do do
create b.make_empty create b.make_empty
f := new_edit_form (a_user, url (location, Void), "edit-user") f := new_edit_form (a_user, url (location, Void), "edit-user")
invoke_form_alter (f, fd) hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then if request.is_post_request_method then
f.submit_actions.extend (agent edit_form_submit (?, a_user, b)) f.submit_actions.extend (agent edit_form_submit (?, a_user, b))
f.process (Current) f.process (Current)
@@ -118,7 +118,7 @@ feature -- Process Delete
do do
create b.make_empty create b.make_empty
f := new_delete_form (a_user, url (location, Void), "edit-user") f := new_delete_form (a_user, url (location, Void), "edit-user")
invoke_form_alter (f, fd) hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then if request.is_post_request_method then
f.process (Current) f.process (Current)
fd := f.last_data fd := f.last_data
@@ -151,7 +151,7 @@ feature -- Process New
do do
create b.make_empty create b.make_empty
f := new_edit_form (l_user, url (location, Void), "create-user") f := new_edit_form (l_user, url (location, Void), "create-user")
invoke_form_alter (f, fd) hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then if request.is_post_request_method then
f.validation_actions.extend (agent new_form_validate (?, b)) f.validation_actions.extend (agent new_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_user, b)) f.submit_actions.extend (agent edit_form_submit (?, l_user, b))

View File

@@ -521,26 +521,26 @@ feature {NONE} -- Helpers
feature {NONE} -- Block views feature {NONE} -- Block views
get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) -- get_block_view_login (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
local -- local
-- vals: CMS_VALUE_TABLE ---- vals: CMS_VALUE_TABLE
do -- do
if attached template_block (a_block_id, a_response) as l_tpl_block then -- if attached template_block (a_block_id, a_response) as l_tpl_block then
-- create vals.make (1) ---- create vals.make (1)
-- -- add the variable to the block ---- -- add the variable to the block
-- value_table_alter (vals, a_response) ---- value_table_alter (vals, a_response)
-- across ---- across
-- vals as ic ---- vals as ic
-- loop ---- loop
-- l_tpl_block.set_value (ic.item, ic.key) ---- l_tpl_block.set_value (ic.item, ic.key)
---- end
-- a_response.put_required_block (l_tpl_block, "content")
-- else
-- debug ("cms")
-- a_response.add_warning_message ("Error with block [" + a_block_id + "]")
-- end -- end
a_response.add_block (l_tpl_block, "content") -- end
else -- end
debug ("cms")
a_response.add_warning_message ("Error with block [" + a_block_id + "]")
end
end
end
get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_register (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
@@ -579,7 +579,6 @@ feature {NONE} -- Block views
end end
end end
get_block_view_reactivate (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view_reactivate (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
do do
if a_response.request.is_get_request_method then if a_response.request.is_get_request_method then

View File

@@ -47,6 +47,7 @@ feature -- HTTP Methods
l_page: CMS_RESPONSE l_page: CMS_RESPONSE
l_url: STRING l_url: STRING
i: INTEGER i: INTEGER
l_message: STRING
do do
api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void) api.logger.put_information (generator + ".do_get Processing basic auth logoff", Void)
if attached req.query_parameter ("prompt") as l_prompt then if attached req.query_parameter ("prompt") as l_prompt then
@@ -72,6 +73,10 @@ feature -- HTTP Methods
l_page.set_status_code ({HTTP_STATUS_CODE}.found) l_page.set_status_code ({HTTP_STATUS_CODE}.found)
l_page.set_redirection (l_url) l_page.set_redirection (l_url)
end end
create l_message.make_from_string (logout_message)
l_message.replace_substring_all ("$site_login", req.absolute_script_url ("/account/roc-login"))
l_message.replace_substring_all ("$site_home", req.absolute_script_url (""))
l_page.set_main_content (l_message)
l_page.execute l_page.execute
end end
end end
@@ -113,4 +118,15 @@ feature -- HTTP Methods
end end
end end
feature {NONE}-- Lougout Message
logout_message: STRING = "[
<div class="cms-logout-message">
<h2>You are now signed out</h2>
<p>You can <a href="$site_login">log</a> in again, or go to the <a href="$site_home">front page</a>.</p>
</div>
]"
end end

View File

@@ -25,7 +25,7 @@ ROC_AUTH.login = function() {
var newdiv = document.createElement('div'); var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>"; newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId'; newdiv.id = 'myModalFormId';
$("body").append(newdiv); $(".primary-tabs").append(newdiv);
} }
}else{ }else{
@@ -49,7 +49,7 @@ ROC_AUTH.login = function() {
var newdiv = document.createElement('div'); var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>"; newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId'; newdiv.id = 'myModalFormId';
$("body").append(newdiv); $(".primary-tabs").append(newdiv);
} }
} }
@@ -93,7 +93,7 @@ ROC_AUTH.login_with_redirect = function() {
var newdiv = document.createElement('div'); var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>"; newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId'; newdiv.id = 'myModalFormId';
$("body").append(newdiv); $(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide(); $("#imgProgressRedirect").hide();
} }
}else{ }else{
@@ -122,8 +122,8 @@ ROC_AUTH.login_with_redirect = function() {
var newdiv = document.createElement('div'); var newdiv = document.createElement('div');
newdiv.innerHTML = "<br>Invalid Credentials</br>"; newdiv.innerHTML = "<br>Invalid Credentials</br>";
newdiv.id = 'myModalFormId'; newdiv.id = 'myModalFormId';
$("body").append(newdiv); $(".primary-tabs").append(newdiv);
$("#imgProgressRedirect").hide(); $("#imgProgressRedirect").hide();
} }
} }

View File

@@ -5,12 +5,12 @@
<div> <div>
<form name="cms_basic_auth" action method="POST"> <form name="cms_basic_auth" action method="POST">
<div> <div>
<input type="text" name="username" required> <input type="text" name="username" id="username" required>
<label>Username</label> <label>Username</label>
</div> </div>
<div> <div>
<input type="password" name="password" required> <input type="password" name="password" id="password" required>
<label>Password</label> <label>Password</label>
</div> </div>

View File

@@ -108,6 +108,18 @@ feature -- Persistence
end end
end end
delete_node (a_node: CMS_BLOG)
-- <Precursor>
local
l_parameters: STRING_TABLE [ANY]
do
if a_node.has_id then
create l_parameters.make (1)
l_parameters.put (a_node.id, "nid")
sql_change (sql_delete_node_data, l_parameters)
end
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
node_data (a_node: CMS_NODE): detachable TUPLE [revision: INTEGER_64; tags: READABLE_STRING_32] node_data (a_node: CMS_NODE): detachable TUPLE [revision: INTEGER_64; tags: READABLE_STRING_32]
@@ -136,12 +148,15 @@ feature {NONE} -- Implementation
check unique_data: n = 0 end check unique_data: n = 0 end
end end
end end
ensure
accepted_revision: Result /= Void implies Result.revision <= a_node.revision
end end
feature -- SQL feature -- SQL
sql_select_node_data: STRING = "SELECT nid, revision, tags FROM blog_post_nodes WHERE nid =:nid AND revision<=:revision ORDER BY revision DESC LIMIT 1;" sql_select_node_data: STRING = "SELECT nid, revision, tags FROM blog_post_nodes WHERE nid=:nid AND revision<=:revision ORDER BY revision DESC LIMIT 1;"
sql_insert_node_data: STRING = "INSERT INTO blog_post_nodes (nid, revision, tags) VALUES (:nid, :revision, :tags);" sql_insert_node_data: STRING = "INSERT INTO blog_post_nodes (nid, revision, tags) VALUES (:nid, :revision, :tags);"
sql_update_node_data: STRING = "UPDATE blog_post_nodes SET nid=:nid, revision=:revision, tags=:tags WHERE nid=:nid AND revision=:revision;" sql_update_node_data: STRING = "UPDATE blog_post_nodes SET nid=:nid, revision=:revision, tags=:tags WHERE nid=:nid AND revision=:revision;"
sql_delete_node_data: STRING = "DELETE FROM blog_post_nodes WHERE nid=:nid;"
end end

View File

@@ -0,0 +1,115 @@
note
description: "Feed aggregation parameters."
date: "$Date$"
revision: "$Revision$"
class
FEED_AGGREGATION
create
make
feature {NONE} -- Initialization
make (a_name: READABLE_STRING_GENERAL)
do
create name.make_from_string_general (a_name)
create {ARRAYED_LIST [READABLE_STRING_8]} locations.make (0)
expiration := 60*60
description_enabled := True
size := 10
end
feature -- Access
name: IMMUTABLE_STRING_32
-- Associated name.
expiration: INTEGER
-- Suggested expiration time in seconds (default: 1 hour).
-- If negative then never expires.
size: INTEGER
-- Number of entries to display per page.
description: detachable IMMUTABLE_STRING_32
-- Optional description.
locations: LIST [READABLE_STRING_8]
-- List of feed location aggregated into current.
included_categories: detachable LIST [READABLE_STRING_32]
-- Optional categories to filter.
-- If Void, include any.
description_enabled: BOOLEAN
-- Display description?
feature -- Element change
set_description (a_desc: detachable READABLE_STRING_GENERAL)
do
if a_desc = Void then
description := Void
else
create description.make_from_string_general (a_desc)
end
end
set_expiration (nb_seconds: INTEGER)
-- Set `expiration' to `nb_seconds'.
do
expiration := nb_seconds
end
set_size (nb: INTEGER)
-- Set `size' to `nb'.
do
size := nb
end
set_description_enabled (b: BOOLEAN)
-- Set `description_enabled' to `b'.
do
description_enabled := b
end
reset_categories
do
included_categories := Void
end
include_category (a_cat: READABLE_STRING_GENERAL)
local
lst: like included_categories
s32: STRING_32
do
lst := included_categories
if lst = Void then
create {ARRAYED_LIST [READABLE_STRING_32]} lst.make (1)
included_categories := lst
lst.compare_objects
end
s32 := a_cat.to_string_32
if not lst.has (s32) then
lst.force (s32)
end
end
feature -- Status report
is_included (e: FEED_ITEM): BOOLEAN
do
Result := True
if attached e.categories as e_cats then
if attached included_categories as lst then
Result := across lst as ic some
across e_cats as e_ic some
e_ic.item.same_string (ic.item)
end
end
end
end
end
end

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="feed_aggregator" uuid="6A78AB37-9B07-4C42-9E24-0CA7D3C61E12" library_target="feed_aggregator">
<target name="feed_aggregator">
<root all_classes="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="feed" location="$ISE_LIBRARY\contrib\library\text\parser\feed\feed-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="http_client" location="$ISE_LIBRARY\contrib\library\network\http_client\http_client-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>
<library name="wsf_html" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf_html\wsf_html-safe.ecf" readonly="false"/>
<cluster name="src" location="." recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,146 @@
note
description: "API for Feed aggregator module."
date: "$Date$"
revision: "$Revision$"
class
FEED_AGGREGATOR_API
inherit
CMS_MODULE_API
create
make
feature -- Access
aggregations: HASH_TABLE [FEED_AGGREGATION, STRING]
-- List of feed aggregations.
local
agg: FEED_AGGREGATION
l_feed_id: READABLE_STRING_32
l_title: detachable READABLE_STRING_GENERAL
l_location_list: detachable LIST [READABLE_STRING_32]
utf: UTF_CONVERTER
l_table: like internal_aggregations
do
l_table := internal_aggregations
if l_table /= Void then
Result := l_table
else
create Result.make (0)
internal_aggregations := Result
if attached cms_api.module_configuration_by_name ({FEED_AGGREGATOR_MODULE}.name, "feeds") as cfg then
if attached cfg.text_list_item ("ids") as l_ids then
across
l_ids as ic
loop
l_feed_id := ic.item
l_location_list := cfg.text_list_item ({STRING_32} "feeds." + l_feed_id + ".locations")
if
attached cfg.text_item ({STRING_32} "feeds." + l_feed_id + ".location") as l_location
then
if l_location_list = Void then
create {ARRAYED_LIST [READABLE_STRING_32]} l_location_list.make (1)
end
l_location_list.force (l_location)
end
if l_location_list /= Void and then not l_location_list.is_empty then
l_title := cfg.text_item ({STRING_32} "feeds." + l_feed_id + ".title")
if l_title = Void then
l_title := l_feed_id
end
create agg.make (l_title)
if attached cfg.text_item ({STRING_32} "feeds." + l_feed_id + ".expiration") as l_expiration then
if l_expiration.is_integer then
agg.set_expiration (l_expiration.to_integer)
end
end
if attached cfg.text_item ({STRING_32} "feeds." + l_feed_id + ".size") as l_size then
if l_size.is_integer then
agg.set_size (l_size.to_integer)
end
end
if attached cfg.text_item ({STRING_32} "feeds." + l_feed_id + ".option_description") as l_description_opt then
agg.set_description_enabled (not l_description_opt.is_case_insensitive_equal_general ("disabled"))
end
across
l_location_list as loc_ic
loop
agg.locations.force (utf.utf_32_string_to_utf_8_string_8 (loc_ic.item))
end
Result.force (agg, l_feed_id)
if attached cfg.text_list_item ({STRING_32} "feeds." + l_feed_id + ".categories") as l_cats then
across
l_cats as cats_ic
loop
agg.include_category (cats_ic.item)
end
end
end
end
end
end
end
end
aggregation (a_name: READABLE_STRING_GENERAL): detachable FEED_AGGREGATION
do
if attached a_name.is_valid_as_string_8 then
Result := aggregations.item (a_name.as_string_8)
end
end
feature {NONE} -- Access: implementation
internal_aggregations: detachable like aggregations
-- Cache value for `aggregations'.
feature -- Operation
feed (a_location: READABLE_STRING_8): detachable FEED
local
fac: FEED_DEFAULT_PARSERS
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
do
create fac
if attached new_http_client_session (a_location).get ("", ctx) as res then
if attached res.body as l_content then
Result := fac.feed_from_string (l_content)
end
end
end
aggregation_feed (agg: FEED_AGGREGATION): detachable FEED
-- Feed from aggregation `agg'.
local
fac: FEED_DEFAULT_PARSERS
f: detachable FEED
do
create fac
across
agg.locations as ic
loop
if attached new_http_client_session (ic.item).get ("", Void).body as res then
f := fac.feed_from_string (res)
if Result /= Void then
if f /= Void then
Result := Result + f
end
else
Result := f
end
end
end
end
new_http_client_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
local
cl: LIBCURL_HTTP_CLIENT
do
create cl.make
Result := cl.new_session (a_url)
Result.set_is_insecure (True)
end
end

View File

@@ -0,0 +1,308 @@
note
description: "CMS module bringing support for feed aggregation."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $"
class
FEED_AGGREGATOR_MODULE
inherit
CMS_MODULE
rename
module_api as feed_aggregator_api
redefine
initialize,
register_hooks,
permissions,
feed_aggregator_api
end
CMS_HOOK_BLOCK
CMS_HOOK_RESPONSE_ALTER
CMS_HOOK_MENU_SYSTEM_ALTER
create
make
feature {NONE} -- Initialization
make
-- Create Current module, disabled by default.
do
version := "1.0"
description := "Feed aggregation"
package := "feed"
end
feature -- Access
name: STRING = "feed_aggregator"
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("manage feed aggregator")
end
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
do
Precursor (api)
create feed_aggregator_api.make (api)
end
feature {CMS_API} -- Access: API
feed_aggregator_api: detachable FEED_AGGREGATOR_API
-- Eventual module api.
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
local
h: WSF_URI_TEMPLATE_HANDLER
do
a_router.handle ("/admin/feed_aggregator/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_feed_aggregator_admin (a_api, ?, ?)), a_router.methods_head_get_post)
create {WSF_URI_TEMPLATE_AGENT_HANDLER} h.make (agent handle_feed_aggregation (a_api, ?, ?))
a_router.handle ("/feed_aggregation/", h, a_router.methods_head_get)
a_router.handle ("/feed_aggregation/{feed_id}", h, a_router.methods_head_get)
end
feature -- Handle
handle_feed_aggregator_admin (a_api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
nyi: NOT_IMPLEMENTED_ERROR_CMS_RESPONSE
do
create nyi.make (req, res, a_api)
nyi.execute
end
handle_feed_aggregation (a_api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
s: STRING
nb: INTEGER
do
if attached {WSF_STRING} req.query_parameter ("size") as p_size and then p_size.is_integer then
nb := p_size.integer_value
else
nb := -1
end
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, a_api)
if attached {WSF_STRING} req.path_parameter ("feed_id") as p_feed_id then
if attached feed_aggregation (p_feed_id.value) as l_agg then
create s.make_empty
s.append ("<h1>")
s.append (r.html_encoded (l_agg.name))
s.append ("</h1>")
if attached l_agg.included_categories as l_categories then
s.append ("<span class=%"category%">")
across
l_categories as cats_ic
loop
s.append (" [")
s.append (r.html_encoded (cats_ic.item))
s.append ("]")
end
s.append ("</span>")
end
if attached l_agg.description as l_desc and then l_desc.is_valid_as_string_8 then
s.append ("<div class=%"description%">")
s.append (l_desc.as_string_8)
s.append ("</div>")
end
s.append ("<ul>")
across
l_agg.locations as ic
loop
s.append ("<li><a href=%"")
s.append (ic.item)
s.append ("%">")
s.append (ic.item)
s.append ("</a></li>")
end
s.append ("</ul>")
if attached feed_to_html (p_feed_id.value, nb, True, r) as l_html then
s.append (l_html)
end
r.set_main_content (s)
else
create {NOT_FOUND_ERROR_CMS_RESPONSE} r.make (req, res, a_api)
end
else
if attached feed_aggregator_api as l_feed_agg_api then
create s.make_empty
across
l_feed_agg_api.aggregations as ic
loop
s.append ("<li>")
s.append (r.link (ic.key, "feed_aggregation/" + r.url_encoded (ic.key), Void))
if attached ic.item.included_categories as l_categories then
s.append ("<span class=%"category%">")
across
l_categories as cats_ic
loop
s.append (" [")
s.append (r.html_encoded (cats_ic.item))
s.append ("]")
end
s.append ("</span>")
end
if attached ic.item.description as l_desc then
if l_desc.is_valid_as_string_8 then
s.append ("<div class=%"description%">")
s.append (l_desc.as_string_8)
s.append ("</div>")
end
end
s.append ("</li>")
end
r.set_main_content (s)
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, a_api)
end
end
r.execute
end
feature -- Hooks configuration
register_hooks (a_response: CMS_RESPONSE)
-- Module hooks configuration.
do
a_response.hooks.subscribe_to_block_hook (Current)
a_response.hooks.subscribe_to_response_alter_hook (Current)
a_response.hooks.subscribe_to_menu_system_alter_hook (Current)
end
feature -- Hook
block_list: ITERABLE [like {CMS_BLOCK}.name]
-- List of block names, managed by current object.
local
res: ARRAYED_LIST [like {CMS_BLOCK}.name]
l_aggs: HASH_TABLE [FEED_AGGREGATION, STRING_8]
do
if attached feed_aggregator_api as l_feed_api then
l_aggs := l_feed_api.aggregations
create res.make (l_aggs.count)
across
l_aggs as ic
loop
res.force ("?feed." + ic.key)
end
else
create res.make (0)
end
Result := res
end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
-- Get block object identified by `a_block_id' and associate with `a_response'.
local
s: READABLE_STRING_8
b: CMS_CONTENT_BLOCK
pref: STRING
do
if attached feed_aggregator_api as l_feed_api then
pref := "feed."
if a_block_id.starts_with (pref) then
s := a_block_id.substring (pref.count + 1, a_block_id.count)
else
s := a_block_id
end
if attached feed_to_html (s, 0, True, a_response) as l_content then
create b.make (a_block_id, Void, l_content, Void)
b.set_is_raw (True)
a_response.add_block (b, "feed_" + s)
end
end
end
feed_aggregation (a_feed_id: READABLE_STRING_GENERAL): detachable FEED_AGGREGATION
do
if attached feed_aggregator_api as l_feed_api then
Result := l_feed_api.aggregation (a_feed_id)
end
end
feed_to_html (a_feed_id: READABLE_STRING_GENERAL; a_count: INTEGER; with_feed_info: BOOLEAN; a_response: CMS_RESPONSE): detachable STRING
local
nb: INTEGER
i: INTEGER
e: FEED_ITEM
l_cache: CMS_FILE_STRING_8_CACHE
lnk: detachable FEED_LINK
vis: FEED_TO_XHTML_VISITOR
s: STRING
do
if attached feed_aggregator_api as l_feed_api then
if attached l_feed_api.aggregation (a_feed_id) as l_agg then
create l_cache.make (a_response.api.files_location.extended (".cache").extended (name).extended ("feed__" + a_feed_id + "__" + a_count.out + "_" + with_feed_info.out))
Result := l_cache.item
if Result = Void or l_cache.expired (Void, l_agg.expiration) then
create Result.make (1024)
Result.append ("<!-- ")
Result.append ("Updated: " + l_cache.cache_date_time.out)
Result.append (" -->")
create vis.make (Result)
if a_count = 0 then
nb := l_agg.size
else
nb := a_count
end
vis.set_limit (nb)
vis.set_description_enabled (l_agg.description_enabled)
if with_feed_info then
create s.make_empty
if attached l_agg.description as l_desc then
s.append ("<div class=%"description%">")
s.append_string_general (l_desc)
s.append ("</div>")
end
vis.set_header (s)
end
create s.make_empty
s.append_string ("<liv class=%"nav%">")
s.append_string (a_response.link ("See more ...", "feed_aggregation/" + a_response.url_encoded (a_feed_id), Void))
s.append_string ("</li>")
vis.set_footer (s)
if attached l_feed_api.aggregation_feed (l_agg) as l_feed then
l_feed.accept (vis)
end
l_cache.put (Result)
end
end
end
end
feature -- Hook
response_alter (a_response: CMS_RESPONSE)
do
a_response.add_style (a_response.url ("/module/" + name + "/files/css/feed_aggregator.css", Void), Void)
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
-- Hook execution on collection of menu contained by `a_menu_system'
-- for related response `a_response'.
do
a_menu_system.navigation_menu.extend (create {CMS_LOCAL_LINK}.make ("Feeds", "feed_aggregation/"))
if a_response.has_permission ("manage feed aggregator") then
a_menu_system.management_menu.extend (create {CMS_LOCAL_LINK}.make ("Feeds (admin)", "admin/feed_aggregator/"))
end
end
end

View File

@@ -0,0 +1,53 @@
div.feed ul {
list-style: none;
position: relative;
padding: 0;
margin: 0;
width: 99%;
}
div.feed li {
/* border-top: solid 1px #ddd; */
padding: 0;
margin: 0 0 5px 0;
}
div.feed li a {
font-weight: bold;
}
div.feed li .date {
font-weight: bold;
font-size: small;
}
div.feed li .category {
margin-left: 20px;
font-size: 8px;
height: 9px;
overflow: hidden;
color: #999;
}
div.feed li .description {
margin-left: 20px;
font-size: small;
height: 18px;
overflow: hidden;
color: #999;
}
div.feed li:hover {
margin-bottom: 23px;
}
div.feed li:hover .description {
padding: 5px;
position: absolute;
height: auto;
overflow-y: scroll;
overflow-x: scroll;
color: #000;
background-color: #fff;
border: solid 1px #000;
z-index: 10;
}
div.feed li:hover:last-child {
margin-bottom: 28px;
}
div.feed li .description::after {
content: "...";
}

View File

@@ -0,0 +1,54 @@
div.feed {
ul {
list-style: none;
position: relative;
padding: 0;
margin: 0;
width: 99%;
}
li {
/* border-top: solid 1px #ddd; */
padding: 0;
margin: 0 0 5px 0;
a {
font-weight: bold;
}
.date {
font-weight: bold;
font-size: small;
}
.category {
margin-left: 20px;
font-size: 8px;
height: 9px;
overflow: hidden;
color: #999;
}
.description {
margin-left: 20px;
font-size: small;
height: 18px;
overflow: hidden;
color: #999;
}
&:hover {
margin-bottom: 23px;
.description {
padding: 5px;
position: absolute;
height: auto;
overflow-y: scroll;
overflow-x: scroll;
color: #000;
background-color: #fff;
border: solid 1px #000;
z-index: 10;
}
&:last-child {
margin-bottom: 28px;
}
}
.description::after { content: "..."; }
}
}

View File

@@ -236,11 +236,17 @@ feature -- Access: Node
end end
recent_nodes (params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_NODE] recent_nodes (params: CMS_DATA_QUERY_PARAMETERS): ITERABLE [CMS_NODE]
-- List of the `a_rows' most recent nodes starting from `a_offset'. -- List of most recent nodes according to `params.offset' and `params.size'.
do do
Result := node_storage.recent_nodes (params.offset.to_integer_32, params.size.to_integer_32) Result := node_storage.recent_nodes (params.offset.to_integer_32, params.size.to_integer_32)
end end
recent_node_changes_before (params: CMS_DATA_QUERY_PARAMETERS; a_date: DATE_TIME): ITERABLE [CMS_NODE]
-- List of recent changes, before `a_date', according to `params' settings.
do
Result := node_storage.recent_node_changes_before (params.offset.to_integer_32, params.size.to_integer_32, a_date)
end
node (a_id: INTEGER_64): detachable CMS_NODE node (a_id: INTEGER_64): detachable CMS_NODE
-- Node by ID. -- Node by ID.
do do
@@ -275,14 +281,17 @@ feature -- Access: Node
else else
Result := l_partial_node Result := l_partial_node
end end
-- Update link with aliasing.
if Result /= Void and then Result.has_id then
Result.set_link (node_link (Result))
end
else else
Result := a_node Result := a_node
if Result.has_id and Result.link = Void then
Result.set_link (node_link (Result))
end
end end
check has_link: Result.has_id implies attached Result.link as lnk and then lnk.location.same_string (node_link (Result).location) end
-- Update link with aliasing.
if a_node /= Void and then a_node.has_id then
a_node.set_link (node_link (a_node))
end
-- Update partial user if needed. -- Update partial user if needed.
if if
@@ -321,6 +330,47 @@ feature -- Access: Node
end end
end end
feature -- Access: page/book outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
-- Children of node `a_node'.
-- note: this is the partial version of the nodes.
do
Result := node_storage.children (a_node)
end
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
-- Potential parent nodes for node `a_node'.
-- Ensure no cycle exists.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
across node_storage.available_parents_for_node (a_node) as ic loop
check distinct: not a_node.same_node (ic.item) end
if not is_node_a_parent_of (a_node, ic.item) then
Result.force (ic.item)
end
end
ensure
no_cycle: across Result as c all not is_node_a_parent_of (a_node, c.item) end
end
is_node_a_parent_of (a_node: CMS_NODE; a_child: CMS_NODE): BOOLEAN
-- Is `a_node' a direct or indirect parent of node `a_child'?
require
distinct_nodes: not a_node.same_node (a_child)
do
if
attached {CMS_PAGE} full_node (a_child) as l_child_page and then
attached l_child_page.parent as l_parent
then
if l_parent.same_node (a_node) then
Result := True
else
Result := is_node_a_parent_of (a_node, l_parent)
end
end
end
feature -- Permission Scope: Node feature -- Permission Scope: Node
has_permission_for_action_on_node (a_action: READABLE_STRING_8; a_node: CMS_NODE; a_user: detachable CMS_USER; ): BOOLEAN has_permission_for_action_on_node (a_action: READABLE_STRING_8; a_node: CMS_NODE; a_user: detachable CMS_USER; ): BOOLEAN
@@ -359,6 +409,7 @@ feature -- Change: Node
delete_node (a_node: CMS_NODE) delete_node (a_node: CMS_NODE)
-- Delete `a_node'. -- Delete `a_node'.
--! remove the node from the storage.
do do
reset_error reset_error
if a_node.has_id then if a_node.has_id then
@@ -377,7 +428,7 @@ feature -- Change: Node
trash_node (a_node: CMS_NODE) trash_node (a_node: CMS_NODE)
-- Trash node `a_node'. -- Trash node `a_node'.
--! remove the node from the storage. -- Soft delete
do do
reset_error reset_error
node_storage.trash_node (a_node) node_storage.trash_node (a_node)

View File

@@ -170,9 +170,12 @@ feature -- Access
Result.force ("trash own " + l_type_name) Result.force ("trash own " + l_type_name)
Result.force ("restore own " + l_type_name) Result.force ("restore own " + l_type_name)
Result.force ("view unpublished " + l_type_name)
Result.force ("view revisions own " + l_type_name) Result.force ("view revisions own " + l_type_name)
end end
end end
Result.force ("view trash")
end end
end end
@@ -241,7 +244,7 @@ feature -- Hooks
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
-- <Precursor> -- <Precursor>
do do
Result := <<"node-info">> Result := <<"?node-info">>
end end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
@@ -255,6 +258,7 @@ feature -- Hooks
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE) menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
local local
lnk: CMS_LOCAL_LINK lnk: CMS_LOCAL_LINK
perms: ARRAYED_LIST [READABLE_STRING_8]
do do
debug debug
create lnk.make ("List of nodes", "nodes") create lnk.make ("List of nodes", "nodes")
@@ -262,46 +266,89 @@ feature -- Hooks
end end
create lnk.make ("Trash", "trash") create lnk.make ("Trash", "trash")
a_menu_system.navigation_menu.extend (lnk) a_menu_system.navigation_menu.extend (lnk)
lnk.set_permission_arguments (<<"view trash">>)
create lnk.make ("Create ..", "node") create lnk.make ("Create ..", "node")
a_menu_system.navigation_menu.extend (lnk) a_menu_system.navigation_menu.extend (lnk)
if attached node_api as l_node_api then
create perms.make (2)
perms.force ("create any node")
across
l_node_api.content_types as ic
loop
perms.force ("create " + ic.item.name)
end
lnk.set_permission_arguments (perms)
end
end end
populate_recent_changes (a_changes: CMS_RECENT_CHANGE_CONTAINER; a_sources: LIST [READABLE_STRING_8]) recent_changes_sources: detachable LIST [READABLE_STRING_8]
-- <Precursor>
local
lst: ARRAYED_LIST [READABLE_STRING_8]
do
if
attached node_api as l_node_api and then
attached l_node_api.content_types as l_types and then
not l_types.is_empty
then
create lst.make (l_types.count)
across
l_types as ic
loop
lst.force (ic.item.name)
end
Result := lst
end
end
populate_recent_changes (a_changes: CMS_RECENT_CHANGE_CONTAINER; a_current_user: detachable CMS_USER)
local local
params: CMS_DATA_QUERY_PARAMETERS params: CMS_DATA_QUERY_PARAMETERS
ch: CMS_RECENT_CHANGE_ITEM ch: CMS_RECENT_CHANGE_ITEM
n: CMS_NODE n: CMS_NODE
l_info: STRING_8 l_info: STRING_8
l_src: detachable READABLE_STRING_8
l_nodes: ITERABLE [CMS_NODE]
l_date: detachable DATE_TIME
do do
create params.make (0, a_changes.limit) create params.make (0, a_changes.limit)
if attached node_api as l_node_api then if attached node_api as l_node_api then
across l_src := a_changes.source
l_node_api.content_types as ic l_date := a_changes.date
loop if l_date = Void then
a_sources.force (ic.item.name) create l_date.make_now_utc
end end
across l_node_api.recent_nodes (params) as ic loop l_nodes := l_node_api.recent_node_changes_before (params, l_date)
n := l_node_api.full_node (ic.item) across l_nodes as ic loop
create ch.make (n.content_type, create {CMS_LOCAL_LINK}.make (n.title, "node/" + n.id.out), n.modification_date) n := ic.item
if n.creation_date ~ n.modification_date then if l_src = Void or else l_src.is_case_insensitive_equal_general (n.content_type) then
l_info := "new" if l_node_api.has_permission_for_action_on_node ("view", n, a_current_user) then
if not n.is_published then n := l_node_api.full_node (n)
l_info.append (" (unpublished)") create ch.make (n.content_type, create {CMS_LOCAL_LINK}.make (n.title, "node/" + n.id.out), n.modification_date)
end if n.creation_date ~ n.modification_date then
else l_info := "new"
if n.is_trashed then if not n.is_published then
l_info := "trashed" l_info.append (" (unpublished)")
else end
l_info := "updated" else
if not n.is_published then if n.is_trashed then
l_info.append (" (unpublished)") l_info := "deleted"
else
l_info := "updated"
if not n.is_published then
l_info.append (" (unpublished)")
end
end
end end
ch.set_information (l_info)
ch.set_author (n.author)
a_changes.force (ch)
else
-- Forbidden
-- FIXME: provide a visual indication!
end end
end end
ch.set_information (l_info)
ch.set_author (n.author)
a_changes.force (ch)
end end
end end
end end

View File

@@ -10,7 +10,7 @@ deferred class
CMS_NODE CMS_NODE
inherit inherit
DEBUG_OUTPUT
REFACTORING_HELPER REFACTORING_HELPER
feature{NONE} -- Initialization feature{NONE} -- Initialization
@@ -166,6 +166,23 @@ feature -- Access: menu
link: detachable CMS_LOCAL_LINK link: detachable CMS_LOCAL_LINK
-- Associated menu link. -- Associated menu link.
feature -- Status report
debug_output: STRING_32
-- <Precursor>
do
create Result.make_from_string_general ("#")
Result.append_integer_64 (id)
Result.append_character (' ')
Result.append_character ('<')
Result.append_string_general (content_type)
Result.append_character ('>')
Result.append_character (' ')
Result.append_character ('%"')
Result.append (title)
Result.append_character ('%"')
end
feature -- Element change feature -- Element change
set_content (a_content: like content; a_summary: like summary; a_format: like format) set_content (a_content: like content; a_summary: like summary; a_format: like format)

View File

@@ -62,14 +62,14 @@ feature -- Forms ...
sum.set_text_value (a_node.summary) sum.set_text_value (a_node.summary)
end end
sum.set_label ("Summary") sum.set_label ("Summary")
sum.set_description ("This is the summary") sum.set_description ("Text displayed in short view.")
sum.set_is_required (False) sum.set_is_required (False)
create fset.make create fset.make
-- Add summary -- Add summary
fset.extend (sum) fset.extend (sum)
fset.extend_html_text("<br />") fset.extend_html_text("<br/>")
-- Add content -- Add content
fset.extend (ta) fset.extend (ta)
@@ -274,10 +274,10 @@ feature -- Output
if a_node.status = {CMS_NODE_API}.trashed then if a_node.status = {CMS_NODE_API}.trashed then
create lnk.make ("Trash", node_api.node_path (a_node) + "/trash") create lnk.make ("Delete", node_api.node_path (a_node) + "/delete")
lnk.set_weight (2) lnk.set_weight (2)
a_response.add_to_primary_tabs (lnk) a_response.add_to_primary_tabs (lnk)
elseif a_node /= Void and then a_node.has_id then elseif a_node.has_id then
-- Node in {{CMS_NODE_API}.published} or {CMS_NODE_API}.not_published} status. -- Node in {{CMS_NODE_API}.published} or {CMS_NODE_API}.not_published} status.
create lnk.make ("Edit", node_api.node_path (a_node) + "/edit") create lnk.make ("Edit", node_api.node_path (a_node) + "/edit")
lnk.set_weight (2) lnk.set_weight (2)
@@ -291,15 +291,16 @@ feature -- Output
end end
if if
node_api.has_permission_for_action_on_node ("delete", a_node, l_user) node_api.has_permission_for_action_on_node ("trash", a_node, l_user)
then then
create lnk.make ("Delete", node_api.node_path (a_node) + "/delete") create lnk.make ("Move to trash", node_api.node_path (a_node) + "/trash")
lnk.set_weight (3) lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk) a_response.add_to_primary_tabs (lnk)
end end
end end
end end
create s.make_empty create s.make_empty
s.append ("<div class=%"cms-node node-" + a_node.content_type + "%">")
s.append ("<div class=%"info%"> ") s.append ("<div class=%"info%"> ")
if attached a_node.author as l_author then if attached a_node.author as l_author then
s.append (" by ") s.append (" by ")
@@ -339,6 +340,7 @@ feature -- Output
s.append ("</p>") s.append ("</p>")
end end
s.append ("</div>")
a_response.set_title (a_node.title) a_response.set_title (a_node.title)
a_response.set_main_content (s) a_response.set_main_content (s)

View File

@@ -10,7 +10,10 @@ inherit
CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_PAGE] CMS_NODE_TYPE_WEBFORM_MANAGER [CMS_PAGE]
redefine redefine
content_type, content_type,
append_html_output_to append_html_output_to,
populate_form,
new_node,
update_node
end end
create create
@@ -21,140 +24,116 @@ feature -- Access
content_type: CMS_PAGE_NODE_TYPE content_type: CMS_PAGE_NODE_TYPE
-- Associated content type. -- Associated content type.
feature -- Forms ... feature -- Forms ...
-- fill_edit_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE) populate_form (response: NODE_RESPONSE; f: CMS_FORM; a_node: detachable CMS_NODE)
-- local local
-- ti: WSF_FORM_TEXT_INPUT ti: WSF_FORM_NUMBER_INPUT
-- fset: WSF_FORM_FIELD_SET fs: WSF_FORM_FIELD_SET
-- ta: WSF_FORM_TEXTAREA l_parent_id: INTEGER_64
-- tselect: WSF_FORM_SELECT do
-- opt: WSF_FORM_SELECT_OPTION Precursor (response, f, a_node)
-- do
-- create ti.make ("title")
-- ti.set_label ("Title")
-- ti.set_size (70)
-- if a_node /= Void then
-- ti.set_text_value (a_node.title)
-- end
-- ti.set_is_required (True)
-- f.extend (ti)
-- f.extend_html_text ("<br/>") if attached {CMS_PAGE} a_node as l_page then
create fs.make
fs.set_legend ("Pages structure")
fs.set_collapsible (True)
f.extend (fs)
create ti.make ("select_parent_node")
ti.set_label ("Parent page")
ti.set_description ("The parent page is the book structure.")
if attached l_page.parent as l_parent_node then
l_parent_id := l_parent_node.id
fs.extend_html_text ("<div><strong>Currently, the parent page is </strong> ")
fs.extend_html_text (response.node_html_link (l_parent_node, l_parent_node.title))
fs.extend_html_text ("</div>")
end
ti.set_validation_action (agent parent_validation (response, ?))
fs.extend (ti)
-- create ta.make ("body") -- FIXME: add notion of "weight"
-- ta.set_rows (10)
-- ta.set_cols (70)
-- if a_node /= Void then
-- ta.set_text_value (a_node.content)
-- end
---- ta.set_label ("Body")
-- ta.set_description ("This is the main content")
-- ta.set_is_required (False)
-- create fset.make if
-- fset.set_legend ("Body") attached {WSF_STRING} response.request.query_parameter ("parent") as p_parent and then
-- fset.extend (ta) p_parent.is_integer
then
l_parent_id := p_parent.integer_value.to_integer_64
end
if l_parent_id > 0 then
ti.set_default_value (l_parent_id.out)
end
end
end
-- fset.extend_html_text ("<br/>") update_node (a_response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: CMS_NODE)
-- <Precursor>
local
l_parent_id: INTEGER_64
do
Precursor (a_response, fd, a_node)
if attached {CMS_PAGE} a_node as l_page then
if attached fd.integer_item ("select_parent_node") as i_parent_node then
l_parent_id := i_parent_node.to_integer_64
end
if
l_parent_id > 0 and then
attached {CMS_PAGE} a_response.node_api.node (l_parent_id) as l_parent_page
then
l_page.set_parent (l_parent_page)
elseif l_parent_id = -1 then
-- Set parent to Void
l_page.set_parent (Void)
end
end
end
-- create tselect.make ("format") new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable like new_node): like content_type.new_node
-- tselect.set_label ("Body's format") -- <Precursor>
-- tselect.set_is_required (True) do
-- across Result := Precursor (response, fd, a_node)
-- content_type.available_formats as c if attached fd.integer_item ("select_parent_node") as l_parent_id then
-- loop if l_parent_id = -1 then
-- create opt.make (c.item.name, c.item.title) Result.set_parent (Void)
-- if attached c.item.html_help as f_help then elseif attached {CMS_PAGE} response.node_api.node (l_parent_id) as l_parent then
-- opt.set_description ("<ul>" + f_help + "</ul>") Result.set_parent (l_parent)
-- end end
-- tselect.add_option (opt) end
-- end end
-- if a_node /= Void and then attached a_node.format as l_format then
-- tselect.set_text_by_value (l_format)
-- end
-- fset.extend (tselect) parent_validation (a_response: NODE_RESPONSE; fd: WSF_FORM_DATA)
local
-- f.extend (fset) node_api: CMS_NODE_API
l_parent_id: INTEGER_64
-- end nid: INTEGER_64
l_parent_node: detachable CMS_NODE
-- change_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: like new_node) do
-- local node_api := a_response.node_api
-- b: detachable READABLE_STRING_8 if attached fd.integer_item ("select_parent_node") as s_parent_node then
-- f: detachable CONTENT_FORMAT l_parent_id := s_parent_node.to_integer_64
-- do else
-- if attached fd.integer_item ("id") as l_id and then l_id > 0 then l_parent_id := 0
-- check a_node.id = l_id end end
-- end if l_parent_id > 0 then
-- if attached fd.string_item ("title") as l_title then l_parent_node := node_api.node (l_parent_id)
-- a_node.set_title (l_title) if l_parent_node = Void then
-- end fd.report_invalid_field ("select_parent_node", "Invalid parent, not found id #" + l_parent_id.out)
else
-- if attached fd.string_item ("body") as l_body then nid := a_response.node_id_path_parameter
-- b := l_body if
-- end nid > 0 and then
-- if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then attached node_api.node (nid) as l_node and then
-- f := f_format node_api.is_node_a_parent_of (l_node, l_parent_node)
-- elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then then
-- f := f_format fd.report_invalid_field ("select_parent_node", "Invalid parent due to cycle (node #" + nid.out + " is already a parent of node #" + l_parent_id.out)
-- else end
-- f := response.api.formats.default_format end
-- end elseif l_parent_id = -1 or else l_parent_id = 0 then
-- if b /= Void then -- -1 is Used to unassign a parent node
-- a_node.set_content (b, Void, f.name) -- FIXME: summary -- 0 is not taken into account, any other input value is considered invalid.
-- end else
-- end fd.report_invalid_field ("select_parent_node", "Invalid node id")
end
-- new_node (response: NODE_RESPONSE; fd: WSF_FORM_DATA; a_node: detachable like new_node): CMS_PAGE end
-- -- <Precursor>
-- local
-- b: detachable READABLE_STRING_8
-- f: detachable CONTENT_FORMAT
-- l_node: detachable like new_node
-- do
-- l_node := a_node
-- if attached fd.integer_item ("id") as l_id and then l_id > 0 then
-- if l_node /= Void then
-- check l_node.id = l_id end
-- else
-- if attached {like new_node} response.node_api.node (l_id) as n then
-- l_node := n
-- else
-- -- FIXME: Error
-- end
-- end
-- end
-- if attached fd.string_item ("title") as l_title then
-- if l_node = Void then
-- l_node := content_type.new_node (Void)
-- l_node.set_title (l_title)
-- else
-- l_node.set_title (l_title)
-- end
-- else
-- if l_node = Void then
-- l_node := content_type.new_node_with_title ("...", Void)
-- end
-- end
-- l_node.set_author (response.user)
-- if attached fd.string_item ("body") as l_body then
-- b := l_body
-- end
-- if attached fd.string_item ("format") as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- elseif a_node /= Void and then attached a_node.format as s_format and then attached response.api.format (s_format) as f_format then
-- f := f_format
-- else
-- f := response.api.formats.default_format
-- end
-- if b /= Void then
-- l_node.set_content (b, Void, f.name)
-- end
-- Result := l_node
-- end
feature -- Output feature -- Output
@@ -162,8 +141,20 @@ feature -- Output
-- <Precursor> -- <Precursor>
local local
s: STRING s: STRING
node_api: CMS_NODE_API
lnk: CMS_LOCAL_LINK
do do
node_api := a_response.node_api
Precursor (a_node, a_response) Precursor (a_node, a_response)
if a_node.has_id and then not a_node.is_trashed then
if node_api.has_permission_for_action_on_node ("create", a_node, a_response.user) then
create lnk.make ("Add Child", "node/add/page?parent=" + a_node.id.out)
lnk.set_weight (3)
a_response.add_to_primary_tabs (lnk)
end
end
if attached a_response.main_content as l_main_content then if attached a_response.main_content as l_main_content then
s := l_main_content s := l_main_content
else else
@@ -171,11 +162,22 @@ feature -- Output
end end
if attached {CMS_PAGE} a_node as l_node_page then if attached {CMS_PAGE} a_node as l_node_page then
s.append ("<ul class=%"page-navigation%">")
if attached l_node_page.parent as l_parent_node then if attached l_node_page.parent as l_parent_node then
s.append ("<div>Parent page is ") s.append ("<li class=%"page-parent%">Go to parent page ")
s.append (a_response.link (l_parent_node.title + " (#" + l_parent_node.id.out + ")", a_response.node_api.node_path (l_parent_node), Void)) s.append (a_response.link (l_parent_node.title, a_response.node_api.node_path (l_parent_node), Void))
s.append ("</div>") s.append ("</li>")
end end
if attached node_api.children (a_node) as l_children then
across
l_children as ic
loop
s.append ("<li>")
s.append (a_response.link (ic.item.title, a_response.node_api.node_path (ic.item), Void))
s.append ("</li>")
end
end
s.append ("</ul>")
end end
a_response.set_main_content (s) a_response.set_main_content (s)

View File

@@ -37,91 +37,30 @@ feature -- Execution
-- Computed response message. -- Computed response message.
local local
b: STRING_8 b: STRING_8
f: like new_edit_form
fd: detachable WSF_FORM_DATA
nid: INTEGER_64 nid: INTEGER_64
do do
create b.make_empty create b.make_empty
nid := node_id_path_parameter (request) nid := node_id_path_parameter
if if
nid > 0 and then nid > 0 and then
attached node_api.node (nid) as l_node attached node_api.node (nid) as l_node
then then
if attached node_api.node_type_for (l_node) as l_type then if attached node_api.node_type_for (l_node) as l_type then
fixme ("refactor: process_edit, process_create process edit")
if if
location.ends_with_general ("/edit") and then location.ends_with_general ("/edit") and then
node_api.has_permission_for_action_on_node ("edit", l_node, user) node_api.has_permission_for_action_on_node ("edit", l_node, user)
then then
f := new_edit_form (l_node, url (location, Void), "edit-" + l_type.name, l_type) edit_node (l_node, l_type, b)
invoke_form_alter (f, fd)
if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b))
f.process (Current)
fd := f.last_data
end
if l_node.has_id then
add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (l_node) + "/edit"), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (l_node) + "/delete"), primary_tabs)
end
if attached redirection as l_location then
-- FIXME: Hack for now
set_title (l_node.title)
b.append (html_encoded (l_type.title) + " saved")
else
set_title (formatted_string (translation ("Edit $1 #$2", Void), [l_type.title, l_node.id]))
f.append_to_html (wsf_theme, b)
end
elseif elseif
location.ends_with_general ("/delete") and then location.ends_with_general ("/delete") and then
node_api.has_permission_for_action_on_node ("delete", l_node, user) node_api.has_permission_for_action_on_node ("delete", l_node, user)
then then
f := new_delete_form (l_node, url (location, Void), "delete-" + l_type.name, l_type) delete_node (l_node, l_type, b)
invoke_form_alter (f, fd)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
end
if l_node.has_id then
add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (l_node) + "/edit"), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (l_node) + "/delete"), primary_tabs)
end
if attached redirection as l_location then
-- FIXME: Hack for now
set_title (l_node.title)
b.append (html_encoded (l_type.title) + " deleted")
else
set_title (formatted_string (translation ("Delete $1 #$2", Void), [l_type.title, l_node.id]))
f.append_to_html (wsf_theme, b)
end
elseif elseif
location.ends_with_general ("/trash") and then location.ends_with_general ("/trash") and then
node_api.has_permission_for_action_on_node ("trash", l_node, user) node_api.has_permission_for_action_on_node ("trash", l_node, user)
then then
f := new_trash_form (l_node, url (location, Void), "trash-" + l_type.name, l_type) trash_node (l_node, l_type, b)
invoke_form_alter (f, fd)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
end
if l_node.has_id then
add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make ("Trash", node_api.node_path (l_node) + "/Trash"), primary_tabs)
end
if attached redirection as l_location then
-- FIXME: Hack for now
set_title (l_node.title)
b.append (html_encoded (l_type.title) + " trashed")
else
set_title (formatted_string (translation ("Trash $1 #$2", Void), [l_type.title, l_node.id]))
f.append_to_html (wsf_theme, b)
end
else else
b.append ("<h1>") b.append ("<h1>")
b.append (translation ("Access denied", Void)) b.append (translation ("Access denied", Void))
@@ -135,29 +74,8 @@ feature -- Execution
attached node_api.node_type (p_type.value) as l_type attached node_api.node_type (p_type.value) as l_type
then then
if has_permissions (<<"create any", "create " + l_type.name>>) then if has_permissions (<<"create any", "create " + l_type.name>>) then
if attached l_type.new_node (Void) as l_node then -- create new node
f := new_edit_form (l_node, url (location, Void), "edit-" + l_type.name, l_type) create_new_node (l_type, b)
invoke_form_alter (f, fd)
if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_node, l_type, b))
f.process (Current)
fd := f.last_data
end
set_title ("Edit " + html_encoded (l_type.title) + " #" + l_node.id.out)
if l_node.has_id then
add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (l_node) + "/edit"), primary_tabs)
end
f.append_to_html (wsf_theme, b)
else
b.append ("<h1>")
b.append (translation ("Server error", Void))
b.append ("</h1>")
end
else else
b.append ("<h1>") b.append ("<h1>")
b.append (translation ("Access denied", Void)) b.append (translation ("Access denied", Void))
@@ -185,6 +103,126 @@ feature -- Execution
set_main_content (b) set_main_content (b)
end end
feature {NONE} -- Create a new node
create_new_node (a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8)
local
f: like new_edit_form
fd: detachable WSF_FORM_DATA
do
if attached a_type.new_node (Void) as l_node then
-- create new node
f := new_edit_form (l_node, url (location, Void), "edit-" + a_type.name, a_type)
hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, l_node, a_type, b))
f.process (Current)
fd := f.last_data
end
if l_node.has_id then
set_title ("Edit " + html_encoded (a_type.title) + " #" + l_node.id.out)
add_to_menu (node_local_link (l_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (l_node) + "/edit"), primary_tabs)
else
set_title ("New " + html_encoded (a_type.title))
end
f.append_to_html (wsf_theme, b)
else
b.append ("<h1>")
b.append (translation ("Server error", Void))
b.append ("</h1>")
end
end
edit_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8)
local
f: like new_edit_form
fd: detachable WSF_FORM_DATA
do
f := new_edit_form (A_node, url (location, Void), "edit-" + a_type.name, a_type)
hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.validation_actions.extend (agent edit_form_validate (?, b))
f.submit_actions.extend (agent edit_form_submit (?, a_node, a_type, b))
f.process (Current)
fd := f.last_data
end
if a_node.has_id then
add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (a_node) + "/edit"), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (a_node) + "/delete"), primary_tabs)
end
if attached redirection as l_location then
-- FIXME: Hack for now
set_title (a_node.title)
b.append (html_encoded (a_type.title) + " saved")
else
set_title (formatted_string (translation ("Edit $1 #$2", Void), [a_type.title, a_node.id]))
f.append_to_html (wsf_theme, b)
end
end
delete_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8)
local
f: like new_edit_form
fd: detachable WSF_FORM_DATA
do
f := new_delete_form (a_node, url (location, Void), "delete-" + a_type.name, a_type)
hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
end
if a_node.has_id then
add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make (translation ("Edit", Void), node_api.node_path (a_node) + "/edit"), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make ("Delete", node_api.node_path (a_node) + "/delete"), primary_tabs)
end
if attached redirection as l_location then
-- FIXME: Hack for now
set_title (a_node.title)
b.append (html_encoded (a_type.title) + " deleted")
else
set_title (formatted_string (translation ("Delete $1 #$2", Void), [a_type.title, a_node.id]))
f.append_to_html (wsf_theme, b)
end
end
trash_node (a_node: CMS_NODE; a_type: CMS_NODE_TYPE [CMS_NODE]; b: STRING_8)
local
f: like new_edit_form
fd: detachable WSF_FORM_DATA
do
f := new_trash_form (a_node, url (location, Void), "trash-" + a_type.name, a_type)
hooks.invoke_form_alter (f, fd, Current)
if request.is_post_request_method then
f.process (Current)
fd := f.last_data
end
if a_node.has_id then
add_to_menu (node_local_link (a_node, translation ("View", Void)), primary_tabs)
add_to_menu (create {CMS_LOCAL_LINK}.make ("Trash", node_api.node_path (a_node) + "/Trash"), primary_tabs)
end
if attached redirection as l_location then
-- FIXME: Hack for now
set_title (a_node.title)
b.append (html_encoded (a_type.title) + " trashed")
else
set_title (formatted_string (translation ("Trash $1 #$2", Void), [a_type.title, a_node.id]))
f.append_to_html (wsf_theme, b)
end
end
feature -- Form feature -- Form
edit_form_validate (fd: WSF_FORM_DATA; b: STRING) edit_form_validate (fd: WSF_FORM_DATA; b: STRING)
@@ -221,6 +259,7 @@ feature -- Form
s: STRING s: STRING
l_path_alias: detachable READABLE_STRING_8 l_path_alias: detachable READABLE_STRING_8
do do
fixme ("Refactor code per operacion: Preview, Save")
l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview") l_preview := attached {WSF_STRING} fd.item ("op") as l_op and then l_op.same_string ("Preview")
if not l_preview then if not l_preview then
debug ("cms") debug ("cms")
@@ -250,6 +289,7 @@ feature -- Form
fixme ("for now, publishing is not implemented, so let's assume any node saved is published.") -- FIXME fixme ("for now, publishing is not implemented, so let's assume any node saved is published.") -- FIXME
l_node.mark_published l_node.mark_published
node_api.save_node (l_node) node_api.save_node (l_node)
if attached user as u then if attached user as u then
api.log ("node", api.log ("node",
@@ -290,7 +330,6 @@ feature -- Form
th: WSF_FORM_HIDDEN_INPUT th: WSF_FORM_HIDDEN_INPUT
do do
create f.make (a_url, a_name) create f.make (a_url, a_name)
create th.make ("node-id") create th.make ("node-id")
if a_node /= Void then if a_node /= Void then
th.set_text_value (a_node.id.out) th.set_text_value (a_node.id.out)
@@ -300,8 +339,8 @@ feature -- Form
f.extend (th) f.extend (th)
populate_form (a_node_type, f, a_node) populate_form (a_node_type, f, a_node)
f.extend_html_text ("<br/>")
f.extend_html_text ("<br/>")
create ts.make ("op") create ts.make ("op")
ts.set_default_value ("Save") ts.set_default_value ("Save")
f.extend (ts) f.extend (ts)
@@ -336,7 +375,7 @@ feature -- Form
ts.set_default_value (translation ("Delete")) ts.set_default_value (translation ("Delete"))
]") ]")
f.extend (ts) f.extend (ts)
fixme ("wsf_html: add support for HTML5 input attributes!!! ") to_implement ("Refactor code to use the new wsf_html HTML5 support")
f.extend_html_text("<input type='submit' value='Cancel' formmethod='GET', formaction='/node/"+a_node.id.out+"'>" ) f.extend_html_text("<input type='submit' value='Cancel' formmethod='GET', formaction='/node/"+a_node.id.out+"'>" )
end end

View File

@@ -99,6 +99,15 @@ feature -- HTTP Methods
edit_response.execute edit_response.execute
elseif req.percent_encoded_path_info.ends_with ("/revision") then elseif req.percent_encoded_path_info.ends_with ("/revision") then
do_revisions (req, res) do_revisions (req, res)
-- elseif req.percent_encoded_path_info.ends_with ("/add_child/page") then
-- -- Add child node
-- l_nid := node_id_path_parameter (req)
-- if l_nid > 0 then
-- -- create a new child node with node id `l_id' as parent.
-- create_new_node (req, res)
-- else
-- send_not_found (req, res)
-- end
else else
-- Display existing node -- Display existing node
l_nid := node_id_path_parameter (req) l_nid := node_id_path_parameter (req)
@@ -117,15 +126,29 @@ feature -- HTTP Methods
then then
l_node := node_api.revision_node (l_nid, l_rev) l_node := node_api.revision_node (l_nid, l_rev)
end end
if if l_node = Void then
l_node /= Void and then (l_rev > 0 or else l_node.is_published)
then
create view_response.make (req, res, api, node_api)
view_response.set_node (l_node)
view_response.set_revision (l_rev)
view_response.execute
else
send_not_found (req, res) send_not_found (req, res)
else
if
l_rev > 0 or else l_node.is_published
then
create view_response.make (req, res, api, node_api)
view_response.set_node (l_node)
view_response.set_revision (l_rev)
view_response.execute
elseif
attached current_user (req) as l_user and then
( node_api.is_author_of_node (l_user, l_node)
or else api.user_api.user_has_permission (l_user, "view unpublished " + l_node.content_type)
)
then
create view_response.make (req, res, api, node_api)
view_response.set_node (l_node)
view_response.set_revision (l_rev)
view_response.execute
else
send_access_denied (req, res)
end
end end
else else
-- redirect_to (req.absolute_script_url ("/node/"), res) -- New node. -- redirect_to (req.absolute_script_url ("/node/"), res) -- New node.
@@ -166,6 +189,9 @@ feature -- HTTP Methods
elseif req.percent_encoded_path_info.starts_with ("/node/add/") then elseif req.percent_encoded_path_info.starts_with ("/node/add/") then
create edit_response.make (req, res, api, node_api) create edit_response.make (req, res, api, node_api)
edit_response.execute edit_response.execute
elseif req.percent_encoded_path_info.ends_with ("/add_child/page") then
create edit_response.make (req, res, api, node_api)
edit_response.execute
else else
to_implement ("REST API") to_implement ("REST API")
send_not_implemented ("REST API not yet implemented", req, res) send_not_implemented ("REST API not yet implemented", req, res)
@@ -179,8 +205,8 @@ feature -- HTTP Methods
send_not_implemented ("REST API not yet implemented", req, res) send_not_implemented ("REST API not yet implemented", req, res)
end end
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE) do_trash (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor> -- Trash a node, soft delete.
do do
if attached current_user (req) as l_user then if attached current_user (req) as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
@@ -188,8 +214,8 @@ feature -- HTTP Methods
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("delete", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("trash", l_node, current_user (req)) then
node_api.delete_node (l_node) node_api.trash_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else else
send_access_denied (req, res) send_access_denied (req, res)
@@ -214,8 +240,8 @@ feature -- HTTP Methods
feature {NONE} -- Trash:Restore feature {NONE} -- Trash:Restore
do_trash (req: WSF_REQUEST; res: WSF_RESPONSE) do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Trash a node from the database. -- Delete a node from the database.
do do
if attached current_user (req) as l_user then if attached current_user (req) as l_user then
if attached {WSF_STRING} req.path_parameter ("id") as l_id then if attached {WSF_STRING} req.path_parameter ("id") as l_id then
@@ -223,8 +249,8 @@ feature {NONE} -- Trash:Restore
l_id.is_integer and then l_id.is_integer and then
attached node_api.node (l_id.integer_value) as l_node attached node_api.node (l_id.integer_value) as l_node
then then
if node_api.has_permission_for_action_on_node ("trash", l_node, current_user (req)) then if node_api.has_permission_for_action_on_node ("delete", l_node, current_user (req)) then
node_api.trash_node (l_node) node_api.delete_node (l_node)
res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url (""))) res.send (create {CMS_REDIRECTION_RESPONSE_MESSAGE}.make (req.absolute_script_url ("")))
else else
send_access_denied (req, res) send_access_denied (req, res)
@@ -355,5 +381,4 @@ feature {NONE} -- Node
send_bad_request (req, res) send_bad_request (req, res)
end end
end end
end end

View File

@@ -27,12 +27,12 @@ feature -- Access
feature -- Helpers feature -- Helpers
node_id_path_parameter (req: WSF_REQUEST): INTEGER_64 node_id_path_parameter: INTEGER_64
-- Node id passed as path parameter for request `req'. -- Node id passed as path parameter for request `request'.
local local
s: STRING s: STRING
do do
if attached {WSF_STRING} req.path_parameter ("id") as p_nid then if attached {WSF_STRING} request.path_parameter ("id") as p_nid then
s := p_nid.value s := p_nid.value
if s.is_integer_64 then if s.is_integer_64 then
Result := s.to_integer_64 Result := s.to_integer_64

View File

@@ -56,13 +56,12 @@ feature -- Execution
process process
-- Computed response message. -- Computed response message.
local local
b: detachable STRING_8
nid: INTEGER_64 nid: INTEGER_64
l_node: like node l_node: like node
do do
l_node := node l_node := node
if l_node = Void then if l_node = Void then
nid := node_id_path_parameter (request) nid := node_id_path_parameter
if nid > 0 then if nid > 0 then
l_node := node_api.node (nid) l_node := node_api.node (nid)
end end
@@ -85,6 +84,8 @@ feature -- Execution
if l_node /= Void and revision > 0 then if l_node /= Void and revision > 0 then
set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title)) set_title ("Revision #" + revision.out + " of " + html_encoded (l_node.title))
end end
end end
end end

View File

@@ -13,6 +13,7 @@
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\..\cms.ecf"/> <library name="cms" location="..\..\cms.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model.ecf" readonly="false"/> <library name="cms_model" location="..\..\library\model\cms_model.ecf" readonly="false"/>
<library name="cms_recent_changes_module" location="..\..\modules\recent_changes\recent_changes.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/> <library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/> <library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="http_authorization" location="$ISE_LIBRARY\contrib\library\network\authentication\http_authorization\http_authorization.ecf" readonly="false"/> <library name="http_authorization" location="$ISE_LIBRARY\contrib\library\network\authentication\http_authorization\http_authorization.ecf" readonly="false"/>

View File

@@ -49,6 +49,13 @@ feature -- Persistence
end end
end end
delete_node (a_node: CMS_NODE)
-- remove node extensions.
require
a_node_accepted: is_accepted (a_node)
deferred
end
feature {NONE} -- Persistence implementation feature {NONE} -- Persistence implementation
store (a_node: G) store (a_node: G)

View File

@@ -44,7 +44,7 @@ feature {NONE} -- Implementation
extended_store (a_node: CMS_NODE) extended_store (a_node: CMS_NODE)
-- Store extended data from `a_node'. -- Store extended data from `a_node'.
require require
not error_handler.has_error not_has_error: not error_handler.has_error
do do
if attached node_storage_extension (a_node) as ext then if attached node_storage_extension (a_node) as ext then
ext.store_node (a_node) ext.store_node (a_node)
@@ -54,13 +54,23 @@ feature {NONE} -- Implementation
extended_load (a_node: CMS_NODE) extended_load (a_node: CMS_NODE)
-- Load extended data into `a_node'. -- Load extended data into `a_node'.
require require
not error_handler.has_error not_has_error: not error_handler.has_error
do do
if attached node_storage_extension (a_node) as ext then if attached node_storage_extension (a_node) as ext then
ext.load_node (a_node) ext.load_node (a_node)
end end
end end
extended_delete (a_node: CMS_NODE)
-- Delete extended data related to node `a_node'.
require
not_has_error: not error_handler.has_error
do
if attached node_storage_extension (a_node) as ext then
ext.delete_node (a_node)
end
end
feature -- Access feature -- Access
nodes_count: NATURAL_64 nodes_count: NATURAL_64
@@ -90,6 +100,11 @@ feature -- Access
deferred deferred
end end
recent_node_changes_before (a_lower: INTEGER; a_count: INTEGER; a_date: DATE_TIME): LIST [CMS_NODE]
-- List of recent changes, before `a_date', according to `params' settings.
deferred
end
node_by_id (a_id: INTEGER_64): detachable CMS_NODE node_by_id (a_id: INTEGER_64): detachable CMS_NODE
-- Retrieve node by id `a_id', if any. -- Retrieve node by id `a_id', if any.
require require
@@ -112,6 +127,21 @@ feature -- Access
deferred deferred
end end
feature -- Access: outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
-- Children of node `a_node'.
-- note: this is the partial version of the nodes.
deferred
end
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
-- Given the node `a_node', return the list of possible parent nodes id
deferred
ensure
a_node_excluded: across Result as ic all not a_node.same_node (ic.item) end
end
feature -- Change: Node feature -- Change: Node
save_node (a_node: CMS_NODE) save_node (a_node: CMS_NODE)
@@ -145,16 +175,19 @@ feature -- Change: Node
delete_node (a_node: CMS_NODE) delete_node (a_node: CMS_NODE)
-- Delete `a_node'. -- Delete `a_node'.
require
valid_node_id: a_node.has_id
do do
if a_node.has_id then -- TODO
delete_node_by_id (a_node.id) -- Check if we need to use a transaction
end -- we delete a node
-- node_revisions
-- and extensions (PAGE, BLOG, etc).
delete_node_base (a_node)
end end
delete_node_by_id (a_id: INTEGER_64) delete_node_base (a_node: CMS_NODE)
-- Remove node by id `a_id'. -- Remove node `a_node'.
require
valid_node_id: a_id > 0
deferred deferred
end end

View File

@@ -59,6 +59,12 @@ feature -- Access: node
create {ARRAYED_LIST [CMS_NODE]} Result.make (0) create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end end
recent_node_changes_before (a_lower: INTEGER; a_count: INTEGER; a_date: DATE_TIME): LIST [CMS_NODE]
-- List of recent changes, before `a_date', according to `params' settings.
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
node_by_id (a_id: INTEGER_64): detachable CMS_NODE node_by_id (a_id: INTEGER_64): detachable CMS_NODE
-- <Precursor> -- <Precursor>
do do
@@ -80,6 +86,19 @@ feature -- Access: node
create {ARRAYED_LIST [CMS_USER]} Result.make (0) create {ARRAYED_LIST [CMS_USER]} Result.make (0)
end end
feature -- Access: outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
-- <Precursor>
do
end
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
-- <Precursor>
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
end
feature -- Node feature -- Node
new_node (a_node: CMS_NODE) new_node (a_node: CMS_NODE)
@@ -87,7 +106,7 @@ feature -- Node
do do
end end
delete_node_by_id (a_id: INTEGER_64) delete_node_base (a_node: CMS_NODE)
-- <Precursor> -- <Precursor>
do do
end end

View File

@@ -53,11 +53,6 @@ feature -- Access
end end
sql_forth sql_forth
end end
-- across
-- Result as ic
-- loop
-- fill_node (ic.item)
-- end
end end
node_revisions (a_node: CMS_NODE): LIST [CMS_NODE] node_revisions (a_node: CMS_NODE): LIST [CMS_NODE]
@@ -95,7 +90,7 @@ feature -- Access
create {ARRAYED_LIST [CMS_NODE]} Result.make (0) create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset error_handler.reset
write_information_log (generator + ".trash_nodes") write_information_log (generator + ".trashed_nodes")
from from
create l_parameters.make (1) create l_parameters.make (1)
@@ -124,11 +119,11 @@ feature -- Access
create {ARRAYED_LIST [CMS_NODE]} Result.make (0) create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset error_handler.reset
write_information_log (generator + ".nodes") write_information_log (generator + ".recent_nodes")
from from
create l_parameters.make (2) create l_parameters.make (2)
l_parameters.put (a_count, "rows") l_parameters.put (a_count, "size")
l_parameters.put (a_lower, "offset") l_parameters.put (a_lower, "offset")
sql_query (sql_select_recent_nodes, l_parameters) sql_query (sql_select_recent_nodes, l_parameters)
sql_start sql_start
@@ -142,13 +137,41 @@ feature -- Access
end end
end end
recent_node_changes_before (a_lower: INTEGER; a_count: INTEGER; a_date: DATE_TIME): LIST [CMS_NODE]
-- List of recent changes, before `a_date', according to `params' settings.
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
write_information_log (generator + ".recent_node_changes_before")
from
create l_parameters.make (3)
l_parameters.put (a_count, "size")
l_parameters.put (a_lower, "offset")
l_parameters.put (a_date, "date")
sql_query (sql_select_recent_node_changes_before, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
end
node_by_id (a_id: INTEGER_64): detachable CMS_NODE node_by_id (a_id: INTEGER_64): detachable CMS_NODE
-- Retrieve node by id `a_id', if any. -- Retrieve node by id `a_id', if any.
local local
l_parameters: STRING_TABLE [ANY] l_parameters: STRING_TABLE [ANY]
do do
error_handler.reset error_handler.reset
write_information_log (generator + ".node") write_information_log (generator + ".node_by_id")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_id, "nid") l_parameters.put (a_id, "nid")
sql_query (sql_select_node_by_id, l_parameters) sql_query (sql_select_node_by_id, l_parameters)
@@ -163,7 +186,7 @@ feature -- Access
l_parameters: STRING_TABLE [ANY] l_parameters: STRING_TABLE [ANY]
do do
error_handler.reset error_handler.reset
write_information_log (generator + ".node") write_information_log (generator + ".node_by_id_and_revision")
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_node_id, "nid") l_parameters.put (a_node_id, "nid")
l_parameters.put (a_revision, "revision") l_parameters.put (a_revision, "revision")
@@ -227,6 +250,58 @@ feature -- Access
-- end -- end
end end
feature -- Access: outline
children (a_node: CMS_NODE): detachable LIST [CMS_NODE]
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
write_information_log (generator + ".children")
from
create l_parameters.make (1)
l_parameters.put (a_node.id, "nid")
sql_query (sql_select_children_of_node, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
end
available_parents_for_node (a_node: CMS_NODE): LIST [CMS_NODE]
-- <Precursor>
local
l_parameters: STRING_TABLE [detachable ANY]
do
create {ARRAYED_LIST [CMS_NODE]} Result.make (0)
error_handler.reset
write_information_log (generator + ".available_parents_for_node")
from
create l_parameters.make (1)
l_parameters.put (a_node.id, "nid")
sql_query (sql_select_available_parents_for_node, l_parameters)
sql_start
until
sql_after
loop
if attached fetch_node as l_node then
Result.force (l_node)
end
sql_forth
end
end
feature -- Change: Node feature -- Change: Node
new_node (a_node: CMS_NODE) new_node (a_node: CMS_NODE)
@@ -241,34 +316,42 @@ feature -- Change: Node
store_node (a_node) store_node (a_node)
end end
delete_node_by_id (a_id: INTEGER_64) trash_node_by_id (a_id: INTEGER_64)
-- Remove node by id `a_id'. -- Remove node by id `a_id'.
local local
l_parameters: STRING_TABLE [ANY] l_parameters: STRING_TABLE [ANY]
do do
write_information_log (generator + ".delete_node {" + a_id.out + "}") write_information_log (generator + ".trash_node_by_id {" + a_id.out + "}")
error_handler.reset error_handler.reset
create l_parameters.make (3) create l_parameters.make (3)
l_parameters.put (create {DATE_TIME}.make_now_utc, "changed") l_parameters.put (create {DATE_TIME}.make_now_utc, "changed")
l_parameters.put ({CMS_NODE_API}.trashed, "status") l_parameters.put ({CMS_NODE_API}.trashed, "status")
l_parameters.put (a_id, "nid") l_parameters.put (a_id, "nid")
sql_change (sql_delete_node, l_parameters) sql_change (sql_trash_node, l_parameters)
end end
trash_node_by_id (a_id: INTEGER_64) delete_node_base (a_node: CMS_NODE)
-- <Precursor> -- <Precursor>
local local
l_parameters: STRING_TABLE [ANY] l_parameters: STRING_TABLE [ANY]
l_time: DATE_TIME l_time: DATE_TIME
do do
create l_time.make_now_utc create l_time.make_now_utc
write_information_log (generator + ".trash_node {" + a_id.out + "}") write_information_log (generator + ".delete_node_base {" + a_node.id.out + "}")
error_handler.reset error_handler.reset
create l_parameters.make (1) create l_parameters.make (1)
l_parameters.put (a_id, "nid") l_parameters.put (a_node.id, "nid")
sql_change (sql_trash_node, l_parameters) sql_change (sql_delete_node, l_parameters)
-- we remove node_revisions and pages.
-- Check: maybe we need a transaction.
sql_change (sql_delete_node_revisions, l_parameters)
if not error_handler.has_error then
extended_delete (a_node)
end
end end
restore_node_by_id (a_id: INTEGER_64) restore_node_by_id (a_id: INTEGER_64)
@@ -278,7 +361,7 @@ feature -- Change: Node
l_time: DATE_TIME l_time: DATE_TIME
do do
create l_time.make_now_utc create l_time.make_now_utc
write_information_log (generator + ".restore_node {" + a_id.out + "}") write_information_log (generator + ".restore_node_by_id {" + a_id.out + "}")
error_handler.reset error_handler.reset
create l_parameters.make (1) create l_parameters.make (1)
@@ -402,7 +485,9 @@ feature {NONE} -- Queries
sql_select_node_by_id_and_revision: STRING = "SELECT nodes.nid, node_revisions.revision, nodes.type, node_revisions.title, node_revisions.summary, node_revisions.content, node_revisions.format, node_revisions.author, nodes.publish, nodes.created, node_revisions.changed, node_revisions.status FROM nodes INNER JOIN node_revisions ON nodes.nid = node_revisions.nid WHERE nodes.nid = :nid AND node_revisions.revision = :revision ORDER BY node_revisions.revision DESC;" sql_select_node_by_id_and_revision: STRING = "SELECT nodes.nid, node_revisions.revision, nodes.type, node_revisions.title, node_revisions.summary, node_revisions.content, node_revisions.format, node_revisions.author, nodes.publish, nodes.created, node_revisions.changed, node_revisions.status FROM nodes INNER JOIN node_revisions ON nodes.nid = node_revisions.nid WHERE nodes.nid = :nid AND node_revisions.revision = :revision ORDER BY node_revisions.revision DESC;"
sql_select_recent_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes ORDER BY nid DESC, publish DESC LIMIT :rows OFFSET :offset ;" sql_select_recent_nodes: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes ORDER BY nid DESC, publish DESC LIMIT :size OFFSET :offset ;"
sql_select_recent_node_changes_before: STRING = "SELECT nid, revision, type, title, summary, content, format, author, publish, created, changed, status FROM nodes WHERE changed <= :date ORDER BY changed DESC, nid DESC LIMIT :size OFFSET :offset ;"
sql_insert_node: STRING = "INSERT INTO nodes (revision, type, title, summary, content, format, publish, created, changed, status, author) VALUES (:revision, :type, :title, :summary, :content, :format, :publish, :created, :changed, :status, :author);" sql_insert_node: STRING = "INSERT INTO nodes (revision, type, title, summary, content, format, publish, created, changed, status, author) VALUES (:revision, :type, :title, :summary, :content, :format, :publish, :created, :changed, :status, :author);"
-- SQL Insert to add a new node. -- SQL Insert to add a new node.
@@ -410,10 +495,10 @@ feature {NONE} -- Queries
sql_update_node : STRING = "UPDATE nodes SET revision=:revision, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, status=:status, author=:author WHERE nid=:nid;" sql_update_node : STRING = "UPDATE nodes SET revision=:revision, type=:type, title=:title, summary=:summary, content=:content, format=:format, publish=:publish, changed=:changed, status=:status, author=:author WHERE nid=:nid;"
-- SQL update node. -- SQL update node.
sql_delete_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid" sql_trash_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
-- Soft deletion with free metadata. -- Soft deletion with free metadata.
sql_trash_node: STRING = "DELETE FROM nodes WHERE nid=:nid" sql_delete_node: STRING = "DELETE FROM nodes WHERE nid=:nid"
-- Physical deletion with free metadata. -- Physical deletion with free metadata.
sql_restore_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid" sql_restore_node: STRING = "UPDATE nodes SET changed=:changed, status =:status WHERE nid=:nid"
@@ -426,6 +511,20 @@ feature {NONE} -- Queries
Sql_last_insert_node_revision: STRING = "SELECT MAX(revision) FROM node_revisions;" Sql_last_insert_node_revision: STRING = "SELECT MAX(revision) FROM node_revisions;"
Sql_last_insert_node_revision_for_nid: STRING = "SELECT MAX(revision) FROM node_revisions WHERE nid=:nid;" Sql_last_insert_node_revision_for_nid: STRING = "SELECT MAX(revision) FROM node_revisions WHERE nid=:nid;"
sql_select_available_parents_for_node : STRING = "[
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid AND node.nid != :nid
WHERE node.nid != :nid AND pn.parent != :nid AND node.status != -1 GROUP BY node.nid, node.revision;
]"
sql_select_children_of_node: STRING = "[
SELECT node.nid, node.revision, node.type, title, summary, content, format, author, publish, created, changed, status
FROM nodes node LEFT JOIN page_nodes pn ON node.nid = pn.nid
WHERE pn.parent = :nid AND node.status != -1 GROUP BY node.nid, node.revision;
]"
sql_delete_node_revisions: STRING = "DELETE FROM node_revisions WHERE nid=:nid;"
feature {NONE} -- Sql Queries: USER_ROLES collaborators, author feature {NONE} -- Sql Queries: USER_ROLES collaborators, author
Select_user_author: STRING = "SELECT uid, name, password, salt, email, users.status, users.created, signed FROM nodes INNER JOIN users ON nodes.author=users.uid AND nodes.nid = :nid AND nodes.revision = :revision;" Select_user_author: STRING = "SELECT uid, name, password, salt, email, users.status, users.created, signed FROM nodes INNER JOIN users ON nodes.author=users.uid AND nodes.nid = :nid AND nodes.revision = :revision;"

View File

@@ -55,7 +55,7 @@ feature -- Persistence
error_handler.reset error_handler.reset
-- Check existing record, if any. -- Check existing record, if any.
if attached node_data (a_node) as d then if attached node_data (a_node) as d then
l_update := True l_update := a_node.revision = d.revision
l_previous_parent_id := d.parent_id l_previous_parent_id := d.parent_id
end end
if not has_error then if not has_error then
@@ -112,9 +112,22 @@ feature -- Persistence
end end
end end
delete_node (a_node: CMS_PAGE)
-- <Precursor>
local
l_parameters: STRING_TABLE [ANY]
do
if a_node.has_id then
create l_parameters.make (1)
l_parameters.put (a_node.id, "nid")
sql_change (sql_delete_node_data, l_parameters)
end
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
node_data (a_node: CMS_NODE): detachable TUPLE [parent_id: INTEGER_64] node_data (a_node: CMS_NODE): detachable TUPLE [revision: INTEGER_64; parent_id: INTEGER_64]
-- Node extension data for node `a_node' as tuple. -- Node extension data for node `a_node' as tuple.
local local
l_parameters: STRING_TABLE [ANY] l_parameters: STRING_TABLE [ANY]
@@ -129,17 +142,20 @@ feature {NONE} -- Implementation
n := sql_rows_count n := sql_rows_count
if n = 1 then if n = 1 then
-- nid, revision, parent -- nid, revision, parent
Result := [sql_read_integer_64 (3)] Result := [sql_read_integer_64 (2), sql_read_integer_64 (3)]
else else
check unique_data: n = 0 end check unique_data: n = 0 end
end end
end end
ensure
accepted_revision: Result /= Void implies Result.revision <= a_node.revision
end end
feature -- SQL feature -- SQL
sql_select_node_data: STRING = "SELECT nid, revision, parent FROM page_nodes WHERE nid =:nid AND revision<=:revision ORDER BY revision DESC LIMIT 1;" sql_select_node_data: STRING = "SELECT nid, revision, parent FROM page_nodes WHERE nid=:nid AND revision<=:revision ORDER BY revision DESC LIMIT 1;"
sql_insert_node_data: STRING = "INSERT INTO page_nodes (nid, revision, parent) VALUES (:nid, :revision, :parent);" sql_insert_node_data: STRING = "INSERT INTO page_nodes (nid, revision, parent) VALUES (:nid, :revision, :parent);"
sql_update_node_data: STRING = "UPDATE page_nodes SET nid=:nid, revision=:revision, parent=:parent WHERE nid=:nid AND revision=:revision;" sql_update_node_data: STRING = "UPDATE page_nodes SET nid=:nid, revision=:revision, parent=:parent WHERE nid=:nid AND revision=:revision;"
sql_delete_node_data: STRING = "DELETE FROM page_nodes WHERE nid=:nid;"
end end

View File

@@ -29,6 +29,10 @@ feature -- Access
date: DATE_TIME date: DATE_TIME
-- Time of the event item. -- Time of the event item.
author_name: detachable READABLE_STRING_32
-- Optional author name.
-- It is possible to have author_name /= Void and author = Void.
author: detachable CMS_USER author: detachable CMS_USER
-- Optional author. -- Optional author.
@@ -41,10 +45,19 @@ feature -- Access
feature -- Element change feature -- Element change
set_author_name (n: like author_name)
-- Set `author_name' to `n'.
do
author_name := n
end
set_author (u: like author) set_author (u: like author)
-- Set `author' to `u'. -- Set `author' to `u'.
do do
author := u author := u
if u /= Void and author_name = Void then
set_author_name (u.name)
end
end end
set_information (a_info: like information) set_information (a_info: like information)

View File

@@ -9,26 +9,16 @@ deferred class
inherit inherit
CMS_HOOK CMS_HOOK
-- CMS_HOOK_WITH_WRAPPER
-- rename
-- wrapper as recent_changes_hook_wrapper
-- redefine
-- recent_changes_hook_wrapper
-- end
feature -- Invocation feature -- Invocation
populate_recent_changes (a_changes: CMS_RECENT_CHANGE_CONTAINER; a_sources: LIST [READABLE_STRING_8]) recent_changes_sources: detachable LIST [READABLE_STRING_8]
-- Populate recent changes inside `a_changes' according to associated parameters. -- Source provided by Current hook.
-- Also provide sources of information.
deferred deferred
end end
--feature -- Wrapper populate_recent_changes (a_changes: CMS_RECENT_CHANGE_CONTAINER; a_current_user: detachable CMS_USER)
-- Populate recent changes inside `a_changes' according to associated parameters.
-- recent_changes_hook_wrapper: detachable CMS_RECENT_CHANGES_HOOK_WRAPPER deferred
-- do end
-- create Result.make (Current)
-- end
end end

View File

@@ -1,5 +1,5 @@
note note
description: "CMS module that bring support for recent changes." description: "CMS module that brings support for recent changes."
date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $" date: "$Date: 2015-02-13 13:08:13 +0100 (ven., 13 févr. 2015) $"
revision: "$Revision: 96616 $" revision: "$Revision: 96616 $"
@@ -11,13 +11,16 @@ inherit
rename rename
module_api as recent_changes_api module_api as recent_changes_api
redefine redefine
register_hooks register_hooks,
permissions
end end
CMS_HOOK_MENU_SYSTEM_ALTER CMS_HOOK_MENU_SYSTEM_ALTER
CMS_HOOK_RESPONSE_ALTER CMS_HOOK_RESPONSE_ALTER
CMS_HOOK_BLOCK
create create
make make
@@ -35,105 +38,349 @@ feature -- Access
name: STRING = "recent_changes" name: STRING = "recent_changes"
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("view recent changes")
end
feature -- Access: router feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API) setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor> -- <Precursor>
do do
a_router.handle ("/recent_changes/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_recent_changes (a_api, ?, ?)), a_router.methods_head_get) a_router.handle ("/recent_changes/", create {WSF_URI_AGENT_HANDLER}.make (agent handle_recent_changes (a_api, ?, ?)), a_router.methods_head_get)
a_router.handle ("/recent_changes/feed", create {WSF_URI_AGENT_HANDLER}.make (agent handle_recent_changes_feed (a_api, ?, ?)), a_router.methods_head_get)
end
feature -- Hook
block_list: ITERABLE [like {CMS_BLOCK}.name]
-- List of block names, managed by current object.
-- If prefixed by "?", condition will be check
-- to determine if it should be displayed (and computed) or not.
do
Result := <<"?recent_changes">>
end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
-- Get block object identified by `a_block_id' and associate with `a_response'.
local
b: CMS_CONTENT_BLOCK
s, l_content: STRING
gen: FEED_TO_XHTML_VISITOR
do
if a_block_id.same_string_general ("recent_changes") then
create l_content.make (1024)
create gen.make (l_content)
create s.make_empty
s.append_string ("<liv class=%"nav%">")
s.append_string (a_response.link ("See more ...", "recent_changes/", Void))
s.append_string ("</li>")
gen.set_footer (s)
recent_changes_feed (a_response, 10, Void).accept (gen)
create b.make (a_block_id, Void, l_content, Void)
a_response.put_block (b, Void, False)
end
end
recent_changes_feed (a_response: CMS_RESPONSE; a_size: NATURAL_32; a_source: detachable READABLE_STRING_8): FEED
local
l_changes: CMS_RECENT_CHANGE_CONTAINER
ch: CMS_RECENT_CHANGE_ITEM
l_user: detachable CMS_USER
l_feed: FEED
l_feed_item: FEED_ITEM
lnk: FEED_LINK
nb: NATURAL_32
do
l_user := Void -- Public access for the feed!
create l_changes.make (a_size, create {DATE_TIME}.make_now_utc, a_source)
if attached a_response.hooks.subscribers ({CMS_RECENT_CHANGES_HOOK}) as lst then
across
lst as ic
loop
if attached {CMS_RECENT_CHANGES_HOOK} ic.item as h then
if attached h.recent_changes_sources as h_sources then
if
a_source = Void
or else across h_sources as h_ic some h_ic.item.is_case_insensitive_equal (a_source) end
then
h.populate_recent_changes (l_changes, l_user)
end
end
end
end
end
create l_feed.make ("CMS Recent changes")
l_feed.set_date (create {DATE_TIME}.make_now_utc)
nb := a_size
across
l_changes as ic
until
nb = 0
loop
ch := ic.item
create l_feed_item.make (ch.link.title)
l_feed_item.set_date (ch.date)
l_feed_item.set_description (ch.information)
l_feed_item.set_category (ch.source)
create lnk.make (a_response.absolute_url (ch.link.location, Void))
l_feed_item.links.force (lnk, "")
l_feed.extend (l_feed_item)
nb := nb - 1
end
l_feed.sort
Result := l_feed
end end
feature -- Handler feature -- Handler
handle_recent_changes_feed (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local
r: CMS_RESPONSE
htdate: HTTP_DATE
l_content: STRING
l_until_date: detachable DATE_TIME
l_until_date_timestamp: INTEGER_64
l_filter_source: detachable READABLE_STRING_8
l_size: NATURAL_32
mesg: CMS_CUSTOM_RESPONSE_MESSAGE
do
if attached {WSF_STRING} req.query_parameter ("date") as p_until_date then
l_until_date_timestamp := p_until_date.value.to_integer_64
create htdate.make_from_timestamp (l_until_date_timestamp)
l_until_date := htdate.date_time
end
if attached {WSF_STRING} req.query_parameter ("source") as p_filter then
l_filter_source := p_filter.url_encoded_value
if l_filter_source.is_empty then
l_filter_source := Void
end
end
if attached {WSF_STRING} req.query_parameter ("size") as p_size then
l_size := p_size.integer_value.to_natural_32
end
if l_size = 0 then
l_size := 25
end
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
create l_content.make (1024)
recent_changes_feed (r, l_size, l_filter_source).accept (create {ATOM_FEED_GENERATOR}.make (l_content))
create mesg.make ({HTTP_STATUS_CODE}.ok)
mesg.set_payload (l_content)
res.send (mesg)
end
handle_recent_changes (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) handle_recent_changes (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
r: CMS_RESPONSE r: CMS_RESPONSE
l_changes: CMS_RECENT_CHANGE_CONTAINER l_changes: CMS_RECENT_CHANGE_CONTAINER
l_sources: ARRAYED_LIST [READABLE_STRING_8] l_sources: ARRAYED_LIST [READABLE_STRING_8]
dt, prev_dt: detachable DATE dt, prev_dt: detachable DATE_TIME
ch: CMS_RECENT_CHANGE_ITEM prev_info: detachable READABLE_STRING_8
ch: detachable CMS_RECENT_CHANGE_ITEM
htdate: HTTP_DATE htdate: HTTP_DATE
l_content: STRING l_content: STRING
l_form: CMS_FORM l_form: CMS_FORM
l_select: WSF_FORM_SELECT l_select: WSF_FORM_SELECT
l_size_field: WSF_FORM_NUMBER_INPUT
l_date_field: WSF_FORM_HIDDEN_INPUT
l_submit: WSF_FORM_SUBMIT_INPUT
l_until_date: detachable DATE_TIME
l_until_date_timestamp: INTEGER_64
l_filter_source: detachable READABLE_STRING_8
l_size: NATURAL_32
l_query: STRING
opt: WSF_FORM_SELECT_OPTION
l_user: detachable CMS_USER
i: INTEGER
do do
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) if attached {WSF_STRING} req.query_parameter ("date") as p_until_date then
create l_changes.make (25, Void, Void) l_until_date_timestamp := p_until_date.value.to_integer_64
create htdate.make_from_timestamp (l_until_date_timestamp)
l_until_date := htdate.date_time
-- l_until_date.second_add (-1)
end
if attached {WSF_STRING} req.query_parameter ("source") as p_filter then
l_filter_source := p_filter.url_encoded_value
if l_filter_source.is_empty then
l_filter_source := Void
end
end
if attached {WSF_STRING} req.query_parameter ("size") as p_size then
l_size := p_size.integer_value.to_natural_32
end
if l_size = 0 then
l_size := 25
end
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if r.has_permission ("view recent changes") then
l_user := r.user
create l_changes.make (l_size, l_until_date, l_filter_source)
create l_content.make (1024)
if attached r.hooks.subscribers ({CMS_RECENT_CHANGES_HOOK}) as lst then
create l_sources.make (lst.count)
across
lst as ic
loop
if attached {CMS_RECENT_CHANGES_HOOK} ic.item as h then
if attached h.recent_changes_sources as h_sources then
l_sources.append (h_sources)
if
l_filter_source = Void
or else across h_sources as h_ic some h_ic.item.is_case_insensitive_equal (l_filter_source) end
then
h.populate_recent_changes (l_changes, l_user)
end
end
end
end
create l_form.make (req.percent_encoded_path_info, "recent-changes")
l_form.set_method_get
create l_select.make ("source")
l_select.set_label ("Sources")
create opt.make ("", "Any source")
l_select.add_option (opt)
across
l_sources as ic
loop
create opt.make (ic.item, ic.item)
if l_filter_source /= Void and then ic.item.is_case_insensitive_equal (l_filter_source) then
opt.set_is_selected (True)
end
l_select.add_option (opt)
end
l_form.extend (l_select)
create l_size_field.make_with_text ("size", l_size.out)
l_size_field.set_size (25)
l_size_field.set_label ("Items per page")
l_form.extend (l_size_field)
if l_until_date /= Void then
create l_date_field.make_with_text ("date", l_until_date_timestamp.out)
l_form.extend (l_date_field)
end
create l_submit.make_with_text ("op", "Filter")
l_form.extend (l_submit)
l_form.extend_html_text ("<br/>")
l_form.append_to_html (create {CMS_TO_WSF_THEME}.make (r, r.theme), l_content)
end
l_changes.reverse_sort
l_content.append ("<table class=%"recent-changes%">")
l_content.append ("<thead>")
l_content.append ("<tr>")
l_content.append ("<th>Date</th>")
l_content.append ("<th>Source</th>")
l_content.append ("<th>Resource</th>")
l_content.append ("<th>User</th>")
l_content.append ("<th>Information</th>")
l_content.append ("</tr>")
l_content.append ("</thead>")
l_content.append ("<tbody>")
create l_content.make (1024)
if attached r.hooks.subscribers ({CMS_RECENT_CHANGES_HOOK}) as lst then
create l_sources.make (lst.count)
across across
lst as ic l_changes as ic
loop loop
if attached {CMS_RECENT_CHANGES_HOOK} ic.item as h then ch := ic.item
h.populate_recent_changes (l_changes, l_sources) dt := ch.date
if prev_dt = Void or else dt.date /~ prev_dt.date then
l_content.append ("<tr>")
l_content.append ("<td class=%"title%" colspan=%"5%">")
l_content.append (dt.date.formatted_out ("ddd, dd mmm yyyy"))
l_content.append ("</td>")
l_content.append ("</tr>")
end
l_content.append ("<tr>")
l_content.append ("<td class=%"date%">")
if dt /~ prev_dt then
create htdate.make_from_date_time (dt)
htdate.append_to_rfc1123_string (l_content)
else
l_content.append ("<span class=%"same-value%">''</span>")
end
l_content.append ("</td>")
l_content.append ("<td class=%"source%">" + ch.source + "</td>")
l_content.append ("<td class=%"resource%">")
l_content.append (r.link (ch.link.title, ch.link.location, Void))
l_content.append ("</td>")
l_content.append ("<td class=%"user%">")
if attached ch.author as u then
l_content.append (r.link (u.name, "user/" + u.id.out, Void))
elseif attached ch.author_name as un then
l_content.append (r.html_encoded (un))
end
l_content.append ("</td>")
l_content.append ("<td class=%"info%">")
if attached ch.information as l_info and then not l_info.is_empty then
if prev_dt ~ dt and prev_info ~ l_info then
l_content.append ("<span class=%"same-value%">''</span>")
else
i := l_info.index_of ('%N', 1)
if i > 0 and i < l_info.count then
l_content.append (l_info.substring (1, i - 1))
l_content.append ("<span class=%"tooltip%">")
l_content.append (l_info.substring (i, l_info.count))
l_content.append ("</span>")
else
l_content.append (l_info)
end
end
prev_info := l_info
else
prev_info := Void
end
l_content.append ("</td>")
l_content.append ("</tr>%N")
prev_dt := dt
end
l_content.append ("</tbody>")
l_content.append ("</table>%N")
if ch /= Void then
if l_until_date /= Void then
l_content.append (" <a href=%"")
l_content.append (r.url (r.location, Void))
l_content.append ("?size=" + l_size.out + "%">&lt;&lt;</a> ")
end
if l_until_date /~ ch.date then
create htdate.make_from_date_time (ch.date)
create l_query.make_from_string ("size=" + l_size.out)
l_query.append ("&date=")
l_query.append (htdate.timestamp.out)
if l_filter_source /= Void then
l_query.append ("&filter=")
l_query.append (l_filter_source)
end
l_content.append ("<a href=%"")
l_content.append (r.url (r.location, create {CMS_API_OPTIONS}.make_from_manifest (<<["query", l_query]>>)))
l_content.append ("%">See more ...</a>")
end end
end end
create l_form.make (req.percent_encoded_path_info, "recent-changes")
create l_select.make ("source")
l_select.set_label ("Sources")
across
l_sources as ic
loop
l_select.add_option (create {WSF_FORM_SELECT_OPTION}.make (ic.item, ic.item))
end
l_form.extend (l_select)
l_form.extend_html_text ("<br/>")
l_form.append_to_html (create {CMS_TO_WSF_THEME}.make (r, r.theme), l_content)
end
l_changes.reverse_sort r.set_main_content (l_content)
l_content.append ("<table class=%"recent-changes%" style=%"border-spacing: 5px;%">") if l_until_date = Void then
l_content.append ("<thead>") r.set_title ("Recent changes")
l_content.append ("<tr>") else
l_content.append ("<th>Date</th>") create htdate.make_from_date_time (l_until_date)
l_content.append ("<th>Source</th>") r.set_title ("Recent changes before " + htdate.string)
l_content.append ("<th>Resource</th>")
l_content.append ("<th>User</th>")
l_content.append ("<th>Information</th>")
l_content.append ("</tr>")
l_content.append ("</thead>")
l_content.append ("<tbody>")
across
l_changes as ic
loop
ch := ic.item
dt := ch.date.date
if dt /~ prev_dt then
l_content.append ("<tr>")
l_content.append ("<td class=%"title%" colspan=%"5%">")
l_content.append (dt.formatted_out ("ddd, dd mmm yyyy"))
l_content.append ("</td>")
l_content.append ("</tr>")
end end
prev_dt := dt else
l_content.append ("<tr>") create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
l_content.append ("<td class=%"date%">")
create htdate.make_from_date_time (ch.date)
htdate.append_to_rfc1123_string (l_content)
l_content.append ("</td>")
l_content.append ("<td class=%"source%">" + ch.source + "</td>")
l_content.append ("<td class=%"resource%">")
l_content.append (r.link (ch.link.title, ch.link.location, Void))
l_content.append ("</td>")
l_content.append ("<td class=%"user%">")
if attached ch.author as u then
l_content.append (r.link (u.name, "user/" + u.id.out, Void))
end
l_content.append ("</td>")
l_content.append ("<td class=%"info%">")
if attached ch.information as l_info then
l_content.append ("<strong>" + l_info + "</strong> ")
end
l_content.append ("</td>")
l_content.append ("</tr>%N")
end end
l_content.append ("</tbody>")
l_content.append ("</table>%N")
r.set_main_content (l_content)
r.set_title ("Last " + l_changes.count.out + " changes")
r.execute r.execute
end end
@@ -145,6 +392,7 @@ feature -- Hooks configuration
do do
a_response.hooks.subscribe_to_menu_system_alter_hook (Current) a_response.hooks.subscribe_to_menu_system_alter_hook (Current)
a_response.hooks.subscribe_to_response_alter_hook (Current) a_response.hooks.subscribe_to_response_alter_hook (Current)
a_response.hooks.subscribe_to_block_hook (Current)
end end
feature -- Hook feature -- Hook
@@ -172,9 +420,5 @@ feature -- Hook
a_menu_system.navigation_menu.extend (lnk) a_menu_system.navigation_menu.extend (lnk)
end end
-- populate_recent_changes (lst: LIST [CMS_RECENT_CHANGE_ITEM]; a_date: detachable DATE_TIME; a_limit: INTEGER)
-- do
-- end
end end

View File

@@ -16,6 +16,7 @@
<library name="cms" location="..\..\cms-safe.ecf"/> <library name="cms" location="..\..\cms-safe.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/> <library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/> <library name="error" location="$ISE_LIBRARY\contrib\library\utility\general\error\error-safe.ecf"/>
<library name="feed" location="$ISE_LIBRARY\contrib\library\text\parser\feed\feed-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/> <library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/> <library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/> <library name="wsf_extension" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf_extension-safe.ecf" readonly="false"/>

View File

@@ -1,4 +1,4 @@
package ROC_cms package ROC
project project
cms = "cms-safe.ecf" cms = "cms-safe.ecf"
@@ -19,6 +19,21 @@ project
basic_auth = "modules/basic_auth/basic_auth.ecf" basic_auth = "modules/basic_auth/basic_auth.ecf"
node = "modules/node/node-safe.ecf" node = "modules/node/node-safe.ecf"
node = "modules/node/node.ecf" node = "modules/node/node.ecf"
demo = "examples/demo/demo-safe.ecf"
cms_demo_module = "examples/demo/modules/demo/cms_demo_module-safe.ecf"
config_tests = "library/configuration/tests/config_tests-safe.ecf"
email_service = "library/email/email-safe.ecf"
persistence_sqlite3 = "library/persistence/sqlite3/persistence_sqlite3-safe.ecf"
store_mysql = "library/persistence/store_mysql/store_mysql-safe.ecf"
persistence_store_odbc = "library/persistence/store_odbc/store_odbc-safe.ecf"
tests_store_odbc = "library/persistence/store_odbc/tests/tests-safe.ecf"
admin = "modules/admin/admin-safe.ecf"
auth_module = "modules/auth/auth-safe.ecf"
cms_blog_module = "modules/blog/cms_blog_module-safe.ecf"
feed_aggregator = "modules/feed_aggregator/feed_aggregator-safe.ecf"
oauth_module = "modules/oauth20/oauth20-safe.ecf"
openid_module = "modules/openid/openid-safe.ecf"
recent_changes = "modules/recent_changes/recent_changes-safe.ecf"
note note
title: ROC CMS title: ROC CMS
@@ -27,5 +42,6 @@ note
license: Eiffel Forum v2 license: Eiffel Forum v2
copyright: Jocelyn Fiat, Javier Velilla, Eiffel Software copyright: Jocelyn Fiat, Javier Velilla, Eiffel Software
link[source]: "Github" https://github.com/EiffelWebFramework/ROC.git link[source]: "Github" https://github.com/EiffelWebFramework/ROC.git
-- link[doc]: "Documentation" http://
end end

94
src/cache/cms_cache.e vendored Normal file
View File

@@ -0,0 +1,94 @@
note
description: "Abstract interface for cache of value conforming to formal {G}."
date: "$Date: 2014-12-03 16:12:08 +0100 (mer., 03 déc. 2014) $"
revision: "$Revision: 96232 $"
deferred class
CMS_CACHE [G -> ANY]
feature -- Status report
exists: BOOLEAN
-- Do associated cache file exists?
deferred
end
expired (a_reference_date: detachable DATE_TIME; a_duration_in_seconds: INTEGER): BOOLEAN
-- Is associated cached item expired?
-- If `a_reference_date' is attached, cache is expired if `a_reference' is more recent than cached item.
local
d1, d2: DATE_TIME
do
if exists then
if
a_reference_date /= Void and then
a_reference_date > cache_date_time
then
Result := True
else
if a_duration_in_seconds = -1 then
Result := False -- Never expires
elseif a_duration_in_seconds = 0 then
Result := True -- Always expires
elseif a_duration_in_seconds > 0 then
d1 := cache_date_time
d2 := current_date_time
d2.second_add (- a_duration_in_seconds) --| do not modify `cache_date_time'
Result := d2 > d1 -- cached date + duration is older than current date
else
-- Invalid expiration value
-- thus always expired.
Result := True
end
end
else
Result := True
end
end
feature -- Access
item: detachable G
-- Value from the cache.
deferred
end
cache_date_time: DATE_TIME
-- Date time for current cache if exists.
-- Note: it may be UTC or not , depending on cache type.
deferred
end
cache_duration_in_seconds: INTEGER_64
-- Number of seconds since cache was set.
require
exists: exists
local
d1, d2: DATE_TIME
do
d1 := cache_date_time
d2 := current_date_time
Result := d2.relative_duration (d1).seconds_count
end
current_date_time: DATE_TIME
-- Current date time for relative duration with cache_date_time.
deferred
end
feature -- Element change
delete
-- Remove cache.
deferred
end
put (g: G)
-- Put `g' into cache.
deferred
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

150
src/cache/cms_file_cache.e vendored Normal file
View File

@@ -0,0 +1,150 @@
note
description: "Cache using a local file."
date: "$Date: 2015-09-24 18:24:06 +0200 (jeu., 24 sept. 2015) $"
revision: "$Revision: 97926 $"
deferred class
CMS_FILE_CACHE [G -> ANY]
inherit
CMS_CACHE [G]
feature {NONE} -- Initialization
make (a_cache_filename: PATH)
do
path := a_cache_filename
end
path: PATH
feature -- Status report
exists: BOOLEAN
-- Do associated cache file exists?
local
ut: FILE_UTILITIES
do
Result := ut.file_path_exists (path)
end
feature -- Access
cache_date_time: DATE_TIME
-- <Precursor>
local
f: RAW_FILE
do
create f.make_with_path (path)
if f.exists then
Result := utc_file_date_time (f)
else
create Result.make_now_utc
end
end
current_date_time: DATE_TIME
-- <Precursor>
do
-- UTC, since `cache_date_time' is UTC!
create Result.make_now_utc
end
file_size: INTEGER
-- Associated file size.
require
exists: exists
local
f: RAW_FILE
do
create f.make_with_path (path)
if f.exists and then f.is_access_readable then
Result := f.count
end
end
item: detachable G
local
f: RAW_FILE
retried: BOOLEAN
do
if not retried then
create f.make_with_path (path)
if f.exists and then f.is_access_readable then
f.open_read
Result := file_to_item (f)
f.close
end
end
rescue
retried := True
retry
end
feature -- Element change
delete
-- <Precursor>
local
f: RAW_FILE
retried: BOOLEAN
do
if not retried then
create f.make_with_path (path)
-- Create recursively parent directory if it does not exists.
if f.exists and then f.is_access_writable then
f.delete
end
end
rescue
retried := True
retry
end
put (g: G)
-- <Precursor>
local
f: RAW_FILE
d: DIRECTORY
do
create f.make_with_path (path)
-- Create recursively parent directory if it does not exists.
create d.make_with_path (path.parent)
if not d.exists then
d.recursive_create_dir
end
if not f.exists or else f.is_access_writable then
f.open_write
item_to_file (g, f)
f.close
end
end
feature -- Helpers
utc_file_date_time (f: FILE): DATE_TIME
-- Last change date for file `f'.
require
f.exists
do
create Result.make_from_epoch (f.date.as_integer_32)
end
feature {NONE} -- Implementation
file_to_item (f: FILE): detachable G
require
is_open_write: f.is_open_read
deferred
end
item_to_file (g: G; f: FILE)
require
is_open_write: f.is_open_write
deferred
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

49
src/cache/cms_file_object_cache.e vendored Normal file
View File

@@ -0,0 +1,49 @@
note
description: "Cache for value conforming to formal {G}, and implemented using local file."
date: "$Date: 2014-10-30 12:13:25 +0100 (jeu., 30 oct. 2014) $"
revision: "$Revision: 96016 $"
class
CMS_FILE_OBJECT_CACHE [G -> ANY]
inherit
CMS_FILE_CACHE [G]
SED_STORABLE_FACILITIES
create
make
feature {NONE} -- Implementation
file_to_item (f: FILE): detachable G
local
retried: BOOLEAN
l_reader: SED_MEDIUM_READER_WRITER
l_void: detachable G
do
if retried then
Result := l_void
else
create l_reader.make_for_reading (f)
if attached {G} retrieved (l_reader, True) as l_data then
Result := l_data
end
end
rescue
retried := True
retry
end
item_to_file (a_data: G; f: FILE)
local
l_writer: SED_MEDIUM_READER_WRITER
do
create l_writer.make_for_writing (f)
basic_store (a_data, l_writer, True)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

69
src/cache/cms_file_string_8_cache.e vendored Normal file
View File

@@ -0,0 +1,69 @@
note
description: "Cache system for STRING_8 value."
date: "$Date: 2014-10-30 12:13:25 +0100 (jeu., 30 oct. 2014) $"
revision: "$Revision: 96016 $"
class
CMS_FILE_STRING_8_CACHE
inherit
CMS_FILE_CACHE [STRING]
create
make
feature -- Access
append_to (a_output: STRING)
-- Append `item' to `a_output'.
local
f: RAW_FILE
retried: BOOLEAN
do
if not retried then
create f.make_with_path (path)
if f.exists and then f.is_access_readable then
f.open_read
if attached file_to_item (f) as s then
a_output.append (s)
end
f.close
end
end
rescue
retried := True
retry
end
feature {NONE} -- Implementation
file_to_item (f: FILE): detachable STRING
local
retried: BOOLEAN
do
if retried then
Result := Void
else
from
create Result.make_empty
until
f.exhausted or f.end_of_file
loop
f.read_stream_thread_aware (1_024)
Result.append (f.last_string)
end
end
rescue
retried := True
retry
end
item_to_file (a_data: STRING; f: FILE)
do
f.put_string (a_data)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

61
src/cache/cms_memory_cache.e vendored Normal file
View File

@@ -0,0 +1,61 @@
note
description: "Cache relying on memory."
date: "$Date: 2014-12-03 16:57:00 +0100 (mer., 03 déc. 2014) $"
revision: "$Revision: 96234 $"
deferred class
CMS_MEMORY_CACHE [G -> ANY]
inherit
CMS_CACHE [G]
feature {NONE} -- Initialization
make
do
cache_date_time := current_date_time
end
feature -- Status report
exists: BOOLEAN
-- Do associated cache memory exists?
do
Result := item /= Void
end
feature -- Access
cache_date_time: DATE_TIME
current_date_time: DATE_TIME
-- <Precursor>
do
create Result.make_now_utc
end
item: detachable G
feature -- Element change
delete
-- <Precursor>
local
l_default: detachable G
do
item := l_default
cache_date_time := current_date_time
end
put (g: G)
-- <Precursor>
do
item := g
cache_date_time := current_date_time
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -68,6 +68,18 @@ feature -- Access
Result := configuration.resolved_text_item (a_name) Result := configuration.resolved_text_item (a_name)
end end
text_list_item (a_name: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- Configuration values associated with `a_name', if any.
do
Result := configuration.text_list_item (a_name)
end
text_table_item (a_name: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Configuration indexed values associated with `a_name', if any.
do
Result := configuration.text_table_item (a_name)
end
string_8_item (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_8 string_8_item (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_8
-- String 8 configuration value associated with `a_name', if any. -- String 8 configuration value associated with `a_name', if any.
local local
@@ -86,13 +98,19 @@ feature -- Access
local local
retried: BOOLEAN retried: BOOLEAN
f: FILE f: FILE
l_chain: NOTIFICATION_CHAIN_MAILER
l_storage_mailer: NOTIFICATION_STORAGE_MAILER
l_mailer: detachable NOTIFICATION_MAILER
do do
if not retried then if not retried then
if attached text_item ("mailer.smtp") as l_smtp then if attached text_item ("mailer.smtp") as l_smtp then
create {NOTIFICATION_SMTP_MAILER} mailer.make (l_smtp) create {NOTIFICATION_SMTP_MAILER} l_mailer.make (l_smtp)
elseif attached text_item ("mailer.sendmail") as l_sendmail then elseif attached text_item ("mailer.sendmail") as l_sendmail then
create {NOTIFICATION_SENDMAIL_MAILER} mailer.make_with_location (l_sendmail) create {NOTIFICATION_SENDMAIL_MAILER} l_mailer.make_with_location (l_sendmail)
elseif attached text_item ("mailer.output") as l_output then end
-- If a mailer.ouput is set, set a notification chain with potential previous mailer
-- and file storage.
if attached text_item ("mailer.output") as l_output then
if l_output.is_case_insensitive_equal ("@stderr") then if l_output.is_case_insensitive_equal ("@stderr") then
f := io.error f := io.error
elseif l_output.is_case_insensitive_equal ("@stdout") then elseif l_output.is_case_insensitive_equal ("@stdout") then
@@ -104,10 +122,18 @@ feature -- Access
f.close f.close
end end
end end
create {NOTIFICATION_STORAGE_MAILER} mailer.make (create {NOTIFICATION_EMAIL_FILE_STORAGE}.make (f)) create {NOTIFICATION_STORAGE_MAILER} l_storage_mailer.make (create {NOTIFICATION_EMAIL_FILE_STORAGE}.make (f))
else if l_mailer /= Void then
create {NOTIFICATION_STORAGE_MAILER} mailer.make (create {NOTIFICATION_EMAIL_FILE_STORAGE}.make (io.error)) create l_chain.make (l_mailer)
l_chain.set_next (l_storage_mailer)
l_mailer := l_chain
else
l_mailer := l_storage_mailer
end
elseif l_mailer = Void then
create {NOTIFICATION_STORAGE_MAILER} l_mailer.make (create {NOTIFICATION_EMAIL_FILE_STORAGE}.make (io.error))
end end
mailer := l_mailer
else else
check valid_mailer: False end check valid_mailer: False end
-- FIXME: should we report persistent error message? If yes, see how. -- FIXME: should we report persistent error message? If yes, see how.

View File

@@ -196,6 +196,16 @@ feature -- Query
deferred deferred
end end
text_list_item (a_name: READABLE_STRING_GENERAL): detachable LIST [READABLE_STRING_32]
-- Configuration values associated with `a_name', if any.
deferred
end
text_table_item (a_name: READABLE_STRING_GENERAL): detachable STRING_TABLE [READABLE_STRING_32]
-- Configuration indexed values associated with `a_name', if any.
deferred
end
text_item_or_default (a_name: READABLE_STRING_GENERAL; a_default_value: READABLE_STRING_GENERAL): READABLE_STRING_32 text_item_or_default (a_name: READABLE_STRING_GENERAL; a_default_value: READABLE_STRING_GENERAL): READABLE_STRING_32
-- `text_item' associated with `a_name' or if none, `a_default_value'. -- `text_item' associated with `a_name' or if none, `a_default_value'.
do do

View File

@@ -15,6 +15,8 @@ feature -- Hook
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
-- List of block names, managed by current object. -- List of block names, managed by current object.
-- If prefixed by "?", condition will be check
-- to determine if it should be displayed (and computed) or not.
deferred deferred
end end
@@ -23,4 +25,7 @@ feature -- Hook
deferred deferred
end end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -138,6 +138,11 @@ feature -- Hook: block
invoke_block (a_response: CMS_RESPONSE) invoke_block (a_response: CMS_RESPONSE)
-- Invoke block hook for response `a_response' in order to get block from modules. -- Invoke block hook for response `a_response' in order to get block from modules.
local
bl: READABLE_STRING_8
bl_optional: BOOLEAN
l_ok: BOOLEAN
l_block_cache: detachable TUPLE [block: CMS_CACHE_BLOCK; region: READABLE_STRING_8; expired: BOOLEAN]
do do
if attached subscribers ({CMS_HOOK_BLOCK}) as lst then if attached subscribers ({CMS_HOOK_BLOCK}) as lst then
across across
@@ -147,7 +152,25 @@ feature -- Hook: block
across across
h.block_list as blst h.block_list as blst
loop loop
h.get_block_view (blst.item, a_response) l_ok := False
bl := blst.item
bl_optional := bl.count > 0 and bl[1] = '?'
if bl_optional then
bl := bl.substring (2, bl.count)
if a_response.is_block_included (bl, False) then
l_ok := True
end
else
l_ok := True
end
if l_ok then
l_block_cache := a_response.block_cache (bl)
if l_block_cache /= Void and then not l_block_cache.expired then
a_response.add_block (l_block_cache.block, l_block_cache.region)
else
h.get_block_view (bl, a_response)
end
end
end end
end end
end end

View File

@@ -24,27 +24,6 @@ feature -- Access
Result := all_subscribers.item (a_type) Result := all_subscribers.item (a_type)
end end
--feature -- Invocation
-- invoke_module_hook (args: TUPLE; a_type: TYPE [CMS_HOOK])
-- -- Invoke module hook typed `a_type' with argument `args'.
-- do
-- if attached subscribers (a_type) as lst then
-- across
-- lst as ic
-- loop
-- if
-- attached {CMS_HOOK_WITH_WRAPPER} ic.item as hw and then
-- attached hw.wrapper as w
-- then
-- if w.valid_arguments (args) then
-- w.invoke (args)
-- end
-- end
-- end
-- end
-- end
feature -- Change feature -- Change
subscribe_to_hook (h: CMS_HOOK; a_hook_type: TYPE [CMS_HOOK]) subscribe_to_hook (h: CMS_HOOK; a_hook_type: TYPE [CMS_HOOK])

View File

@@ -0,0 +1,24 @@
note
description: "Condition for block to be displayed."
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_BLOCK_CONDITION
feature -- Access
description: READABLE_STRING_32
deferred
end
feature -- Evaluation
satisfied_for_response (res: CMS_RESPONSE): BOOLEAN
deferred
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,60 @@
note
description: "Condition for block to be displayed."
date: "$Date$"
revision: "$Revision$"
class
CMS_BLOCK_EXPRESSION_CONDITION
inherit
CMS_BLOCK_CONDITION
create
make,
make_none
feature {NONE} -- Initialization
make (a_exp: READABLE_STRING_8)
do
expression := a_exp
end
make_none
do
make ("<none>")
end
feature -- Access
description: STRING_32
do
create Result.make_from_string_general ("Expression: %"")
Result.append_string_general (expression)
Result.append_character ('%"')
end
expression: STRING
feature -- Evaluation
satisfied_for_response (res: CMS_RESPONSE): BOOLEAN
local
exp: like expression
do
exp := expression
if exp.same_string ("is_front") then
Result := res.is_front
elseif exp.same_string ("*") then
Result := True
elseif exp.same_string ("<none>") then
Result := False
elseif exp.starts_with ("path:") then
Result := res.location.same_string (exp.substring (6, exp.count))
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,45 @@
note
description: "Condition for block to be displayed based on location."
date: "$Date$"
revision: "$Revision$"
class
CMS_BLOCK_LOCATION_CONDITION
inherit
CMS_BLOCK_CONDITION
create
make_with_location
feature {NONE} -- Initialization
make_with_location (a_location: READABLE_STRING_8)
do
location := a_location
end
feature -- Access
description: STRING_32
do
create Result.make_from_string_general ("Location: %"")
Result.append_string_general (location)
Result.append_character ('%"')
end
location: STRING
feature -- Evaluation
satisfied_for_response (res: CMS_RESPONSE): BOOLEAN
local
loc: like location
do
Result := res.location.same_string (loc)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -23,7 +23,12 @@ feature -- Access
html_options: detachable CMS_HTML_OPTIONS html_options: detachable CMS_HTML_OPTIONS
-- Optional addition html options. -- Optional addition html options.
feature -- status report feature -- Status report
is_empty: BOOLEAN
-- Is current block empty?
deferred
end
is_enabled: BOOLEAN is_enabled: BOOLEAN
-- Is current block enabled? -- Is current block enabled?
@@ -34,6 +39,9 @@ feature -- status report
deferred deferred
end end
conditions: detachable LIST [CMS_BLOCK_CONDITION]
-- Optional block condition to be enabled.
feature -- Element change feature -- Element change
add_css_class (a_class: READABLE_STRING_8) add_css_class (a_class: READABLE_STRING_8)
@@ -62,6 +70,19 @@ feature -- Element change
opts.remove_css_class (a_class) opts.remove_css_class (a_class)
end end
add_condition (a_condition: CMS_BLOCK_CONDITION)
-- Add condition `a_condition'.
local
l_conditions: like conditions
do
l_conditions := conditions
if l_conditions = Void then
create {ARRAYED_LIST [CMS_BLOCK_CONDITION]} l_conditions.make (1)
conditions := l_conditions
end
l_conditions.force (a_condition)
end
feature -- Conversion feature -- Conversion
to_html (a_theme: CMS_THEME): STRING_8 to_html (a_theme: CMS_THEME): STRING_8
@@ -93,4 +114,7 @@ feature -- Status report
end end
end end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -21,11 +21,10 @@ feature {NONE} -- Initialization
feature -- Access feature -- Access
name: READABLE_STRING_8 name: READABLE_STRING_8
-- Block region name. -- Block region name.
blocks: ARRAYED_LIST [CMS_BLOCK] blocks: ARRAYED_LIST [CMS_BLOCK]
-- List of blocks. -- List of blocks.
feature -- Element change feature -- Element change
@@ -35,8 +34,15 @@ feature -- Element change
blocks.force (b) blocks.force (b)
end end
;note remove (b: CMS_BLOCK)
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" require
has_block_b: blocks.has (b)
do
blocks.prune_all (b)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -0,0 +1,99 @@
note
description: "[
CMS_BLOCK implemented with a `cache'
as caching solution.
]"
date: "$Date: 2014-11-18 10:13:13 +0100 (mar., 18 nov. 2014) $"
revision: "$Revision: 96110 $"
class
CMS_CACHE_BLOCK
inherit
CMS_BLOCK
create
make
feature {NONE} -- Initialization
make (a_name: like name; a_cache: like cache)
require
a_name_not_blank: not a_name.is_whitespace
do
is_enabled := True
name := a_name
cache := a_cache
set_is_raw (True)
end
feature -- Access
name: READABLE_STRING_8
-- <Precursor>
title: detachable READABLE_STRING_32
-- <Precursor>
cache: CMS_CACHE [READABLE_STRING_8]
-- Cache content.
feature -- Status report
is_empty: BOOLEAN
-- Is current block empty?
do
Result := is_raw and not cache.exists
end
is_raw: BOOLEAN assign set_is_raw
-- Is raw?
-- If True, do not get wrapped it with block specific div
feature -- Element change
set_is_raw (b: BOOLEAN)
do
is_raw := b
end
set_name (n: like name)
-- Set `name' to `n'.
require
not n.is_whitespace
do
name := n
end
set_title (a_title: like title)
-- Set `title' to `a_title'.
do
title := a_title
end
feature -- Conversion
to_html (a_theme: CMS_THEME): STRING_8
do
debug
print ("REUSE CACHE for [" + name + "]!!!%N")
end
-- Why in this particular case theme is not used to generate the content?
if attached cache.item as l_content then
Result := l_content
else
Result := ""
check exists: False end
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -49,6 +49,12 @@ feature -- Access
feature -- Status report feature -- Status report
is_empty: BOOLEAN
-- Is current block empty?
do
Result := is_raw and content.is_empty
end
is_raw: BOOLEAN assign set_is_raw is_raw: BOOLEAN assign set_is_raw
-- Is raw? -- Is raw?
-- If True, do not get wrapped it with block specific div -- If True, do not get wrapped it with block specific div
@@ -87,7 +93,7 @@ feature -- Conversion
end end
end end
note note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -57,12 +57,39 @@ feature -- Access
root_path: detachable PATH root_path: detachable PATH
-- Root location for files universe. -- Root location for files universe.
resolved_location: PATH
-- Path of related file, taking into account `root_path' and `location'.
do
-- Process html generation
if attached root_path as l_root_path then
Result := l_root_path.extended_path (location)
else
Result := location
end
end
feature -- Status report feature -- Status report
is_empty: BOOLEAN
-- Is current block empty?
do
Result := not exists
debug ("cms")
Result := True
end
end
is_raw: BOOLEAN is_raw: BOOLEAN
-- Is raw? -- Is raw?
-- If True, do not get wrapped it with block specific div -- If True, do not get wrapped it with block specific div
exists: BOOLEAN
local
ut: FILE_UTILITIES
do
Result := ut.file_path_exists (resolved_location)
end
feature -- Element change feature -- Element change
set_is_raw (b: BOOLEAN) set_is_raw (b: BOOLEAN)
@@ -94,11 +121,7 @@ feature -- Conversion
ut: FILE_UTILITIES ut: FILE_UTILITIES
do do
-- Process html generation -- Process html generation
if attached root_path as l_root_path then p := resolved_location
p := l_root_path.extended_path (location)
else
p := location
end
if ut.file_path_exists (p) then if ut.file_path_exists (p) then
create f.make_with_path (p) create f.make_with_path (p)
if f.exists and then f.is_access_readable then if f.exists and then f.is_access_readable then
@@ -145,4 +168,7 @@ feature -- Debug
end end
Result.append ("%N") Result.append ("%N")
end end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -31,6 +31,12 @@ feature -- Access
feature -- Status report feature -- Status report
is_empty: BOOLEAN
-- Is current block empty?
do
Result := menu.is_empty
end
is_horizontal: BOOLEAN assign set_is_horizontal is_horizontal: BOOLEAN assign set_is_horizontal
-- Is horizontal layout for the menu? -- Is horizontal layout for the menu?
@@ -72,4 +78,7 @@ feature -- Conversion
Result := a_theme.menu_html (menu, is_horizontal, html_options) Result := a_theme.menu_html (menu, is_horizontal, html_options)
end end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -69,6 +69,9 @@ feature -- Access
feature -- Status report feature -- Status report
is_empty: BOOLEAN = False
-- Is current block empty?
is_raw: BOOLEAN is_raw: BOOLEAN
-- Is raw? -- Is raw?
-- If True, do not get wrapped it with block specific div -- If True, do not get wrapped it with block specific div
@@ -192,4 +195,7 @@ feature -- Debug
end end
Result.append ("%N}") Result.append ("%N}")
end end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -8,6 +8,7 @@ deferred class
CMS_EDITOR CMS_EDITOR
feature -- Initialisation feature -- Initialisation
load_assets : STRING load_assets : STRING
-- Loads all assest needed to show the editor -- Loads all assest needed to show the editor
deferred deferred
@@ -15,57 +16,55 @@ feature -- Initialisation
feature -- Javascript feature -- Javascript
javascript_replace_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING javascript_replace_textarea (a_textarea : WSF_FORM_TEXTAREA): STRING
-- Javascript code that replaces a textarea with the editor. The editor instance should be saved in editor_variable -- Javascript code that replaces a textarea with the editor. The editor instance should be saved in editor_variable
deferred deferred
end end
javascript_restore_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING javascript_restore_textarea (a_textarea : WSF_FORM_TEXTAREA): STRING
-- Javascript code that restores a textarea -- Javascript code that restores a textarea
deferred deferred
end end
javascript_textarea_to_editor(a_textarea : WSF_FORM_TEXTAREA) : STRING javascript_textarea_to_editor (a_textarea : WSF_FORM_TEXTAREA): STRING
-- Javascript code to display the textarea as a WYSIWIG editor as soon as the document is loaded -- Javascript code to display the textarea as a WYSIWIG editor as soon as the document is loaded
do do
Result := javascript_ready(javascript_replace_textarea (a_textarea)) Result := javascript_ready (javascript_replace_textarea (a_textarea))
end end
javascript_textarea_to_editor_if_selected (a_textarea : WSF_FORM_TEXTAREA; a_select_field : WSF_FORM_SELECT; a_value : STRING) : STRING javascript_textarea_to_editor_if_selected (a_textarea: WSF_FORM_TEXTAREA; a_select_field : WSF_FORM_SELECT; a_value : STRING) : STRING
-- Javascript code to display the textarea as a WYSIWIG editor if a_select_field has a_value -- Javascript code to display the textarea as a WYSIWIG editor if a_select_field has a_value
local local
initial_replace_code, on_select_replace_code : STRING initial_replace_code, on_select_replace_code: STRING
do do
-- Javascript that replaces the textarea if a_value is selected at load time -- Javascript that replaces the textarea if a_value is selected at load time
initial_replace_code := javascript_ready(javascript_if_selected(a_select_field, a_value, javascript_replace_textarea(a_textarea))) initial_replace_code := javascript_ready (javascript_if_selected (a_select_field, a_value, javascript_replace_textarea (a_textarea)))
-- Javascript code that replaces the textarea as soon as value is selected at a_select_field -- Javascript code that replaces the textarea as soon as value is selected at a_select_field
on_select_replace_code := javascript_ready( on_select_replace_code := javascript_ready(
javascript_init_editor_variable(a_textarea) + javascript_init_editor_variable (a_textarea) +
javascript_on_select(a_select_field, a_value, javascript_on_select (a_select_field, a_value,
-- If a_value is selected, replace textarea -- If a_value is selected, replace textarea
javascript_replace_textarea(a_textarea), javascript_replace_textarea (a_textarea),
-- Otherwise restore it -- Otherwise restore it
javascript_restore_textarea(a_textarea) javascript_restore_textarea (a_textarea)
) )
) )
Result := initial_replace_code + " " + on_select_replace_code Result := initial_replace_code + " " + on_select_replace_code
end end
javascript_init_editor_variable(a_textarea : WSF_FORM_TEXTAREA) : STRING javascript_init_editor_variable (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- Returns the javascript code that initializes a local variable to store the editor instance -- Returns the javascript code that initializes a local variable to store the editor instance
do do
Result := "var " + editor_variable(a_textarea) + "; " Result := "var " + editor_variable (a_textarea) + "; "
end end
javascript_if_selected (a_select_field : WSF_FORM_SELECT; a_value : STRING; a_code : STRING) : STRING
javascript_if_selected(a_select_field : WSF_FORM_SELECT; a_value : STRING; a_code : STRING) : STRING
-- Javascript that executes a_code if a_value is selected at a_select_field -- Javascript that executes a_code if a_value is selected at a_select_field
do do
Result := "if($('#" + field_id(a_select_field) + "').val() == %"" + a_value + "%"){ " + a_code + " }" Result := "if($('#" + field_id (a_select_field) + "').val() == %"" + a_value + "%"){ " + a_code + " }"
end end
javascript_ready (a_code : STRING) : STRING javascript_ready (a_code : STRING) : STRING
@@ -77,8 +76,8 @@ feature -- Javascript
javascript_on_select (a_select_field : WSF_FORM_SELECT; a_value : STRING; a_then : STRING; a_else : STRING) : STRING javascript_on_select (a_select_field : WSF_FORM_SELECT; a_value : STRING; a_then : STRING; a_else : STRING) : STRING
-- Javascript code that executes a_then if at the given select_field the given string value is selected, otherwise it executes a_else -- Javascript code that executes a_then if at the given select_field the given string value is selected, otherwise it executes a_else
do do
Result := "$('#" + field_id(a_select_field) + "').change(function(){" + Result := "$('#" + field_id (a_select_field) + "').change(function(){" +
javascript_if_selected(a_select_field, a_value, a_then) + javascript_if_selected (a_select_field, a_value, a_then) +
"else{" + "else{" +
a_else + a_else +
"}" + "}" +
@@ -100,7 +99,10 @@ feature -- Helper
editor_variable (a_textarea : WSF_FORM_TEXTAREA) : STRING editor_variable (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- Returns the variable name that stores the editor instance of the given textarea -- Returns the variable name that stores the editor instance of the given textarea
do do
Result := "editor_" + a_textarea.name Result := "cms_ckeditor_" + a_textarea.name
end end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -23,15 +23,20 @@ feature -- Javascript
javascript_replace_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING javascript_replace_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- <Precursor> -- <Precursor>
do do
-- Replaces the textarea with an editor instance. Save the instance in a variable -- Replaces the textarea with an editor instance. Save the instance in a variable
Result := editor_variable(a_textarea) + " = CKEDITOR.replace( '" + a_textarea.name + "' );" Result := "$(%"textarea[name="+ a_textarea.name +"]%").each(function() {"
Result.append (editor_variable (a_textarea) + " = CKEDITOR.replace(this);")
Result.append ("});")
end end
javascript_restore_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING javascript_restore_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- <Precursor> -- <Precursor>
do do
-- Replaces the textarea with an editor instance. Save the instance in a variable -- Replaces the textarea with an editor instance. Save the instance in a variable
Result := "if (" + editor_variable(a_textarea) + " != undefined) " + editor_variable(a_textarea) + ".destroy();" Result := "if (" + editor_variable (a_textarea) + " != undefined) " + editor_variable (a_textarea) + ".destroy();"
end end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -19,7 +19,7 @@ feature -- Basic operation
prepare (a_response: CMS_RESPONSE) prepare (a_response: CMS_RESPONSE)
do do
a_response.invoke_form_alter (Current, Void) a_response.hooks.invoke_form_alter (Current, Void, a_response)
end end
process (a_response: CMS_RESPONSE) process (a_response: CMS_RESPONSE)
@@ -29,7 +29,7 @@ feature -- Basic operation
on_prepared (a_response: CMS_RESPONSE; fd: WSF_FORM_DATA) on_prepared (a_response: CMS_RESPONSE; fd: WSF_FORM_DATA)
do do
a_response.invoke_form_alter (Current, fd) a_response.hooks.invoke_form_alter (Current, fd, a_response)
end end
on_processed (a_response: CMS_RESPONSE; fd: WSF_FORM_DATA) on_processed (a_response: CMS_RESPONSE; fd: WSF_FORM_DATA)

View File

@@ -24,15 +24,15 @@ feature -- Initialisation
make (a_name: like name) make (a_name: like name)
-- <Precursor> -- <Precursor>
do do
precursor(a_name) Precursor (a_name)
-- By default we don't replace the textarea by an editor -- By default we don't replace the textarea by an editor
editor := False; editor_activated := False;
end end
feature -- Access feature -- Access
editor : BOOLEAN editor_activated : BOOLEAN
-- True if the textarea should be replaced by the editor. Default is false. -- True if the textarea should be replaced by the editor. Default is false.
format_field : detachable WSF_FORM_SELECT format_field : detachable WSF_FORM_SELECT
@@ -45,13 +45,13 @@ feature -- Editor
show_as_editor show_as_editor
-- The textarea will be replaced by a wysiwyg editor -- The textarea will be replaced by a wysiwyg editor
do do
editor := True editor_activated := True
end end
show_as_editor_if_selected (a_select_field : WSF_FORM_SELECT; a_value : STRING) show_as_editor_if_selected (a_select_field : WSF_FORM_SELECT; a_value : STRING)
-- Replaces the textarea only if a_select_field has a_value (or the value gets selected) -- Replaces the textarea only if a_select_field has a_value (or the value gets selected)
do do
editor := True editor_activated := True
format_field := a_select_field format_field := a_select_field
condition_value := a_value condition_value := a_value
end end
@@ -61,8 +61,8 @@ feature -- Conversion
append_item_to_html (a_theme: WSF_THEME; a_html: STRING_8) append_item_to_html (a_theme: WSF_THEME; a_html: STRING_8)
do do
-- Add javascript to replace textarea with editor -- Add javascript to replace textarea with editor
precursor(a_theme, a_html) Precursor (a_theme, a_html)
if editor then if editor_activated then
a_html.append (load_assets) a_html.append (load_assets)
a_html.append ("<script type=%"text/javascript%">"); a_html.append ("<script type=%"text/javascript%">");
if attached format_field as l_field and then attached condition_value as l_value then if attached format_field as l_field and then attached condition_value as l_value then

View File

@@ -51,14 +51,14 @@ feature -- Hooks configuration
-- Module hooks configuration. -- Module hooks configuration.
do do
auto_subscribe_to_hooks (a_response) auto_subscribe_to_hooks (a_response)
a_response.subscribe_to_block_hook (Current) a_response.hooks.subscribe_to_block_hook (Current)
end end
feature -- Hooks feature -- Hooks
block_list: ITERABLE [like {CMS_BLOCK}.name] block_list: ITERABLE [like {CMS_BLOCK}.name]
do do
Result := <<"debug-info">> Result := <<"?debug-info">>
end end
get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE) get_block_view (a_block_id: READABLE_STRING_8; a_response: CMS_RESPONSE)
@@ -67,14 +67,13 @@ feature -- Hooks
dbg: WSF_DEBUG_INFORMATION dbg: WSF_DEBUG_INFORMATION
s: STRING s: STRING
do do
if a_response.theme.has_region ("debug") then create dbg.make
create dbg.make create s.make_empty
create s.make_empty dbg.append_information_to (a_response.request, a_response.response, s)
dbg.append_information_to (a_response.request, a_response.response, s) append_info_to ("Storage", a_response.api.storage.generator, a_response, s)
append_info_to ("Storage", a_response.api.storage.generator, a_response, s) create b.make ("debug-info", "Debug", s, a_response.formats.plain_text)
create b.make ("debug-info", "Debug", s, a_response.formats.plain_text) b.add_condition (create {CMS_BLOCK_EXPRESSION_CONDITION}.make_none)
a_response.add_block (b, "footer") a_response.add_block (b, "footer")
end
end end
feature -- Handler feature -- Handler

View File

@@ -101,7 +101,19 @@ feature -- Custom
custom_value (a_name: READABLE_STRING_GENERAL; a_type: detachable READABLE_STRING_8): detachable READABLE_STRING_32 custom_value (a_name: READABLE_STRING_GENERAL; a_type: detachable READABLE_STRING_8): detachable READABLE_STRING_32
-- Data for name `a_name' and type `a_type' (or default if none). -- Data for name `a_name' and type `a_type' (or default if none).
local
s: STRING_32
do do
if attached api as l_api then
create s.make_from_string_general ("custom_values")
if a_type /= Void then
s.append_character ('.')
s.append_string_general (a_type)
end
s.append_character ('.')
s.append_string_general (a_name)
Result := l_api.setup.text_item (s)
end
end end

View File

@@ -403,19 +403,79 @@ feature -- Blocks initialization
create regions.make_caseless (5) create regions.make_caseless (5)
create l_table.make_caseless (10) create l_table.make_caseless (10)
l_table["top"] := "top" l_table["top"] := block_region_preference ("top", "top")
l_table["header"] := "header" l_table["header"] := block_region_preference ("header", "header")
l_table["highlighted"] := "highlighted" l_table["highlighted"] := block_region_preference ("highlighted", "highlighted")
l_table["help"] := "help" l_table["help"] := block_region_preference ("help", "help")
l_table["content"] := "content" l_table["content"] := block_region_preference ("content", "content")
l_table["footer"] := "footer" l_table["footer"] := block_region_preference ("footer", "footer")
l_table["management"] := "sidebar_first" l_table["management"] := block_region_preference ("management", "sidebar_first")
l_table["navigation"] := "sidebar_first" l_table["navigation"] := block_region_preference ("navigation", "sidebar_first")
l_table["user"] := "sidebar_first" l_table["user"] := block_region_preference ("user", "sidebar_first")
l_table["bottom"] := "page_bottom" l_table["bottom"] := block_region_preference ("bottom", "page_bottom")
block_region_settings := l_table block_region_settings := l_table
end end
block_region_preference (a_block_id: READABLE_STRING_8; a_default_region: READABLE_STRING_8): READABLE_STRING_8
-- Region associated with `a_block_id' in configuration, if any.
do
Result := setup.text_item_or_default ("blocks." + a_block_id + ".region", a_default_region)
end
block_conditions (a_block_id: READABLE_STRING_8): detachable ARRAYED_LIST [CMS_BLOCK_EXPRESSION_CONDITION]
-- Condition associated with `a_block_id' in configuration, if any.
do
if attached setup.text_item ("blocks." + a_block_id + ".condition") as s then
create Result.make (1)
Result.force (create {CMS_BLOCK_EXPRESSION_CONDITION}.make (s))
end
if attached setup.text_list_item ("blocks." + a_block_id + ".conditions") as lst then
if Result = Void then
create Result.make (lst.count)
across
lst as ic
loop
Result.force (create {CMS_BLOCK_EXPRESSION_CONDITION}.make (ic.item))
end
end
end
end
is_block_included (a_block_id: READABLE_STRING_8; dft: BOOLEAN): BOOLEAN
-- Is block `a_block_id' included in current response?
-- If no preference, return `dft'.
do
if attached block_conditions (a_block_id) as l_conditions then
Result := across l_conditions as ic some ic.item.satisfied_for_response (Current) end
else
Result := dft
end
end
block_cache (a_block_id: READABLE_STRING_8): detachable TUPLE [block: CMS_CACHE_BLOCK; region: READABLE_STRING_8; expired: BOOLEAN]
-- Cached version of block `a_block_id'.
local
l_cache: CMS_FILE_STRING_8_CACHE
do
if
attached setup.text_item ("blocks." + a_block_id + ".expiration") as nb_secs and then
nb_secs.is_integer
then
if attached block_region_preference (a_block_id, "none") as l_region and then not l_region.same_string_general ("none") then
create l_cache.make (api.files_location.extended (".cache").extended ("blocks").extended (a_block_id).appended_with_extension ("html"))
if
l_cache.exists and then
not l_cache.expired (Void, nb_secs.to_integer)
then
Result := [create {CMS_CACHE_BLOCK} .make (a_block_id, l_cache), l_region, False]
else
Result := [create {CMS_CACHE_BLOCK} .make (a_block_id, l_cache), l_region, True]
end
end
end
end
feature -- Blocks regions feature -- Blocks regions
regions: STRING_TABLE [CMS_BLOCK_REGION] regions: STRING_TABLE [CMS_BLOCK_REGION]
@@ -430,12 +490,15 @@ feature -- Blocks regions
do do
l_region_name := block_region_settings.item (b.name) l_region_name := block_region_settings.item (b.name)
if l_region_name = Void then if l_region_name = Void then
if a_default_region /= Void then if attached setup.text_item ("blocks." + b.name + ".region") as l_setup_name then
l_region_name := l_setup_name.as_string_8 -- FIXME: potential truncated string 32.
-- Remember for later.
block_region_settings.force (l_region_name, b.name)
elseif a_default_region /= Void then
l_region_name := a_default_region l_region_name := a_default_region
else else
-- Default .. put it in same named region -- Default .. put it in same named region
-- Maybe a bad idea -- Maybe a bad idea
l_region_name := b.name.as_lower l_region_name := b.name.as_lower
end end
end end
@@ -447,10 +510,25 @@ feature -- Blocks regions
end end
end end
feature -- Blocks feature -- Blocks
put_block (b: CMS_BLOCK; a_default_region: detachable READABLE_STRING_8; is_block_included_by_default: BOOLEAN)
-- Add block `b' to associated region or `a_default_region' if provided
-- and check optional associated condition.
-- If no condition then use `is_block_included_by_default' to
-- decide if block is included or not.
local
l_region: detachable like block_region
do
if is_block_included (b.name, is_block_included_by_default) then
l_region := block_region (b, a_default_region)
l_region.extend (b)
end
end
add_block (b: CMS_BLOCK; a_default_region: detachable READABLE_STRING_8) add_block (b: CMS_BLOCK; a_default_region: detachable READABLE_STRING_8)
-- Add block `b' to associated region or `a_default_region' if provided. -- Add block `b' to associated region or `a_default_region' if provided.
-- WARNING: ignore any block condition! USE WITH CARE!
local local
l_region: detachable like block_region l_region: detachable like block_region
do do
@@ -463,29 +541,29 @@ feature -- Blocks
debug ("refactor_fixme") debug ("refactor_fixme")
fixme ("find a way to have this in configuration or database, and allow different order") fixme ("find a way to have this in configuration or database, and allow different order")
end end
add_block (top_header_block, "top") put_block (top_header_block, "top", True)
add_block (header_block, "header") put_block (header_block, "header", True)
if attached message_block as m then if attached message_block as m then
add_block (m, "content") put_block (m, "content", True)
end end
if attached primary_tabs_block as m then if attached primary_tabs_block as m then
add_block (m, "content") put_block (m, "content", True)
end end
add_block (content_block, "content") add_block (content_block, "content") -- Can not be disabled!
if attached management_menu_block as l_block then if attached management_menu_block as l_block then
add_block (l_block, "sidebar_first") put_block (l_block, "sidebar_first", True)
end end
if attached navigation_menu_block as l_block then if attached navigation_menu_block as l_block then
add_block (l_block, "sidebar_first") put_block (l_block, "sidebar_first", True)
end end
if attached user_menu_block as l_block then if attached user_menu_block as l_block then
add_block (l_block, "sidebar_second") put_block (l_block, "sidebar_second", True)
end end
invoke_block hooks.invoke_block (Current)
debug ("cms") debug ("cms")
add_block (create {CMS_CONTENT_BLOCK}.make ("made_with", Void, "Made with <a href=%"http://www.eiffel.com/%">EWF</a>", Void), "footer") put_block (create {CMS_CONTENT_BLOCK}.make ("made_with", Void, "Made with <a href=%"https://www.eiffel.org/%">EWF</a>", Void), "footer", True)
end end
end end
@@ -599,90 +677,6 @@ feature -- Hooks
hooks: CMS_HOOK_CORE_MANAGER hooks: CMS_HOOK_CORE_MANAGER
-- Manager handling hook subscriptions. -- Manager handling hook subscriptions.
feature -- Hook: value alter
-- subscribe_to_value_table_alter_hook (h: CMS_HOOK_VALUE_TABLE_ALTER)
-- -- Add `h' as subscriber of value table alter hooks CMS_HOOK_VALUE_TABLE_ALTER.
-- do
-- hooks.subscribe_to_value_table_alter_hook (h)
-- end
invoke_value_table_alter (a_table: CMS_VALUE_TABLE)
-- Invoke value table alter hook for table `a_table'.
do
hooks.invoke_value_table_alter (a_table, Current)
end
feature -- Hook: response
-- subscribe_to_response_alter_hook (h: CMS_HOOK_RESPONSE_ALTER)
-- -- Add `h' as subscriber of response alter hooks CMS_HOOK_RESPONSE_ALTER.
-- do
-- hooks.subscribe_to_response_alter_hook (h)
-- end
invoke_response_alter (a_response: CMS_RESPONSE)
-- Invoke response alter hook for response `a_response'.
do
hooks.invoke_response_alter (a_response)
end
feature -- Hook: menu_system_alter
-- subscribe_to_menu_system_alter_hook (h: CMS_HOOK_MENU_SYSTEM_ALTER)
-- -- Add `h' as subscriber of menu system alter hooks CMS_HOOK_MENU_SYSTEM_ALTER.
-- do
-- hooks.subscribe_to_menu_system_alter_hook (h)
-- end
invoke_menu_system_alter (a_menu_system: CMS_MENU_SYSTEM)
-- Invoke menu system alter hook for menu `a_menu_system'.
do
hooks.invoke_menu_system_alter (menu_system, Current)
end
feature -- Hook: menu_alter
-- subscribe_to_menu_alter_hook (h: CMS_HOOK_MENU_ALTER)
-- -- Add `h' as subscriber of menu alter hooks CMS_HOOK_MENU_ALTER.
-- do
-- hooks.subscribe_to_menu_alter_hook (h)
-- end
invoke_menu_alter (a_menu: CMS_MENU)
-- Invoke menu alter hook for menu `a_menu'.
do
hooks.invoke_menu_alter (a_menu, Current)
end
feature -- Hook: form_alter
-- subscribe_to_form_alter_hook (h: CMS_HOOK_FORM_ALTER)
-- -- Add `h' as subscriber of form alter hooks CMS_HOOK_FORM_ALTER.
-- do
-- hooks.subscribe_to_form_alter_hook (h)
-- end
invoke_form_alter (a_form: CMS_FORM; a_form_data: detachable WSF_FORM_DATA)
-- Invoke form alter hook for form `a_form' and associated data `a_form_data'
do
hooks.invoke_form_alter (a_form, a_form_data, Current)
end
feature -- Hook: block
subscribe_to_block_hook (h: CMS_HOOK_BLOCK)
-- Add `h' as subscriber of hooks CMS_HOOK_BLOCK.
do
hooks.subscribe_to_hook (h, {CMS_HOOK_BLOCK})
end
invoke_block
-- Invoke block hook in order to get block from modules.
do
hooks.invoke_block (Current)
end
feature -- Menu: change feature -- Menu: change
add_to_main_menu (lnk: CMS_LINK) add_to_main_menu (lnk: CMS_LINK)
@@ -704,9 +698,6 @@ feature -- Menu: change
add_to_menu (lnk: CMS_LINK; m: CMS_MENU) add_to_menu (lnk: CMS_LINK; m: CMS_MENU)
do do
-- if attached {CMS_LOCAL_LINK} lnk as l_local then
-- l_local.get_is_active (request)
-- end
m.extend (lnk) m.extend (lnk)
end end
@@ -815,13 +806,16 @@ feature -- Generation
prepare (page: CMS_HTML_PAGE) prepare (page: CMS_HTML_PAGE)
local local
lnk: CMS_LINK lnk: CMS_LINK
l_region: CMS_BLOCK_REGION
l_menu_list_prepared: ARRAYED_LIST [CMS_LINK_COMPOSITE] l_menu_list_prepared: ARRAYED_LIST [CMS_LINK_COMPOSITE]
l_empty_blocks: detachable ARRAYED_LIST [CMS_BLOCK]
l_block_html: STRING
do do
-- Menu -- Menu
create {CMS_LOCAL_LINK} lnk.make ("Home", "") create {CMS_LOCAL_LINK} lnk.make ("Home", "")
lnk.set_weight (-10) lnk.set_weight (-10)
add_to_primary_menu (lnk) add_to_primary_menu (lnk)
invoke_menu_system_alter (menu_system) hooks.invoke_menu_system_alter (menu_system, Current)
if api.enabled_modules.count = 0 then if api.enabled_modules.count = 0 then
add_to_primary_menu (create {CMS_LOCAL_LINK}.make ("Install", "admin/install")) add_to_primary_menu (create {CMS_LOCAL_LINK}.make ("Install", "admin/install"))
@@ -833,14 +827,29 @@ feature -- Generation
across across
regions as reg_ic regions as reg_ic
loop loop
l_region := reg_ic.item
across across
reg_ic.item.blocks as ic l_region.blocks as ic
loop loop
if attached {CMS_MENU_BLOCK} ic.item as l_menu_block then if attached {CMS_MENU_BLOCK} ic.item as l_menu_block then
l_menu_list_prepared.force (l_menu_block.menu) l_menu_list_prepared.force (l_menu_block.menu)
prepare_links (l_menu_block.menu) prepare_links (l_menu_block.menu)
if l_menu_block.menu.is_empty then
if l_empty_blocks = Void then
create l_empty_blocks.make (1)
end
l_empty_blocks.force (l_menu_block)
end
end end
end end
if l_empty_blocks /= Void then
across
l_empty_blocks as ic
loop
l_region.remove (ic.item)
end
l_empty_blocks := Void
end
end end
-- Prepare menu not in a block. -- Prepare menu not in a block.
@@ -864,10 +873,10 @@ feature -- Generation
custom_prepare (page) custom_prepare (page)
-- Cms response -- Cms response
invoke_response_alter (Current) hooks.invoke_response_alter (Current)
-- Cms values -- Cms values
invoke_value_table_alter (values) hooks.invoke_value_table_alter (values, Current)
-- Predefined values -- Predefined values
page.register_variable (page, "page") -- DO NOT REMOVE page.register_variable (page, "page") -- DO NOT REMOVE
@@ -899,7 +908,13 @@ feature -- Generation
end end
end end
end end
page.add_to_region (theme.block_html (ic.item), reg_ic.item.name) l_block_html := theme.block_html (ic.item)
if attached {CMS_CACHE_BLOCK} ic.item then
elseif attached block_cache (ic.item.name) as l_cache_block then
l_cache_block.block.cache.put (l_block_html)
end
page.add_to_region (l_block_html, reg_ic.item.name)
end end
end end
@@ -1055,6 +1070,7 @@ feature {NONE} -- Execution
page: CMS_HTML_PAGE_RESPONSE page: CMS_HTML_PAGE_RESPONSE
utf: UTF_CONVERTER utf: UTF_CONVERTER
h: HTTP_HEADER h: HTTP_HEADER
l_new_location: READABLE_STRING_8
do do
if attached {READABLE_STRING_GENERAL} values.item ("optional_content_type") as l_type then if attached {READABLE_STRING_GENERAL} values.item ("optional_content_type") as l_type then
create cms_page.make_typed (utf.utf_32_string_to_utf_8_string_8 (l_type)) create cms_page.make_typed (utf.utf_32_string_to_utf_8_string_8 (l_type))
@@ -1070,12 +1086,12 @@ feature {NONE} -- Execution
if attached redirection as l_location then if attached redirection as l_location then
-- FIXME: find out if this is safe or not. -- FIXME: find out if this is safe or not.
if l_location.has_substring ("://") then if l_location.has_substring ("://") then
-- h.put_location (l_location) l_new_location := l_location
response.redirect_now (l_location)
else else
-- h.put_location (request.absolute_url (l_location, Void)) l_new_location := absolute_url (l_location, Void)
response.redirect_now (absolute_url (l_location, Void))
end end
-- h.put_location (l_new_location)
response.redirect_now (l_new_location)
else else
h.put_header_object (header) h.put_header_object (header)

View File

@@ -20,9 +20,10 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
set_status_code ({HTTP_STATUS_CODE}.bad_request)
page.register_variable (absolute_url (request.percent_encoded_path_info, Void), "request") page.register_variable (absolute_url (request.percent_encoded_path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.bad_request) page.set_status_code (status_code)
page.register_variable (page.status_code.out, "code") page.register_variable (status_code.out, "code")
end end
feature -- Execution feature -- Execution

View File

@@ -20,9 +20,10 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
set_status_code ({HTTP_STATUS_CODE}.forbidden)
page.register_variable (absolute_url (request.percent_encoded_path_info, Void), "request") page.register_variable (absolute_url (request.percent_encoded_path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.forbidden) page.set_status_code (status_code)
page.register_variable (page.status_code.out, "code") page.register_variable (status_code.out, "code")
end end
feature -- Execution feature -- Execution

View File

@@ -20,9 +20,10 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
page.register_variable (absolute_url (location, Void), "request") page.register_variable (absolute_url (location, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.internal_server_error) page.set_status_code (status_code)
page.register_variable (page.status_code.out, "code") page.register_variable (status_code.out, "code")
end end
feature -- Execution feature -- Execution

View File

@@ -20,9 +20,10 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
set_status_code ({HTTP_STATUS_CODE}.not_found)
page.register_variable (absolute_url (request.percent_encoded_path_info, Void), "request") page.register_variable (absolute_url (request.percent_encoded_path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.not_found) page.set_status_code (status_code)
page.register_variable (page.status_code.out, "code") page.register_variable (status_code.out, "code")
end end
feature -- Execution feature -- Execution

View File

@@ -20,9 +20,10 @@ feature -- Generation
custom_prepare (page: CMS_HTML_PAGE) custom_prepare (page: CMS_HTML_PAGE)
do do
set_status_code ({HTTP_STATUS_CODE}.not_implemented)
page.register_variable (absolute_url (request.percent_encoded_path_info, Void), "request") page.register_variable (absolute_url (request.percent_encoded_path_info, Void), "request")
page.set_status_code ({HTTP_STATUS_CODE}.not_implemented) page.set_status_code (status_code)
page.register_variable (page.status_code.out, "code") page.register_variable (status_code.out, "code")
end end
feature -- Execution feature -- Execution

39
tools/roc.bat Normal file
View File

@@ -0,0 +1,39 @@
@echo off
setlocal
if "%ROC_TOOL_PATH%" == "" goto LOCAL_ROC_TOOL
goto start
:LOCAL_ROC_TOOL
if exist "%~dp0roc.exe" set ROC_TOOL_PATH=%~dp0
if "%ROC_TOOL_PATH%" == "" goto SEARCH_ROC_TOOL
goto START
:SEARCH_ROC_TOOL
for %%f in (roc.exe) do (
if exist "%%~dp$PATH:f" set ROC_TOOL_PATH="%%~dp$PATH:f"
)
if "%ROC_TOOL_PATH%" == "" goto BUILD_ROC_TOOL
echo Using roc.exe from %ROC_TOOL_PATH%
goto START
:BUILD_ROC_TOOL
set ROC_SRCDIR=%~dp0roc
set ROC_COMPDIR=%~dp0.roc-comp
mkdir %ROC_COMPDIR%
call ecb -config %ROC_SRCDIR%\roc.ecf -finalize -c_compile -project_path %ROC_COMPDIR%
copy %ROC_COMPDIR%\EIFGENs\roc\F_code\roc.exe %~dp0roc.exe
rd /q/s %ROC_COMPDIR%
set ROC_TOOL_PATH=%~dp0
goto START
:START
echo Calling %ROC_TOOL_PATH%roc.exe %*
call %ROC_TOOL_PATH%roc.exe %*
goto END
:END
endlocal
exit /B 0