2008-12-15 15:53:33 +03:00
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# Contributor(s): Dan Mosedale <dmose@mozilla.org>
# Frédéric Buclin <LpSolit@gmail.com>
# Myk Melez <myk@mozilla.org>
2010-05-15 00:02:34 +04:00
# Greg Hendricks <ghendricks@novell.com>
2014-03-24 17:13:56 +04:00
#
# Deep refactoring by Vitaliy Filippov <vitalif@mail.ru>
# http://wiki.4intra.net/Bugzilla4Intranet
2008-12-15 15:53:33 +03:00
= head1 NAME
Bugzilla:: Field - a particular piece of information about bugs
and useful routines for form field manipulation
= head1 SYNOPSIS
use Bugzilla ;
use Data::Dumper ;
# Display information about all fields.
print Dumper ( Bugzilla - > get_fields ( ) ) ;
# Display information about non-obsolete custom fields.
print Dumper ( Bugzilla - > active_custom_fields ) ;
use Bugzilla::Field ;
# Display information about non-obsolete custom fields.
# Bugzilla->get_fields() is a wrapper around Bugzilla::Field->match(),
# so both methods take the same arguments.
print Dumper ( Bugzilla::Field - > match ( { obsolete = > 0 , custom = > 1 } ) ) ;
# Create or update a custom field or field definition.
my $ field = Bugzilla::Field - > create (
{ name = > 'cf_silly' , description = > 'Silly' , custom = > 1 } ) ;
# Instantiate a Field object for an existing field.
2014-07-25 19:02:52 +04:00
my $ field = new Bugzilla:: Field ( { name = > 'target_milestone' } ) ;
2008-12-15 15:53:33 +03:00
if ( $ field - > obsolete ) {
print $ field - > description . " is obsolete\n" ;
}
= head1 DESCRIPTION
Field . pm defines field objects , which represent the particular pieces
of information that Bugzilla stores about bugs .
This package also provides functions for dealing with CGI form fields .
C <Bugzilla::Field> is an implementation of L <Bugzilla::Object> , and
so provides all of the methods available in L <Bugzilla::Object> ,
in addition to what is documented here .
= cut
package Bugzilla::Field ;
use strict ;
use base qw( Exporter Bugzilla::Object ) ;
2014-07-08 15:44:05 +04:00
@ Bugzilla:: Field:: EXPORT = qw( update_visibility_values ) ;
2008-12-15 15:53:33 +03:00
use Bugzilla::Constants ;
use Bugzilla::Error ;
2009-07-29 15:21:49 +04:00
use Bugzilla::Util ;
2013-09-13 18:54:14 +04:00
require 'Bugzilla/Field/Choice.pm' ;
2009-07-29 15:21:49 +04:00
use Scalar::Util qw( blessed ) ;
2010-03-12 22:18:39 +03:00
use Encode ;
use JSON ;
2010-12-09 20:16:43 +03:00
use POSIX ;
2008-12-15 15:53:33 +03:00
###############################
#### Initialization ####
###############################
use constant DB_TABLE = > 'fielddefs' ;
use constant LIST_ORDER = > 'sortkey, name' ;
2009-07-29 15:21:49 +04:00
use constant DB_COLUMNS = > qw(
id
name
description
type
custom
mailhead
sortkey
obsolete
2014-06-18 15:46:54 +04:00
is_mandatory
2010-10-07 15:31:09 +04:00
clone_bug
2010-12-09 20:16:43 +03:00
delta_ts
2010-12-10 21:02:52 +03:00
has_activity
2010-12-13 20:08:25 +03:00
add_to_deps
2011-12-19 17:11:45 +04:00
url
2014-06-30 19:31:24 +04:00
default_value
visibility_field_id
value_field_id
2014-07-01 18:05:29 +04:00
null_field_id
default_field_id
2014-07-08 15:44:05 +04:00
clone_field_id
2008-12-15 15:53:33 +03:00
) ;
use constant REQUIRED_CREATE_FIELDS = > qw( name description ) ;
use constant VALIDATORS = > {
2014-06-18 15:46:54 +04:00
custom = > \ & Bugzilla::Object:: check_boolean ,
description = > \ & _check_description ,
clone_bug = > \ & Bugzilla::Object:: check_boolean ,
mailhead = > \ & Bugzilla::Object:: check_boolean ,
obsolete = > \ & Bugzilla::Object:: check_boolean ,
is_mandatory = > \ & Bugzilla::Object:: check_boolean ,
sortkey = > \ & _check_sortkey ,
type = > \ & _check_type ,
2009-07-29 15:21:49 +04:00
visibility_field_id = > \ & _check_visibility_field_id ,
2014-06-18 15:46:54 +04:00
add_to_deps = > \ & _check_add_to_deps ,
2014-07-01 18:05:29 +04:00
null_field_id = > \ & _check_visibility_field_id ,
default_field_id = > \ & _check_visibility_field_id ,
2014-07-08 15:44:05 +04:00
clone_field_id = > \ & _check_visibility_field_id ,
2009-07-29 15:21:49 +04:00
} ;
use constant UPDATE_VALIDATORS = > {
2014-06-30 19:31:24 +04:00
value_field_id = > \ & _check_value_field_id ,
default_value = > \ & _check_default_value ,
2008-12-15 15:53:33 +03:00
} ;
2014-07-31 20:52:24 +04:00
use constant UPDATE_COLUMNS = > grep { $ _ ne 'id' } DB_COLUMNS ( ) ;
2008-12-15 15:53:33 +03:00
# How various field types translate into SQL data definitions.
use constant SQL_DEFINITIONS = > {
# Using commas because these are constants and they shouldn't
# be auto-quoted by the "=>" operator.
FIELD_TYPE_FREETEXT , { TYPE = > 'varchar(255)' } ,
2011-12-19 17:11:45 +04:00
FIELD_TYPE_EXTURL , { TYPE = > 'varchar(255)' } ,
2014-04-11 19:15:59 +04:00
FIELD_TYPE_SINGLE_SELECT , { TYPE = > 'INT4' } ,
2008-12-15 15:53:33 +03:00
FIELD_TYPE_TEXTAREA , { TYPE = > 'MEDIUMTEXT' } ,
FIELD_TYPE_DATETIME , { TYPE = > 'DATETIME' } ,
2014-03-08 02:36:38 +04:00
FIELD_TYPE_BUG_ID , { TYPE = > 'INT4' } ,
2010-12-06 21:32:19 +03:00
FIELD_TYPE_NUMERIC , { TYPE = > 'NUMERIC' , NOTNULL = > 1 , DEFAULT = > '0' } ,
2008-12-15 15:53:33 +03:00
} ;
# Field definitions for the fields that ship with Bugzilla.
# These are used by populate_field_definitions to populate
# the fielddefs table.
2014-07-01 19:50:14 +04:00
use constant DEFAULT_FIELD_COLUMNS = > [ qw( name description is_mandatory mailhead clone_bug type value_field null_field default_field ) ] ;
2014-06-18 15:46:54 +04:00
use constant DEFAULT_FIELDS = > ( map { my $ i = 0 ; $ _ = { ( map { ( DEFAULT_FIELD_COLUMNS - > [ $ i + + ] = > $ _ ) } @$ _ ) } } (
2014-06-23 18:18:14 +04:00
[ 'bug_id' , 'Bug ID' , 1 , 1 , 0 ] ,
2014-07-08 15:44:05 +04:00
[ 'short_desc' , 'Summary' , 1 , 1 , 1 , FIELD_TYPE_FREETEXT ] ,
2014-06-23 18:18:14 +04:00
[ 'classification' , 'Classification' , 1 , 1 , 0 , FIELD_TYPE_SINGLE_SELECT ] ,
[ 'product' , 'Product' , 1 , 1 , 0 , FIELD_TYPE_SINGLE_SELECT ] ,
2014-07-01 19:50:14 +04:00
[ 'version' , 'Version' , 0 , 1 , 1 , FIELD_TYPE_SINGLE_SELECT , 'product' , 'product' , 'component' ] ,
2014-07-08 15:44:05 +04:00
[ 'rep_platform' , 'Platform' , 0 , 1 , 1 , FIELD_TYPE_SINGLE_SELECT ] ,
2014-06-23 18:18:14 +04:00
[ 'bug_file_loc' , 'URL' , 0 , 1 , 1 ] ,
[ 'op_sys' , 'OS/Version' , 0 , 1 , 1 , FIELD_TYPE_SINGLE_SELECT ] ,
[ 'bug_status' , 'Status' , 1 , 1 , 0 , FIELD_TYPE_SINGLE_SELECT ] ,
[ 'status_whiteboard' , 'Status Whiteboard' , 0 , 1 , 1 , 1 , FIELD_TYPE_FREETEXT ] ,
2014-07-31 20:52:24 +04:00
[ 'keywords' , 'Keywords' , 0 , 1 , 1 , FIELD_TYPE_MULTI_SELECT ] ,
2014-06-23 18:18:14 +04:00
[ 'resolution' , 'Resolution' , 0 , 1 , 0 , FIELD_TYPE_SINGLE_SELECT ] ,
[ 'bug_severity' , 'Severity' , 0 , 1 , 1 , FIELD_TYPE_SINGLE_SELECT ] ,
[ 'priority' , 'Priority' , 0 , 1 , 1 , FIELD_TYPE_SINGLE_SELECT ] ,
2014-06-24 17:13:46 +04:00
[ 'component' , 'Component' , 1 , 1 , 1 , FIELD_TYPE_SINGLE_SELECT , 'product' ] ,
2014-06-23 18:18:14 +04:00
[ 'assigned_to' , 'Assignee' , 1 , 1 , 0 ] ,
[ 'reporter' , 'Reporter' , 1 , 1 , 0 ] ,
[ 'votes' , 'Votes' , 0 , 1 , 0 ] ,
[ 'qa_contact' , 'QA Contact' , 0 , 1 , 0 ] ,
[ 'cc' , 'CC' , 0 , 1 , 1 ] , # Also reporter/assigned_to/qa are added to cloned bug...
[ 'dependson' , 'Depends on' , 0 , 1 , 0 ] ,
[ 'blocked' , 'Blocks' , 0 , 1 , 0 ] ,
[ 'dup_id' , 'Duplicate of' , 0 , 1 , 0 , FIELD_TYPE_BUG_ID ] ,
[ 'attachments.description' , 'Attachment description' , 0 , 0 , 0 ] ,
[ 'attachments.filename' , 'Attachment filename' , 0 , 0 , 0 ] ,
[ 'attachments.mimetype' , 'Attachment mime type' , 0 , 0 , 0 ] ,
[ 'attachments.ispatch' , 'Attachment is patch' , 0 , 0 , 0 ] ,
[ 'attachments.isobsolete' , 'Attachment is obsolete' , 0 , 0 , 0 ] ,
[ 'attachments.isprivate' , 'Attachment is private' , 0 , 0 , 0 ] ,
2014-07-01 19:50:14 +04:00
[ 'target_milestone' , 'Target Milestone' , 0 , 1 , 1 , FIELD_TYPE_SINGLE_SELECT , 'product' , 'product' , 'product' ] ,
2014-06-23 18:18:14 +04:00
[ 'creation_ts' , 'Creation time' , 1 , 0 , 0 , FIELD_TYPE_DATETIME ] ,
[ 'delta_ts' , 'Last changed time' , 1 , 0 , 0 , FIELD_TYPE_DATETIME ] ,
[ 'longdesc' , 'Comment' , 0 , 0 , 0 ] ,
[ 'alias' , 'Alias' , 0 , 1 , 0 , FIELD_TYPE_FREETEXT ] ,
[ 'everconfirmed' , 'Ever Confirmed' , 0 , 0 , 0 ] ,
[ 'reporter_accessible' , 'Reporter Accessible' , 0 , 1 , 0 ] ,
[ 'cclist_accessible' , 'CC Accessible' , 0 , 1 , 0 ] ,
[ 'bug_group' , 'Group' , 0 , 0 , 0 ] , # FIXME maybe clone_bug=1?
[ 'estimated_time' , 'Estimated Hours' , 0 , 1 , 0 , FIELD_TYPE_NUMERIC ] ,
[ 'remaining_time' , 'Remaining Hours' , 0 , 0 , 0 , FIELD_TYPE_NUMERIC ] ,
[ 'deadline' , 'Deadline' , 0 , 1 , 1 , FIELD_TYPE_DATETIME ] ,
[ 'flagtypes.name' , 'Flags and Requests' , 0 , 0 , 0 ] ,
[ 'work_time' , 'Hours Worked' , 0 , 0 , 0 ] ,
[ 'content' , 'Content' , 0 , 0 , 0 ] ,
[ 'see_also' , 'See Also' , 0 , 1 , 0 , FIELD_TYPE_BUG_URLS ] ,
2014-06-18 15:46:54 +04:00
) ) ;
2008-12-15 15:53:33 +03:00
2014-08-01 21:13:45 +04:00
# Tweaks allowed for standard field properties
use constant CAN_TWEAK = > {
obsolete = > { map { $ _ = > 1 } qw( alias classification op_sys qa_contact rep_platform see_also status_whiteboard target_milestone votes ) } ,
clone_bug = > { map { $ _ = > 1 } qw( short_desc version rep_platform bug_file_loc op_sys status_whiteboard
keywords bug_severity priority component assigned_to votes qa_contact dependson blocked target_milestone estimated_time remaining_time see_also ) } ,
default_value = > { map { $ _ = > 1 } qw( bug_severity deadline keywords op_sys priority rep_platform short_desc status_whiteboard target_milestone version ) } ,
nullable = > { map { $ _ = > 1 } qw( alias bug_severity deadline keywords op_sys priority rep_platform status_whiteboard target_milestone version ) } ,
visibility_field_id = > { map { $ _ = > 1 } qw( bug_severity op_sys priority rep_platform status_whiteboard target_milestone version ) } ,
value_field_id = > { map { $ _ = > 1 } qw( bug_severity op_sys priority rep_platform ) } ,
default_field_id = > { map { $ _ = > 1 } qw( bug_severity keywords op_sys priority component rep_platform status_whiteboard ) } ,
} ;
2009-07-29 15:21:49 +04:00
################
# Constructors #
################
# Override match to add is_select.
2014-04-14 02:56:52 +04:00
sub match
{
2009-07-29 15:21:49 +04:00
my $ self = shift ;
my ( $ params ) = @ _ ;
2014-04-14 02:56:52 +04:00
if ( delete $ params - > { is_select } )
{
$ params - > { type } = [ FIELD_TYPE_SINGLE_SELECT , FIELD_TYPE_MULTI_SELECT ] ;
2009-07-29 15:21:49 +04:00
}
return $ self - > SUPER:: match ( @ _ ) ;
}
2008-12-15 15:53:33 +03:00
##############
# Validators #
##############
2014-04-14 02:56:52 +04:00
sub _check_description
{
2008-12-15 15:53:33 +03:00
my ( $ invocant , $ desc ) = @ _ ;
$ desc = clean_text ( $ desc ) ;
$ desc || ThrowUserError ( 'field_missing_description' ) ;
return $ desc ;
}
2014-04-14 02:56:52 +04:00
sub _check_name
{
2008-12-15 15:53:33 +03:00
my ( $ invocant , $ name , $ is_custom ) = @ _ ;
$ name = lc ( clean_text ( $ name ) ) ;
$ name || ThrowUserError ( 'field_missing_name' ) ;
# Don't want to allow a name that might mess up SQL.
my $ name_regex = qr/^[\w\.]+$/ ;
# Custom fields have more restrictive name requirements than
# standard fields.
2009-11-09 22:23:43 +03:00
$ name_regex = qr/^[a-zA-Z0-9_]+$/ if $ is_custom ;
2008-12-15 15:53:33 +03:00
# Custom fields can't be named just "cf_", and there is no normal
# field named just "cf_".
2014-04-14 02:56:52 +04:00
if ( $ name !~ $ name_regex || $ name eq "cf_" )
{
ThrowUserError ( 'field_invalid_name' , { name = > $ name } ) ;
}
2008-12-15 15:53:33 +03:00
2010-09-30 15:27:44 +04:00
# If it's custom, prepend cf_ to the custom field name to distinguish
2008-12-15 15:53:33 +03:00
# it from standard fields.
2014-04-14 02:56:52 +04:00
if ( $ name !~ /^cf_/ && $ is_custom )
{
2008-12-15 15:53:33 +03:00
$ name = 'cf_' . $ name ;
}
# Assure the name is unique. Names can't be changed, so we don't have
# to worry about what to do on updates.
2010-10-14 14:31:30 +04:00
my $ field = Bugzilla - > get_field ( $ name ) ;
2008-12-15 15:53:33 +03:00
ThrowUserError ( 'field_already_exists' , { 'field' = > $ field } ) if $ field ;
return $ name ;
}
2014-04-14 02:56:52 +04:00
sub _check_sortkey
{
2008-12-15 15:53:33 +03:00
my ( $ invocant , $ sortkey ) = @ _ ;
my $ skey = $ sortkey ;
2014-04-14 02:56:52 +04:00
if ( ! defined $ skey || $ skey eq '' )
{
( $ sortkey ) = Bugzilla - > dbh - > selectrow_array ( 'SELECT MAX(sortkey) + 100 FROM fielddefs' ) || 100 ;
2008-12-15 15:53:33 +03:00
}
2014-04-14 02:56:52 +04:00
detaint_natural ( $ sortkey ) || ThrowUserError ( 'field_invalid_sortkey' , { sortkey = > $ skey } ) ;
2008-12-15 15:53:33 +03:00
return $ sortkey ;
}
2014-04-14 02:56:52 +04:00
sub _check_type
{
2008-12-15 15:53:33 +03:00
my ( $ invocant , $ type ) = @ _ ;
2014-06-18 16:19:48 +04:00
if ( ref $ invocant )
{
# Do not allow to change type of an existing custom field
return $ invocant - > type ;
}
2008-12-15 15:53:33 +03:00
my $ saved_type = $ type ;
# The constant here should be updated every time a new,
# higher field type is added.
2014-06-18 16:19:48 +04:00
if ( ! detaint_natural ( $ type ) || $ type <= FIELD_TYPE_UNKNOWN ||
2014-06-10 19:01:51 +04:00
$ type > FIELD_TYPE_KEYWORDS && $ type < FIELD_TYPE_NUMERIC ||
2014-06-11 19:28:29 +04:00
$ type > FIELD_TYPE_BUG_ID_REV )
2014-04-14 02:56:52 +04:00
{
ThrowCodeError ( 'invalid_customfield_type' , { type = > $ saved_type } ) ;
}
2014-06-18 16:19:48 +04:00
elsif ( $ type == FIELD_TYPE_BUG_URLS || $ type == FIELD_TYPE_KEYWORDS )
{
ThrowUserError ( 'useless_customfield_type' , { type = > $ type } ) ;
}
2008-12-15 15:53:33 +03:00
return $ type ;
}
2014-03-26 18:50:03 +04:00
sub _check_value_field_id
{
2014-06-11 19:28:29 +04:00
my ( $ invocant , $ field_id , undef , $ type ) = @ _ ;
$ type = $ invocant - > type if ! defined $ type ;
if ( $ type == FIELD_TYPE_BUG_ID_REV )
{
# For fields of type "reverse relation of BUG_ID field"
# value_field indicates the needed direct relation
my $ field = Bugzilla - > get_field ( trim ( $ field_id ) ) ;
if ( ! $ field || $ field - > type != FIELD_TYPE_BUG_ID )
{
ThrowUserError ( 'direct_field_needed_for_reverse' ) ;
}
for ( Bugzilla - > get_fields ( { type = > FIELD_TYPE_BUG_ID_REV , value_field_id = > $ field - > id } ) )
{
if ( ! ref $ invocant || $ _ - > id != $ invocant - > id )
{
ThrowUserError ( 'duplicate_reverse_field' ) ;
}
}
return $ field - > id ;
}
if ( $ field_id && $ type != FIELD_TYPE_SINGLE_SELECT && $ type != FIELD_TYPE_MULTI_SELECT )
2014-03-26 18:50:03 +04:00
{
2009-07-29 15:21:49 +04:00
ThrowUserError ( 'field_value_control_select_only' ) ;
}
return $ invocant - > _check_visibility_field_id ( $ field_id ) ;
}
2014-03-26 18:50:03 +04:00
sub _check_visibility_field_id
{
2009-07-29 15:21:49 +04:00
my ( $ invocant , $ field_id ) = @ _ ;
$ field_id = trim ( $ field_id ) ;
return undef if ! $ field_id ;
2010-10-14 14:31:30 +04:00
my $ field = Bugzilla - > get_field ( $ field_id ) ;
2014-03-26 18:50:03 +04:00
if ( blessed ( $ invocant ) && $ field - > id == $ invocant - > id )
{
2009-07-29 15:21:49 +04:00
ThrowUserError ( 'field_cant_control_self' , { field = > $ field } ) ;
}
2014-03-26 18:50:03 +04:00
if ( ! $ field - > is_select )
{
ThrowUserError ( 'field_control_must_be_select' , { field = > $ field } ) ;
2009-07-29 15:21:49 +04:00
}
return $ field - > id ;
}
2010-12-13 20:08:25 +03:00
# This has effect only for fields of FIELD_TYPE_BUG_ID type
# When 1, add field value (bug id) to list of bugs blocked by current
# When 2, add field value (bug id) to list of bugs depending on current
sub _check_add_to_deps
{
my ( $ invocant , $ value ) = @ _ ;
my % addto = ( '' = > 0 , 1 = > 1 , 2 = > 2 , no = > 0 , blocked = > 1 , dependson = > 2 ) ;
return $ addto { $ value || '' } ;
}
2014-06-30 19:31:24 +04:00
sub _check_default_value
{
my ( $ self , $ value ) = @ _ ;
if ( $ self - > type == FIELD_TYPE_SINGLE_SELECT )
{
# ID
detaint_natural ( $ value ) || undef ;
}
elsif ( $ self - > type == FIELD_TYPE_MULTI_SELECT )
{
# Array of IDs
$ value = [ $ value ] if ! ref $ value ;
detaint_natural ( $ _ ) for @$ value ;
$ value = @$ value ? join ( ',' , @$ value ) : undef ;
}
elsif ( $ self - > type == FIELD_TYPE_BUG_ID_REV )
{
return undef ;
}
return $ value ;
}
2008-12-15 15:53:33 +03:00
= pod
= head2 Instance Properties
= over
= item C <name>
the name of the field in the database ; begins with "cf_" if field
is a custom field , but test the value of the boolean "custom" property
2014-03-24 17:13:56 +04:00
to determine if a given field is a custom field
2008-12-15 15:53:33 +03:00
= item C <description>
a short string describing the field ; displayed to Bugzilla users
in several places within Bugzilla ' s UI , f . e . as the form field label
2014-03-24 17:13:56 +04:00
on the "show bug" page
2008-12-15 15:53:33 +03:00
= back
= cut
sub description { return $ _ [ 0 ] - > { description } }
= over
= item C <type>
an integer specifying the kind of field this is ; values correspond to
the FIELD_TYPE_ * constants in Constants . pm
= back
= cut
sub type { return $ _ [ 0 ] - > { type } }
= over
= item C <custom>
a boolean specifying whether or not the field is a custom field ;
if true , field name should start "cf_" , but use this property to determine
2014-03-24 17:13:56 +04:00
which fields are custom fields
2008-12-15 15:53:33 +03:00
= back
= cut
sub custom { return $ _ [ 0 ] - > { custom } }
= over
= item C <in_new_bugmail>
a boolean specifying whether or not the field is displayed in bugmail
for newly - created bugs ;
= back
= cut
sub in_new_bugmail { return $ _ [ 0 ] - > { mailhead } }
= over
= item C <sortkey>
2014-03-24 17:13:56 +04:00
an integer specifying the sortkey of the field
2008-12-15 15:53:33 +03:00
= back
= cut
sub sortkey { return $ _ [ 0 ] - > { sortkey } }
= over
= item C <obsolete>
2014-03-24 17:13:56 +04:00
a boolean specifying whether or not the field is obsolete
2008-12-15 15:53:33 +03:00
= back
= cut
sub obsolete { return $ _ [ 0 ] - > { obsolete } }
2014-08-01 21:13:45 +04:00
sub enabled { return ! $ _ [ 0 ] - > { obsolete } }
2008-12-15 15:53:33 +03:00
= over
2014-03-24 18:44:31 +04:00
= item C <nullable>
2014-06-18 15:46:54 +04:00
a boolean specifying whether empty value is allowed for this field
= back
= item C <is_mandatory>
the reverse of nullable
2014-03-24 18:44:31 +04:00
= back
= cut
2014-06-18 15:46:54 +04:00
sub nullable { return ! $ _ [ 0 ] - > type || $ _ [ 0 ] - > type == FIELD_TYPE_BUG_ID_REV || ! $ _ [ 0 ] - > { is_mandatory } }
sub is_mandatory { return ! $ _ [ 0 ] - > nullable }
2014-03-24 18:44:31 +04:00
= over
2010-10-07 15:31:09 +04:00
= item C <clone_bug>
A boolean specifying whether or not this field should be copied on bug clone
= back
= cut
sub clone_bug { return $ _ [ 0 ] - > { clone_bug } }
= over
2009-07-29 15:21:49 +04:00
= item C <is_select>
True if this is a C <FIELD_TYPE_SINGLE_SELECT> or C <FIELD_TYPE_MULTI_SELECT>
field . It is only safe to call L </legal_values> if this is true .
2008-12-15 15:53:33 +03:00
= item C <legal_values>
2009-07-29 15:21:49 +04:00
Valid values for this field , as an array of L <Bugzilla::Field::Choice>
objects .
2008-12-15 15:53:33 +03:00
= back
= cut
2014-03-24 17:13:56 +04:00
sub is_select
{
2010-09-30 15:27:44 +04:00
return ( $ _ [ 0 ] - > type == FIELD_TYPE_SINGLE_SELECT
2014-03-24 17:13:56 +04:00
|| $ _ [ 0 ] - > type == FIELD_TYPE_MULTI_SELECT ) ? 1 : 0 ;
2009-07-29 15:21:49 +04:00
}
2010-12-10 21:02:52 +03:00
sub has_activity { $ _ [ 0 ] - > { has_activity } }
2014-04-08 19:27:40 +04:00
sub add_to_deps { $ _ [ 0 ] - > type == FIELD_TYPE_BUG_ID && $ _ [ 0 ] - > { add_to_deps } }
2010-12-13 20:08:25 +03:00
2011-12-19 17:11:45 +04:00
sub url { $ _ [ 0 ] - > { url } }
2014-06-30 19:31:24 +04:00
sub default_value { $ _ [ 0 ] - > { default_value } }
2014-03-27 14:46:24 +04:00
sub value_type
{
my $ self = shift ;
return Bugzilla::Field::Choice - > type ( $ self ) ;
}
2014-08-01 21:13:45 +04:00
# Checks if a certain property can be changed for this field (either it is custom or standard)
sub can_tweak
{
my $ self = shift ;
my ( $ prop ) = @ _ ;
return $ self - > name !~ /^attachments\./ && $ self - > name ne 'longdesc' if $ prop eq 'mailhead' ;
$ prop = 'clone_bug' if $ prop eq 'clone_field_id' ;
$ prop = 'nullable' if $ prop eq 'null_field_id' ;
return 0 if ! $ self - > custom && ! CAN_TWEAK - > { $ prop } - > { $ self - > name } ;
return $ self - > type && $ self - > type != FIELD_TYPE_BUG_ID_REV if $ prop eq 'default_value' || $ prop eq 'nullable' ;
return $ self - > is_select if $ prop eq 'value_field_id' ;
return 1 ;
}
2012-02-17 14:49:15 +04:00
# Includes disabled values is $include_disabled = true
2010-10-27 19:53:21 +04:00
sub legal_values
{
2008-12-15 15:53:33 +03:00
my $ self = shift ;
2012-02-17 14:49:15 +04:00
my ( $ include_disabled ) = @ _ ;
2010-10-27 19:53:21 +04:00
return [] unless $ self - > is_select ;
2013-09-13 18:54:14 +04:00
return [ Bugzilla::Field::Choice - > type ( $ self ) - > get_all ( $ include_disabled ) ] ;
2008-12-15 15:53:33 +03:00
}
2012-02-17 14:49:15 +04:00
# Always excludes disabled values
2010-12-10 02:53:33 +03:00
sub legal_value_names
{
my $ self = shift ;
return [] unless $ self - > is_select ;
2014-03-27 18:26:36 +04:00
return [ map { $ _ - > { name } } @ { Bugzilla::Field::Choice - > type ( $ self ) - > get_all_names } ] ;
}
sub legal_value_names_with_ids
{
my $ self = shift ;
return [] unless $ self - > is_select ;
return Bugzilla::Field::Choice - > type ( $ self ) - > get_all_names ;
2010-12-10 02:53:33 +03:00
}
2014-07-23 00:48:21 +04:00
# Return the set of possible values for selected $controller_value of controlling field
2012-02-17 14:49:15 +04:00
# Always excludes disabled values
2009-09-07 21:19:11 +04:00
sub restricted_legal_values
{
my $ self = shift ;
2010-03-12 22:18:39 +03:00
my ( $ controller_value ) = @ _ ;
2010-11-24 20:22:45 +03:00
$ controller_value = $ controller_value - > id if ref $ controller_value ;
2014-07-23 00:48:21 +04:00
$ controller_value || = 0 ;
2013-09-13 18:54:14 +04:00
my $ rc_cache = Bugzilla - > rc_cache_fields ;
if ( ! $ rc_cache - > { $ self } - > { restricted_legal_values } - > { $ controller_value } )
2010-10-27 19:53:21 +04:00
{
2014-06-30 19:31:24 +04:00
my $ hash = Bugzilla - > fieldvaluecontrol - > { $ self - > value_field_id } - > { values } - > { $ self - > id } ;
2013-09-13 18:54:14 +04:00
$ rc_cache - > { $ self } - > { restricted_legal_values } - > { $ controller_value } = [
2014-08-01 16:08:17 +04:00
grep { $ hash - > { $ _ - > id } && $ hash - > { $ _ - > id } - > { $ controller_value } }
2014-07-22 18:32:45 +04:00
@ { $ self - > legal_values }
2010-10-27 19:53:21 +04:00
] ;
}
2013-09-13 18:54:14 +04:00
return $ rc_cache - > { $ self } - > { restricted_legal_values } - > { $ controller_value } ;
2009-09-07 21:19:11 +04:00
}
2010-03-12 22:18:39 +03:00
sub visibility_values
{
my $ self = shift ;
2010-11-24 16:41:09 +03:00
return undef if ! $ self - > visibility_field_id ;
2014-06-30 19:31:24 +04:00
my $ h = Bugzilla - > fieldvaluecontrol
2014-06-23 19:57:26 +04:00
- > { $ self - > visibility_field_id } - > { fields } - > { $ self - > id } ;
return $ h && %$ h ? $ h : undef ;
2010-03-12 22:18:39 +03:00
}
2009-07-29 15:21:49 +04:00
2010-03-12 22:18:39 +03:00
sub has_visibility_value
{
my $ self = shift ;
2010-11-24 16:41:09 +03:00
return 1 if ! $ self - > visibility_field_id ;
2010-03-12 22:18:39 +03:00
my ( $ value ) = @ _ ;
2010-11-24 16:41:09 +03:00
$ value = $ value - > id if ref $ value ;
2014-06-30 19:31:24 +04:00
my $ hash = Bugzilla - > fieldvaluecontrol
2014-06-24 15:02:49 +04:00
- > { $ self - > visibility_field_id } - > { fields } - > { $ self - > id } ;
2014-07-22 18:32:45 +04:00
return $ hash && $ hash - > { $ value } ;
2010-03-12 22:18:39 +03:00
}
2009-07-29 15:21:49 +04:00
2014-06-23 19:57:26 +04:00
sub null_visibility_values
2014-04-02 15:10:21 +04:00
{
my $ self = shift ;
2014-07-01 18:05:29 +04:00
return undef if ! $ self - > null_field_id ;
2014-06-30 19:31:24 +04:00
my $ h = Bugzilla - > fieldvaluecontrol
2014-07-01 18:05:29 +04:00
- > { $ self - > null_field_id } - > { null } - > { $ self - > id } ;
2014-06-23 19:57:26 +04:00
return $ h && %$ h ? $ h : undef ;
2014-04-02 15:10:21 +04:00
}
2014-07-08 15:44:05 +04:00
sub clone_visibility_values
{
my $ self = shift ;
2014-07-09 17:22:17 +04:00
return undef if ! $ self - > clone_field_id ;
2014-07-08 15:44:05 +04:00
my $ h = Bugzilla - > fieldvaluecontrol
- > { $ self - > clone_field_id } - > { clone } - > { $ self - > id } ;
return $ h && %$ h ? $ h : undef ;
}
2014-05-26 17:21:29 +04:00
# Check visibility of field for a bug or for a hashref with default value names
2010-03-12 22:18:39 +03:00
sub check_visibility
{
2009-07-29 15:21:49 +04:00
my $ self = shift ;
2010-11-24 16:41:09 +03:00
my $ bug = shift || return 1 ;
my $ vf = $ self - > visibility_field || return 1 ;
2014-05-26 17:21:29 +04:00
my $ value = bug_or_hash_value ( $ bug , $ vf ) ;
return $ value ? $ self - > has_visibility_value ( $ value ) : 1 ;
2009-07-29 15:21:49 +04:00
}
2014-07-08 15:44:05 +04:00
# Check if a field is nullable for a bug or for a hashref with default value names
2014-06-23 19:57:26 +04:00
sub check_is_nullable
{
my $ self = shift ;
$ self - > nullable || return 0 ;
2014-07-01 18:05:29 +04:00
my $ vf = $ self - > null_field || return 1 ;
2014-07-22 18:32:45 +04:00
$ self - > null_visibility_values || return 0 ;
my $ bug = shift || return 1 ;
2014-06-23 19:57:26 +04:00
my $ value = bug_or_hash_value ( $ bug , $ vf ) ;
2014-06-24 15:02:49 +04:00
return $ value ? $ self - > null_visibility_values - > { $ value } : 1 ;
2014-06-23 19:57:26 +04:00
}
2014-07-08 15:44:05 +04:00
# Check if a field should be copied when cloning $bug
sub check_clone
{
my $ self = shift ;
$ self - > clone_bug || return 0 ;
my $ vf = $ self - > clone_field || return 1 ;
2014-07-22 18:32:45 +04:00
$ self - > clone_visibility_values || return 0 ;
2014-07-08 15:44:05 +04:00
my $ bug = shift || return 1 ;
my $ value = bug_or_hash_value ( $ bug , $ vf ) ;
return $ value ? $ self - > clone_visibility_values - > { $ value } : 1 ;
}
# Get default value for this field in bug $bug
2014-07-11 18:06:58 +04:00
sub get_default_value
2014-07-01 19:50:14 +04:00
{
my $ self = shift ;
2014-07-11 18:06:58 +04:00
my ( $ bug , $ useGlobal ) = @ _ ;
my $ default = $ useGlobal ? $ self - > default_value : undef ;
2014-07-01 19:50:14 +04:00
if ( $ self - > default_field_id )
{
2014-07-11 18:06:58 +04:00
my $ value = bug_or_hash_value ( $ bug , $ self - > default_field ) ;
2014-07-01 19:50:14 +04:00
my $ d = Bugzilla - > fieldvaluecontrol - > { $ self - > default_field_id }
- > { defaults } - > { $ self - > id } ;
for ( ref $ value ? @$ value : $ value )
{
$ default = $ d - > { $ _ } if $ d - > { $ _ } ;
}
}
if ( $ default && $ self - > is_select )
{
$ default = $ self - > value_type - > new_from_list ( [ split /,/ , $ default ] ) ;
$ default = $ default - > [ 0 ] if $ self - > type == FIELD_TYPE_SINGLE_SELECT ;
}
return $ default ;
}
2014-07-11 18:06:58 +04:00
sub default_value_hash { $ _ [ 0 ] - > is_select ? { map { $ _ = > 1 } split /,/ , $ _ [ 0 ] - > { default_value } } : undef }
sub default_value_hash_for
{
my ( $ self , $ visibility_value_id ) = @ _ ;
return undef if ! $ self - > is_select ;
return { map { $ _ = > 1 } split /,/ , Bugzilla - > fieldvaluecontrol
- > { $ self - > default_field_id } - > { defaults }
- > { $ self - > id } - > { $ visibility_value_id } } ;
}
2009-07-29 15:21:49 +04:00
= pod
= over
= item C <controls_visibility_of>
An arrayref of C <Bugzilla::Field> objects , representing fields that this
field controls the visibility of .
= back
= cut
2014-03-24 17:13:56 +04:00
sub controls_visibility_of
{
2009-07-29 15:21:49 +04:00
my $ self = shift ;
2014-04-02 15:10:21 +04:00
$ self - > { controls_visibility_of } || = [ Bugzilla - > get_fields ( { visibility_field_id = > $ self - > id , obsolete = > 0 } ) ] ;
2009-07-29 15:21:49 +04:00
return $ self - > { controls_visibility_of } ;
}
2014-07-08 15:44:05 +04:00
sub visibility_field_id { $ _ [ 0 ] - > { visibility_field_id } }
sub null_field_id { $ _ [ 0 ] - > { null_field_id } }
sub default_field_id { $ _ [ 0 ] - > { default_field_id } }
sub clone_field_id { $ _ [ 0 ] - > { clone_field_id } }
2009-07-29 15:21:49 +04:00
2014-07-08 15:44:05 +04:00
sub value_field_id
{
my $ self = shift ;
return undef if ! $ self - > is_select && $ self - > type != FIELD_TYPE_BUG_ID_REV ;
return $ self - > { value_field_id } ;
}
2009-07-29 15:21:49 +04:00
2014-07-08 15:44:05 +04:00
# Field that controls visibility of this one
sub visibility_field
2014-03-24 17:13:56 +04:00
{
2009-07-29 15:21:49 +04:00
my $ self = shift ;
2014-07-08 15:44:05 +04:00
if ( $ self - > { visibility_field_id } )
2014-03-24 17:13:56 +04:00
{
2014-07-08 15:44:05 +04:00
return Bugzilla - > get_field ( $ self - > { visibility_field_id } ) ;
2009-07-29 15:21:49 +04:00
}
2014-07-08 15:44:05 +04:00
return undef ;
2009-07-29 15:21:49 +04:00
}
2014-07-08 15:44:05 +04:00
# Field that controls values of this one, if this one is a select,
# and related direct BUG_ID field, if this one is BUG_ID_REV
sub value_field
2010-10-14 14:31:30 +04:00
{
my $ self = shift ;
2014-07-08 15:44:05 +04:00
if ( my $ id = $ self - > value_field_id )
{
return Bugzilla - > get_field ( $ id ) ;
}
return undef ;
2010-10-14 14:31:30 +04:00
}
2014-07-08 15:44:05 +04:00
# Field that allows/forbids empty value for this one
2014-07-01 18:05:29 +04:00
sub null_field
{
my $ self = shift ;
if ( $ self - > { null_field_id } )
{
return Bugzilla - > get_field ( $ self - > { null_field_id } ) ;
}
return undef ;
}
2014-07-08 15:44:05 +04:00
# Field that controls default values for this one
2014-07-01 18:05:29 +04:00
sub default_field
{
my $ self = shift ;
if ( $ self - > { default_field_id } )
{
return Bugzilla - > get_field ( $ self - > { default_field_id } ) ;
}
return undef ;
}
2014-07-08 15:44:05 +04:00
# Field that controls copying the value of this field when cloning
sub clone_field
2014-07-01 18:05:29 +04:00
{
my $ self = shift ;
2014-07-08 15:44:05 +04:00
if ( $ self - > { clone_field_id } )
{
return Bugzilla - > get_field ( $ self - > { clone_field_id } ) ;
}
return undef ;
2014-07-01 18:05:29 +04:00
}
2009-07-29 15:21:49 +04:00
= pod
= over
= item C <controls_values_of>
An arrayref of C <Bugzilla::Field> objects , representing fields that this
field controls the values of .
= back
= cut
2014-03-24 17:13:56 +04:00
sub controls_values_of
{
2009-07-29 15:21:49 +04:00
my $ self = shift ;
2010-10-14 14:31:30 +04:00
$ self - > { controls_values_of } || = [ Bugzilla - > get_fields ( { value_field_id = > $ self - > id } ) ] ;
2009-07-29 15:21:49 +04:00
return $ self - > { controls_values_of } ;
}
= pod
2008-12-15 15:53:33 +03:00
= head2 Instance Mutators
These set the particular field that they are named after .
They take a single value - - the new value for that field .
They will throw an error if you try to set the values to something invalid .
= over
= item C <set_description>
2010-10-07 15:31:09 +04:00
= item C <set_clone_bug>
2008-12-15 15:53:33 +03:00
= item C <set_obsolete>
2014-03-24 18:44:31 +04:00
= item C <set_nullable>
2008-12-15 15:53:33 +03:00
= item C <set_sortkey>
= item C <set_in_new_bugmail>
2009-07-29 15:21:49 +04:00
= item C <set_visibility_field>
= item C <set_value_field>
2008-12-15 15:53:33 +03:00
= back
= cut
2014-06-30 19:31:24 +04:00
sub set_description { $ _ [ 0 ] - > set ( 'description' , $ _ [ 1 ] ) ; }
sub set_clone_bug { $ _ [ 0 ] - > set ( 'clone_bug' , $ _ [ 1 ] ) ; }
sub set_obsolete { $ _ [ 0 ] - > set ( 'obsolete' , $ _ [ 1 ] ) ; }
sub set_is_mandatory { $ _ [ 0 ] - > set ( 'is_mandatory' , $ _ [ 1 ] ) ; }
sub set_sortkey { $ _ [ 0 ] - > set ( 'sortkey' , $ _ [ 1 ] ) ; }
sub set_in_new_bugmail { $ _ [ 0 ] - > set ( 'mailhead' , $ _ [ 1 ] ) ; }
sub set_add_to_deps { $ _ [ 0 ] - > set ( 'add_to_deps' , $ _ [ 1 ] ) ; }
sub set_url { $ _ [ 0 ] - > set ( 'url' , $ _ [ 1 ] ) ; }
sub set_default_value { $ _ [ 0 ] - > set ( 'default_value' , $ _ [ 1 ] ) ; }
2010-03-12 22:18:39 +03:00
sub set_visibility_field
{
2009-07-29 15:21:49 +04:00
my ( $ self , $ value ) = @ _ ;
$ self - > set ( 'visibility_field_id' , $ value ) ;
}
2010-03-12 22:18:39 +03:00
sub set_visibility_values
{
my $ self = shift ;
my ( $ value_ids ) = @ _ ;
2014-07-08 15:44:05 +04:00
update_visibility_values ( $ self , FLAG_VISIBLE , $ value_ids ) ;
2014-06-23 19:57:26 +04:00
return $ value_ids && @$ value_ids ;
}
sub set_null_visibility_values
{
my $ self = shift ;
my ( $ value_ids ) = @ _ ;
2014-07-08 15:44:05 +04:00
update_visibility_values ( $ self , FLAG_NULLABLE , $ value_ids ) ;
return $ value_ids && @$ value_ids ;
}
sub set_clone_visibility_values
{
my $ self = shift ;
my ( $ value_ids ) = @ _ ;
update_visibility_values ( $ self , FLAG_CLONED , $ value_ids ) ;
2010-05-26 20:03:17 +04:00
return $ value_ids && @$ value_ids ;
2009-07-29 15:21:49 +04:00
}
2010-03-12 22:18:39 +03:00
sub set_value_field
{
2009-07-29 15:21:49 +04:00
my ( $ self , $ value ) = @ _ ;
$ self - > set ( 'value_field_id' , $ value ) ;
}
2014-07-01 18:05:29 +04:00
sub set_null_field
{
my ( $ self , $ value ) = @ _ ;
$ self - > set ( 'null_field_id' , $ value ) ;
}
2014-07-09 17:22:17 +04:00
sub set_clone_field
{
my ( $ self , $ value ) = @ _ ;
$ self - > set ( 'clone_field_id' , $ value ) ;
}
2014-07-01 18:05:29 +04:00
sub set_default_field
{
my ( $ self , $ value ) = @ _ ;
$ self - > set ( 'default_field_id' , $ value ) ;
}
2009-07-29 15:21:49 +04:00
# This is only used internally by upgrade code in Bugzilla::Field.
2014-06-18 18:21:51 +04:00
sub _set_type
{
trick_taint ( $ _ [ 1 ] ) ;
$ _ [ 0 ] - > { type } = int ( $ _ [ 1 ] ) ;
}
2008-12-15 15:53:33 +03:00
= pod
= head2 Instance Method
= over
= item C <remove_from_db>
Attempts to remove the passed in field from the database .
Deleting a field is only successful if the field is obsolete and
there are no values specified ( or EVER specified ) for the field .
= back
= cut
2014-04-14 02:56:52 +04:00
sub remove_from_db
{
2008-12-15 15:53:33 +03:00
my $ self = shift ;
my $ dbh = Bugzilla - > dbh ;
my $ name = $ self - > name ;
2014-04-14 02:56:52 +04:00
if ( ! $ self - > custom )
{
ThrowCodeError ( 'field_not_custom' , { name = > $ name } ) ;
2008-12-15 15:53:33 +03:00
}
2014-04-14 02:56:52 +04:00
if ( ! $ self - > obsolete )
{
ThrowUserError ( 'customfield_not_obsolete' , { name = > $ self - > name } ) ;
2008-12-15 15:53:33 +03:00
}
$ dbh - > bz_start_transaction ( ) ;
# Check to see if bugs table has records (slow)
my $ bugs_query = "" ;
2014-04-14 02:56:52 +04:00
if ( $ self - > type == FIELD_TYPE_MULTI_SELECT )
{
2008-12-15 15:53:33 +03:00
$ bugs_query = "SELECT COUNT(*) FROM bug_$name" ;
}
2014-04-14 02:56:52 +04:00
else
{
2010-05-15 00:02:34 +04:00
$ bugs_query = "SELECT COUNT(*) FROM bugs WHERE $name IS NOT NULL" ;
2014-04-14 02:56:52 +04:00
if ( $ self - > type != FIELD_TYPE_BUG_ID && $ self - > type != FIELD_TYPE_DATETIME )
{
2010-05-15 00:02:34 +04:00
$ bugs_query . = " AND $name != ''" ;
}
2008-12-15 15:53:33 +03:00
}
my $ has_bugs = $ dbh - > selectrow_array ( $ bugs_query ) ;
2014-04-14 02:56:52 +04:00
if ( $ has_bugs )
{
ThrowUserError ( 'customfield_has_contents' , { name = > $ name } ) ;
2008-12-15 15:53:33 +03:00
}
# Once we reach here, we should be OK to delete.
$ dbh - > do ( 'DELETE FROM fielddefs WHERE id = ?' , undef , $ self - > id ) ;
my $ type = $ self - > type ;
# the values for multi-select are stored in a seperate table
2014-04-14 02:56:52 +04:00
if ( $ type != FIELD_TYPE_MULTI_SELECT )
{
2008-12-15 15:53:33 +03:00
$ dbh - > bz_drop_column ( 'bugs' , $ name ) ;
}
2014-04-14 02:56:52 +04:00
if ( $ self - > is_select )
{
2008-12-15 15:53:33 +03:00
# Delete the table that holds the legal values for this field.
$ dbh - > bz_drop_field_tables ( $ self ) ;
}
2010-03-12 22:18:39 +03:00
$ self - > set_visibility_values ( undef ) ;
2014-07-08 15:44:05 +04:00
$ self - > set_null_visibility_values ( undef ) ;
$ self - > set_clone_visibility_values ( undef ) ;
2010-03-12 22:18:39 +03:00
2010-12-10 22:14:39 +03:00
# Update some other field (refresh the cache)
Bugzilla - > get_field ( 'delta_ts' ) - > touch ;
2010-12-10 02:53:33 +03:00
Bugzilla - > refresh_cache_fields ;
2010-11-24 21:25:51 +03:00
2010-03-12 22:18:39 +03:00
$ dbh - > bz_commit_transaction ( ) ;
2008-12-15 15:53:33 +03:00
}
2010-11-24 21:25:51 +03:00
# Overridden update() method - flushes field cache
sub update
{
my $ self = shift ;
2010-12-09 20:16:43 +03:00
$ self - > { delta_ts } = POSIX:: strftime ( '%Y-%m-%d %H:%M:%S' , localtime ) ;
2014-06-10 18:21:20 +04:00
# FIXME Merge something like VALIDATOR_DEPENDENCIES from 4.4
if ( $ self - > { type } != FIELD_TYPE_BUG_ID )
{
2014-06-11 19:28:29 +04:00
$ self - > { add_to_deps } = 0 ;
2014-06-10 18:21:20 +04:00
}
if ( $ self - > { type } != FIELD_TYPE_EXTURL )
{
$ self - > { url } = undef ;
}
2010-11-24 21:25:51 +03:00
my ( $ changes , $ old_self ) = $ self - > SUPER:: update ( @ _ ) ;
2010-12-10 02:53:33 +03:00
Bugzilla - > refresh_cache_fields ;
2014-08-01 21:13:45 +04:00
if ( $ self - > { name } eq 'classification' )
{
my $ prod = Bugzilla - > get_field ( 'product' ) ;
$ prod - > set_value_field ( $ self - > obsolete ? $ self - > id : undef ) ;
$ prod - > update ;
}
2010-11-24 21:25:51 +03:00
return wantarray ? ( $ changes , $ old_self ) : $ changes ;
}
2010-12-10 02:53:33 +03:00
# Update field change timestamp (needed for cache flushing)
sub touch
{
my $ self = shift ;
2010-12-10 21:02:52 +03:00
$ self - > update ;
2010-12-10 02:53:33 +03:00
}
2008-12-15 15:53:33 +03:00
= pod
= head2 Class Methods
= over
= item C <create>
Just like L <Bugzilla::Object/create> . Takes the following parameters:
= over
= item C <name> B <Required> - The name of the field .
= item C <description> B <Required> - The field label to display in the UI .
= item C <mailhead> - boolean - Whether this field appears at the
top of the bugmail for a newly - filed bug . Defaults to 0 .
= item C <custom> - boolean - True if this is a Custom Field . The field
will be added to the C <bugs> table if it does not exist . Defaults to 0 .
= item C <sortkey> - integer - The sortkey of the field . Defaults to 0 .
C <obsolete> - boolean - Whether this field is obsolete . Defaults to 0 .
= back
= back
= cut
2014-04-11 19:15:59 +04:00
sub create
{
2008-12-15 15:53:33 +03:00
my $ class = shift ;
2010-09-30 15:27:44 +04:00
my ( $ params ) = @ _ ;
# We must set up database schema BEFORE inserting a row into fielddefs!
2010-12-10 22:14:39 +03:00
$ params - > { delta_ts } = POSIX:: strftime ( '%Y-%m-%d %H:%M:%S' , localtime ) ;
2010-09-30 15:27:44 +04:00
$ class - > check_required_create_fields ( $ params ) ;
my $ field_values = $ class - > run_create_validators ( $ params ) ;
my $ obj = bless $ field_values , ref ( $ class ) || $ class ;
2008-12-15 15:53:33 +03:00
my $ dbh = Bugzilla - > dbh ;
2014-04-11 19:15:59 +04:00
if ( $ obj - > custom )
{
2010-09-30 15:27:44 +04:00
my $ name = $ obj - > name ;
my $ type = $ obj - > type ;
2014-04-11 19:15:59 +04:00
if ( SQL_DEFINITIONS - > { $ type } )
{
2008-12-15 15:53:33 +03:00
# Create the database column that stores the data for this field.
$ dbh - > bz_add_column ( 'bugs' , $ name , SQL_DEFINITIONS - > { $ type } ) ;
}
2014-04-11 19:15:59 +04:00
if ( $ obj - > is_select )
{
2008-12-15 15:53:33 +03:00
# Create the table that holds the legal values for this field.
2010-09-30 15:27:44 +04:00
$ dbh - > bz_add_field_tables ( $ obj ) ;
2008-12-15 15:53:33 +03:00
}
2014-04-11 19:15:59 +04:00
# Add foreign keys
if ( $ type == FIELD_TYPE_SINGLE_SELECT )
{
$ dbh - > bz_add_fk ( 'bugs' , $ name , { TABLE = > $ obj - > name , COLUMN = > 'id' } ) ;
}
elsif ( $ type == FIELD_TYPE_BUG_ID )
{
$ dbh - > bz_add_fk ( 'bugs' , $ name , { TABLE = > 'bugs' , COLUMN = > 'bug_id' } ) ;
}
2008-12-15 15:53:33 +03:00
}
2010-09-30 15:27:44 +04:00
# Call real constructor
2010-11-24 21:25:51 +03:00
my $ self = $ class - > SUPER:: create ( $ params ) ;
2010-12-10 02:53:33 +03:00
# Refresh fields inside single request
Bugzilla - > refresh_cache_fields ;
2010-11-24 21:25:51 +03:00
return $ self ;
2008-12-15 15:53:33 +03:00
}
2010-12-09 20:16:43 +03:00
sub run_create_validators
{
2008-12-15 15:53:33 +03:00
my $ class = shift ;
my $ dbh = Bugzilla - > dbh ;
my $ params = $ class - > SUPER:: run_create_validators ( @ _ ) ;
$ params - > { name } = $ class - > _check_name ( $ params - > { name } , $ params - > { custom } ) ;
2014-04-14 02:56:52 +04:00
if ( ! exists $ params - > { sortkey } )
{
$ params - > { sortkey } = $ dbh - > selectrow_array ( "SELECT MAX(sortkey) + 100 FROM fielddefs" ) || 100 ;
2008-12-15 15:53:33 +03:00
}
2009-07-29 15:21:49 +04:00
my $ type = $ params - > { type } || 0 ;
2010-09-30 15:27:44 +04:00
2014-04-14 02:56:52 +04:00
if ( $ params - > { custom } && ! $ type )
{
2010-05-15 00:02:34 +04:00
ThrowCodeError ( 'field_type_not_specified' ) ;
}
2010-09-30 15:27:44 +04:00
2014-06-11 19:28:29 +04:00
$ params - > { value_field_id } = $ class - > _check_value_field_id ( $ params - > { value_field_id } , undef , $ type ) ;
2014-06-10 18:21:20 +04:00
# FIXME Merge something like VALIDATOR_DEPENDENCIES from 4.4
if ( $ type != FIELD_TYPE_BUG_ID )
{
2014-06-11 19:28:29 +04:00
$ params - > { add_to_deps } = 0 ;
2014-06-10 18:21:20 +04:00
}
if ( $ type != FIELD_TYPE_EXTURL )
{
$ params - > { url } = undef ;
}
2014-06-30 19:31:24 +04:00
# Check default value
if ( $ type == FIELD_TYPE_SINGLE_SELECT || $ type == FIELD_TYPE_MULTI_SELECT ||
$ type == FIELD_TYPE_BUG_ID || $ type == FIELD_TYPE_BUG_ID_REV )
{
$ params - > { default_value } = undef ;
}
2008-12-15 15:53:33 +03:00
return $ params ;
}
= over
= item C <populate_field_definitions()>
Description: Populates the fielddefs table during an installation
or upgrade .
Params: none
Returns: nothing
= back
= cut
2014-03-24 17:13:56 +04:00
sub populate_field_definitions
{
2008-12-15 15:53:33 +03:00
my $ dbh = Bugzilla - > dbh ;
2014-06-18 15:46:54 +04:00
my ( $ has_clone_bug ) = $ dbh - > selectrow_array ( 'SELECT 1 FROM fielddefs WHERE clone_bug AND NOT custom' ) ;
2014-04-11 16:07:25 +04:00
# Add/update field definitions
2014-06-18 15:46:54 +04:00
foreach my $ def ( DEFAULT_FIELDS ( ) )
2014-03-24 17:13:56 +04:00
{
2008-12-15 15:53:33 +03:00
my $ field = new Bugzilla:: Field ( { name = > $ def - > { name } } ) ;
2014-03-24 17:13:56 +04:00
if ( $ field )
{
2014-06-18 15:46:54 +04:00
$ field - > set_description ( $ def - > { description } ) ;
$ field - > set_in_new_bugmail ( $ def - > { mailhead } ) ;
$ field - > set_clone_bug ( $ def - > { clone_bug } ) if ! $ has_clone_bug ;
2014-07-31 20:52:24 +04:00
$ field - > set_is_mandatory ( $ def - > { is_mandatory } ) if $ def - > { is_mandatory } || $ def - > { name } eq 'keywords' && $ def - > { type } ne $ field - > type ;
2014-06-24 17:13:46 +04:00
$ field - > set_value_field ( $ dbh - > selectrow_array ( 'SELECT id FROM fielddefs WHERE name=?' , undef , $ def - > { value_field } ) ) if $ def - > { value_field } ;
2014-07-01 18:05:29 +04:00
$ field - > set_null_field ( $ dbh - > selectrow_array ( 'SELECT id FROM fielddefs WHERE name=?' , undef , $ def - > { null_field } ) ) if $ def - > { null_field } ;
2014-07-01 19:50:14 +04:00
$ field - > set_default_field ( $ dbh - > selectrow_array ( 'SELECT id FROM fielddefs WHERE name=?' , undef , $ def - > { default_field } ) ) if $ def - > { default_field } ;
2014-07-31 20:52:24 +04:00
$ field - > _set_type ( $ def - > { type } ) if $ def - > { type } ;
2008-12-15 15:53:33 +03:00
$ field - > update ( ) ;
}
2014-03-24 17:13:56 +04:00
else
{
2008-12-15 15:53:33 +03:00
Bugzilla::Field - > create ( $ def ) ;
}
}
# MODIFY old field definitions
# 2005-11-13 LpSolit@gmail.com - Bug 302599
# One of the field names was a fragment of SQL code, which is DB dependent.
# We have to rename it to a real name, which is DB independent.
my $ new_field_name = 'days_elapsed' ;
my $ field_description = 'Days since bug changed' ;
2014-03-24 17:13:56 +04:00
my ( $ old_field_id , $ old_field_name ) = $ dbh - > selectrow_array (
'SELECT id, name FROM fielddefs WHERE description = ?' ,
undef , $ field_description
) ;
2008-12-15 15:53:33 +03:00
2014-03-24 17:13:56 +04:00
if ( $ old_field_id && ( $ old_field_name ne $ new_field_name ) )
{
2008-12-15 15:53:33 +03:00
print "SQL fragment found in the 'fielddefs' table...\n" ;
print "Old field name: " . $ old_field_name . "\n" ;
# We have to fix saved searches first. Queries have been escaped
# before being saved. We have to do the same here to find them.
$ old_field_name = url_quote ( $ old_field_name ) ;
2014-03-24 17:13:56 +04:00
my $ broken_named_queries = $ dbh - > selectall_arrayref (
'SELECT userid, name, query FROM namedqueries WHERE ' .
$ dbh - > sql_istrcmp ( 'query' , '?' , 'LIKE' ) ,
undef , "%=$old_field_name%"
) ;
2008-12-15 15:53:33 +03:00
2014-03-24 17:13:56 +04:00
my $ sth_UpdateQueries = $ dbh - > prepare (
'UPDATE namedqueries SET query = ? WHERE userid = ? AND name = ?'
) ;
2008-12-15 15:53:33 +03:00
2014-03-24 17:13:56 +04:00
print "Fixing saved searches...\n" if scalar @$ broken_named_queries ;
foreach my $ named_query ( @$ broken_named_queries )
{
2008-12-15 15:53:33 +03:00
my ( $ userid , $ name , $ query ) = @$ named_query ;
$ query =~ s/=\Q$old_field_name\E(&|$)/=$new_field_name$1/gi ;
$ sth_UpdateQueries - > execute ( $ query , $ userid , $ name ) ;
}
# We now do the same with saved chart series.
2014-03-24 17:13:56 +04:00
my $ broken_series = $ dbh - > selectall_arrayref (
'SELECT series_id, query FROM series WHERE ' .
$ dbh - > sql_istrcmp ( 'query' , '?' , 'LIKE' ) ,
undef , "%=$old_field_name%"
) ;
2008-12-15 15:53:33 +03:00
2014-03-24 17:13:56 +04:00
my $ sth_UpdateSeries = $ dbh - > prepare ( 'UPDATE series SET query = ? WHERE series_id = ?' ) ;
2008-12-15 15:53:33 +03:00
2014-03-24 17:13:56 +04:00
print "Fixing saved chart series...\n" if scalar @$ broken_series ;
foreach my $ series ( @$ broken_series )
{
2008-12-15 15:53:33 +03:00
my ( $ series_id , $ query ) = @$ series ;
$ query =~ s/=\Q$old_field_name\E(&|$)/=$new_field_name$1/gi ;
$ sth_UpdateSeries - > execute ( $ query , $ series_id ) ;
}
2014-03-24 17:13:56 +04:00
2008-12-15 15:53:33 +03:00
# Now that saved searches have been fixed, we can fix the field name.
print "Fixing the 'fielddefs' table...\n" ;
print "New field name: " . $ new_field_name . "\n" ;
2014-03-24 17:13:56 +04:00
$ dbh - > do ( 'UPDATE fielddefs SET name = ? WHERE id = ?' , undef , $ new_field_name , $ old_field_id ) ;
2008-12-15 15:53:33 +03:00
}
# This field has to be created separately, or the above upgrade code
# might not run properly.
2014-03-24 17:13:56 +04:00
unless ( new Bugzilla:: Field ( { name = > $ new_field_name } ) )
{
Bugzilla::Field - > create ( {
name = > $ new_field_name ,
description = > $ field_description
} ) ;
}
2014-08-01 16:19:04 +04:00
# DELETE fields which were added only accidentally, or which
# were never tracked in bugs_activity. Note that you should not
# delete fields which are used by bugs_activity.
$ dbh - > do (
" DELETE FROM fielddefs WHERE name IN ( 'cc_accessible' , 'requesters.login_name' ,
'attachments.thedata' , 'attach_data.thedata' , 'content' , 'requestees.login_name' ,
'setters.login_name' , 'longdescs.isprivate' , 'assignee_accessible' , 'qacontact_accessible' ,
'commenter' , 'owner_idle_time' , 'attachments.submitter' , 'days_elapsed' , 'percentage_complete' ) "
) ;
2008-12-15 15:53:33 +03:00
}
2014-05-26 17:21:29 +04:00
# Get choice value object for a bug or for a hashref with default value names
sub bug_or_hash_value
{
my ( $ bug , $ vf ) = @ _ ;
my $ value ;
if ( blessed $ bug )
{
2014-07-11 18:06:58 +04:00
# Bug object
2014-05-26 17:21:29 +04:00
$ value = $ bug - > get_ids ( $ vf - > name ) ;
}
2014-07-11 18:06:58 +04:00
elsif ( ref $ bug )
2014-05-26 17:21:29 +04:00
{
2014-07-11 18:06:58 +04:00
# Hashref with value names
2014-05-26 17:21:29 +04:00
$ value = $ bug - > { $ vf - > name } ;
2014-07-30 16:02:24 +04:00
if ( ! ref $ value && defined $ value )
2014-05-26 17:21:29 +04:00
{
# FIXME: This does not allow selecting of fields
# non-uniquely identified by name, as a visibility
# controller field (for example, "component")
$ value = Bugzilla::Field::Choice - > type ( $ vf ) - > new ( { name = > $ value } ) ;
2014-06-24 15:02:49 +04:00
$ value = $ value - > id if $ value ;
2014-05-26 17:21:29 +04:00
}
}
2014-07-11 18:06:58 +04:00
else
{
# Just value ID
$ value = $ bug ;
}
2014-05-26 17:21:29 +04:00
return $ value ;
}
2014-07-08 15:44:05 +04:00
sub flag_field
{
my ( $ self , $ flag ) = @ _ ;
return $ self - > value_field if $ flag > 0 ;
return $ self - > visibility_field if $ flag == FLAG_VISIBLE ;
return $ self - > null_field if $ flag == FLAG_NULLABLE ;
return $ self - > clone_field if $ flag == FLAG_CLONED ;
}
2010-03-12 22:18:39 +03:00
# Shared between Bugzilla::Field and Bugzilla::Field::Choice
sub update_visibility_values
{
2010-11-10 21:21:19 +03:00
my ( $ controlled_field , $ controlled_value_id , $ visibility_value_ids ) = @ _ ;
2010-11-11 16:36:23 +03:00
$ visibility_value_ids || = [] ;
2014-07-08 15:44:05 +04:00
my $ vis_field = $ controlled_field - > flag_field ( $ controlled_value_id ) ;
2010-11-10 21:21:19 +03:00
if ( ! $ vis_field )
{
return undef ;
}
$ controlled_field = Bugzilla - > get_field ( $ controlled_field ) if ! ref $ controlled_field ;
$ controlled_value_id = int ( $ controlled_value_id ) ;
if ( @$ visibility_value_ids )
{
my $ type = Bugzilla::Field::Choice - > type ( $ vis_field ) ;
2014-07-23 00:48:21 +04:00
$ visibility_value_ids = [
( grep { $ _ == 0 } @$ visibility_value_ids ? ( 0 ) : ( ) ) ,
map { $ _ - > id } @ { $ type - > new_from_list ( $ visibility_value_ids ) }
] ;
2010-11-10 21:21:19 +03:00
}
2010-03-12 22:18:39 +03:00
Bugzilla - > dbh - > do (
"DELETE FROM fieldvaluecontrol WHERE field_id=? AND value_id=?" ,
2010-11-10 21:21:19 +03:00
undef , $ controlled_field - > id , $ controlled_value_id ) ;
if ( @$ visibility_value_ids )
2010-03-12 22:18:39 +03:00
{
2010-11-10 21:21:19 +03:00
my $ f = $ controlled_field - > id ;
2010-03-12 22:18:39 +03:00
Bugzilla - > dbh - > do (
"INSERT INTO fieldvaluecontrol (field_id, value_id, visibility_value_id) VALUES " .
2010-11-10 21:21:19 +03:00
join ( "," , map { "($f, $controlled_value_id, $_)" } @$ visibility_value_ids )
) ;
2010-03-12 22:18:39 +03:00
}
2010-12-09 20:16:43 +03:00
# Touch the field
2010-12-10 02:53:33 +03:00
$ controlled_field - > touch ;
2010-11-10 21:21:19 +03:00
return 1 ;
2010-03-12 22:18:39 +03:00
}
2014-07-22 18:32:45 +04:00
sub update_control_lists
2014-06-27 19:22:25 +04:00
{
2014-07-22 18:32:45 +04:00
my ( $ controlling_field_id , $ controlling_value_id , $ params ) = @ _ ;
$ controlling_field_id = $ controlling_field_id - > id if ref $ controlling_field_id ;
$ controlling_value_id = Bugzilla - > get_field ( $ controlling_field_id ) - > value_type - > new ( $ controlling_value_id ) ;
$ controlling_value_id = $ controlling_value_id ? $ controlling_value_id - > id : return undef ;
# Save all visible, nullable and clone flags at once
2014-08-01 18:44:49 +04:00
my $ mod = { del = > [] , add = > [] } ;
2014-07-22 18:32:45 +04:00
for my $ f ( Bugzilla - > get_fields ( { obsolete = > 0 , visibility_field_id = > $ controlling_field_id } ) )
{
push @ { $ mod - > { $ params - > { 'is_visible_' . $ f - > name } ? 'add' : 'del' } } , [ $ f - > id , FLAG_VISIBLE ] ;
}
for my $ f ( Bugzilla - > get_fields ( { obsolete = > 0 , null_field_id = > $ controlling_field_id } ) )
2014-06-27 19:22:25 +04:00
{
2014-07-22 18:32:45 +04:00
push @ { $ mod - > { $ params - > { 'is_nullable_' . $ f - > name } ? 'add' : 'del' } } , [ $ f - > id , FLAG_NULLABLE ] ;
2014-06-27 19:22:25 +04:00
}
2014-07-22 18:32:45 +04:00
for my $ f ( Bugzilla - > get_fields ( { obsolete = > 0 , clone_field_id = > $ controlling_field_id } ) )
{
push @ { $ mod - > { $ params - > { 'is_cloned_' . $ f - > name } ? 'add' : 'del' } } , [ $ f - > id , FLAG_CLONED ] ;
}
if ( @ { $ mod - > { del } } || @ { $ mod - > { add } } )
2014-06-27 19:22:25 +04:00
{
Bugzilla - > dbh - > do (
2014-07-22 18:32:45 +04:00
'DELETE FROM fieldvaluecontrol WHERE visibility_value_id=? AND (field_id, value_id) IN (' .
join ( ',' , map { "($_->[0], $_->[1])" } ( @ { $ mod - > { add } } , @ { $ mod - > { del } } ) ) . ')' , undef ,
$ controlling_value_id
2014-06-27 19:22:25 +04:00
) ;
}
2014-07-22 18:32:45 +04:00
if ( @ { $ mod - > { add } } )
2014-06-27 19:22:25 +04:00
{
Bugzilla - > dbh - > do (
2014-07-22 18:32:45 +04:00
'INSERT INTO fieldvaluecontrol (visibility_value_id, field_id, value_id) VALUES ' .
join ( ',' , map { "($controlling_value_id, $_->[0], $_->[1])" } @ { $ mod - > { add } } )
2014-06-27 19:22:25 +04:00
) ;
}
2014-07-22 18:32:45 +04:00
# Save all dependent defaults at once
my $ touched = { map { $ _ - > [ 0 ] = > 1 } ( @ { $ mod - > { add } } , @ { $ mod - > { del } } ) } ;
2014-08-01 18:44:49 +04:00
$ mod = { del = > [] , add = > [] } ;
2014-07-22 18:32:45 +04:00
for my $ f ( Bugzilla - > get_fields ( { obsolete = > 0 , default_field_id = > $ controlling_field_id } ) )
2014-07-11 18:06:58 +04:00
{
2014-07-22 18:32:45 +04:00
next if $ f eq 'version' || $ f eq 'target_milestone' ; # FIXME: default version is hardcoded to depend on component, default milestone is hardcoded to depend on product
my $ default = $ params - > { 'default_' . $ f - > name } ;
$ default = $ f - > _check_default_value ( $ default ) ;
if ( ! $ default )
{
push @ { $ mod - > { del } } , [ $ f - > id ] ;
}
else
{
trick_taint ( $ default ) ;
push @ { $ mod - > { add } } , [ $ f - > id , $ default ] ;
}
$ touched - > { $ f - > id } = 1 ;
2014-07-11 18:06:58 +04:00
}
2014-07-22 18:32:45 +04:00
if ( @ { $ mod - > { del } } || @ { $ mod - > { add } } )
2014-07-11 18:06:58 +04:00
{
2014-07-22 18:32:45 +04:00
Bugzilla - > dbh - > do (
'DELETE FROM field_defaults WHERE visibility_value_id=? AND field_id IN (' .
join ( ',' , map { $ _ - > [ 0 ] } ( @ { $ mod - > { add } } , @ { $ mod - > { del } } ) ) . ')' ,
undef , $ controlling_value_id
) ;
2014-07-11 18:06:58 +04:00
}
2014-07-22 18:32:45 +04:00
if ( @ { $ mod - > { add } } )
2014-07-11 18:06:58 +04:00
{
2014-07-22 18:32:45 +04:00
Bugzilla - > dbh - > do (
'INSERT INTO field_defaults (visibility_value_id, field_id, default_value) VALUES ' .
join ( ',' , map { "($controlling_value_id, $_->[0], ?)" } @ { $ mod - > { add } } ) ,
undef , map { $ _ - > [ 1 ] } @ { $ mod - > { add } }
) ;
2014-07-11 18:06:58 +04:00
}
2014-07-22 18:32:45 +04:00
# Update metadata timestamp for many fields at once
if ( %$ touched )
2014-07-11 18:06:58 +04:00
{
2014-07-22 18:32:45 +04:00
Bugzilla - > dbh - > do (
'UPDATE fielddefs SET delta_ts=? WHERE id IN (' .
join ( ',' , keys %$ touched ) . ')' , undef , POSIX:: strftime ( '%Y-%m-%d %H:%M:%S' , localtime )
) ;
Bugzilla - > refresh_cache_fields ;
2014-07-11 18:06:58 +04:00
}
}
2013-07-01 19:14:37 +04:00
sub update_controlled_values
2013-07-01 18:42:45 +04:00
{
2014-06-30 19:31:24 +04:00
my ( $ controlled_field , $ controlled_value_ids , $ visibility_value_id ) = @ _ ;
2014-07-22 18:32:45 +04:00
$ controlled_field = Bugzilla - > get_field ( $ controlled_field ) if ! ref $ controlled_field ;
2013-07-01 18:42:45 +04:00
$ controlled_value_ids || = [] ;
2014-06-27 19:22:25 +04:00
my $ vis_field = $ controlled_field - > value_field ;
2013-07-01 18:42:45 +04:00
if ( ! $ vis_field )
{
return undef ;
}
$ visibility_value_id = int ( $ visibility_value_id ) ;
Bugzilla - > dbh - > do (
2013-11-07 14:44:17 +04:00
"DELETE FROM fieldvaluecontrol WHERE field_id=? AND visibility_value_id=? AND value_id!=0" ,
2013-07-01 18:42:45 +04:00
undef , $ controlled_field - > id , $ visibility_value_id ) ;
if ( @$ controlled_value_ids )
{
my $ type = Bugzilla::Field::Choice - > type ( $ controlled_field ) ;
$ controlled_value_ids = [ map { $ _ - > id } @ { $ type - > new_from_list ( $ controlled_value_ids ) } ] ;
my $ f = $ controlled_field - > id ;
2014-06-30 19:31:24 +04:00
my $ sql = "INSERT INTO fieldvaluecontrol (field_id, visibility_value_id, value_id) VALUES " .
join ( "," , map { "($f, $visibility_value_id, $_)" } @$ controlled_value_ids ) ;
2013-07-01 18:42:45 +04:00
Bugzilla - > dbh - > do ( $ sql ) ;
}
# Touch the field
$ controlled_field - > touch ;
return 1 ;
}
2014-05-21 17:53:50 +04:00
sub update_default_values
{
2014-06-30 19:31:24 +04:00
my ( $ controlled_field , $ visibility_value_id , $ default_value ) = @ _ ;
2014-05-21 17:53:50 +04:00
$ controlled_field = Bugzilla - > get_field ( $ controlled_field ) if ! ref $ controlled_field ;
$ visibility_value_id = int ( $ visibility_value_id ) ;
2014-06-30 19:31:24 +04:00
$ default_value = $ controlled_field - > _check_default_value ( $ default_value ) ;
if ( ! $ default_value )
{
Bugzilla - > dbh - > do (
'DELETE FROM field_defaults WHERE field_id=? AND visibility_value_id=?' ,
undef , $ controlled_field - > id , $ visibility_value_id
) ;
}
else
{
2014-07-11 18:06:58 +04:00
trick_taint ( $ default_value ) ;
2014-06-30 19:31:24 +04:00
Bugzilla - > dbh - > do (
'REPLACE INTO field_defaults (field_id, visibility_value_id, default_value) VALUES (?, ?, ?)' ,
undef , $ controlled_field - > id , $ visibility_value_id , $ default_value
) ;
}
2014-05-21 17:53:50 +04:00
# Touch the field
$ controlled_field - > touch ;
return 1 ;
}
2014-03-31 18:43:20 +04:00
# Field and value dependency data, intended for use in client JavaScript
2010-03-12 22:18:39 +03:00
sub json_visibility
{
my $ self = shift ;
2010-11-10 21:21:19 +03:00
my $ data = {
2014-03-31 18:43:20 +04:00
legal = > [ map { [ $ _ - > id , $ _ - > name ] } @ { $ self - > legal_values } ] ,
visibility_field = > $ self - > visibility_field ? $ self - > visibility_field - > name : undef ,
value_field = > $ self - > value_field ? $ self - > value_field - > name : undef ,
2014-07-01 18:05:29 +04:00
null_field = > $ self - > null_field ? $ self - > null_field - > name : undef ,
default_field = > $ self - > default_field ? $ self - > default_field - > name : undef ,
2014-03-31 20:00:26 +04:00
nullable = > $ self - > nullable ? 1 : 0 ,
2014-07-01 16:30:12 +04:00
default_value = > $ self - > default_value || undef ,
2010-10-27 19:53:21 +04:00
fields = > { } ,
values = > { } ,
2014-07-01 16:30:12 +04:00
defaults = > { } ,
2014-06-24 16:13:54 +04:00
null = > { } ,
2010-10-27 19:53:21 +04:00
} ;
2014-06-30 19:31:24 +04:00
my $ hash = Bugzilla - > fieldvaluecontrol - > { $ self - > id } ;
2014-07-01 16:30:12 +04:00
for my $ key ( qw( fields values defaults null ) )
2014-06-24 16:13:54 +04:00
{
$ data - > { $ key } = { map { Bugzilla - > get_field ( $ _ ) - > name = > $ hash - > { $ key } - > { $ _ } } keys % { $ hash - > { $ key } } } ;
}
2010-11-10 21:21:19 +03:00
return $ data ;
2010-03-12 22:18:39 +03:00
}
2010-11-10 21:21:19 +03:00
1 ;
__END__