<commit_before>%% @author <NAME> <<EMAIL>>
%% @copyright 2009 <NAME>
%%
%% @doc Model for resource data. Interfaces between zotonic, templates and the database.

%% Copyright 2009 <NAME>
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%% 
%%     http://www.apache.org/licenses/LICENSE-2.0
%% 
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.

-module(m_rsc).
-author("<NAME> <<EMAIL>>").

-behaviour(gen_model).

-export([
    m_find_value/3,
    m_to_list/2,
    m_value/2,
    
    name_to_id/2,
    name_to_id_check/2,
    name_to_id_cat/3,
    name_to_id_cat_check/3,

    page_path_to_id/2,
    
    get/2,
    get_raw/2,
    get_acl_props/2,
    insert/2,
    delete/2,
    update/3,
    duplicate/3,
    touch/2,
    
	exists/2, 
	
	is_visible/2, is_editable/2, is_deletable/2, is_me/2, 
	is_cat/3,
	is_a/2,
	is_a_id/2,
	is_a/3,
	
	p/3,
	p/4,
	p_no_acl/3,
	
	op/2, o/2, o/3, o/4,
	sp/2, s/2, s/3, s/4,
	media/2,
	page_url/2,
	rid/2,

    name_lookup/2,
    uri_lookup/2
]).

-include_lib("zotonic.hrl").


