Added export of core data, such as users, path_aliases, custom_values.

Added export of node revisions.
This commit is contained in:
2015-11-12 18:19:06 +01:00
parent a5973c9c8a
commit 79d30ee3a7
12 changed files with 298 additions and 15 deletions

View File

@@ -105,6 +105,7 @@ feature -- Security
Result.force ("admin core caches")
Result.force ("clear blocks cache")
Result.force ("admin export")
Result.force ("export core")
end
feature -- Hooks

View File

@@ -64,7 +64,12 @@ feature -- Execution
fd.is_valid
then
if attached fd.string_item ("op") as l_op and then l_op.same_string (text_export_all_data) then
create l_exportation_parameters.make (api.site_location.extended ("export").extended ((create {DATE_TIME}.make_now_utc).formatted_out ("yyyy-[0]mm-[0]dd_hh12-[0]mi-[0]ss")))
if attached fd.string_item ("folder") as l_folder then
create l_exportation_parameters.make (api.site_location.extended ("export").extended (l_folder))
else
create l_exportation_parameters.make (api.site_location.extended ("export").extended ((create {DATE_TIME}.make_now_utc).formatted_out ("yyyy-[0]mm-[0]dd---hh24-[0]mi-[0]ss")))
end
l_response.hooks.invoke_export_to (Void, l_exportation_parameters, l_response)
l_response.add_notice_message ("All data exported (if allowed)!")
create s.make_empty
@@ -90,9 +95,16 @@ feature -- Widget
exportation_web_form (a_response: CMS_RESPONSE): CMS_FORM
local
f_name: WSF_FORM_TEXT_INPUT
but: WSF_FORM_SUBMIT_INPUT
do
create Result.make (a_response.url (a_response.location, Void), "export_all_data")
Result.extend_raw_text ("Export CMS data to ")
create f_name.make_with_text ("folder", (create {DATE_TIME}.make_now_utc).formatted_out ("yyyy-[0]mm-[0]dd---hh24-[0]mi-[0]ss"))
f_name.set_label ("Export folder name")
f_name.set_description ("Folder name under 'exports' folder.")
f_name.set_is_required (True)
Result.extend (f_name)
create but.make_with_text ("op", text_export_all_data)
Result.extend (but)
end

View File

@@ -188,7 +188,10 @@ feature -- Hooks
a_export_id_list = Void
or else across a_export_id_list as ic some ic.item.same_string ("blog") end
then
if attached blog_api as l_blog_api then
if
a_response.has_permissions (<<"export any node", "export blog">>) and then
attached blog_api as l_blog_api
then
lst := l_blog_api.blogs_order_created_desc
a_export_parameters.log ("Exporting " + lst.count.out + " blogs")
across
@@ -207,6 +210,30 @@ feature -- Hooks
f.put_string (json_to_string (blog_node_to_json (n)))
f.close
end
-- Revisions.
if
attached node_api as l_node_api and then
attached l_node_api.node_revisions (n) as l_revisions and then l_revisions.count > 1
then
a_export_parameters.log (n.content_type + " " + l_revisions.count.out + " revisions.")
p := a_export_parameters.location.extended ("nodes").extended (n.content_type).extended (n.id.out)
create d.make_with_path (p)
if not d.exists then
d.recursive_create_dir
end
across
l_revisions as revs_ic
loop
if attached {CMS_BLOG} revs_ic.item as l_blog then
create f.make_with_path (p.extended ("rev-" + n.revision.out).appended_with_extension ("json"))
if not f.exists or else f.is_access_writable then
f.open_write
f.put_string (json_to_string (blog_node_to_json (l_blog)))
end
f.close
end
end
end
end
end
end

View File

