Commit 5ba754ff authored by Frank Bergmann's avatar Frank Bergmann

- OpenACS 5.9

parent 8e2ff12b
declare
attr_id acs_attributes.attribute_id%TYPE;
exists_p integer;
begin
select count(*) into exists_p
from acs_attributes
where object_type = 'content_revision'
and attribute_name = 'item_id';
if exists_p = 0 then
attr_id := content_type.create_attribute (
content_type => 'content_revision',
attribute_name => 'item_id',
datatype => 'integer',
pretty_name => 'Item id',
pretty_plural => 'Item ids'
);
end if;
select count(*) into exists_p
from acs_attributes
where object_type = 'content_revision'
and attribute_name = 'content';
if exists_p = 0 then
attr_id := content_type.create_attribute (
content_type => 'content_revision',
attribute_name => 'content',
datatype => 'text',
pretty_name => 'content',
pretty_plural => 'content'
);
end if;
end;
/
show errors
insert into cr_mime_types(label, mime_type, file_extension) select 'Archive Zip', 'application/x-zip', 'zip' from dual where not exists (select 1 from cr_mime_types where mime_type = 'application/x-zip');
insert into cr_mime_types(label, mime_type, file_extension) select 'Shell Script', 'application/x-sh', 'sh' from dual where not exists (select 1 from cr_mime_types where mime_type = 'application/x-sh');
insert into cr_mime_types(label, mime_type, file_extension) select 'RDF/XML', 'application/rdf+xml', 'rdf' from dual where not exists (select 1 from cr_mime_types where mime_type = 'application/rdf+xml');
create or replace trigger cr_cleanup_cr_files_del_trg
before delete on cr_revisions
for each row
begin
insert into cr_files_to_delete (
path, storage_area_key
) select :old.filename, i.storage_area_key
from cr_items i
where i.item_id = :old.item_id
and i.storage_type = 'file'
and r.content is not null;
end cr_cleanup_cr_files_del_trg;
/
show errors
-- @author Victor Guerra (vguerra@gmail.com)
-- @creation-date 2010-09-29
--
-- The following function get's rid of the foreign key constraint between cr_folders(folder_id) and cr_items(item_id),
-- at somepoint ( upgrade-5.4.2d1-5.42d2 ) it was added using the statement "alter table add foreign key .. "
-- which will add a constraint with name $1, since this is not for sure ( could have also other name assigned), we better look for the right constraint name
-- to be deleted using pg_constraint and pg_class table
create function inline_0 ()
returns integer as '
declare
constraints RECORD;
begin
for constraints in select conname
from pg_constraint con ,pg_class cla1, pg_class cla2
where con.contype = ''f'' and con.conrelid = cla1.oid and cla1.relname = ''cr_folders''
and con.confrelid = cla2.oid and cla2.relname = ''cr_items''
loop
EXECUTE ''alter table cr_folders drop constraint '' || constraints.conname;
end loop;
return null;
end;' language 'plpgsql';
select inline_0();
drop function inline_0();
-- Now we create the foreign key constraint with the right name
alter table cr_folders add constraint cr_folders_folder_id_fk foreign key (folder_id) references cr_items(item_id) on delete cascade;
create or replace function content_template__new (varchar,integer,integer,timestamptz,integer,varchar)
returns integer as '
declare
new__name alias for $1;
new__parent_id alias for $2; -- default null
new__template_id alias for $3; -- default null
new__creation_date alias for $4; -- default now()
new__creation_user alias for $5; -- default null
new__creation_ip alias for $6; -- default null
v_template_id cr_templates.template_id%TYPE;
v_parent_id cr_items.parent_id%TYPE;
begin
if new__parent_id is null then
select c_root_folder_id into v_parent_id from content_template_globals;
else
v_parent_id := new__parent_id;
end if;
-- make sure we''re allowed to create a template in this folder
if content_folder__is_folder(new__parent_id) = ''t'' and
content_folder__is_registered(new__parent_id,''content_template'',''f'') = ''f'' then
raise EXCEPTION ''-20000: This folder does not allow templates to be created'';
else
v_template_id := content_item__new (
new__name,
v_parent_id,
new__template_id,
null,
new__creation_date,
new__creation_user,
null,
new__creation_ip,
''content_item'',
''content_template'',
null,
null,
''text/plain'',
null,
null,
''text''
);
insert into cr_templates (
template_id
) values (
v_template_id
);
return v_template_id;
end if;
end;' language 'plpgsql';
create or replace function content_template__new (varchar,integer,integer,timestamptz,integer,varchar,text,bool)
returns integer as '
declare
new__name alias for $1;
new__parent_id alias for $2; -- default null
new__template_id alias for $3; -- default null
new__creation_date alias for $4; -- default now()
new__creation_user alias for $5; -- default null
new__creation_ip alias for $6; -- default null
new__text alias for $7; -- default null
new__is_live alias for $8; -- default ''f''
v_template_id cr_templates.template_id%TYPE;
v_parent_id cr_items.parent_id%TYPE;
begin
if new__parent_id is null then
select c_root_folder_id into v_parent_id from content_template_globals;
else
v_parent_id := new__parent_id;
end if;
-- make sure we''re allowed to create a template in this folder
if content_folder__is_folder(new__parent_id) = ''t'' and
content_folder__is_registered(new__parent_id,''content_template'',''f'') = ''f'' then
raise EXCEPTION ''-20000: This folder does not allow templates to be created'';
else
v_template_id := content_item__new (
new__template_id, -- new__item_id
new__name, -- new__name
v_parent_id, -- new__parent_id
null, -- new__title
new__creation_date, -- new__creation_date
new__creation_user, -- new__creation_user
null, -- new__context_id
new__creation_ip, -- new__creation_ip
new__is_live, -- new__is_live
''text/plain'', -- new__mime_type
new__text, -- new__text
''text'', -- new__storage_type
''t'', -- new__security_inherit_p
''CR_FILES'', -- new__storage_area_key
''content_item'', -- new__item_subtype
''content_template'' -- new__content_type
);
insert into cr_templates (
template_id
) values (
v_template_id
);
return v_template_id;
end if;
end;' language 'plpgsql';
select define_function_args('content_type__create_type','content_type,supertype;content_revision,pretty_name,pretty_plural,table_name,id_column,name_method');
create or replace function content_type__create_type (varchar,varchar,varchar,varchar,varchar,varchar,varchar)
returns integer as '
declare
create_type__content_type alias for $1;
create_type__supertype alias for $2; -- default ''content_revision''
create_type__pretty_name alias for $3;
create_type__pretty_plural alias for $4;
create_type__table_name alias for $5;
create_type__id_column alias for $6; -- default ''XXX''
create_type__name_method alias for $7; -- default null
v_temp_p boolean;
v_supertype_table acs_object_types.table_name%TYPE;
begin
if (create_type__supertype <> ''content_revision'')
and (create_type__content_type <> ''content_revision'') then
select count(*) > 0 into v_temp_p
from acs_object_type_supertype_map
where object_type = create_type__supertype
and ancestor_type = ''content_revision'';
if not v_temp_p then
raise EXCEPTION ''-20000: supertype % must be a subtype of content_revision'', create_type__supertype;
end if;
end if;
select count(*) = 0 into v_temp_p
from pg_class
where relname = lower(create_type__table_name);
PERFORM acs_object_type__create_type (
create_type__content_type,
create_type__pretty_name,
create_type__pretty_plural,
create_type__supertype,
create_type__table_name,
create_type__id_column,
null,
''f'',
null,
create_type__name_method,
v_temp_p,
''f''
);
PERFORM content_type__refresh_view(create_type__content_type);
return 0;
end;' language 'plpgsql';
select define_function_args('content_type__create_attribute','content_type,attribute_name,datatype,pretty_name,pretty_plural,sort_order,default_value,column_spec;text');
create or replace function content_type__create_attribute (varchar,varchar,varchar,varchar,varchar,integer,varchar,varchar)
returns integer as '
declare
create_attribute__content_type alias for $1;
create_attribute__attribute_name alias for $2;
create_attribute__datatype alias for $3;
create_attribute__pretty_name alias for $4;
create_attribute__pretty_plural alias for $5; -- default null
create_attribute__sort_order alias for $6; -- default null
create_attribute__default_value alias for $7; -- default null
create_attribute__column_spec alias for $8; -- default ''text''
v_attr_id acs_attributes.attribute_id%TYPE;
v_table_name acs_object_types.table_name%TYPE;
v_column_exists boolean;
begin
-- add the appropriate column to the table
select table_name into v_table_name from acs_object_types
where object_type = create_attribute__content_type;
if NOT FOUND then
raise EXCEPTION ''-20000: Content type % does not exist in content_type.create_attribute'', create_attribute__content_type;
end if;
select count(*) > 0 into v_column_exists
from pg_class c, pg_attribute a
where c.relname::varchar = v_table_name
and c.oid = a.attrelid
and a.attname = lower(create_attribute__attribute_name);
v_attr_id := acs_attribute__create_attribute (
create_attribute__content_type,
create_attribute__attribute_name,
create_attribute__datatype,
create_attribute__pretty_name,
create_attribute__pretty_plural,
null,
null,
create_attribute__default_value,
1,
1,
create_attribute__sort_order,
''type_specific'',
''f'',
not v_column_exists,
null,
null,
null,
null,
null,
create_attribute__column_spec
);
PERFORM content_type__refresh_view(create_attribute__content_type);
return v_attr_id;
end;' language 'plpgsql';
-- Need to guard against xotcl-core which sneakily modifies core behind
-- our backs (rather than having fixed acs-core like nice people would do)
begin;
select content_type__create_attribute (
'content_revision',
'item_id',
'integer',
'Item id',
'Item ids',
null,
null,
'integer'
)
where not exists (select 1
from acs_attributes
where object_type = 'content_revision'
and attribute_name = 'item_id');
select content_type__create_attribute (
'content_revision',
'content',
'text',
'Content',
'Content',
null,
null,
'text'
)
where not exists (select 1
from acs_attributes
where object_type = 'content_revision'
and attribute_name = 'content');
end;
insert into cr_mime_types(label, mime_type, file_extension) select 'Archive Zip', 'application/x-zip', 'zip' from dual where not exists (select 1 from cr_mime_types where mime_type = 'application/x-zip');
insert into cr_mime_types(label, mime_type, file_extension) select 'Shell Script', 'application/x-sh', 'sh' from dual where not exists (select 1 from cr_mime_types where mime_type = 'application/x-sh');
insert into cr_mime_types(label, mime_type, file_extension) select 'RDF/XML', 'application/rdf+xml', 'rdf' from dual where not exists (select 1 from cr_mime_types where mime_type = 'application/rdf+xml');
\ No newline at end of file
This diff is collapsed.
-- content_revision__del() uses cr_item_publish_audit.old_revision and
-- cr_item_publish_audit.new_revision to delete entries from
-- cr_item_publish_audit. This takes forever on non-toy databases.
create index cr_item_publish_audit_orev_idx on cr_item_publish_audit(old_revision);
create index cr_item_publish_audit_nrev_idx on cr_item_publish_audit(new_revision);
-- make sure, we can add the foreign keys
DELETE FROM cr_item_publish_audit a WHERE NOT EXISTS (SELECT 1 FROM cr_items i WHERE a.item_id = i.item_id);
DELETE FROM cr_item_publish_audit a WHERE NOT EXISTS (SELECT 1 FROM cr_revisions r WHERE a.old_revision = r.revision_id);
DELETE FROM cr_item_publish_audit a WHERE NOT EXISTS (SELECT 1 FROM cr_revisions r WHERE a.new_revision = r.revision_id);
-- add the foreign keys
alter table cr_item_publish_audit add constraint cr_item_publish_audit_item_fk foreign key (item_id) references cr_items (item_id);
alter table cr_item_publish_audit add constraint cr_item_publish_audit_orev_fk foreign key (old_revision) references cr_revisions (revision_id);
alter table cr_item_publish_audit add constraint cr_item_publish_audit_nrev_fk foreign key (new_revision) references cr_revisions (revision_id);
-- providing upgrade for content_extlink__new and content_revision__copy
-- in order to get next values of sequences using nextval()
--
-- procedure content_extlink__new/10
--
CREATE OR REPLACE FUNCTION content_extlink__new(
new__name varchar, -- default null
new__url varchar,
new__label varchar, -- default null
new__description varchar, -- default null
new__parent_id integer,
new__extlink_id integer, -- default null
new__creation_date timestamptz, -- default now() -- default 'now'
new__creation_user integer, -- default null
new__creation_ip varchar, -- default null
new__package_id integer -- default null
) RETURNS integer AS $$
DECLARE
v_extlink_id cr_extlinks.extlink_id%TYPE;
v_package_id acs_objects.package_id%TYPE;
v_label cr_extlinks.label%TYPE;
v_name cr_items.name%TYPE;
BEGIN
if new__label is null then
v_label := new__url;
else
v_label := new__label;
end if;
if new__name is null then
select nextval('t_acs_object_id_seq') into v_extlink_id from dual;
v_name := 'link' || v_extlink_id;
else
v_name := new__name;
end if;
if new__package_id is null then
v_package_id := acs_object__package_id(new__parent_id);
else
v_package_id := new__package_id;
end if;
v_extlink_id := content_item__new(
v_name,
new__parent_id,
new__extlink_id,
null,
new__creation_date,
new__creation_user,
null,
new__creation_ip,
'content_item',
'content_extlink',
null,
null,
'text/plain',
null,
null,
'text',
v_package_id
);
insert into cr_extlinks
(extlink_id, url, label, description)
values
(v_extlink_id, new__url, v_label, new__description);
update acs_objects
set title = v_label
where object_id = v_extlink_id;
return v_extlink_id;
END;
$$ LANGUAGE plpgsql;
--
-- procedure content_revision__copy/5
--
CREATE OR REPLACE FUNCTION content_revision__copy(
copy__revision_id integer,
copy__copy_id integer, -- default null
copy__target_item_id integer, -- default null
copy__creation_user integer, -- default null
copy__creation_ip varchar -- default null
) RETURNS integer AS $$
DECLARE
v_copy_id cr_revisions.revision_id%TYPE;
v_target_item_id cr_items.item_id%TYPE;
type_rec record;
BEGIN
-- use the specified item_id or the item_id of the original revision
-- if none is specified
if copy__target_item_id is null then
select item_id into v_target_item_id from cr_revisions
where revision_id = copy__revision_id;
else
v_target_item_id := copy__target_item_id;
end if;
-- use the copy_id or generate a new copy_id if none is specified
-- the copy_id is a revision_id
if copy__copy_id is null then
select nextval('t_acs_object_id_seq') into v_copy_id from dual;
else
v_copy_id := copy__copy_id;
end if;
-- create the basic object
insert into acs_objects (
object_id,
object_type,
context_id,
security_inherit_p,
creation_user,
creation_date,
creation_ip,
last_modified,
modifying_user,
modifying_ip,
title,
package_id)
select
v_copy_id as object_id,
object_type,
v_target_item_id,
security_inherit_p,
copy__creation_user as creation_user,
now() as creation_date,
copy__creation_ip as creation_ip,
now() as last_modified,
copy__creation_user as modifying_user,
copy__creation_ip as modifying_ip,
title,
package_id
from
acs_objects
where
object_id = copy__revision_id;
-- create the basic revision (using v_target_item_id)
insert into cr_revisions
select
v_copy_id as revision_id,
v_target_item_id as item_id,
title,
description,
publish_date,
mime_type,
nls_language,
lob,
content,
content_length
from
cr_revisions
where
revision_id = copy__revision_id;
-- iterate over the ancestor types and copy attributes
for type_rec in select ot2.object_type, tree_level(ot2.tree_sortkey) as level
from acs_object_types ot1, acs_object_types ot2, acs_objects o
where ot2.object_type <> 'acs_object'
and ot2.object_type <> 'content_revision'
and o.object_id = copy__revision_id
and ot1.object_type = o.object_type
and ot1.tree_sortkey between ot2.tree_sortkey and tree_right(ot2.tree_sortkey)
order by level desc
LOOP
PERFORM content_revision__copy_attributes(type_rec.object_type,
copy__revision_id, v_copy_id);
end loop;
return v_copy_id;
END;
$$ LANGUAGE plpgsql;
create index cr_revisions_content_idx on cr_revisions (substring(content for 100));
\ No newline at end of file
select define_function_args('content_template__get_root_folder','');
--
-- Ancient version of PostgreSQL (most likely before pg8) had a
-- bug in the handling of referential integrities (sometimes referred
-- to as the RI bug) which made extra triggers necessary. AFIKT,
-- this bug is gone and now the triggers should be removed as well
-- and replaced by fk constraints (sometimes already done).
--
--
-- Some old installations (like openacs.org) have still the following
-- functions although the create script do not define this triggers.
-- It seems that an update script was missing.
--
DROP TRIGGER IF EXISTS cr_folder_del_ri_trg ON cr_items;
DROP FUNCTION IF EXISTS cr_folder_del_ri_trg();
DROP TRIGGER IF EXISTS cr_folder_ins_up_ri_trg ON cr_folders;
DROP FUNCTION IF EXISTS cr_folder_ins_up_ri_trg();
--
-- Handle latest_revision and live_revision via foreign keys
--
ALTER TABLE cr_items ADD CONSTRAINT cr_items_latest_fk
FOREIGN KEY (latest_revision) REFERENCES cr_revisions(revision_id);
ALTER TABLE cr_items ADD CONSTRAINT cr_items_live_fk
FOREIGN KEY (live_revision) REFERENCES cr_revisions(revision_id);
DROP TRIGGER IF EXISTS cr_revision_del_ri_tr on cr_items;
DROP FUNCTION IF EXISTS cr_revision_del_ri_tr();
DROP TRIGGER IF EXISTS cr_revision_ins_ri_tr on cr_items;
DROP FUNCTION IF EXISTS cr_revision_ins_ri_tr();
DROP TRIGGER IF EXISTS cr_revision_up_ri_tr on cr_items;
DROP FUNCTION IF EXISTS cr_revision_up_ri_tr();
DROP TRIGGER IF EXISTS cr_revision_del_rev_ri_tr on cr_revisions;
DROP FUNCTION IF EXISTS cr_revision_del_rev_ri_tr();
--
-- Scalability reform part 3 (content-repository):
--
-- - content_revision__del:
-- * Removed manual nulling of live_revision and latest_revision
-- by using appropriate ond delete actions on foreign keys
-- * Removed manual deletion of old_revision and new_revision in
-- cr_item_publish_audit by using "on delete cascade"
--
-- - content_item__del:
-- * Removed manual deletion of item_id in cr_item_publish_audit
-- by using "on delete cascade"
-- * Removed manual deletion of item_id in cr_release_periods
-- by using "on delete cascade"
-- * Removed manual deletion of item_id in cr_item_template_map
-- by using "on delete cascade"
-- * Removed manual deletion of item_id in cr_item_keyword_map
-- by using "on delete cascade"
-- * Removed manual deletion of direct permissions (was already
-- cascading)
--
-- - Added missing index for child_id to cr_child_rels.
-- This index was in the create scripts (with a non-conformant name),
-- but not in the upgrade scripts
-- constraints from acs-content-repository/sql/postgresql/content-revision.sql
ALTER TABLE cr_item_publish_audit DROP CONSTRAINT cr_item_publish_audit_orev_fk;
ALTER TABLE cr_item_publish_audit ADD CONSTRAINT cr_item_publish_audit_orev_fk
FOREIGN KEY (old_revision) REFERENCES cr_revisions(revision_id) ON DELETE CASCADE;
ALTER TABLE cr_item_publish_audit DROP CONSTRAINT cr_item_publish_audit_nrev_fk;
ALTER TABLE cr_item_publish_audit ADD CONSTRAINT cr_item_publish_audit_nrev_fk
FOREIGN KEY (new_revision) REFERENCES cr_revisions(revision_id) ON DELETE CASCADE;
-- constraints from acs-content-repository/sql/postgresql/content-item.sql
ALTER TABLE cr_release_periods DROP CONSTRAINT cr_release_periods_item_id_fk;
ALTER TABLE cr_release_periods ADD CONSTRAINT cr_release_periods_item_id_fk
FOREIGN KEY (item_id) REFERENCES cr_items(item_id) ON DELETE CASCADE;
ALTER TABLE cr_item_publish_audit DROP CONSTRAINT cr_item_publish_audit_item_fk;
ALTER TABLE cr_item_publish_audit ADD CONSTRAINT cr_item_publish_audit_item_fk
FOREIGN KEY (item_id) REFERENCES cr_items(item_id) ON DELETE CASCADE;
ALTER TABLE cr_item_template_map DROP CONSTRAINT cr_item_template_map_item_fk;
ALTER TABLE cr_item_template_map ADD CONSTRAINT cr_item_template_map_item_fk
FOREIGN KEY (item_id) REFERENCES cr_items(item_id) ON DELETE CASCADE;
ALTER TABLE cr_item_keyword_map DROP CONSTRAINT cr_item_keyword_map_item_id_fk;
ALTER TABLE cr_item_keyword_map ADD CONSTRAINT cr_item_keyword_map_item_id_fk
FOREIGN KEY (item_id) REFERENCES cr_items(item_id) ON DELETE CASCADE;
ALTER TABLE cr_items DROP CONSTRAINT cr_items_latest_fk;
ALTER TABLE cr_items ADD CONSTRAINT cr_items_latest_fk
FOREIGN KEY (latest_revision) REFERENCES cr_revisions(revision_id) on delete set null;
ALTER TABLE cr_items DROP CONSTRAINT cr_items_live_fk;
ALTER TABLE cr_items ADD CONSTRAINT cr_items_live_fk
FOREIGN KEY (live_revision) REFERENCES cr_revisions(revision_id) on delete set null;
DROP INDEX if exists CR_CHILD_RELS_kids_IDx;
DROP INDEX if exists cr_child_rels_child_id_idx;
CREATE INDEX cr_child_rels_child_id_idx on cr_child_rels(child_id);
--
-- updated functions
--
CREATE OR REPLACE FUNCTION content_revision__del(
delete__revision_id integer
) RETURNS integer AS $$
DECLARE
v_item_id cr_items.item_id%TYPE;
v_latest_revision cr_revisions.revision_id%TYPE;
BEGIN
--
-- Get item_id and the latest revision
--
select item_id
into v_item_id
from cr_revisions
where revision_id = delete__revision_id;
select latest_revision
into v_latest_revision
from cr_items
where item_id = v_item_id;
--
-- Recalculate latest revision in case it was deleted
--
if v_latest_revision = delete__revision_id then
select r.revision_id
into v_latest_revision
from cr_revisions r, acs_objects o
where o.object_id = r.revision_id
and r.item_id = v_item_id
and r.revision_id <> delete__revision_id
order by o.creation_date desc limit 1;
if NOT FOUND then
v_latest_revision := null;
end if;
update cr_items set latest_revision = v_latest_revision
where item_id = v_item_id;
end if;
--
-- Delete the revision
--
PERFORM acs_object__delete(delete__revision_id);
return 0;
END;
$$ LANGUAGE plpgsql;
--
-- procedure content_item__del/1
--
CREATE OR REPLACE FUNCTION content_item__del(
delete__item_id integer
) RETURNS integer AS $$
DECLARE
v_symlink_val record;
v_revision_val record;
v_rel_val record;
BEGIN
--
-- Delete all symlinks to this item
--
for v_symlink_val in select symlink_id
from cr_symlinks
where target_id = delete__item_id
LOOP
PERFORM content_symlink__delete(v_symlink_val.symlink_id);
end loop;
--
-- Delete all revisions of this item
--
-- The following loop could be dropped / replaced by a cascade
-- operation, when proper foreign keys are used along the
-- inheritence path.
--
for v_revision_val in select revision_id
from cr_revisions
where item_id = delete__item_id
LOOP
PERFORM acs_object__delete(v_revision_val.revision_id);
end loop;
--
-- Delete all relations on this item
--
for v_rel_val in select rel_id
from cr_item_rels
where item_id = delete__item_id
or related_object_id = delete__item_id
LOOP
PERFORM acs_rel__delete(v_rel_val.rel_id);
end loop;
for v_rel_val in select rel_id
from cr_child_rels
where child_id = delete__item_id
LOOP
PERFORM acs_rel__delete(v_rel_val.rel_id);
end loop;
for v_rel_val in select rel_id, child_id
from cr_child_rels
where parent_id = delete__item_id
LOOP
PERFORM acs_rel__delete(v_rel_val.rel_id);
PERFORM content_item__delete(v_rel_val.child_id);
end loop;
--
-- Delete associated comments
--
PERFORM journal_entry__delete_for_object(delete__item_id);
--
-- Finally, delete the acs_object of the item.
--
PERFORM acs_object__delete(delete__item_id);
return 0;
END;
$$ LANGUAGE plpgsql;
--
-- procedure content_type__refresh_view/1
--
CREATE OR REPLACE FUNCTION content_type__refresh_view(
refresh_view__content_type varchar
) RETURNS integer AS $$
DECLARE
cols varchar default '';
tabs varchar default '';
joins varchar default '';
v_table_name varchar;
join_rec record;
BEGIN
for join_rec in select ot2.table_name, ot2.id_column, tree_level(ot2.tree_sortkey) as level
from acs_object_types ot1, acs_object_types ot2
where ot2.object_type <> 'acs_object'
and ot2.object_type <> 'content_revision'
and lower(ot2.table_name) <> 'acs_objects'
and lower(ot2.table_name) <> 'cr_revisions'
and ot1.object_type = refresh_view__content_type
and ot1.tree_sortkey between ot2.tree_sortkey and tree_right(ot2.tree_sortkey)
order by ot2.tree_sortkey desc
LOOP
if join_rec.table_name is not null then
cols := cols || ', ' || join_rec.table_name || '.*';
tabs := tabs || ', ' || join_rec.table_name;
joins := joins || ' and acs_objects.object_id = ' ||
join_rec.table_name || '.' || join_rec.id_column;
end if;
end loop;
-- Since we allow null table name use object type if table name is null so
-- we still can have a view.
select coalesce(table_name,object_type) into v_table_name from acs_object_types
where object_type = refresh_view__content_type;
if length(v_table_name) > 57 then
raise exception 'Table name cannot be longer than 57 characters, because that causes conflicting rules when we create the views.';
end if;
-- create the input view (includes content columns)
if table_exists(v_table_name || 'i') then
execute 'drop view ' || v_table_name || 'i' || ' CASCADE';
end if;
-- FIXME: need to look at content_revision__get_content. Since the CR
-- can store data in a lob, a text field or in an external file, getting
-- the data attribute for this view will be problematic.
execute 'create view ' || v_table_name ||
'i as select acs_objects.object_id,
acs_objects.object_type,
acs_objects.title as object_title,
acs_objects.package_id as object_package_id,
acs_objects.context_id,
acs_objects.security_inherit_p,
acs_objects.creation_user,
acs_objects.creation_date,
acs_objects.creation_ip,
acs_objects.last_modified,
acs_objects.modifying_user,
acs_objects.modifying_ip,
cr.revision_id, cr.title, cr.item_id,
content_revision__get_content(cr.revision_id) as data,
cr_text.text_data as text,
cr.description, cr.publish_date, cr.mime_type, cr.nls_language' ||
cols ||
' from acs_objects, cr_revisions cr, cr_text' || tabs || ' where
acs_objects.object_id = cr.revision_id ' || joins;
-- create the output view (excludes content columns to enable SELECT *)
if table_exists(v_table_name || 'x') then
execute 'drop view ' || v_table_name || 'x cascade';
end if;
execute 'create view ' || v_table_name ||
'x as select acs_objects.object_id,
acs_objects.object_type,
acs_objects.title as object_title,
acs_objects.package_id as object_package_id,
acs_objects.context_id,
acs_objects.security_inherit_p,
acs_objects.creation_user,
acs_objects.creation_date,
acs_objects.creation_ip,
acs_objects.last_modified,
acs_objects.modifying_user,
acs_objects.modifying_ip,
cr.revision_id, cr.title, cr.item_id,
cr.description, cr.publish_date, cr.mime_type, cr.nls_language,
i.name, i.parent_id' ||
cols ||
' from acs_objects, cr_revisions cr, cr_items i, cr_text' || tabs ||
' where acs_objects.object_id = cr.revision_id
and cr.item_id = i.item_id' || joins;
PERFORM content_type__refresh_trigger(refresh_view__content_type);
-- exception
-- when others then
-- dbms_output.put_line('Error creating attribute view or trigger for'
-- || content_type);
return 0;
END;
$$ LANGUAGE plpgsql;
-- upgrade types
SELECT t2.object_type, content_type__refresh_view(t2.object_type)
from acs_object_types t1, acs_object_types t2
where t2.tree_sortkey between t1.tree_sortkey and
tree_right(t1.tree_sortkey) and t1.object_type = 'content_revision';
<?xml version="1.0"?>
<queryset>
<rdbms><type>postgresql</type><version>7.1</version></rdbms>
<fullquery name="cr_delete_scheduled_files.fetch_paths">
<querytext>
SELECT distinct crftd.path, crftd.storage_area_key
FROM cr_files_to_delete crftd
WHERE not exists (
SELECT 1 FROM cr_revisions r
WHERE substring(r.content for 100) = substring(crftd.path for 100)
)
</querytext>
</fullquery>
</queryset>
nsv_set mutex cr_file_creation [ns_mutex create oacs:cr_file_creation]
exec touch [cr_fs_path]/file-creation.log
<master>
<property name="doc(title)">Content Repository Administration</property>
<ul>
<li>
<a href="mime-types">Manage MIME types</a>
</li>
</ul>
<master>
<property name="&doc">doc</property>
<property name="context">@context;literal@</property>
<listtemplate name="extensions"></listtemplate>
ad_page_contract {
@author Emmanuelle Raffenne (eraffenne@gmail.com)
@creation-date 22-feb-2010
@cvs-id $Id$
} {
mime_type:notnull
}
set mime_type_label [db_string get_mime_type {} -default $mime_type]
set doc(title) "Extensions Mapped to $mime_type_label"
set context [list [list "./" "Mime Types"] $doc(title)]
set return_url [export_vars -base "extensions" {mime_type}]
set actions [list "Add extension" [export_vars -base "map" {mime_type return_url}] ""]
template::list::create \
-name extensions \
-multirow extensions \
-actions $actions \
-elements {
mime_type {
label "MIME type"
}
extension {
label "Extension"
}
action {
label "Action"
link_url_col action_url
}
}
db_multirow -extend {action action_url} extensions get_extensions {} {
set action_url [export_vars -base "unmap" {mime_type extension return_url}]
set action "Unmap"
}
<?xml version="1.0"?>
<queryset>
<fullquery name="get_mime_type">
<querytext>
select label
from cr_mime_types
where mime_type = :mime_type
</querytext>
</fullquery>
<fullquery name="get_extensions">
<querytext>
select mime_type,extension
from cr_extension_mime_type_map
where mime_type = :mime_type
order by extension
</querytext>
</fullquery>
</queryset>
<master>
<property name="&doc">doc</property>
<property name="context">@context;literal@</property>
<listtemplate name="mime_types"></listtemplate>
ad_page_contract {
@author Emmanuelle Raffenne (eraffenne@gmail.com)
@creation-date 22-feb-2010
@cvs-id $Id$
} {
{ extension_p:boolean 0 }
{ orderby "mime_type" }
}
set return_url [export_vars -base "index" {extension_p orderby}]
set actions [list "Create MIME type" [export_vars -base "new" {return_url}] ""]
if { $extension_p } {
set doc(title) "MIME Type Extension Map"
lappend actions "Show MIME types only" [export_vars -base "./" {{extension_p 0}}] ""
set elms {
extension {
label "Mapped extension"
orderby "map.extension"
}
mime_type {
label "MIME type"
link_url_col extensions_url
html {title "Manage mapped extensions for this MIME type"}
orderby "mime.mime_type"
}
label {
label "Description"
link_url_col extensions_url
html {title "Manage mapped extensions for this MIME type"}
orderby "mime.label"
}
action {
label "Action"
link_url_col action_url
}
}
} else {
set doc(title) "MIME Types"
lappend actions "Show extension map" [export_vars -base "./" {{extension_p 1}}] ""
set elms {
mime_type {
label "MIME type"
link_url_col extensions_url
html {title "Manage mapped extensions for this MIME type"}
orderby "mime_type"
}
label {
label "Description"
link_url_col extensions_url
html {title "Manage mapped extensions for this MIME type"}
orderby "label"
}
extension {
label "Default extension"
orderby "extension"
}
}
}
template::list::create \
-name mime_types \
-multirow mime_types \
-actions $actions \
-orderby_name orderby \
-bulk_action_export_vars {extension_p} \
-elements $elms \
-filters { extension_p }
if { $extension_p } {
db_multirow -extend {extensions_url action action_url} mime_types get_mime_type_map {} {
set extensions_url [export_vars -base "extensions" {mime_type}]
if { $extension ne "" } {
set action "unmap"
set action_url [export_vars -base "unmap" {return_url extension mime_type}]
}
}
} else {
db_multirow -extend {extensions_url} mime_types get_mime_types {} {
set extensions_url [export_vars -base "extensions" {mime_type}]
}
}
set context [list $doc(title)]
<?xml version="1.0"?>
<queryset>
<fullquery name="get_mime_type_map">
<querytext>
select mime.mime_type, mime.label, map.extension
from cr_mime_types mime left join cr_extension_mime_type_map map
on (mime.mime_type = map.mime_type)
[template::list::orderby_clause -orderby -name mime_types]
</querytext>
</fullquery>
<fullquery name="get_mime_types">
<querytext>
select mime_type, label, file_extension as extension
from cr_mime_types
[template::list::orderby_clause -orderby -name mime_types]
</querytext>
</fullquery>
</queryset>
<master>
<property name="&doc">doc</property>
<property name="context">@context;literal@</property>
<formtemplate id="extension_new"></formtemplate>
ad_page_contract {
@author Emmanuelle Raffenne (eraffenne@gmail.com)
@creation-date 22-feb-2010
@cvs-id $Id$
} {
mime_type:notnull
{return_url ""}
}
set doc(title) "Add an extension"
set label [db_string get_mime_type {} -default $mime_type]
set context [list [list [export_vars -base "extensions" {mime_type}] "Extensions mapped to $label"] $doc(title)]
if { $return_url eq "" } {
set return_url [export_vars -base "extensions" {mime_type}]
}
ad_form -name extension_new -export {return_url} -cancel_url $return_url -form {
{mime_type:text(inform)
{label "MIME type"}
{html {size 25}}
}
{label:text(inform)
{label "Description"}
{html {size 50}}
}
{extension:text(text)
{label "Extension"}
{html {size 5}}
}
} -on_request {
} -on_submit {
cr_create_mime_type \
-extension $extension \
-mime_type $mime_type
} -after_submit {
ad_returnredirect $return_url
ad_script_abort
}
<?xml version="1.0"?>
<queryset>
<fullquery name="get_mime_type">
<querytext>
select label
from cr_mime_types
where mime_type = :mime_type
</querytext>
</fullquery>
</queryset>
<master>
<property name="&doc">doc</property>
<property name="context">@context;literal@</property>
<formtemplate id="mime_type_new"></formtemplate>
ad_page_contract {
@author Emmanuelle Raffenne (eraffenne@gmail.com)
@creation-date 22-feb-2010
@cvs-id $Id$
} {
{return_url ""}
}
set doc(title) "Create a new MIME Type"
set context [list [list "./" "MIME types"] $doc(title)]
if { $return_url eq "" } {
set return_url "./"
}
ad_form -name mime_type_new -export {return_url} -cancel_url $return_url -form {
{mime_type:text(text)
{label "MIME type"}
{html {size 25}}
}
{label:text(text)
{label "Description"}
{html {size 50}}
}
{extension:text(text),optional
{label "Default extension"}
{html {size 5}}
}
} -on_request {
} -on_submit {
cr_create_mime_type \
-extension $extension \
-mime_type $mime_type \
-description $label
} -after_submit {
ad_returnredirect $return_url
ad_script_abort
}
ad_page_contract {
@author Emmanuelle Raffenne (eraffenne@gmail.com)
@creation-date 22-feb-2010
@cvs-id $Id$
} {
extension:notnull
mime_type:notnull
{return_url ""}
}
if { $return_url eq "" } {
set return_url "index"
}
db_dml extension_unmap {}
ad_returnredirect $return_url
<?xml version="1.0"?>
<queryset>
<fullquery name="extension_unmap">
<querytext>
delete from cr_extension_mime_type_map
where extension = :extension and mime_type = :mime_type
</querytext>
</fullquery>
</queryset>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Package: content_extlink}</property>
<property name="doc(title)">Package: content_extlink</property>
<master>
<body>
<h2>content_extlink</h2><p>
<a href="../index.html">Content Repository</a> :
content_extlink</p><hr><ul>
<li><a href="#overview">Overview</a></li><li><a href="#related">Related Objects</a></li><li><a href="#api">API</a></li>
</ul><p> </p><h3><a name="overview">Overview</a></h3><p>External links are references to content pages on other web
sites. They provide the basis for maintaining a hierarchy of
"bookmarks" that may be managed in a manner analogous to other
content items. In particular, external links may be tagged with
keywords and related to the site's own content items.</p><p> </p><h3><a name="related">Related Objects</a></h3>
See also: {content_item }
<p> </p><h3><a name="api">API</a></h3><ul><li>
<font size="+1">Function:</font>
content_extlink.is_extlink</li></ul><p>Determines if the item is a extlink</p><table cellpadding="3" cellspacing="0" border="0">
<tr>
<th align="left">Author:</th><td align="left">Karl Goldstein</td>
</tr><tr>
<th align="left">Returns:</th><td align="left">'t' if the item is a extlink, 'f' otherwise</td>
</tr><tr><th align="left" colspan="2">Parameters:</th></tr><tr><td></td></tr><tr><td align="left" colspan="2"><blockquote><table border="0" cellpadding="0" cellspacing="1"><tr>
<th align="right" valign="top">item_id:</th><td>  </td><td>The item id</td>
</tr></table></blockquote></td></tr><tr><th align="left" colspan="2">Declaration:</th></tr><tr align="left"><td colspan="2" align="left"><pre><tt>
function is_extlink (
item_id in cr_items.item_id%TYPE
) return char;
</tt></pre></td></tr><tr>
<th align="left" valign="top">See Also:</th><td>content_extlink.new, content_extlink.resolve</td>
</tr>
</table><p> </p><ul><li>
<font size="+1">Function:</font> content_extlink.new</li></ul><p>Create a new extlink, an item pointing to an off-site
resource</p><table cellpadding="3" cellspacing="0" border="0">
<tr>
<th align="left">Author:</th><td align="left">Karl Goldstein</td>
</tr><tr>
<th align="left">Returns:</th><td align="left">The id of the newly created extlink</td>
</tr><tr><th align="left" colspan="2">Parameters:</th></tr><tr><td></td></tr><tr><td align="left" colspan="2"><blockquote><table border="0" cellpadding="0" cellspacing="1">
<tr>
<th align="right" valign="top">name:</th><td>  </td><td>The name for the new extlink, defaults to the name of the
target item</td>
</tr><tr>
<th align="right" valign="top">url:</th><td>  </td><td>The URL of the item</td>
</tr><tr>
<th align="right" valign="top">label:</th><td>  </td><td>The text label or title of the item</td>
</tr><tr>
<th align="right" valign="top">description:</th><td>  </td><td>A brief description of the item</td>
</tr><tr>
<th align="right" valign="top">parent_id:</th><td>  </td><td>The parent folder for the extlink. This must actually be a
folder and not a generic content item.</td>
</tr><tr>
<th align="right" valign="top">extlink_id:</th><td>  </td><td>The id of the new extlink. A new id will be allocated by
default</td>
</tr><tr>
<th align="right" valign="top">creation_date:</th><td>  </td><td>As in <tt>acs_object.new</tt>
</td>
</tr><tr>
<th align="right" valign="top">creation_ip:</th><td>  </td><td>As in <tt>acs_object.new</tt>
</td>
</tr><tr>
<th align="right" valign="top">creation_user:</th><td>  </td><td>As in <tt>acs_object.new</tt>
</td>
</tr>
</table></blockquote></td></tr><tr><th align="left" colspan="2">Declaration:</th></tr><tr align="left"><td colspan="2" align="left"><pre><tt>
function new (
name in cr_items.name%TYPE default null,
url in cr_extlinks.url%TYPE,
label in cr_extlinks.label%TYPE default null,
description in cr_extlinks.description%TYPE default null,
parent_id in acs_objects.context_id%TYPE,
extlink_id in cr_extlinks.extlink_id%TYPE default null,
creation_date in acs_objects.creation_date%TYPE
default sysdate,
creation_user in acs_objects.creation_user%TYPE
default null,
creation_ip in acs_objects.creation_ip%TYPE default null
) return cr_extlinks.extlink_id%TYPE;
</tt></pre></td></tr><tr>
<th align="left" valign="top">See Also:</th><td>acs_object.new, content_item.new, content_extlink.resolve</td>
</tr>
</table><p> </p><ul><li>
<font size="+1">Procedure:</font> content_extlink.delete</li></ul><p>Deletes the extlink</p><table cellpadding="3" cellspacing="0" border="0">
<tr>
<th align="left">Author:</th><td align="left">Karl Goldstein</td>
</tr><tr><th align="left" colspan="2">Parameters:</th></tr><tr><td></td></tr><tr><td align="left" colspan="2"><blockquote><table border="0" cellpadding="0" cellspacing="1"><tr>
<th align="right" valign="top">extlink_id:</th><td>  </td><td>The id of the extlink to delete</td>
</tr></table></blockquote></td></tr><tr><th align="left" colspan="2">Declaration:</th></tr><tr align="left"><td colspan="2" align="left"><pre><tt>
procedure delete (
extlink_id in cr_extlinks.extlink_id%TYPE
);
</tt></pre></td></tr><tr>
<th align="left" valign="top">See Also:</th><td>content_extlink.new, acs_object.delete</td>
</tr>
</table><p> </p>
Last Modified: $Id: extlink.html,v 1.1.1.1 2001/03/13 22:59:26 ben
Exp $
</body>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<property name="context">{/doc/acs-content-repository {Content Repository}} {Package: content_template}</property>
<property name="doc(title)">Package: content_template</property>
<master>
<body>
<h2>content_template</h2><p>
<a href="../index.html">Content Repository</a> :
content_template</p><hr><ul>
<li><a href="#overview">Overview</a></li><li><a href="#related">Related Objects</a></li><li><a href="#api">API</a></li>
</ul><p> </p><h3><a name="overview">Overview</a></h3><p>Templates are a special class of text objects that are used for
specifying the layout of a content item. They may be mapped to
content types, meaning that every item of that type will display
using that template unless a specific item overrides the default by
mapping to a template itself.</p><p> </p><h3><a name="related">Related Objects</a></h3>
See also: content_item, content_folder
<p> </p><h3><a name="api">API</a></h3><ul><li>
<font size="+1">Function:</font> content_template.get_path</li></ul><p>Retrieves the full path to the template, as described in
content_item.get_path</p><table cellpadding="3" cellspacing="0" border="0">
<tr>
<th align="left">Author:</th><td align="left">Karl Goldstein</td>
</tr><tr>
<th align="left">Returns:</th><td align="left">The path to the template, starting with the
specified root folder</td>
</tr><tr><th align="left" colspan="2">Parameters:</th></tr><tr><td></td></tr><tr><td align="left" colspan="2"><blockquote><table border="0" cellpadding="0" cellspacing="1">
<tr>
<th align="right" valign="top">template_id:</th><td>  </td><td>The id of the template for which the path is to be
retrieved</td>
</tr><tr>
<th align="right" valign="top">root_folder_id:</th><td>  </td><td>Starts path resolution at this folder</td>
</tr>
</table></blockquote></td></tr><tr><th align="left" colspan="2">Declaration:</th></tr><tr align="left"><td colspan="2" align="left"><pre><tt>
function get_path (
template_id in cr_templates.template_id%TYPE,
root_folder_id in cr_folders.folder_id%TYPE default c_root_folder_id
) return varchar2;
</tt></pre></td></tr><tr>
<th align="left" valign="top">See Also:</th><td>content_item.get_path</td>
</tr>
</table><p> </p><ul><li>
<font size="+1">Function:</font>
content_template.get_root_folder</li></ul><table cellpadding="3" cellspacing="0" border="0">
<tr><th align="left" colspan="2">Parameters:</th></tr><tr><td></td></tr><tr><td align="left" colspan="2"><i>Not yet documented</i></td></tr><tr><th align="left" colspan="2">Declaration:</th></tr><tr align="left"><td colspan="2" align="left"><pre><tt>
function get_root_folder return cr_folders.folder_id%TYPE;
</tt></pre></td></tr>
</table><p> </p><ul><li>
<font size="+1">Function:</font>
content_template.is_template</li></ul><p>Determine if an item is a template.</p><table cellpadding="3" cellspacing="0" border="0">
<tr>
<th align="left">Author:</th><td align="left">Karl Goldstein</td>
</tr><tr>
<th align="left">Returns:</th><td align="left">'t' if the item is a template, 'f' otherwise</td>
</tr><tr><th align="left" colspan="2">Parameters:</th></tr><tr><td></td></tr><tr><td align="left" colspan="2"><blockquote><table border="0" cellpadding="0" cellspacing="1"><tr>
<th align="right" valign="top">item_id:</th><td>  </td><td>The item id</td>
</tr></table></blockquote></td></tr><tr><th align="left" colspan="2">Declaration:</th></tr><tr align="left"><td colspan="2" align="left"><pre><tt>
function is_template (
template_id in cr_templates.template_id%TYPE
) return varchar2;
</tt></pre></td></tr><tr>
<th align="left" valign="top">See Also:</th><td>content_template.new</td>
</tr>
</table><p> </p><ul><li>
<font size="+1">Function:</font> content_template.new</li></ul><p>Creates a new content template which can be used to render
content items.</p><table cellpadding="3" cellspacing="0" border="0">
<tr>
<th align="left">Author:</th><td align="left">Karl Goldstein</td>
</tr><tr>
<th align="left">Returns:</th><td align="left">The id of the newly created template</td>
</tr><tr><th align="left" colspan="2">Parameters:</th></tr><tr><td></td></tr><tr><td align="left" colspan="2"><blockquote><table border="0" cellpadding="0" cellspacing="1">
<tr>
<th align="right" valign="top">name:</th><td>  </td><td>The name for the template, must be a valid UNIX-like filename.
If a template with this name already exists under the specified
parent item, an error is thrown</td>
</tr><tr>
<th align="right" valign="top">parent_id:</th><td>  </td><td>The parent of this item, defaults to null</td>
</tr><tr>
<th align="right" valign="top">template_id:</th><td>  </td><td>The id of the new template. A new id will be allocated if this
parameter is null</td>
</tr><tr>
<th align="right" valign="top">creation_date:</th><td>  </td><td>As in <tt>acs_object.new</tt>
</td>
</tr><tr>
<th align="right" valign="top">creation_ip:</th><td>  </td><td>As in <tt>acs_object.new</tt>
</td>
</tr><tr>
<th align="right" valign="top">creation_user:</th><td>  </td><td>As in <tt>acs_object.new</tt>
</td>
</tr>
</table></blockquote></td></tr><tr><th align="left" colspan="2">Declaration:</th></tr><tr align="left"><td colspan="2" align="left"><pre><tt>
function new (
name in cr_items.name%TYPE,
parent_id in acs_objects.context_id%TYPE default null,
template_id in cr_templates.template_id%TYPE default null,
creation_date in acs_objects.creation_date%TYPE
default sysdate,
creation_user in acs_objects.creation_user%TYPE
default null,
creation_ip in acs_objects.creation_ip%TYPE default null
) return cr_templates.template_id%TYPE;
</tt></pre></td></tr><tr>
<th align="left" valign="top">See Also:</th><td>acs_object.new, content_item.new,
content_item.register_template, content_type.register_template</td>
</tr>
</table><p> </p><ul><li>
<font size="+1">Procedure:</font> content_template.delete</li></ul><p>Deletes the specified template, and unregisters the template
from all content types and content items. Use with caution - this
operation cannot be undone.</p><table cellpadding="3" cellspacing="0" border="0">
<tr>
<th align="left">Author:</th><td align="left">Karl Goldstein</td>
</tr><tr><th align="left" colspan="2">Parameters:</th></tr><tr><td></td></tr><tr><td align="left" colspan="2"><blockquote><table border="0" cellpadding="0" cellspacing="1"><tr>
<th align="right" valign="top">template_id:</th><td>  </td><td>The id of the template to delete</td>
</tr></table></blockquote></td></tr><tr><th align="left" colspan="2">Declaration:</th></tr><tr align="left"><td colspan="2" align="left"><pre><tt>
procedure delete (
template_id in cr_templates.template_id%TYPE
);
</tt></pre></td></tr><tr>
<th align="left" valign="top">See Also:</th><td>acs_object.delete, content_item.unregister_template,
content_type.unregister_template,</td>
</tr>
</table><p> </p>
Last Modified: $Id: template.html,v 1.1.1.1 2001/03/13 22:59:26 ben
Exp $
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository Design}</property>
<property name="doc(title)">Content Repository Design</property>
<master>
<body>
<h2>Content Repository Design</h2><h3>I. Essentials</h3><ul><li><a href="requirements">Feature Requirements
Document</a></li></ul><h3>II. Introduction</h3><p>Serving <em>content</em> is a basic function of any web site.
Common types of content include:</p><ul>
<li>Journal articles and stories</li><li>Documentation</li><li>News reports</li><li>Product reviews</li><li>Press releases</li><li>Message board postings</li><li>Photographs</li>
</ul><p>Note that the definition of content is not limited to what is
produced by the publisher. User-contributed content such as
reviews, comments, or message board postings may come to dominate
active community sites.</p><p>Regardless of its type or origin, it is often useful for
developers, publishers and users to handle all content in a
consistent fashion. Developers benefit because they can base all
their content-driven applications on a single core API, thereby
reducing the need for custom (and often redundant) development.
Publishers benefit because they can subject all types of content to
the same management and production practices, including access
control, workflow, categorization and syndication. Users benefit
because they can enjoy a single interface for searching, browsing
and managing their own contributions.</p><p>The content repository itself is intended <em>only</em> as a
common substrate for developing content-driven applications. It
provides the developer with a core set of content-related
services:</p><ul>
<li>Defining arbitrary content types.</li><li>Common storage of content items (each item consists of a text
or binary data with additional attributes as specified by the
content type).</li><li>Establishing relationships among items of any type.</li><li>Versioning</li><li>Consistent interaction with other services included in the ACS
core, including permissions, workflow and object
relationships.</li><li>Categorization</li><li>Searching</li>
</ul><p>As a substrate layer, the content repository is not intended to
ever have its own administrative or user interface. ACS modules and
custom applications built on the repository remain responsible for
implementing an appropriate interface. (Note that the ACS Content
Management System provides a general interface for interacting with
the content repository).</p><h3>III. Historical Considerations</h3><p>The content repository was originally developed in the Spring of
2000 as a standalone data model. It was based on an earlier custom
system developed for an ArsDigita client. Many of the principle
design features of the original data model were also reflected in
the ACS Objects system implemented in the ACS 4.0 core. The content
repository was subsequently rewritten as an extension of ACS
Objects.</p><h3>V. Design Tradeoffs</h3><p>The content repository is a direct extension of the core ACS
Object Model. As such the same design tradeoffs apply.</p><p>The content repository stores all revisions of all content items
in a single table, rather than maintaining separate tables for
"live" and other revisions. The single-table approach dramatically
simplifies most operations on the repository, including adding
revisions, marking a "live" revision, and maintaining a full
version history. The drawback of this approach is that accessing
live content is less efficient. Given the ID of a content item, it
is not possible to directly access the live content associated with
that item. Instead, an extra join to the revisions table is
required. Depending on the production habits of the publisher, the
amount of live content in the repository may be eclipsed by large
numbers of infrequently accessed working drafts. The impact of this
arrangement is minimized by storing the actual content data in a
separate tablespace (preferably on a separate disk) from the actual
revisions table, reducing its size and allows the database server
to scan and read it more efficiently.</p><h3>VI. Further Reading</h3><p>The <a href="object-model">Object Model</a> provides a
graphic overview of the the how the content repository is designed.
The model links to pages of the API Guide that describe individual
objects. The Developer Guide describes how to address common
development tasks using the content repository.</p><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><br>
Last Modified: $Id: design.html,v 1.1.1.1 2001/03/13 22:59:26 ben
Exp $
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {}</property>
<property name="doc(title)"></property>
<master>
<body><p>Last Modified: $Id: access-control.html,v 1.1.1.1 2001/03/13
22:59:26 ben Exp $</p></body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository Developer Guide: Organizing Content
Items}</property>
<property name="doc(title)">Content Repository Developer Guide: Organizing Content
Items</property>
<master>
<body>
<h2>Organizing Content Items</h2><p>The content repository organizes content items in a hierarchical
structure similar to a file system. You manage content items in the
repository using the same basic operations as in a file system:</p><ul>
<li>A freshly installed content repository consists of a single
"root" folder (analogous to the root directory <tt>/</tt> in UNIX
or an empty partition in Windows or MacOS).</li><li>You organize items by creating subfolders under the root.</li><li>You can move or copy items from one folder to another.</li><li>You can create "links" or "shortcuts" for items to make them
accessible from within other directories.</li><li>Each item has a "file name" and an absolute "path" that is
determined by its location on a particular branch of the repository
tree. For example, the path to an item named <tt>widget</tt> in the
folder <tt>products</tt> would be <tt>/products/widget</tt>.</li>
</ul><p>The content repository adds an additional twist to a traditional
filesystem: <em>any</em> content item, not just a folder, may serve
as a container for any number of other content items. For example,
imagine a book consisting of a preface, a number of chapters and a
bibliography (which in turn may have any number of entries). The
book itself is a content item, in that it has attributes
(publisher, ISBN number, publication date, synopsis, etc.)
associated with it. It also is the logical container for all its
components.</p><p>It is important to note that folders are simply a special
subtype of content item. The content repository's representation of
a parent-child relationship between a folder and the items it
contains is no different from the relationship between a book and
its chapters. Folders may be thought of simply as generic
containers for grouping items that are not necessarily part of a
greater whole.</p><h3>An Example</h3><p>Consider a simple repository structure with the following
contents:</p><img src="organization.gif" height="360" width="440" border="1"><p>Note the following:</p><ul>
<li>The root folder of the content repository has a special ID
which is returned by the function
<tt>content_item.get_root_folder</tt>.</li><li>Regular content items such as <tt>index</tt> and <tt>about</tt>
may be stored directly under the root folder.</li><li>The "About Us" page has a photo as a child item. Note that the
path to the photo is <tt>/about/photo</tt>. Internally, the photo's
<tt>parent_id</tt> (in the <tt>cr_items</tt> table) is set to the
<tt>item_id</tt> of the "About Us" page.</li><li>The "Press" folder contains two items. Internally, the
<tt>parent_id</tt> of the "Press Index" and "Release One" items are
set to the <tt>item_id</tt> of the "Press" folder.</li>
</ul><p>Note that the same effective organization could have been
achieved by creating the "Press Index" item under the root, and
having press releases as its children. Using the folder approach
may have the following advantages:</p><ul>
<li>Content management systems can take advantage of the folder
structure to implement an intuitive user interface analagous to
familiar desktop tools (Windows Explorer, MacOS Finder, etc.).</li><li>You can use the content repository API to constraint the type
of content that a folder may contain (except for the index page).
For example, it is possible to limit the contents of the "Press"
folder to items of type "Press Release." See the <a href="../api/folder.html">Content Folder</a> API for more details.</li>
</ul><h3>Using your own root</h3><p>By default, the content repository has one root folder for
content items and one for templates. In some situations, that is
not enough. For example, a package that can be instantiated several
times might wish to store the content for each instance in its own
content root. Creating your own content (and template) root also
has the advantage that you will not accidentally access another
package's content nor will another package access your content. Not
that that could do any harm, because you have secured all your
content through appropriate permissions.</p><p>We only talk about creating content roots from here on —
creating template roots is completely analogous. You create your
own content root by calling <tt>content_folder.new</tt> in
PL/SQL:</p><pre>
declare
v_my_content_root integer;
begin
v_my_content_root := content_folder.new(
name =&gt; 'my_root',
label =&gt; 'My Root',
parent_id =&gt; 0
);
-- Store v_my_content_root in a safe place
end;
/
</pre><p>The important point is that you have to pass in <tt>0</tt> for
the <tt>parent_id</tt>. This <tt>parent_id</tt> is special in that
it indicates folders with no parent.</p><p>The content repository does not keep track of who created what
root folders. You have to do that yourself. In the above example,
you need to store the value <tt>v_my_content_root</tt> somewhere,
for example a table that is specific for your package, otherwise
you won't have a reliable way of accessing your new content
root.</p><p>With multiple content roots, there can be many items with
<tt>item_path</tt><tt>'/news/article'</tt> and you need to tell
the content repository which root you are talking about. For
example, to retrieve content through <tt>content_item.get_id</tt>,
you pass the id of your content root as the <tt>root_folder_id</tt>
parameter to specify the content root under which the
<tt>item_path</tt> should be resolved.</p><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><br>
Last Modified: $Id: file-system.html,v 1.1.1.1 2001/03/13 22:59:26
ben Exp $
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository Developer Guide: Subject Keywords
(Categories)}</property>
<property name="doc(title)">Content Repository Developer Guide: Subject Keywords
(Categories)</property>
<master>
<body>
<h2>Subject Keywords (Categories)</h2><hr><h3>Overview</h3><p>
<em>Subject Keywords</em> are used to implement categorization
for the Content Management system. A Subject Keyword is a small
label, such as "Oracle Documentation" or "My Favorite Foods", which
can be associated with any number of content items. Thus, content
items may be grouped by arbitrary categories. For example,
assigning the Subject Keyword "My Favorite Foods" to the content
items "Potstickers", "Strawberries" and "Ice Cream" would indicate
that all the three items belong in the same category - namely, the
category of the user's favorite foods. The actual physical location
of these items within the repository is irrelevant.</p><p>Subject Keywords may be nested to provide more detailed control
over categorization; for example, "My Favorite Foods" may be
further subdivided into "Healthy" and "Unhealthy". Subject Keywords
which have descendants are referred to as "<em>Subject
Categories</em>".</p><h3>Data Model</h3><p>The <tt>content_keyword</tt> object type is used to represent
Subject Keywords (see <tt>content_keyword.sql</tt>) The
<tt>content_keyword</tt> type inherits from
<tt>acs_object</tt>:</p><pre>
acs_object_type.create_type ( supertype =&gt; 'acs_object', object_type
=&gt; 'content_keyword', pretty_name =&gt; 'Content Keyword',
pretty_plural =&gt; 'Content Keywords', table_name =&gt; 'cr_keywords',
id_column =&gt; 'keyword_id', name_method =&gt; 'acs_object.default_name'
);
</pre>
In addition, the <tt>cr_keywords</tt> table (see
<tt>content-create.sql</tt>) contains extended attributes of
Subject Keywords:
<pre>
create table cr_keywords (
keyword_id integer
constraint cr_keywords_pk
primary key,
heading varchar2(600)
constraint cr_keywords_name_nil
not null,
description varchar2(4000)
);
</pre>
In <tt>content-keyword.sql</tt>:
<pre>
attr_id := acs_attribute.create_attribute (
object_type =&gt; 'acs_object',
attribute_name =&gt; 'heading',
datatype =&gt; 'string',
pretty_name =&gt; 'Heading',
pretty_plural =&gt; 'Headings'
);
attr_id := acs_attribute.create_attribute (
object_type =&gt; 'content_keyword',
attribute_name =&gt; 'description',
datatype =&gt; 'string',
pretty_name =&gt; 'Description',
pretty_plural =&gt; 'Descriptions'
);
</pre><p>Thus, each Subject Keyword has a <tt>heading</tt>, which is a
user-readable heading for the keyword, and a <tt>description</tt>,
which is a somewhat longer description of the keyword.</p><p>The <tt>cr_item_keyword_map</tt> table (see
<tt>content-create.sql</tt>) is used to relate content items to
keywords:</p><pre>
create table cr_item_keyword_map (
item_id integer
constraint cr_item_keyword_map_item_fk
references cr_items
constraint cr_item_keyword_map_item_nil
not null,
keyword_id integer
constraint cr_item_keyword_map_kw_fk
references cr_keywords
constraint cr_item_keyword_map_kw_nil
not null
constraint cr_item_keyword_map_pk
primary key (item_id, keyword_id)
);
</pre><h3><a href="/api-doc/procs-file-view?path=packages/acs-content-repository/tcl/content-keyword-procs.tcl">
API Access</a></h3><p>The API used to access and modify content keywords are outlined
below. The function names are links that will take you to a more
detailed description of the function and its parameters.</p><table border="1" cellpadding="4" cellspacing="0">
<tr>
<th>Function/Procedure</th><th>Purpose</th><th>Description</th>
</tr><tr>
<td><a href="/api-doc/proc-view?proc=content::keyword::new">new</a></td><td>Create a new Subject Keyword</td><td>This is a standard <tt>new</tt> function, used to create a new
Subject Keyword. If the parent id is specified, the new keword
becomes a child of the parent keyword (which may now be called a
Subject Category)</td>
</tr><tr>
<td><a href="/api-doc/proc-view?proc=content::keyword::delete">delete</a></td><td>Delete a Subject Keyword</td><td>This is a standard <tt>delete</tt> function, used to delete a
Subject Keyword</td>
</tr><tr>
<td>
<a href="/api-doc/proc-view?proc=content::keyword::get_heading">get_heading</a><br><a href="/api-doc/proc-view?proc=content::keyword::set_heading">set_heading</a><br><a href="/api-doc/proc-view?proc=content::keyword::get_description">get_description</a><br><a href="/api-doc/proc-view?proc=content::keyword::set_description">set_description</a>
</td><td>Manipulate properties of the Keyword</td><td>You must use these functions to manipulate the properties of a
keyword. In the future, the data model will be updated to handle
internatiolization, but the API will not change.</td>
</tr><tr>
<td>
<a href="/api-doc/proc-view?proc=content::keyword::item_assign">item_assign</a><br><a href="/api-doc/proc-view?proc=content::keyword::item_unassign">item_unassign</a><br><a href="/api-doc/proc-view?proc=content::keyword::is_assigned">is_assigned</a>
</td><td>Assign Keywords to Items</td><td>These functions should be used to assign Subject Keywords to
content items, to unassign keywords from items, and to determine
whether a particular keyword is assigned to an item.
<p>The <tt>is_assigned</tt> function can be used to determine if a
keyword matches a content item, based on the <tt>recurse</tt>
parameter:</p><ul>
<li>If <tt>recurse</tt> is set to <tt>'none'</tt>,
<tt>is_assigned</tt> will return <tt>'t'</tt> if and only if there
is an exact assignment of the keyword to the item.</li><li>If <tt>recurse</tt> is set to <tt>'down'</tt>,
<tt>is_assigned</tt> will return <tt>'t'</tt> if there is an exact
assignment of the keyword to the item, or if a narrower keyword is
assigned to the item. For example, a query whether "Potstickers" is
assigned the category "My Favorite Foods" will return <tt>'t'</tt>
even if "Potstickers" is only assigned the category "Healthy".</li><li>If <tt>recurse</tt> is set to <tt>'up'</tt>,
<tt>is_assigned</tt> will return <tt>'t'</tt> if there is an exact
assignment of the keyword to the item, or if a broader Subject
Category is assigned to the item. For example, a query whether
"Potstickers" is assigned the category "Healthy" will return
<tt>'t'</tt> even if "Potstickers" is assigned the broader category
"My Favorite Foods".</li>
</ul>
</td>
</tr>
</table>
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository Developer Guide: Object
Relationships}</property>
<property name="doc(title)">Content Repository Developer Guide: Object
Relationships</property>
<master>
<body>
<h2>Object Relationships</h2><p>Many applications of the content repository require that content
items be related to each other as well as to other classes of
objects. Examples include:</p><ul>
<li>News stories may be linked to other stories on the same
topic.</li><li>An article may be linked to any number of photos or charts that
are embedded in the article.</li><li>A long article is divided into multiple sections, each of which
is intended for separate display.</li><li>Product reviews are linked to specific products.</li><li>User portraits are linked to specific users.</li>
</ul><p>The ACS kernel provides a standard, highly flexible data model
and API for relating objects to other objects. If you have a highly
specific problem and are developing your own user interface on the
content repository, you can use the ACS relationships framework
directly. The relationship framework in the content repository
itself is simply intended as a convenience for handling common
relationship situations involving content items.</p><h3>Parent-Child Relationships</h3><p>In many cases one content item may serve as a natural container
for another item. An article divided into sections, or a news story
with an associated photo are one example of this. These
"parent-child" relationships are handled by the basic hierarchical
organization of the content repository. Every item has a parent
item, represented internally by the <tt>parent_id</tt> column in
the <tt>cr_items</tt> table.</p><p>It is often desirable to constrain the number and content type
of child items. For example, the specifications for a news story
may only allow for a single photo. A structured report may have
exactly three sections. Furthermore, it may be necessary to
classify or identify child items of the same type. Clearly the
sections of a report would have a logical order in which they would
need to be presented to the user. The layout for a photo album may
have a special position for a "featured" photo.</p><table border="0" width="100%"><tr>
<td align="center"><img src="article.gif" border="1"></td><td align="center"><img src="photo.gif" border="1"></td>
</tr></table><p>The content repository accomodates these situations in the
following ways:</p><ul>
<li>An API procedure, <tt>content_type.register_child_type</tt>,
may be used to specify the minimum and maximum number of children
of a particular content type that an item may have. You may
optionally specify a "tag" for identifying child items of the same
type. For example, you may want to allow only 1 image with the
"featured" tag, and up to 8 other images without this.</li><li>A Boolean API function, <tt>content_item.is_valid_child</tt>,
which checks all registered child constraints on the content type
of an item and returns true if it is currently possible to add an
child of a particular type to tan item. Note that this function
does not protect against concurrent transactions; it is only
foolproof if you lock the <tt>cr_child_rels</tt> table
beforehand.</li><li>A mapping table, <tt>cr_child_rels</tt>, which contains two
attributes, <tt>order_n</tt> and <tt>relation_tag</tt>, that may be
used to characterize the parent-child relationship. Parent-child
relationships are themselves treated as ACS Objects, so this table
may be extended with additional attributes as required by the
developer.</li>
</ul><p>Note that there is no currently no explicit API to "add a
child." You specify the parent of an item upon creating it. You can
use the API procedure <tt>content_item.move</tt> to change the
parent of an item.</p><h3>Item-Object Relationships</h3><p>In addition to the relationships to their parents and children
in the content repository hierarchy, content items may be linked to
any number of other objects in the system. This may include
products, users or content items on related subjects.</p><p>The same need to constrain the possibilities for an item-object
relationship, as described above for parents and children, also
apply to items and objects in general. The content repository
provides a data model and API for managing these constraints that
parallels what is provided for parent-child relationships:</p><ul>
<li>An API procedure, <tt>content_type.register_relation_type</tt>,
may be used to specify the minimum and maximum number of relations
with a particular object type that an item may have. There is no
limitation on the type of objects that may be related to content
items. If you wish to relate content items to other content items,
however, the object type should specify a content type (a subtype
of <tt>content_revision</tt>) rather than simply
<tt>content_item</tt>. As for parent-child relationship
constraints, ou may optionally specify a "tag" for identifying
related objects of the same type.</li><li>A Boolean API function,
<tt>content_item.is_valid_relation</tt>, which checks all
registered constraints on the content type of an item and returns
true if it is currently possible to relate an object of a
particular type to an item.</li><li>A mapping table, <tt>cr_item_rels</tt>, which contains two
attributes, <tt>order_n</tt> and <tt>relation_tag</tt>, that may be
used to characterize the item-object relationship. Item-object
relationships are themselves treated as ACS Objects, so this table
may be extended with additional attributes as required by the
developer.</li>
</ul><h3>Extending Parent-Child and Item-Object Relationships</h3><p>The simple relation mechanisms described above may not be
sufficient for some applications. However, because both
relationships defined by the content repository are
<em>themselves</em> objects, you have the option to extend their
types as you would for any other ACS object.</p><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><br>
Last modified: <tt>$Id: object-relationships.html,v 1.1.1.1
2001/03/13 22:59:26 ben Exp $</tt>
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository Developer Guide: Publishing
Content}</property>
<property name="doc(title)">Content Repository Developer Guide: Publishing
Content</property>
<master>
<body>
<h2>Publishing Content</h2><p>The content repository does not place any restrictions on the
methods employed for delivering content via a public server
infrastructure. Applications are free to query the repository and
process the data in any way desired.</p><p>Although there are no restrictions on publishing methodology,
the repository API is intended to facilitate generic template-based
publication, regardless of the specific presentation layer used.
The following diagram illustrates the steps typically involved in
such a publication process:</p><img src="flow.gif" border="1"><p>In general, there is an initial <em>resolution</em> step in
which the server must identify the appropriate content item and
then decide which template to actually parse. Following that is an
<em>execution</em> step, during which setup tasks associated with
the template are performed. Finally, the <em>merging</em> step
combines the data and layout into a rendered page.</p><h3>Matching URLs to Content Items</h3><p>The primary mechanism for matching URLs to Content Items are
<em>virtual URL handlers</em>, <tt>.vuh</tt> files. An explanation
of virtual URL handlers can be found in the tutorial on the
<a href="/doc/request-processor">Request Processor</a>.</p><p>Here is an example <tt>index.vuh</tt> file that you can adapt to
your own purposes:</p><pre>
# Get the paths
set the_url [ad_conn path_info]
set the_root $::acs::pageroot
# Get the IDs
set content_root \
[db_string content_root "select content_item.get_root_folder from dual"]
set template_root \
[db_string template_root "select content_template.get_root_folder from dual"]
# Serve the page
# DRB: Note that content::init modifies the local variable the_root, which is treated
# as though it's been passed by reference. This requires that the redirect treat the
# path as an absolute path within the filesystem.
if { [content::init the_url the_root $content_root $template_root] } {
set file "$the_root/$the_url"
rp_internal_redirect -absolute_path $file
} else {
ns_returnnotfound
}
</pre><p>The <tt>content_root</tt> and <tt>template_root</tt> parameters
select the content and template root folders. In the example, they
are just the default roots that the content repository initializes
on installation. If you want to store your content completely
independent from that of other packages, you can initialize your
own content root and pass that folder's ID on to
<tt>content::init</tt>.</p><p>To publish content through URLs that are underneath
<tt>/mycontent</tt> you need to do the following:</p><ol>
<li>Create a directory <tt>mycontent</tt> in your server's page
root and an <tt>index.vuh</tt> file in that directory.</li><li>Adapt the <tt>set content_root ...</tt> and <tt>set
template_root ..</tt> statements in the example above so that they
are being set to the content and template root folders that you
want to publish content from.</li><li>Change the <tt>set the_url ...</tt> statement so that the
variable <tt>the_url</tt> contains the absolute path to the content
item you wish to serve from your (or the default) content
root.</li>
</ol><p>If you use the example <tt>index.vuh</tt> file above unaltered
for requests to <tt>my_content</tt>, a request for
<tt>http://yourserver/mycontent/news/articles/42</tt> would request
the content item <tt>/news/articles/42</tt> from the content
repository on the default content root folder.</p><h3>Matching Content Items to Templates</h3><h3>Querying Content</h3><h3>Querying Attributes</h3><p>When you create a new content type or add an attribute to an
existing content type, a view is created (or recreated) that joins
the attribute tables for the entire chain of inheritance for that
content type. The view always has the same name as the attribute
table for the content table, with an "x" appended to distinguish it
from the table itself (for example, if the attribute table for
<b>Press Releases</b> is <tt>press_releases</tt>, then the view
will be named <tt>press_releasesx</tt>. Querying this view is a
convenient means of accessing any attribute associated with a
content item.</p><p>As a shortcut, the item's template may call
<tt>content::get_content</tt> in its Tcl file in order to
automatically retrieve the current item's attributes. The
attributes will be placed in a onerow datasource called
<tt>content</tt> . The template may then call
<tt>template::util::array_to_vars content</tt> in order to convert
the onerow datasource to local variables.</p><p>In addition to the "x" view, the Content Repository creates an
"i" view, which simplifies the creation of new revisions. The "i"
view has the same name as the content table, with "i" appended at
the end. You may insert into the view as if it was a normal table;
the insert trigger on the view takes care of inserting the actual
values into the content tables.</p><h3>Querying Additional Data</h3><p>Templates often display more than simple content attributes.
Additional queries may be necessary to obtain data about related
objects not described directly in attribute tables. The setup code
associated with a template typically performs these queries after
the initial query for any needed attributes.</p><h3>Merging Data with Templates</h3><h3>Returning Output</h3><ol>
<li>Write to the file system</li><li>Service public requests directly</li>
</ol><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><br>
Last Modified: $Id: publish.html,v 1.4 2013/04/12 16:12:56 gustafn
Exp $
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository Developer Guide: Creating Content
Revisions}</property>
<property name="doc(title)">Content Repository Developer Guide: Creating Content
Revisions</property>
<master>
<body>
<h2>Creating Content Revisions</h2><p>At a basic level, creating a new revision of a content item
involves the following steps:</p><ol>
<li>Insert a row in the <tt>acs_objects</tt> table to create the
object.</li><li>Insert a corresponding row in the <tt>cr_revisions</tt> table
with the basic attributes for the revision.</li><li>Write the content data into the <tt>content</tt> BLOB column of
the <tt>cr_revisions</tt> table.</li><li>Insert a corresponding row into the attribute table of each
ancestor of the content type of the item. This is not applicable if
the content type is <b>Basic Item</b> or an immediate subtype
thereof.</li><li>Insert a corresponding row into the attribute table of the
content type of the item. This is not applicable if the content
type is <b>Basic Item</b>.</li>
</ol><h3>Use the Content Revision API to create a revision</h3><p>Content revisions are initialized using the
<tt>content_revision.new</tt> function. The only parameters
required to create the revision are a title, a content item ID, and
some text:</p><pre>
revision_id := content_revision.new(
title =&gt; 'A Revision',
item_id =&gt; :item_id,
text =&gt; 'Once upon a time Goldilocks crossed the street.
Here comes a car...uh oh! The End'
);
</pre><p>The <tt>item_id</tt> parameter is ID of the content item with
which the revision is associated.</p><p>The <tt>content_item.new</tt> function accepts a number of other
optional parameters: <tt>description</tt>, <tt>mime_type</tt>, and
<tt>publish_date</tt>. The standard <tt>creation_date</tt>,
<tt>creation_user</tt>, and <tt>creation_ip</tt> should be
specified for auditing purposes. Instead of the <tt>text</tt>
parameter, this function can be called with a <tt>data</tt>
parameter, in which <tt>data</tt> is a blob:</p><pre>
revision_id := content_revision.new(
title =&gt; 'A Revision',
description =&gt; 'A Description of a revision',
mime_type =&gt; 'text/html',
publish_date =&gt; to_date('Jan 22, 2000','Mon DD, YYYY'),
item_id =&gt; :item_id,
data =&gt; :blob_of_content,
creation_date =&gt; sysdate,
creation_user =&gt; :user_id,
creation_ip =&gt; :ip_address
);
</pre><h3>Insert additional attributes</h3><p>Given that there is no way (AFAIK) to pass variable parameters
to a PL/SQL function, there is no way to make
<tt>content_revision.new</tt> generic enough to support submission
of the attributes for all different content types. This leaves you
with three alternatives:</p><ol>
<li>Call <tt>content_revision.new</tt> followed by manual DML
statements to write data into the content BLOB and insert
attributes.</li><li>Write a PL/SQL package for each of your content types, which
encapsulates the above code.</li><li>Create revisions by inserting into the attribute view for each
content type.</li>
</ol><p>The last option is made possible by an <tt>instead of
insert</tt> trigger on the attribute view for each content type.
(An <em>attribute view</em> joins together the storage tables for
the ancestors of each content type, including <tt>acs_objects</tt>
and <tt>cr_revisions</tt>). Normally it is not possible to insert
into a view. Oracle allows you to create an <tt>instead of</tt>
trigger for a view, however, which intercepts the DML statement and
allows you to execute an arbitrary block of PL/SQL instead. The
code to create or replace the trigger is automatically generated
and executed with each call to
<tt>content_type.create_attribute</tt>. The trigger makes it
possible to create complete revisions with a single insert
statement:</p><pre>
insert into cr_revisionsx (
item_id, revision_id, title
) values (
18, 19, 'All About Revisions'
);
</pre><p>Because a special trigger is generated for each content type
that includes insert statements for all inherited tables, revisions
with extended attributes may be created in the same fashion:</p><pre>
insert into cr_imagesx (
item_id, revision_id, title, height, width
) values (
18, 19, 'A Nice Drawing', 300, 400
);
</pre><h3>Inserting content via file or text upload</h3><h3>Selecting a live revision</h3><p>The live revision of a content item can be obtained with the
<tt>content_item.get_live_revision</tt> function:</p><pre>
live_revision_id := content_item.get_live_revision(
item_id =&gt; :item_id
);
</pre><p>The <tt>item_id</tt> identifies the content item with which the
revision is associated.</p><p>Likewise, the most recent revision of a content item can be
obtained with the <tt>content_item.get_latest_revision</tt>
function:</p><pre>
latest_revision_id := content_item.get_latest_revision(
item_id =&gt; :item_id
);
</pre><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><p>Last Modified: $Id: revisions.html,v 1.1.1.1 2001/03/13 22:59:26
ben Exp $</p>
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository Developer Guide: Search}</property>
<property name="doc(title)">Content Repository Developer Guide: Search</property>
<master>
<body>
<h2>Search</h2><p>The content repository provides a consistent sitewide interface
for searching content. It uses Intermedia to index the
<tt>content</tt> column of <tt>cr_revisions</tt>) as well as all
the attribute columns for each content type.</p><h3>Searching Content</h3><p>The <tt>content</tt> column in <tt>cr_revisions</tt> may contain
data in any text or binary format. To accomodate searches across
multiple file types, the content repository uses an Intermedia
index with the INSO filtering option. The INSO filter automatically
detects the the file type of a binary object, and extracts text
from it for indexing. Most common file types are supported,
including PDF and Microsoft Word, and Excel and PowerPoint.</p><p>Searching for content requires the same syntax as any text
index:</p><pre>
select
score(1), revision_id, item_id
from
cr_revisions r
where
contains(content, 'company', 1) &gt; 0
</pre><p>The above query may be useful for an administrative interface
where you wish to search across all revisions, but in most cases
you only want to search live revisions:</p><pre>
select
score(1), revision_id, item_id, content_item.get_path(item_id) url, title
from
cr_revisions
where
contains(content, 'company', 1) &gt; 0
and
revision_id = content_item.get_live_revision(item_id)
</pre><p>The URL and title may be used to construct a hyperlink directly
to the item.</p><p>You may implement any number of variants on this basic query to
place additional constraints on the results, such as publication
date, content type, subject heading or a particular attribute (see
below).</p><p>Some limitations of the current implementation include:</p><ul>
<li>Multilingual searches are not enabled by default. You may
enable them for one more languages by setting the appropriate
Intermedia preferences when creating
<tt>cr_rev_content_index</tt>.</li><li>Some items are not appropriate to display "stand-alone", but
rather need to appear only in the context of a container document
(typically their parent in the content repository). This is
probably a limitation of <tt>content_item.get_path</tt>: it should
be possible to specify an arbitrary function to return the path for
items of a particular content type, with
<tt>content_item.get_path</tt> as the default.</li>
</ul><h3>Searching Attributes</h3><p>This task is primarily handled to two Intermedia indices:</p><p>Providing a generic mechanism for searching attributes is
complicated by the fact that the attributes for each content type
are different. The content repository takes advantage of the XML
features in Oracle 8.1.6 to address this:</p><ol>
<li><p>After creating a new revision and inserting attributes into the
storage table for the content type and all its ancestors, you must
execute the <tt>content_revision.index_attributes</tt> procedure.
(Note that this cannot be called automatically by
<tt>content_revision.new</tt>, since the attributes in all extended
storage tables must be inserted first).</p></li><li><p>This procedure creates a row in the
<tt>cr_revision_attributes</tt> table, and writes an XML document
including all attributes into this row. A Java stored procedure
using the Oracle XML Parser for Java v2 is used to actually
generate the XML document.</p></li><li><p>A special Intermedia index configured to parse XML documents is
built on the column containing the XML documents for all
revisions.</p></li>
</ol><p>The Intermedia index allows you to use the WITHIN operator to
search on individual attributes if desired.</p><pre>
select
revision_id,score(1)
from
cr_revisions
where
contains(attributes, 'company WITHIN title', 1) &gt; 0
</pre><p>Some limitations of the current implementation include:</p><ol>
<li>A <tt>USER_DATASTORE</tt> associated with each row of the
<tt>cr_items</tt> table, which feeds Intermedia the contents of the
<tt>content</tt> column (a BLOB) for the <em>live</em> revision of
an item. This should theoretically be more efficient for searching
live content, especially in production environments where content
is revised often.</li><li>A second <tt>USER_DATASTORE</tt> associated with each row of
the <tt>cr_items</tt> table, which feeds Intermedia the XML
document representing all attributes for the <em>live</em> revision
of an item (from <tt>cr_revision_attributes</tt>).</li><li>The default XML document handler for the content repository
simply provides a flat file of all attributes. Content types should
also be able implement custom handlers, to allow the XML document
to reflect one-to-many relationships or special formatting of
attributes as well. The handler should specify a java class and
method, which a dispatch method can call by reflection.</li>
</ol><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><br>
Last Modified: $Id: search.html,v 1.1.1.1 2001/03/13 22:59:26 ben
Exp $
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {}</property>
<property name="doc(title)"></property>
<master>
<body>
<h2>Storing Data in the Content Repository</h2><p>This document provides an introduction to using the content
repository for storing data (binary or text files) and associated
attributes. It describes how to store user portraits as an
example.</p><h3>Define an Item Type</h3><p>The first step towards using the content repository is to define
one or more <em>content types</em> for the data you wish to
manage.</p><p>The basic content item includes the following attributes:</p><ul>
<li>Title</li><li>Description</li><li>Publication or Posting Date</li><li>Author or Contributor</li><li>MIME Type</li><li>Binary or Text Data</li>
</ul><p>Most types of content require additional attributes. For a
photo, we probably also want to store the pixel width and height at
the very least:</p><pre>
create table images (
image_id integer
constraint images_image_id_fk
references cr_revisions
constraint images_pk
primary key,
width integer,
height integer
);
</pre><p>Content types are nothing more than standard ACS Objects that
inherit from <tt>content_revision</tt>:</p><pre>
begin
acs_object_type.create_type (
supertype =&gt; 'content_revision',
object_type =&gt; 'image',
pretty_name =&gt; 'Image',
pretty_plural =&gt; 'Images',
table_name =&gt; 'images',
id_column =&gt; 'image_id',
name_method =&gt; 'acs_object.default_name'
);
acs_attribute.create_attribute (
object_type =&gt; 'image',
attribute_name =&gt; 'width',
datatype =&gt; 'number',
pretty_name =&gt; 'Width',
pretty_plural =&gt; 'Widths'
);
acs_attribute.create_attribute (
object_type =&gt; 'image',
attribute_name =&gt; 'height',
datatype =&gt; 'number',
pretty_name =&gt; 'Height',
pretty_plural =&gt; 'Heights'
);
end;
/
show errors
</pre><p>Note that content types always extend <tt>content_revision</tt>,
rather than <tt>content_item</tt>. This is because we want to store
multiple revisions of both the actual data (in this case the image)
as well as associated attributes (the width and height of the image
may vary among revisions).</p><h3>Define a Relationship to a Target Object</h3><p>The content repository implements a flexible mechanism for
organizing data in a hierarchical fashion in a manner similar to a
file system. This would be useful if we ever decided to allow each
user to manage an entire personal photo gallery rather than a
single portrait.</p><p>In the simple case where each user is allowed a single portrait,
we can simply define a relationship between user and image as ACS
Objects:</p><pre>
acs_rel_type.create_role('user');
acs_rel_type.create_role('portrait');
acs_rel_type.create_type( rel_type =&gt; 'user_portrait_rel',
pretty_name =&gt; 'User Portrait',
pretty_plural =&gt; 'User Portraits',
object_type_one =&gt; 'user',
role_one =&gt; 'user',
min_n_rels_one =&gt; 1,
max_n_rels_one =&gt; 1,
object_type_two =&gt; 'content_item',
min_n_rels_two =&gt; 0,
max_n_rels_two =&gt; 1
);
</pre><p>Note that the <tt>user</tt> object is related to a
<tt>content_item</tt> object rather than an <tt>image</tt> object
directly. Each <tt>image</tt> object represents only a single
revision of a portrait. Revisions always exist in the context of an
item.</p><h3>Store Objects</h3><p>Now we have defined both a content type and relationship type,
we can start storing portraits. The DML for processing a new
portrait upload form would look like this:</p><pre>
begin transaction
:item_id := content_item.new(:name, :item_id, sysdate, NULL, '[ns_conn peeraddr]');
# maybe have content_revision return the LOB locator so that it can
# be used directly with blob_dml_file
:revision_id := content_revision.new(:title, :description, $publish_date, :mime_type, NULL, :text, 'content_revision',
:item_id, :revision_id);
blob_dml_file update cr_revisions set content = empty_blob() ...
:rel_id := acs_rel.new(...)
</pre><h3>Retrieve Objects</h3><pre>
ns_ora write_blob ...
</pre><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><p>Last Modified: $Id: storage.html,v 1.1.1.1 2001/03/13 22:59:26
ben Exp $</p>
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository Developer Guide: Applying
Templates}</property>
<property name="doc(title)">Content Repository Developer Guide: Applying
Templates</property>
<master>
<body>
<h2>Applying Templates</h2><p>The content repository allows you to associate templates with
both content types and individual content items. A template
determines how a content item is rendered when exported to the file
system or served directly to a client.</p><p>The content repository does not make any assumptions about the
type of templating system used by the application server with which
it is being used. Templates are simply made available to the
application server as text objects. The server is responsible for
merging the template with the actual content.</p><h3>Creating templates</h3><p>The content repository handle templates as a special class of
text object. The interface for handling templates builds on that of
simple content items:</p><pre>
template_id := content_template.new(
name =&gt; 'image_template',
parent_id =&gt; :parent_id
);
</pre><p>The name represents the tail of the location for that content
template. The parent ID must be another content item, or a subclass
of content item such as a folder.</p><p>
<tt>The content_template.new</tt> function accepts the standard
<tt>creation_date</tt>, <tt>creation_user</tt>, and
<tt>creation_ip</tt> auditing parameters.</p><p>Content items and templates are organized in two separate
hierarchies within the content repository. For example, you may
place all your press releases in the <tt>press</tt> folder under
the item root (having the ID returned by
<tt>content_item.get_root_folder</tt>). You may have 5 different
templates used to render press releases. These my be stored in the
<tt>press</tt> folder under the <em>template</em> root (having the
ID returned by <tt>content_template.get_root_folder</tt>).</p><p>Templates are placed under their own root to ensures that bare
templates are never accessible via a public URL. This is also done
because the relationship with the file system may be different for
templates than for content items. For example, templates may be
associated with additional code or resource files that developers
maintain under separate source control.</p><h3>Associating templates with content types</h3><p>You use the <tt>content_type.register_template</tt> procedure to
associate a template with a particular content type:</p><pre>
content_type.register_template(
content_type =&gt; 'content_revision',
template_id =&gt; :template_id,
use_context =&gt; 'public',
is_default =&gt; 't'
);
</pre><p>The <tt>use_context</tt> is a simple keyword that specifies the
situation in which the template is appropriate. One general
context, <tt>public</tt>, is loaded when the content repository is
installed. Templates in this context are for presenting content to
users of the site. Some sites may wish to distinguish this further,
for example using <tt>intranet</tt>, <tt>extranet</tt> and
<tt>public</tt> contexts.</p><p>The <tt>is_default</tt> flag specifies that this template will
serve as the default template in the case that no template is
registered to a content item of this content type and this use
context. Any content type/context pair may have any number of
templates registered to it, but there can be only one default
template per pair.</p><p>To make a template the default template for a content
type/context pair:</p><pre>
content_type.set_default_template(
content_type =&gt; 'content_revision',
template_id =&gt; :template_id,
use_context =&gt; 'public'
);
</pre><h3>Associating templates with content items</h3><p>Individual items may also be associated with templates using the
<tt>content_item.register_template</tt> procedure:</p><pre>
content_item.register_template(
item_id =&gt; :item_id,
template_id =&gt; :template_id,
use_context =&gt; 'intranet'
);
</pre><p>Unlike the case with content types, only one template may be
registered with a content item for a particular context.</p><p>The content management system uses this functionality to allow
publishers to choose templates for each content they create. For
example, a company may have three different templates for
presenting press releases. Depending on the subject, geographic
region or any other criterion, a different template may be used for
each press release.</p><h3>Retrieving the template for a content item</h3><p>The application server (AOLserver or servlet container) may use
the <tt>content_item.get_template</tt> function to determine the
proper template to use for rendering a page in any particular
context:</p><pre>
template_id := content_item.get_template(
item_id =&gt; :item_id,
use_context =&gt; 'public'
);
template_path := content_template.get_path(
template_id =&gt; :template_id
);
</pre><p>In the case that no template is registered to given item/context
pair, <tt>content_item.get_template</tt> will return the default
template (if it exists) for the related content type/context
pair.</p><h3>Unregistering templates</h3><p>The procedure for disassociating templates with content types is
as follows:</p><pre>
content_type.unregister_template(
content_type =&gt; 'content_revision',
template_id =&gt; :template_id,
use_context =&gt; 'intranet'
);
</pre><p>The corresponding procedure to disassociate templates with
content items is:</p><pre>
content_item.unregister_template(
item_id =&gt; :item_id,
template_id =&gt; :template_id,
use_context =&gt; 'admin'
);
</pre><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><p>Last Modified: $Id: template.html,v 1.1.1.1 2001/03/13 22:59:26
ben Exp $</p>
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository Developer Guide: Workflow}</property>
<property name="doc(title)">Content Repository Developer Guide: Workflow</property>
<master>
<body>
<h2>Applying Workflow to Content Items</h2><p>This document describes the workflow API calls necessary to
apply a simple workflow to a content item.</p><h3>Workflow Description</h3><p>Most publishers wish to follow some variation of the following
workflow:</p><table border="1" cellspacing="0" cellpadding="4">
<tr bgcolor="#CCCCCC">
<th>State</th><th>Task</th><th>Description</th>
</tr><tr>
<td>Created</td><td>Authoring</td><td>The publisher has created the item.</td>
</tr><tr>
<td>Authored</td><td>Editing</td><td>The author has written the item.</td>
</tr><tr>
<td>Edited</td><td>Publishing</td><td>The editor has approved the item.</td>
</tr><tr>
<td>Published</td><td>None</td><td>The publisher has approved the item.</td>
</tr>
</table><p>At any point in the workflow, an assigned user should be able to
check out an item, such that other users are advised that someone
is working on it. When checking an item in, a user should have up
to three options:</p><ol>
<li>Check the item in but do not mark the task as finished
(allowing someone else to work on the task. The currently enabled
task (whether it is authoring, editing or approving) does not
change.</li><li>Check the item in and move to the next task. For the authoring
task, this signifies that the authoring is complete. For subsequent
tasks, this signifies approval.</li><li>Check the item in and move to a previous task, indicating
rejection.</li>
</ol><p>This simple workflow is defined in
<tt>sql/workflows/author-edit-publish.sql</tt>.</p><h3>Workflow Creation</h3><p>Production of a content item frequently begins with a concept
which is initiated by the publisher and then executed by the staff.
In this scenario, the publisher creates the workflow and then
assigns each task in the workflow to one or more people. The API
calls to initialize a new workflow are as follows:</p><pre>
declare
v_case_id integer;
sample_object_id integer := 9;
sample_user_id integer := 10;
begin
v_case_id := workflow_case.new( workflow_key =&gt; 'publishing_wf',
context_key =&gt; NULL,
object_id =&gt; sample_object_id);
workflow_case.add_manual_assignment(v_case_id, 'authoring', sample_user_id);
workflow_case.add_manual_assignment(v_case_id, 'editing', sample_user_id);
workflow_case.add_manual_assignment(v_case_id,'approval', sample_user_id);
workflow_case.start_case(case_id =&gt; v_case_id, msg =&gt; 'Here we go.');
end;
/
</pre><p>In this case, only one assignment is made per task. You can make
as many assignments per task as desired. There is currently no
workflow API to set deadlines, so you must write your own DML to
insert a row into <tt>wf_case_deadlines</tt> if you wish to allow
the publisher to set deadlines ahead of time.</p><p>The above workflow is created in the <b>Default</b> context. In
practice, you may wish to create one or more contexts in which to
create your workflows. Contexts may be used to represent different
departments within an organization.</p><p>The <tt>start_case</tt> enables the first task in the workflow,
in this case <b>Authoring</b>.</p><h3>Check Out Item</h3><p>If multiple persons are assigned to the same task, it is useful
to allow a single person to "check out" or lock an item while they
are working. This is accomplished with the following API calls:</p><pre>
declare
v_journal_id integer;
sample_task_id := 1000;
sample_user_id := 10;
sample_ip := '127.0.0.1';
begin
v_journal_id := workflow_case.begin_task_action(sample_task_id, 'start',
sample_ip, sample_user_id, 'Checking it out');
workflow_case.end_task_action(v_journal_id, 'start', sample_task_id);
end;
/
</pre><p>A mininum of two calls are required to perform any action
related to a task. In this case we are simply notifying the
workflow engine that someone has started the task. You may specify
NULL for the journal message if the user does not wish to comment
on the check out.</p><h3>Check In Item</h3><p>Unless given a timeout period, a lock on a content item will
persist until the holding user checks the item back in. This
involves notifying the workflow engine that the user has finished
the task:</p><pre>
declare
v_journal_id integer;
sample_task_id integer := 1000;
sample_user_id integer := 10;
sample_ip := '127.0.0.1';
begin
v_journal_id := workflow_case.begin_task_action(sample_task_id, 'finish',
sample_ip, sample_user_id, 'Done for now');
workflow_case.set_attribute_value(v_journal_id, 'next_place', 'start');
workflow_case.end_task_action(v_journal_id, 'finish', sample_task_id);
end;
/
</pre><p>Upon finishing a task, you must notify the workflow engine where
to go next. In this case, an author wishes to simply check an item
back in without actually completing the authoring task. The
<tt>set_attribute_value</tt> procedure must thus be used to set
<tt>next_place</tt> to the starting place of the workflow.</p><h3>Finish Task</h3><p>The process to finish a task varies slightly depending on
whether the user has previously checked out the item out or not. If
the user has not already checked it out (has been working on the
item without locking it, the code looks like this:</p><pre>
declare
v_journal_id integer;
sample_task_id integer := 1002;
sample_user_id integer := 10;
sample_ip := '127.0.0.1';
begin
-- start the task
v_journal_id := workflow_case.begin_task_action(sample_task_id, 'start',
sample_ip, sample_user_id, NULL);
workflow_case.end_task_action(v_journal_id, 'start', sample_task_id);
-- finish the task
v_journal_id := workflow_case.begin_task_action(sample_task_id, 'finish',
sample_ip, sample_user_id, 'Authoring complete');
workflow_case.set_attribute_value(v_journal_id, 'next_place', 'authored');
workflow_case.end_task_action(v_journal_id, 'finish', sample_task_id);
end;
/
</pre><p>In this case an author is finishing the <b>Authoring</b> task,
upon which the workflow engine will move the workflow to the
<b>Authored</b> state (as indicated by the <tt>next_place</tt>
attribute). If the author had previously checked out the item, then
only the second step is required.</p><h3>Approve or Reject</h3><p>Approval steps more commonly do not involve an explicit
check-out process. The code is thus virtually identical to that
above:</p><pre>
declare
v_journal_id integer;
sample_task_id integer := 1003;
sample_user_id integer := 10;
sample_ip := '127.0.0.1';
begin
v_journal_id := workflow_case.begin_task_action(sample_task_id, 'start',
sample_ip, sample_user_id, NULL);
workflow_case.end_task_action(v_journal_id, 'start', sample_task_id);
v_journal_id := workflow_case.begin_task_action(sample_task_id, 'finish',
sample_ip, sample_user_id, 'Authoring complete');
workflow_case.set_attribute_value(v_journal_id, 'next_place', 'edited');
workflow_case.end_task_action(v_journal_id, 'finish', sample_task_id);
end;
/
</pre><p>Note the distinction between approval or rejection is determined
solely by the value of the <tt>next_place</tt> attribute.</p><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><br>
Last Modified: <tt>$Id: workflow.html,v 1.1.1.1 2001/03/13 22:59:26
ben Exp $</tt>
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository: Installation}</property>
<property name="doc(title)">Content Repository: Installation</property>
<master>
<body>
<h2>Installing the Content Repository</h2><p>The content repository is a part of the core data model of ACS
4.0 and greater, and is loaded automatically as part of the ACS
installation process.</p><p>If you wish to install the content repository in a database
schema outside the context of ACS, the following instructions
apply.</p><p>First install the data model and PL/SQL API:</p><ol>
<li>Obtain the latest distribution of ACS.</li><li>Run the SQL script
<tt>packages/acs-kernel/sql/acs-kernel-create.sql</tt> to load the
core ACS Objects data model.</li><li>Run the SQL script
<tt>packages/acs-workflow/sql/acs-workflow-create.sql</tt> to load
the workflow package.</li><li>Run the SQL script
<tt>packages/acs-workflow/sql/acs-content-repository-create.sql</tt>
to load the content repository itself.</li>
</ol><h3>Java</h3><p>In additional to SQL and PL/SQL, the content repository
implements a limited set of key methods in Java. The XML import and
export methods are dependent on Oracle's XML Parser for Java v2,
available from the Oracle Technology Network:</p><a href="http://technet.us.oracle.com/tech/xml/parser_java2/index.htm"><tt>http://technet.us.oracle.com/tech/xml/parser_java2/index.htm</tt></a><p>To load the XML parser, download and untar the distribution.
Load the class package <tt>lib/xmlparserv2.jar</tt> into Oracle
from a shell prompt:</p><pre>
$ loadjava -user user/password xmlparserv2.jar
</pre><p>Finally, load the SQLJ files in
<tt>packages/acs-content-repository/java</tt>:</p><pre>
$ loadjava -user user/password -resolve *.sqlj
</pre><p>Installation of the data model and API should now be
complete.</p><h3>Intermedia</h3><p>The content repository relies on an Intermedia with the INSO
filtering option to search text within a wide variety of file
formats, including PDF and Microsoft Word. When the index on the
<tt>content</tt> column of <tt>cr_revisions</tt> is built, the INSO
filter automatically detects the file type of each entry and
extracts all available text for indexing.</p><p>If your searches are not returning any results even after
rebuilding the index, INSO filtering may be silently failing. You
can verifying this by checking for entries in the
<tt>ctx_user_index_errors</tt> view following an <tt>alter
index</tt> statement.</p><p>If you experience errors on a UNIX system, check the
following:</p><ul>
<li>The operating system user running the Oracle database must have
execute permission on the files
<tt>$ORACLE_HOME/ctx/lib/*.flt</tt>.</li><li>The directory <tt>$ORACLE_HOME/ctx/lib</tt> must be in the
<tt>$PATH</tt> environment variable of the operating system user
running the Oracle database.</li><li>The directory <tt>$ORACLE_HOME/ctx/lib</tt> must be in the
<tt>$LD_LIBRARY_PATH</tt> of the operating system user running the
Oracle database.</li><li>The <tt>LD_LIBRARY_PATH</tt> environment variable must be
specified in the entry for <tt>PLSExtProc</tt> in the
<tt>$ORACLE_HOME/network/admin/listener.ora.</tt> For example:</li>
</ul><pre>
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = /ora8/m01/app/oracle/product/8.1.6)
(ENVS = LD_LIBRARY_PATH=/ora8/m01/app/oracle/product/8.1.6/lib:/usr/lib:/lib:/usr/openwin/lib:/ora8/m01/app/oracle/product/8.1.6/ctx/lib)
(PROGRAM = extproc)
)
</pre><p>If your searches are still failing even after following these
instructions, try a simple <a href="intermedia">test case</a>
to determine whether the problem has something to do with the
content repository data model itself.</p><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><br>
Last revised: $Id: install.html,v 1.1.1.1 2001/03/13 22:59:26 ben
Exp $
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository: Object Model}</property>
<property name="doc(title)">Content Repository: Object Model</property>
<master>
<body>
<h2>Object Model</h2><p>The content repository is an extension of the ACS Object Model.
The following diagram illustrates the relationship among the
standard object types defined by the content repository (click on a
box to view a description and API summary for a particular object
type):</p><img name="objectmodel" src="object-model.gif" width="500" height="400" border="0" usemap="#m_object_model" id="objectmodel"><map name="m_object_model" id="m_object_model">
<area shape="rect" coords="191,45,287,90" href="api/keyword.html"><area shape="rect" coords="39,224,135,269" href="api/object.html"><area shape="rect" coords="345,306,440,364" href="api/custom.html"><area shape="rect" coords="191,123,287,168" href="api/item.html"><area shape="rect" coords="191,313,287,358" href="api/revision.html"><area shape="rect" coords="343,25,439,70" href="api/folder.html"><area shape="rect" coords="345,89,441,134" href="api/template.html"><area shape="rect" coords="344,154,440,199" href="api/symlink.html"><area shape="rect" coords="345,221,441,266" href="api/extlink.html">
</map><p>Note that content revisions and content items inherit separately
from the root of the object model. Each item may be related to one
or more revisions, but they are fundamentally different types of
objects.</p><p>Also important to note is the relationship between custom
content types and the rest of the object model. You define new
content types as subtypes of Content Revision, not of Content Item.
This is because new content types are characterized by their
attributes, which are stored at the revision level to make changes
easy to audit. Custom content types typically do not require
additional unaudited attributes or methods beyond those already
provided by the Content Item type. It is thereful almost never
necessary to create a custom subtype of Content Item itself.</p><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><br>
Last revised: $Id: object-model.html,v 1.1.1.1 2001/03/13 22:59:26
ben Exp $
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {}</property>
<property name="doc(title)"></property>
<master>
<body>
<h2>To Do List for Content Management System</h2><pre>
Documentation
Write ASAP.
Sign In/Out
--Eventually need to use regular acs40 API (maintain existing interface)
Work Items 3
--Display deadlines highlighted depending on whether the deadline has
passed
--Add ability to check items in and out in addition to simply finishing
enabled tasks.
Site Map
--Tree widget is buggy (not updating correctly on delete, not showing blue
arrows in proper context, etc.) 1-Stas
--Improve design of folder listing (sortable by name, date, size, mod_time) Michael
--Symlink title is confusing (Stas)
--Ideally bookmark graphics show change to show items that are currently
marked.
Items
--UI around each item needs polishing (better display of revisions etc.)
--support for display and editing of additional simple attributes 2
--for now just allow assignment of one template. 1-Karl
--We currently have no way of setting access controls (also applies to
folders). 1
Content Types
--Not much to do here, seems OK.
--Need UI for creating content types, adding attributes, etc. (3)
Subject Categories
--Need to reintegrate simple category table (depends on message catalog)
Previous UI was functional, should be reusable.
-- 1
Message Catalog
--Remove as a module for now.
Users
--Should display the party hierarchy here, with tools for adding/removing
users. Parties and usersshould be markable to the clipboard so they can
be used in building workflow contexts and access control lists.
-- 2
Workflows
--index.tcl: Display a list of available workflows, add/remove users from
the eligible list for each transition, etc.
--reintegration with notifications!
Clipboard
--think about improving UI for this. 2
</pre><p>Last Modified: $Id: todo.html,v 1.1.1.1 2001/03/13 22:59:26 ben
Exp $</p>
</body>
<property name="context">{/doc/acs-content-repository {Content Repository}} {Content Repository: Uninstalling}</property>
<property name="doc(title)">Content Repository: Uninstalling</property>
<master>
<body>
<h2>Uninstalling the Content Repository</h2><p>The content repository includes an uninstall script,
<tt>sql/content-drop.sql</tt>. This script does two things:</p><ol>
<li>Drops the attribute storage tables for all content types you
have defined.</li><li>Drops the general tables for the content repository.</li>
</ol><p>The uninstall script does <b>not</b> do the following:</p><ol>
<li>It does <b>not</b> delete rows from the <tt>acs_objects</tt>
table. Many other tables reference the <tt>object_id</tt> column in
this table, so there is the possibility that the uninstall script
will encounter foreign key reference errors.</li><li>It does <b>not</b> delete types from the
<tt>acs_object_types</tt> table. As for objects themselves, it is
impossible for an automatic script to properly handle disposal of
all foreign key references.</li>
</ol><p>Because of what the uninstall script does <b>not</b> do, it is
only appropriate for removing the content repository <em>in
preparation for removing the entire ACS Objects data model</em>. If
you wish to upgrade an existing installation and cannot afford to
lose your data, you must run an upgrade script rather than
uninstalling the entire data model.</p><hr><a href="mailto:karlg@arsdigita.com">karlg@arsdigita.com</a><br>
Last revised: $Id: uninstall.html,v 1.1.1.1 2001/03/13 22:59:26 ben
Exp $
</body>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment