<commit_before>## OpenXPKI::Workflow::Factory
##
## Written 2007 by <NAME> for the OpenXPKI project
## (C) Copyright 2007 by The OpenXPKI Project
package OpenXPKI::Workflow::Factory;

use strict;
use warnings;

use Workflow 1.36;
use base qw( Workflow::Factory );
use English;
use OpenXPKI::Exception;
use OpenXPKI::Debug;
use OpenXPKI::Server::Context qw( CTX );
use OpenXPKI::Server::Workflow;
use OpenXPKI::Workflow::Context;
use OpenXPKI::MooseParams;
use Workflow::Exception qw( configuration_error workflow_error );
use Data::Dumper;

sub new {
    my $class = ref $_[0] || $_[0];
    return bless( {} => $class );
}

sub instance {
    # To stay compatible to the Workflow Module, accept instance on exisiting instances
    my $self = shift;
    return $self if (ref $self);

    # use "new", not instance to create a new one
    OpenXPKI::Exception->throw (message => "I18N_OPENXPKI_WORKFLOW_FACTORY_INSTANCE_METHOD_NOT_SUPPORTED");
}

sub create_workflow{
    my ( $self, $wf_type, $context ) = @_;
    ##! 1: 'start'

    $self->__authorize_workflow({
        ACTION => 'create',
        TYPE   => $wf_type,
    });

    if (!$context) {
        $context = OpenXPKI::Workflow::Context->new();
    }

    return $self->SUPER::create_workflow( $wf_type, $context, 'OpenXPKI::Server::Workflow' );
}

sub fetch_workflow {
    my ( $self, $wf_type, $wf_id ) = @_;
    ##! 1: 'start'

    ##! 2: 'calling Workflow::Factory::fetch_workflow()'
    my $wf = $self->SUPER::fetch_workflow($wf_type, $wf_id, undef, 'OpenXPKI::Server::Workflow' )
        or OpenXPKI::Exception->throw(
            message => 'Requested workflow not found',
            params  => {
                WORKFLOW_TYPE => $wf_type,
                WORKFLOW_ID => $wf_id,
            },
        );
    # the following both checks whether the user is allowed to
    # read the workflow at all and deletes context entries from $wf if
    # the configuration mandates it

    ##! 16: 'Fetch Wfl: ' . Dumper $wf;

    $self->__authorize_workflow({
        ACTION   => 'access',
        WORKFLOW => $wf,
        FILTER   => 1,
    });

    $wf->context()->reset_updated();

    return $wf;

}

sub fetch_unfiltered_workflow {
    my ( $self, $wf_type, $wf_id ) = @_;
    ##! 1: 'start'

    my $wf = $self->SUPER::fetch_workflow($wf_type, $wf_id, undef, 'OpenXPKI::Server::Workflow' )
        or OpenXPKI::Exception->throw(
            message => 'Requested workflow not found',
            params  => {
                WORKFLOW_TYPE => $wf_type,
                WORKFLOW_ID => $wf_id,
            },
        );

    $self->__authorize_workflow({
        ACTION   => 'access',
        WORKFLOW => $wf,
        FILTER   => 0,
    });

    CTX('log')->workflow()->info('Unfiltered access to workflow');

    return $wf;

}

sub list_workflow_titles {
    my $self = shift;
    ##! 1: 'start'

    my $result = {};
    # Nothing initialised
    if (ref $self->{_workflow_config} ne 'HASH') {
        return $result;
    }

    foreach my $item (keys %{$self->{_workflow_config}}) {
        my $type = $self->{_workflow_config}->{$item}->{type};
        my $desc = $self->{_workflow_config}->{$item}->{description};
        $result->{$type} = { label => $type, description => $desc || $type }
    }
    return $result;
}

=head2 get_action_info

Return the UI info for the named action.

Todo: Some of this code is duplicated in the OpenXPKI::Workflow::Config - might
be useful to merge this into a helper. Might be useful in the API.

=cut
sub get_action_info {
    my $self = shift;
    my $action_name = shift;
    my $wf_name = shift; # this can be replaced after creating a lookup map for prefix -> workflow
    ##! 1: 'start'

    my $conn = CTX('config');

    # Check if it is a global or local action
    my ($prefix, $name) = ($action_name =~ m{ \A (\w+?)_(\w+) \z }xs);

    my @path;
    if ($prefix eq 'global') {
        @path = ('workflow','global','action',$name);
    } else {
        @path = ('workflow','def', $wf_name, 'action' , $name);
    }

    my $action = { name => $action_name, label => $action_name };
    foreach my $key (qw(label tooltip description template abort resume uihandle button)) {
        my $val = $conn->get([ @path, $key]);
        if (defined $val) {
            $action->{$key} = $val;
        }
    }

    my @input = $conn->get_scalar_as_list([ @path, 'input' ]);
    my @fields;
    foreach my $field_name (@input) {
        ##! 64: 'Field info ' . Dumper $field

        my $field = $self->get_field_info( $field_name, $wf_name );

        $field->{type} = 'text' unless ($field->{type});
        $field->{clonable} = (defined $field->{min} || $field->{max}) || 0;

        push @fields, $field;
    }

    $action->{field} = \@fields if (scalar @fields);

    return $action;
}