@@ -151,6 +151,7 @@ feature -- Access
do
Result := Precursor
Result.force ("create any node")
Result.force ("export any node")
if attached node_api as l_node_api then
across
@@ -177,6 +178,8 @@ feature -- Access
Result.force ("view unpublished " + l_type_name)
Result.force ("view revisions own " + l_type_name)
Result.force ("export " + l_type_name)
end
end
Result.force ("view trash")
@@ -375,6 +378,7 @@ feature -- Hooks
loop
l_node_type := types_ic.item
if
a_response.has_permissions (<<"export any node", "export " + l_node_type.name>>) and then
l_node_type.name.same_string_general ("page") and then
( a_export_id_list = Void
or else across a_export_id_list as ic some ic.item.same_string (l_node_type.name) end
@@ -384,17 +388,17 @@ feature -- Hooks
-- For now, handle only page from this node module.
lst := l_node_api.nodes_of_type (l_node_type)
a_export_parameters.log ("Exporting " + lst.count.out + " nodes of type " + l_node_type.name)
p := a_export_parameters.location.extended ("nodes").extended (l_node_type.name)
create d.make_with_path (p)
if not d.exists then
d.recursive_create_dir
end
across
lst as ic
loop
n := l_node_api.full_node (ic.item)
a_export_parameters.log (l_node_type.name + " #" + n.id.out)
p := a_export_parameters.location.extended ("nodes").extended (l_node_type.name).extended (n.id.out)
create d.make_with_path (p.parent)
if not d.exists then
d.recursive_create_dir
end
create f.make_with_path (p)
a_export_parameters.log (l_node_type.name + " #" + n.id.out + " rev=" + n.revision.out)
create f.make_with_path (p.extended (n.id.out).appended_with_extension ("json"))
if not f.exists or else f.is_access_writable then
f.open_write
if attached {CMS_PAGE} n as l_page then
@@ -404,6 +408,30 @@ feature -- Hooks
end
f.close
end
-- Revisions.
if attached l_node_api.node_revisions (n) as l_revisions and then l_revisions.count > 1 then
a_export_parameters.log (l_node_type.name + " " + l_revisions.count.out + " revisions.")
p := a_export_parameters.location.extended ("nodes").extended (l_node_type.name).extended (n.id.out)
create d.make_with_path (p)
if not d.exists then
d.recursive_create_dir
end
across
l_revisions as revs_ic
loop
n := revs_ic.item
create f.make_with_path (p.extended ("rev-" + n.revision.out).appended_with_extension ("json"))
if not f.exists or else f.is_access_writable then
f.open_write
if attached {CMS_PAGE} n as l_page then
f.put_string (json_to_string (page_node_to_json (l_page)))
else
f.put_string (json_to_string (node_to_json (n)))
end
f.close
end
end
end
end
end
end

View File

@@ -7,12 +7,12 @@ note
class
CMS_EXPORT_NODE_UTILITIES
inherit
CMS_EXPORT_JSON_UTILITIES
feature -- Access
node_to_json (n: CMS_NODE): JSON_OBJECT
local
jo,j_author: JSON_OBJECT

View File

@@ -10,6 +10,13 @@ class
feature -- Access
put_string_into_json (st: detachable READABLE_STRING_GENERAL; a_key: JSON_STRING; j: JSON_OBJECT)
do
if st /= Void then
j.put_string (st, a_key)
end
end
put_date_into_json (dt: detachable DATE_TIME; a_key: JSON_STRING; j: JSON_OBJECT)
local
hd: HTTP_DATE
@@ -20,7 +27,7 @@ feature -- Access
end
end
json_to_string (j: JSON_OBJECT): STRING
json_to_string (j: JSON_VALUE): STRING
local
pp: JSON_PRETTY_STRING_VISITOR
do

View File

@@ -80,6 +80,12 @@ feature -- URL aliases
do
end
path_aliases: STRING_TABLE [READABLE_STRING_8]
-- <Precursor>.
do
create Result.make (0)
end
feature -- Logs
save_log (a_log: CMS_LOG)
@@ -116,5 +122,12 @@ feature -- Custom
end
end
custom_values: detachable LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]
-- Values as list of [name, type, value].
do
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