%% @doc Fetch the value for the key from a model source
%% @spec m_find_value(Key, Source, Context) -> term()
m_find_value(Id, #m{value=undefined} = M, Context) ->
    case rid(Id, Context) of
        undefined -> undefined;
        RId -> 
			case z_acl:rsc_visible(RId, Context) of
				true ->
					M#m{value=RId};
				false -> 
					fun(is_a, C) -> is_a(RId, C);
					   (_, _C) -> undefined
					end
			end
    end;
m_find_value(is_cat, #m{value=Id} = M, _Context) when is_integer(Id) -> 
    M#m{value={is_cat, Id}};
m_find_value(Key, #m{value={is_cat, Id}}, Context) -> 
    is_cat(Id, Key, Context);
m_find_value(Key, #m{value=Id}, Context) when is_integer(Id) ->
    p_no_acl(Id, Key, Context).

%% @doc Transform a m_config value to a list, used for template loops
%% @spec m_to_list(Source, Context) -> List
m_to_list(#m{value=#rsc_list{list=List}}, _Context) ->
    List;
m_to_list(#m{}, _Context) ->
    [].

%% @doc Transform a model value so that it can be formatted or piped through filters
%% @spec m_value(Source, Context) -> term()
m_value(#m{value=undefined}, _Context) ->
    undefined;
m_value(#m{value=V}, _Context) ->
    V.

%% @doc Return the id of the resource with the name
% @spec name_to_id(NameString, Context) -> {ok, int()} | {error, Reason}
name_to_id(Name, Context) ->
    case is_list(Name) andalso z_utils:only_digits(Name) of
        true ->
            {ok, z_convert:to_integer(Name)};
        false ->
            case name_lookup(Name, Context) of
                Id when is_integer(Id) -> {ok, Id};
                _ -> {error, {unknown_rsc, Name}}
            end
    end.

name_to_id_check(Name, Context) ->
    {ok, Id} = name_to_id(Name, Context),
    Id.

name_to_id_cat(Name, Cat, Context) when is_integer(Name) ->
    F = fun() ->
        CatId = m_category:name_to_id_check(Cat, Context),
        case z_db:q1("select id from rsc where id = $1 and category_id = $2", [Name, CatId], Context) of
            undefined -> {error, {unknown_rsc_cat, Name, Cat}};
            Id -> {ok, Id}
        end
    end,
    z_depcache:memo(F, {rsc_name, Name, Cat}, ?DAY, [Cat], Context);
name_to_id_cat(Name, Cat, Context) ->
    F = fun() ->
        CatId = m_category:name_to_id_check(Cat, Context),
        case z_db:q1("select id from rsc where Name = $1 and category_id = $2", [Name, CatId], Context) of
            undefined -> {error, {unknown_rsc_cat, Name, Cat}};
            Id -> {ok, Id}
        end
    end,
    z_depcache:memo(F, {rsc_name, Name, Cat}, ?DAY, [Cat], Context).

name_to_id_cat_check(Name, Cat, Context) ->
    {ok, Id} = name_to_id_cat(Name, Cat, Context),
    Id.

page_path_to_id(Path, Context) ->
    Path1 = [ $/, string:strip(Path, both, $/)],
    case z_db:q1("select id from rsc where page_path = $1", [Path1], Context) of
        undefined -> {error, {unknown_page_path, Path1}};
        Id -> {ok, Id}
    end.


%% @doc Read a whole resource
%% @spec get(Id, Context) -> PropList | undefined
get(Id, Context) ->
    case rid(Id, Context) of
        Rid when is_integer(Rid) ->
            F = fun() ->
                get_raw(Rid, Context)
            end,
            z_depcache:memo(F, Rid, ?WEEK, Context)
    end.

%% @doc Get the resource from the database, do not fetch the pivot fields.
get_raw(Id, Context) when is_integer(Id) ->
    z_db:assoc_props_row("
            select
                id, uri, name, page_path, 
                is_authoritative, is_published, is_featured, is_protected,
                publication_start, publication_end,
                creator_id, modifier_id, version, category_id,
                visible_for, slug, props, created, modified
            from rsc
            where id = $1", [Id], Context).


%% @doc Get the ACL fields for the resource with the id. The id must be an integer
%% @spec get_acl_fields(Id, #context) -> #acl_props<commit_msg>Allow site specific columns to be added to the rsc table.<commit_after>%% @author <NAME> <<EMAIL>>
%% @copyright 2009 <NAME>
%%
%% @doc Model for resource data. Interfaces between zotonic, templates and the database.

%% Copyright 2009 <NAME>
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%% 
%%     http://www.apache.org/licenses/LICENSE-2.0
%% 
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.

-module(m_rsc).
-author("<NAME> <<EMAIL>>").

-behaviour(gen_model).

-export([
    m_find_value/3,
    m_to_list/2,
    m_value/2,
    
    name_to_id/2,
    name_to_id_check/2,
    name_to_id_cat/3,
    name_to_id_cat_check/3,

    page_path_to_id/2,
    
    get/2,
    get_raw/2,
    get_acl_props/2,
    insert/2,
    delete/2,
    update/3,
    duplicate/3,
    touch/2,
    
	exists/2, 
	
	is_visible/2, is_editable/2, is_deletable/2, is_me/2, 
	is_cat/3,
	is_a/2,
	is_a_id/2,
	is_a/3,
	
	p/3,
	p/4,
	p_no_acl/3,
	
	op/2, o/2, o/3, o/4,
	sp/2, s/2, s/3, s/4,
	media/2,
	page_url/2,
	rid/2,

    name_lookup/2,
    uri_lookup/2
]).

-include_lib("zotonic.hrl").


%% @doc Fetch the value for the key from a model source
%% @spec m_find_value(Key, Source, Context) -> term()
m_find_value(Id, #m{value=undefined} = M, Context) ->
    case rid(Id, Context) of
        undefined -> undefined;
        RId -> 
			case z_acl:rsc_visible(RId, Context) of
				true ->
					M#m{value=RId};
				false -> 
					fun(is_a, C) -> is_a(RId, C);
					   (_, _C) -> undefined
					end
			end
    end;
m_find_value(is_cat, #m{value=Id} = M, _Context) when is_integer(Id) -> 
    M#m{value={is_cat, Id}};
m_find_value(Key, #m{value={is_cat, Id}}, Context) -> 
    is_cat(Id, Key, Context);
m_find_value(Key, #m{value=Id}, Context) when is_integer(Id) ->
    p_no_acl(Id, Key, Context).

%% @doc Transform a m_config value to a list, used for template loops
%% @spec m_to_list(Source, Context) -> List
m_to_list(#m{value=#rsc_list{list=List}}, _Context) ->
    List;
m_to_list(#m{}, _Context) ->
    [].

%% @doc Transform a model value so that it can be formatted or piped through filters
%% @spec m_value(Source, Context) -> term()
m_value(#m{value=undefined}, _Context) ->
    undefined;
m_value(#m{value=V}, _Context) ->
    V.

%% @doc Return the id of the resource with the name
% @spec name_to_id(NameString, Context) -> {ok, int()} | {error, Reason}
name_to_id(Name, Context) ->
    case is_list(Name) andalso z_utils:only_digits(Name) of
        true ->
            {ok, z_convert:to_integer(Name)};
        false ->
            case name_lookup(Name, Context) of
                Id when is_integer(Id) -> {ok, Id};
                _ -> {error, {unknown_rsc, Name}}
            end
    end.

name_to_id_check(Name, Context) ->
    {ok, Id} = name_to_id(Name, Context),
    Id.

name_to_id_cat(Name, Cat, Context) when is_integer(Name) ->
    F = fun() ->
        CatId = m_category:name_to_id_check(Cat, Context),
        case z_db:q1("select id from rsc where id = $1 and category_id = $2", [Name, CatId], Context) of
            undefined -> {error, {unknown_rsc_cat, Name, Cat}};
            Id -> {ok, Id}
        end
    end,
    z_depcache:memo(F, {rsc_name, Name, Cat}, ?DAY, [Cat], Context);
name_to_id_cat(Name, Cat, Context) ->
    F = fun() ->
        CatId = m_category:name_to_id_check(Cat, Context),
        case z_db:q1("select id from rsc where Name = $1 and category_id = $2", [Name, CatId], Context) of
            undefined -> {error, {unknown_rsc_cat, Name, Cat}};
            Id -> {ok, Id}
        end
    end,
    z_depcache:memo(F, {rsc_name, Name, Cat}, ?DAY, [Cat], Context).

name_to_id_cat_check(Name, Cat, Context) ->
    {ok, Id} = name_to_id_cat(Name, Cat, Context),
    Id.

page_path_to_id(Path, Context) ->
    Path1 = [ $/, string:strip(Path, both, $/)],
    case z_db:q1("select id from rsc where page_path = $1", [Path1], Context) of
        undefined -> {error, {unknown_page_path, Path1}};
        Id -> {ok, Id}
    end.


%% @doc Read a whole resource
%% @spec get(Id, Context) -> PropList | undefined
get(Id, Context) ->
    case rid(Id, Context) of
        Rid when is_integer(Rid) ->
            F = fun() ->
                get_raw(Rid, Context)
            end,
            z_depcache:memo(F, Rid, ?WEEK, Context)
    end.

%% @doc Get the resource from the database, do not fetch the pivot fields.
get_raw(Id, Context) when is_integer(Id) ->
    SQL = case z_memo:get(rsc_raw_sql) of
            undefined ->
                AllCols = [ z_convert:to_list(C) || C <- z_db:column_names(rsc, Context) ],
                DataCols = lists:filter(fun("pivot_" ++ _) -> false; (_) -> true end, AllCols),
                Query = "select "++string:join(DataCols, ",") ++ " from rsc where id = $1",
                z_memo:set(rsc_raw_sql, Query),
                Query;
            Memo ->
                Memo
          end,
    z_db:assoc_props_row(SQL, [Id], Context).


%% @doc Get the ACL fields for the resource with the id. The id must be an integer
%% @spec get_acl_fields(Id, #context) -> #acl_props
{'fraction_non_alphanumeric': 0.14159209545115586, 'fraction_numerical': 0.012117822520507084, 'mean_word_length': 2.731826086956522, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 107, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7109591', 'n_tokens_mistral': 4030, 'n_tokens_neox': 3879, 'n_words': 1050}