sub get_field_info {
    my $self = shift;
    my $field_name = shift;
    my $wf_name = shift;
    ##! 1: 'start'

    my $conn = CTX('config');

    my @field_path;
    # Fields can be defined local or global (only actions inside workflow)
    if ($wf_name) {
        @field_path = ( 'workflow', 'def', $wf_name, 'field', $field_name );
        if (!$conn->exists( \@field_path )) {
            @field_path = ( 'workflow', 'global', 'field', $field_name );
        }
    } else {
        @field_path = ( 'workflow', 'global', 'field', $field_name );
    }

    my $field = $conn->get_hash( \@field_path );

    # Check for option tag and do explicit calls to ensure recursive resolving.
    # This code is duplicated in OpenXPKI::Server::API2::Plugin::Profile::Util
    # as we need the same syntax in the profiles - TODO move to common API
    if ($field->{option}) {

        my $mode = $conn->get( [ @field_path, 'option', 'mode' ] ) || 'list';
        my @option;
        if ($mode eq 'keyvalue') {
            @option = $conn->get_list( [ @field_path, 'option', 'item' ] );
            if (my $label = $conn->get( [ @field_path, 'option', 'label' ] )) {
                @option = map { { label => sprintf($label, $_->{label}, $_->{value}), value => $_->{value} } } @option;
            }
        } else {
            my @item;
            if ($mode eq 'keys' || $mode eq 'map') {
                @item = sort $conn->get_keys( [ @field_path, 'option', 'item' ] );<commit_msg>Set field's context key to field name if 'name' parameter is missing<commit_after>## OpenXPKI::Workflow::Factory
##
## Written 2007 by <NAME> for the OpenXPKI project
## (C) Copyright 2007 by The OpenXPKI Project
package OpenXPKI::Workflow::Factory;

use strict;
use warnings;

use Workflow 1.36;
use base qw( Workflow::Factory );
use English;
use OpenXPKI::Exception;
use OpenXPKI::Debug;
use OpenXPKI::Server::Context qw( CTX );
use OpenXPKI::Server::Workflow;
use OpenXPKI::Workflow::Context;
use OpenXPKI::MooseParams;
use Workflow::Exception qw( configuration_error workflow_error );
use Data::Dumper;

sub new {
    my $class = ref $_[0] || $_[0];
    return bless( {} => $class );
}

sub instance {
    # To stay compatible to the Workflow Module, accept instance on exisiting instances
    my $self = shift;
    return $self if (ref $self);

    # use "new", not instance to create a new one
    OpenXPKI::Exception->throw (message => "I18N_OPENXPKI_WORKFLOW_FACTORY_INSTANCE_METHOD_NOT_SUPPORTED");
}

sub create_workflow{
    my ( $self, $wf_type, $context ) = @_;
    ##! 1: 'start'

    $self->__authorize_workflow({
        ACTION => 'create',
        TYPE   => $wf_type,
    });

    if (!$context) {
        $context = OpenXPKI::Workflow::Context->new();
    }

    return $self->SUPER::create_workflow( $wf_type, $context, 'OpenXPKI::Server::Workflow' );
}

sub fetch_workflow {
    my ( $self, $wf_type, $wf_id ) = @_;
    ##! 1: 'start'

    ##! 2: 'calling Workflow::Factory::fetch_workflow()'
    my $wf = $self->SUPER::fetch_workflow($wf_type, $wf_id, undef, 'OpenXPKI::Server::Workflow' )
        or OpenXPKI::Exception->throw(
            message => 'Requested workflow not found',
            params  => {
                WORKFLOW_TYPE => $wf_type,
                WORKFLOW_ID => $wf_id,
            },
        );
    # the following both checks whether the user is allowed to
    # read the workflow at all and deletes context entries from $wf if
    # the configuration mandates it

    ##! 16: 'Fetch Wfl: ' . Dumper $wf;

    $self->__authorize_workflow({
        ACTION   => 'access',
        WORKFLOW => $wf,
        FILTER   => 1,
    });

    $wf->context()->reset_updated();

    return $wf;

}

sub fetch_unfiltered_workflow {
    my ( $self, $wf_type, $wf_id ) = @_;
    ##! 1: 'start'

    my $wf = $self->SUPER::fetch_workflow($wf_type, $wf_id, undef, 'OpenXPKI::Server::Workflow' )
        or OpenXPKI::Exception->throw(
            message => 'Requested workflow not found',
            params  => {
                WORKFLOW_TYPE => $wf_type,
                WORKFLOW_ID => $wf_id,
            },
        );

    $self->__authorize_workflow({
        ACTION   => 'access',
        WORKFLOW => $wf,
        FILTER   => 0,
    });

    CTX('log')->workflow()->info('Unfiltered access to workflow');

    return $wf;

}

sub list_workflow_titles {
    my $self = shift;
    ##! 1: 'start'

    my $result = {};
    # Nothing initialised
    if (ref $self->{_workflow_config} ne 'HASH') {
        return $result;
    }

    foreach my $item (keys %{$self->{_workflow_config}}) {
        my $type = $self->{_workflow_config}->{$item}->{type};
        my $desc = $self->{_workflow_config}->{$item}->{description};
        $result->{$type} = { label => $type, description => $desc || $type }
    }
    return $result;
}

=head2 get_action_info

Return the UI info for the named action.

Todo: Some of this code is duplicated in the OpenXPKI::Workflow::Config - might
be useful to merge this into a helper. Might be useful in the API.

=cut
sub get_action_info {
    my $self = shift;
    my $action_name = shift;
    my $wf_name = shift; # this can be replaced after creating a lookup map for prefix -> workflow
    ##! 1: 'start'

    my $conn = CTX('config');

    # Check if it is a global or local action
    my ($prefix, $name) = ($action_name =~ m{ \A (\w+?)_(\w+) \z }xs);

    my @path;
    if ($prefix eq 'global') {
        @path = ('workflow','global','action',$name);
    } else {
        @path = ('workflow','def', $wf_name, 'action' , $name);
    }

    my $action = { name => $action_name, label => $action_name };
    foreach my $key (qw(label tooltip description template abort resume uihandle button)) {
        my $val = $conn->get([ @path, $key]);
        if (defined $val) {
            $action->{$key} = $val;
        }
    }

    my @input = $conn->get_scalar_as_list([ @path, 'input' ]);
    my @fields;
    foreach my $field_name (@input) {
        ##! 64: 'Field info ' . Dumper $field

        my $field = $self->get_field_info( $field_name, $wf_name );

        $field->{type} = 'text' unless ($field->{type});
        $field->{clonable} = (defined $field->{min} || $field->{max}) || 0;

        push @fields, $field;
    }

    $action->{field} = \@fields if (scalar @fields);

    return $action;
}

sub get_field_info {
    my $self = shift;
    my $field_name = shift;
    my $wf_name = shift;
    ##! 1: 'start'

    my $conn = CTX('config');

    my @field_path;
    # Fields can be defined local or global (only actions inside workflow)
    if ($wf_name) {
        @field_path = ( 'workflow', 'def', $wf_name, 'field', $field_name );
        if (!$conn->exists( \@field_path )) {
            @field_path = ( 'workflow', 'global', 'field', $field_name );
        }
    } else {
        @field_path = ( 'workflow', 'global', 'field', $field_name );
    }

    my $field = $conn->get_hash( \@field_path );

    # set field's context key to the field name
    $field->{name} //= $field_name;

    # Check for option tag and do explicit calls to ensure recursive resolving.
    # This code is duplicated in OpenXPKI::Server::API2::Plugin::Profile::Util
    # as we need the same syntax in the profiles - TODO move to common API
    if ($field->{option}) {

        my $mode = $conn->get( [ @field_path, 'option', 'mode' ] ) || 'list';
        my @option;
        if ($mode eq 'keyvalue') {
            @option = $conn->get_list( [ @field_path, 'option', 'item' ] );
            if (my $label = $conn->get( [ @field_path, 'option', 'label' ] )) {
                @option = map { { label => sprintf($label, $_->{label}, $_->{value}), value => $_->{value} } } @option;
            }
        } else {
            my @item;
            if ($mode eq 'keys' || $mode eq 'map') {
                @item = sort $conn->get_keys( [ @field_path, 'option', 'item' ] );
{'fraction_non_alphanumeric': 0.16096753860699653, 'fraction_numerical': 0.004884966908288686, 'mean_word_length': 2.6369627507163322, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 142, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '5437388', 'n_tokens_mistral': 4425, 'n_tokens_neox': 4187, 'n_words': 1155}