@@ -47,6 +47,11 @@ feature -- URL aliases
deferred
end
path_aliases: STRING_TABLE [READABLE_STRING_8]
-- All path aliases as a table containing sources indexed by alias.
deferred
end
feature -- Logs
save_log (a_log: CMS_LOG)
@@ -71,5 +76,12 @@ feature -- Misc
deferred
end
custom_values: detachable LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]
-- Values as list of [name, type, value].
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

@@ -127,6 +127,35 @@ feature -- URL aliases
sql_finalize
end
path_aliases: STRING_TABLE [READABLE_STRING_8]
-- All path aliases as a table containing sources indexed by alias.
local
l_source: READABLE_STRING_8
do
error_handler.reset
create Result.make (5)
sql_query (sql_select_all_path_alias, Void)
if not has_error then
from
sql_start
until
sql_after or has_error
loop
if attached sql_read_string (1) as s_src then
l_source := s_src
if attached sql_read_string (2) as s_alias then
Result.force (l_source, s_alias)
end
end
sql_forth
end
end
sql_finalize
end
sql_select_all_path_alias: STRING = "SELECT source, alias, lang FROM path_aliases;"
-- SQL select all path aliases.
sql_select_path_alias: STRING = "SELECT source FROM path_aliases WHERE alias=:alias ;"
-- SQL select path aliases.
@@ -251,6 +280,39 @@ feature -- Misc
sql_finalize
end
custom_values: detachable LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]
-- Values as list of [name, type, value].
local
l_type, l_name: READABLE_STRING_8
l_value: READABLE_STRING_32
do
error_handler.reset
create {ARRAYED_LIST [TUPLE [name: READABLE_STRING_GENERAL; type: detachable READABLE_STRING_8; value: detachable READABLE_STRING_32]]} Result.make (5)
sql_query (sql_select_all_custom_values, Void)
if not has_error then
from
sql_start
until
sql_after or has_error
loop
if attached sql_read_string (1) as s_type then
l_type := s_type
if attached sql_read_string (2) as s_name then
l_name := s_name
if attached sql_read_string_32 (3) as s_value then
Result.force ([l_name, l_type, s_value])
end
end
end
sql_forth
end
end
sql_finalize
end
sql_select_all_custom_values: STRING = "SELECT type, name, value FROM custom_values;"
-- SQL Insert to add a new custom value.
sql_select_custom_value: STRING = "SELECT value FROM custom_values WHERE type=:type AND name=:name;"
-- SQL Insert to add a new custom value.

View File

@@ -829,6 +829,7 @@ feature {NONE} -- Implementation: User
end
fetch_user: detachable CMS_USER
-- Fetch user from fields: 1:uid, 2:name, 3:password, 4:salt, 5:email, 6:status, 7:created, 8:signed.
local
l_id: INTEGER_64
l_name: detachable READABLE_STRING_32
@@ -919,10 +920,10 @@ feature {NONE} -- Sql Queries: USER
Select_user_by_name: STRING = "SELECT * FROM users WHERE name =:name;"
-- Retrieve user by name if exists.
Sql_select_recent_users: STRING = "SELECT * FROM users ORDER BY uid DESC, created DESC LIMIT :rows OFFSET :offset ;"
Sql_select_recent_users: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users ORDER BY uid DESC, created DESC LIMIT :rows OFFSET :offset ;"
-- Retrieve recent users
Select_user_by_email: STRING = "SELECT * FROM users WHERE email =:email;"
Select_user_by_email: STRING = "SELECT uid, name, password, salt, email, status, created, signed FROM users WHERE email =:email;"
-- Retrieve user by email if exists.
Select_salt_by_username: STRING = "SELECT salt FROM users WHERE name =:name;"

View File

@@ -9,6 +9,10 @@ class
inherit
ANY
CMS_HOOK_EXPORT
CMS_EXPORT_JSON_UTILITIES
REFACTORING_HELPER
CMS_ENCODERS
@@ -316,6 +320,14 @@ feature -- Query: API
Result := l_api
end
feature -- Hooks
register_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
-- Register hooks associated with the cms core.
do
a_hooks.subscribe_to_export_hook (Current)
end
feature -- Path aliases
is_valid_path_alias (a_alias: READABLE_STRING_8): BOOLEAN
@@ -581,6 +593,113 @@ feature -- Environment/ modules and theme
Result := module_configuration_by_name (a_module.name, a_name)
end
feature -- Hook
export_to (a_export_id_list: detachable ITERABLE [READABLE_STRING_GENERAL]; a_export_parameters: CMS_EXPORT_PARAMETERS; a_response: CMS_RESPONSE)
-- <Precursor>.
local
p: PATH
d: DIRECTORY
ja: JSON_ARRAY
jobj,jo,j: JSON_OBJECT
f: PLAIN_TEXT_FILE
u: CMS_USER
s: STRING_32
do
if attached a_response.has_permissions (<<"admin export", "export core">>) then
if a_export_id_list = Void then -- Include everything
p := a_export_parameters.location.extended ("core")
create d.make_with_path (p)
if not d.exists then
d.recursive_create_dir
end
-- path_aliases export.
a_export_parameters.log ("Exporting path_aliases")
create jo.make_empty
across storage.path_aliases as ic loop
jo.put_string (ic.item, ic.key)
end
create f.make_with_path (p.extended ("path_aliases.json"))
f.create_read_write
f.put_string (json_to_string (jo))
f.close
-- custom_values export.
if attached storage.custom_values as lst then
a_export_parameters.log ("Exporting custom_values")
create ja.make_empty
across
lst as ic
loop
create j.make_empty
if attached ic.item.type as l_type then
j.put_string (l_type, "type")
end
j.put_string (ic.item.name, "name")
if attached ic.item.type as l_value then
j.put_string (l_value, "value")
end
ja.extend (j)
end
create f.make_with_path (p.extended ("custom_values.json"))
f.create_read_write
f.put_string (json_to_string (ja))
f.close
end
-- users export.
a_export_parameters.log ("Exporting users")
create jo.make_empty
create jobj.make_empty
across user_api.recent_users (create {CMS_DATA_QUERY_PARAMETERS}.make (0, user_api.users_count.as_natural_32)) as ic loop
u := ic.item
create j.make_empty
j.put_string (u.name, "name")
j.put_integer (u.status, "status")
put_string_into_json (u.email, "email", j)
put_string_into_json (u.password, "password", j)
put_string_into_json (u.hashed_password, "hashed_password", j)
put_date_into_json (u.creation_date, "creation_date", j)
put_date_into_json (u.last_login_date, "last_login_date", j)
if attached u.roles as l_roles then
create ja.make (l_roles.count)
across
l_roles as roles_ic
loop
ja.extend (create {JSON_STRING}.make_from_string_32 ({STRING_32} " %"" + roles_ic.item.name + {STRING_32} "%" #" + roles_ic.item.id.out))
end
j.put (ja, "roles")
end
jobj.put (j, u.id.out)
end
jo.put (jobj, "users")
create jobj.make_empty
across user_api.roles as ic loop
create j.make_empty
j.put_string (ic.item.name, "name")
if attached ic.item.permissions as l_perms then
create ja.make (l_perms.count)
across
l_perms as perms_ic
loop
ja.extend (create {JSON_STRING}.make_from_string (perms_ic.item))
end
j.put (ja, "permissions")
end
jobj.put (j, ic.item.id.out)
end
jo.put (jobj, "roles")
create f.make_with_path (p.extended ("users.json"))
f.create_read_write
f.put_string (json_to_string (jo))
f.close
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)"

View File

@@ -71,6 +71,7 @@ feature {NONE} -- Initialization
l_module: CMS_MODULE
l_enabled_modules: CMS_MODULE_COLLECTION
do
api.register_hooks (hooks)
l_enabled_modules := api.enabled_modules
across
l_enabled_modules as ic