pax_global_header00006660000000000000000000000064122274277470014530gustar00rootroot0000000000000052 comment=5cc0ae1b1564261af1f2174d75dc4ae2cf438f02 rt-extension-assettracker-3.0.0/000077500000000000000000000000001222742774700166605ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/.gitignore000066400000000000000000000001411222742774700206440ustar00rootroot00000000000000Makefile Makefile.old MYMETA.json MYMETA.yml blib/ pm_to_blib lib/RTx/AssetTracker/Test.pm t/tmp rt-extension-assettracker-3.0.0/COPYING000077500000000000000000000430701222742774700177220ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. rt-extension-assettracker-3.0.0/META.yml000066400000000000000000000011651222742774700201340ustar00rootroot00000000000000--- abstract: 'RT AssetTracker Extension' author: - 'Todd Chapman ' build_requires: ExtUtils::MakeMaker: 6.59 configure_requires: ExtUtils::MakeMaker: 6.59 distribution_type: module dynamic_config: 1 generated_by: 'Module::Install version 1.06' license: gpl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: RTx-AssetTracker no_index: directory: - etc - html - inc - po - t requires: Set::Scalar: 0 XML::Parser: 0 XML::Simple: 0 perl: 5.8.3 resources: license: http://opensource.org/licenses/gpl-license.php version: 2.000000_004 rt-extension-assettracker-3.0.0/Makefile.PL000066400000000000000000000012261222742774700206330ustar00rootroot00000000000000use inc::Module::Install; RTx('RTx-AssetTracker'); name('RTx-AssetTracker'); license('GPL version 2'); author('Todd Chapman '); perl_version('5.008003'); all_from('lib/RTx/AssetTracker.pm'); #requires_rt('3.8.12'); requires( #'RT' => '3.008001', 'XML::Simple' => 0, 'XML::Parser' => 0, 'Set::Scalar' => 0, ); # XXX: This should be reported by M::I::RTx my ($lib_path) = $INC{'RT.pm'} =~ /^(.*)[\\\/]/; my $local_lib_path = "$RT::LocalPath/lib"; substitute( { RT_LIB_PATH => join( ' ', $local_lib_path, $lib_path ), }, { sufix => '.in', }, 'lib/RTx/AssetTracker/Test.pm', ); WriteAll(); rt-extension-assettracker-3.0.0/README000066400000000000000000000062341222742774700175450ustar00rootroot00000000000000LICENSE The work is released under the GPL PREREQUISITES A working RT 4 installation (4.0.14 or greater) is required for AT 3.0. For RT 3.8, use AT 2.0. WARNING Only mysql and SQLite are tested by the author. Feel free to send me a schema file for other databases supported by RT. Oracle schema provided by Joop van de Wege. Postgres schema provided by Rolf Schaufelberger. INSTALLATION Please make a backup of your RT installation and database! $ RTHOME=/path/to/rt perl Makefile.PL $ make $ make install $ make initdb Add RTx::AssetTracker to your Plugins in etc/RT_SiteConfig.pm and restart your web server. If you like to use Assets as CF SourceValues, you can activate it as example like this inside RT_SiteConfig.pm: Set(@CustomFieldValuesSources, (qw(RT::CustomFieldValues::ATServers))); UGRADING Follow any version-specific instructions below, then type: ls etc/upgrade For each item in that directory whose name is greater than your previously installed AT version, you must run upgrade commands. Each step is described below and may have additional instructions. Read them before running upgrade commands. Commands you should run to upgrade DB using data from etc/upgrade/ directory: If the dir has any schema files then run: /opt/rt4/sbin/rt-setup-database --dba \ --prompt-for-dba-password --action schema \ --datadir etc/upgrade/ If the dir has a file named 'content' then run: /opt/rt4/sbin/rt-setup-database --dba \ --prompt-for-dba-password --action insert \ --datadir etc/upgrade/ UPGRADING FROM AT 1.2.4 AND EARLIER Do the following: 1. Make a backup of any AT customizations. 2. AT now uses the RT plugins system. Remove all AT files fom the RT directories. After the "make install" step above put any AT customizations in $RTHOME/local/plugins/RTx-AssetTracker, add 3. Add to RT_SiteConfig: Set(@Plugins,(qw(RTx::AssetTracker))); UPGRADING FROM AT 2.0.x AND EARLIER Custom link types are now set with a hash, %AssetLinkTypes, instead of an array, @AssetLinkTypes. For example, if you had @AssetLinkTypes = qw( LocatedAt AtThisLocation ); in your *_SiteConfig.pm file, you should change it to Set( %AssetLinkTypes, LocatedAt => 'AtThisLocation' ); UPGRADING FROM AT 3.0.x AND EARLIER The setting 'ModifyBothAssetsForLink' has been renamed to 'StrictAssetLinkACL'. UPGRADING FROM an AT installed under RT 3.6.* or older If you installed an older version of AT under RT < 3.8 the best strategy is to remove AT files from your installation and install AT as above, but skipping the rt-setup-database commands. Any AT customization will have to be ported to the new version AT, which uses the RT plugins system. Also, most AT configuration option have been renamed, and they are all now in the RT namespace. So, for example the configuration option $RTx::AssetTracker::DefaultSearchResultFormat is now $RT::DefaultAssetSearchResultFormat. TODO: Document converting asset saved searches. AUTHOR Todd Chapman todd@chaka.net PROPS Thanks to Jesse and Best Practical for giving me something to stand on. rt-extension-assettracker-3.0.0/etc/000077500000000000000000000000001222742774700174335ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/etc/AT_Config.pm000066400000000000000000000132411222742774700215630ustar00rootroot00000000000000# # WARNING: NEVER EDIT AT_Config.pm. Instead, copy any sections you want to change to AT_SiteConfig.pm # and edit them there. # =head1 NAME AT::Config =for testing use AT::Config; =cut =item C<$AssetsItemMapSize> On the display page of a asset from search results, RT provides links to the first, next, previous and last asset from the results. In order to build these links, RT needs to fetch the full result set from the database, which can be resource-intensive. Set C<$AssetsItemMapSize> to number of assets you want RT to examine to build these links. If the full result set is larger than this number, RT will omit the "last" link in the menu. Set this to zero to always examine all results. =cut Set($AssetsItemMapSize, 1000); # $DefaultAssetSearchResultFormat is the default format for AT search results Set ($DefaultAssetSearchResultFormat, qq{'__Name__/TITLE:Name', Description, Status, TypeName, } ); # Asset name uniqueness is now enforced by the API instead of the DB # The rules are evaluated in this order: Set ($GlobalUniqueAssetName, 1); Set ($TypeUniqueAssetName, 0); Set ($TypeStatusUniqueAssetName, 0); # If you don't want to use IPs, or would rather use CFs for IPs # then set this to zero to disable IP features in AT Set ($EnableIP, 1); =item C<$StrictAssetLinkACL> When this feature is enabled a user needs I rights on both assets to link them together; otherwise, I rights on either of them is sufficient. =cut Set($StrictAssetLinkACL, 0); # The number of history items to display on the Asset main page # (set to 0 to turn off) Set ($ShowAssetHistory, 10); # You can define new Link types by adding new key => value pairs. # The key in each pair is the forward link and the value element is # the reverse link. # Set(%AssetLinkTypes, LocatedAt => 'AtThisLocation' ); Set(%AssetLinkTypes, RunsOn => 'IsRunning', RefersTo => 'ReferredToBy', DependsOn => 'DependedOnBy', ComponentOf => 'HasComponent', ); # List of role names. Need more/less/different roles? Change it! # @AssetRoles = qw( Admin Owner ); #default # When displaying asset watchers descend into groups and show # the user members. Turning this off make the display more succinct. Set ($ShowGroupMembers, 1); # If you turn this on not only will the asset display # show watchers directly assigned to the asset, but type # watchers will also display. This may clear up confusion # where users think there is no watcher assigned when there really is. Set ($ShowTypeWatchersInAsset, 0); =item C<%AdminSearchResultFormat> In admin interface format strings similar to tickets search result formats are used. Use C<%AdminSearchResultFormat> to define format strings per RT class. =cut Set(%AdminSearchResultFormat, AT_Types => q{'__id__/TITLE:#'} .q{,'__Name__/TITLE:Name'} .q{,__Description__,__Disabled__}, AT_Scrips => q{'__id__/TITLE:#'} .q{,'__Description__/TITLE:Description' } .q{,__Stage__, __Condition__, __Action__, __Template__}, AT_GlobalScrips => q{'__id__/TITLE:#'} .q{,'__Description__/TITLE:Description' } .q{,__Stage__, __Condition__, __Action__, __Template__}, AT_Templates => q{'__id__/TITLE:#'} .q{,'__Name__/TITLE:Name'} .q{,'__Description__'}, ); local $rt_comps = RT->Config->Get("HomepageComponents"); RT->Config->Set("HomepageComponents", [@$rt_comps, qw(AssetQuickSearch)]); =head1 Lifecycles =cut Set(%Lifecycles, at_default => { initial => [ ], active => [ 'production', 'development', 'qa', 'dr', 'pilot', 'test' ], inactive => [ 'retired', 'deleted' ], defaults => { on_create => 'production', }, transitions => { '' => [qw(production development qa dr pilot test retired)], # from => [ to list ], production => [qw(development qa dr pilot test retired deleted)], development => [qw(production qa dr pilot test retired deleted)], qa => [qw(production development dr pilot test retired deleted)], dr => [qw(production development qa pilot test retired deleted)], pilot => [qw(production development qa dr test retired deleted)], test => [qw(production development qa dr pilot retired deleted)], retired => [qw(production development qa dr pilot test deleted)], deleted => [qw(production development qa dr pilot test retired)], }, rights => { '* -> retired' => 'RetireAsset', '* -> deleted' => 'DeleteAsset', '* -> *' => 'ModifyAsset', }, actions => [ 'production -> retired' => { label => 'Retire', }, '* -> deleted' => { label => 'Delete', } ], }, ); 1; rt-extension-assettracker-3.0.0/etc/acl.Pg000066400000000000000000000005521222742774700204640ustar00rootroot00000000000000sub acl { my $dbh = shift; my @acls; my @tables = qw ( at_types at_types_id_seq at_assets at_assets_id_seq at_ips at_ips_id_seq at_ports at_ports_id_seq ); foreach my $table (@tables) { push @acls, "GRANT SELECT, INSERT, UPDATE, DELETE ON $table to " . $RT::DatabaseUser . ";"; } return (@acls); } 1; rt-extension-assettracker-3.0.0/etc/acl.mysql000066400000000000000000000000311222742774700212530ustar00rootroot00000000000000sub acl { return(); } 1; rt-extension-assettracker-3.0.0/etc/drop_schema.Pg000066400000000000000000000003361222742774700222110ustar00rootroot00000000000000DROP TABLE AT_Assets ; DROP TABLE AT_Types ; DROP TABLE AT_IPs ; DROP TABLE AT_Ports ; DELETE FROM Transactions where ObjectType LIKE 'RTx::AssetTracker%' ; DELETE FROM Groups where Domain LIKE 'RTx::AssetTracker%' ; rt-extension-assettracker-3.0.0/etc/drop_schema.mysql000066400000000000000000000003361222742774700230100ustar00rootroot00000000000000DROP TABLE AT_Assets ; DROP TABLE AT_Types ; DROP TABLE AT_IPs ; DROP TABLE AT_Ports ; DELETE FROM Transactions where ObjectType LIKE 'RTx::AssetTracker%' ; DELETE FROM Groups where Domain LIKE 'RTx::AssetTracker%' ; rt-extension-assettracker-3.0.0/etc/initialdata000066400000000000000000000043261222742774700216460ustar00rootroot00000000000000# Initial data for a fresh AT Installation. use RTx::AssetTracker; use RTx::AssetTracker::Type; @Types = ({ Name => 'Servers', Description => 'The default asset type', }); @Final = ( \&AssetTypes, \&AssetScripConditions, \&AssetScripActions, \&AssetTemplates, ); my $CurrentUser = RT::CurrentUser->new(); $CurrentUser->LoadByName('RT_System'); sub AssetTypes { print "Creating default asset types..."; for $item (@Types) { my $new_entry = new RTx::AssetTracker::Type($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } sub AssetScripConditions { print "Creating default asset scrips conditions..."; my @Conditions = ( { Name => 'User Defined', Description => 'Whenever a user-defined condition occurs', ExecModule => 'UserDefined', Argument => undef, ApplicableTransTypes => 'Any' }, ); for $item (@Conditions) { my $new_entry = new RTx::AssetTracker::ScripCondition($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } sub AssetScripActions { print "Creating default asset scrips actions..."; my @Conditions = ( { Name => 'User Defined', Description => 'Perform a user-defined action', ExecModule => 'UserDefined', Argument => undef }, ); for $item (@Conditions) { my $new_entry = new RTx::AssetTracker::ScripAction($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } sub AssetTemplates { print "Creating default asset templates..."; my @Conditions = ( { AssetType => 0, Name => 'Blank', Description => 'A blank template', Content => undef }, ); for $item (@Conditions) { my $new_entry = new RTx::AssetTracker::Template($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } rt-extension-assettracker-3.0.0/etc/schema.Oracle000066400000000000000000000077051222742774700220330ustar00rootroot00000000000000# Courtesy of Joop van de Wege # {{{ AT_Types CREATE SEQUENCE AT_TYPES_seq; CREATE TABLE AT_Types ( id Number(11,0) constraint At_types_key Primary key, Name varchar2(200) NOT NULL , Description varchar2(255) , DefaultAdmin number(11,0) DEFAULT 0 , Lifecycle varchar2(32) , Creator number(11,0) DEFAULT 0 NOT NULL , Created DATE , LastUpdatedBy Number(11,0) DEFAULT 0 NOT NULL , LastUpdated DATE , Disabled number(11,0) DEFAULT 0 NOT NULL ); CREATE UNIQUE INDEX AT_Types1 ON AT_Types (Name) ; CREATE INDEX AT_Types2 ON AT_Types (Disabled) ; # }}} # {{{ Assets CREATE SEQUENCE AT_ASSETS_seq; CREATE TABLE AT_Assets ( id Number(11,0) constraint At_Assets_key Primary key, Type number(11,0) DEFAULT 0 NOT NULL , Name varchar2(200) NOT NULL , Description varchar2(255) , Status varchar2(64) , URI varchar2(255), LastUpdatedBy number(11,0) DEFAULT 0 NOT NULL , LastUpdated DATE , Creator number(11,0) DEFAULT 0 NOT NULL , Created DATE ); CREATE INDEX AT_Assets1 ON AT_Assets (Type, Status) ; CREATE INDEX AT_Assets2 ON AT_Assets (Name, Type, Status) ; # }}} # {{{ AT_IPs CREATE SEQUENCE AT_IPs_seq; CREATE TABLE AT_IPs ( id Number(11,0) constraint At_IPs_key Primary key, IP varchar2(15) NOT NULL , MAC varchar2(12) , Interface varchar2(25) DEFAULT 0 , Asset number(11,0) DEFAULT 0 NOT NULL , Creator number(11,0) DEFAULT 0 NOT NULL , Created DATE , LastUpdatedBy number(11,0) DEFAULT 0 NOT NULL , LastUpdated DATE ); CREATE UNIQUE INDEX AT_IPs1 ON AT_IPs (IP) ; CREATE INDEX AT_IPs2 ON AT_IPs (Asset) ; # }}} # {{{ AT_Ports CREATE SEQUENCE AT_Ports_seq; CREATE TABLE AT_Ports ( id Number(11,0) constraint At_Ports_key Primary key, Transport varchar2(15) NOT NULL , Port varchar2(12) , IP number(11,0) DEFAULT 0 NOT NULL , Creator number(11,0) DEFAULT 0 NOT NULL , Created DATE , LastUpdatedBy number(11,0) DEFAULT 0 NOT NULL , LastUpdated DATE ); CREATE INDEX AT_Ports1 ON AT_Ports (Port) ; CREATE INDEX AT_Ports2 ON AT_Ports (Transport,Port) ; CREATE INDEX AT_Ports3 ON AT_Ports (IP) ; # }}} CREATE SEQUENCE AT_SCRIPCONDITIONS_seq; CREATE TABLE AT_ScripConditions ( id NUMBER(11, 0) CONSTRAINT AT_ScripConditions_Key PRIMARY KEY, Name VARCHAR2(200), Description VARCHAR2(255), ExecModule VARCHAR2(60), Argument VARCHAR2(255), ApplicableTransTypes VARCHAR2(60), Creator NUMBER(11,0) DEFAULT 0 NOT NULL, Created DATE, LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL, LastUpdated DATE ); CREATE SEQUENCE AT_SCRIPS_seq; CREATE TABLE AT_Scrips ( id NUMBER(11,0) CONSTRAINT AT_Scrips_Key PRIMARY KEY, Description VARCHAR2(255), ScripCondition NUMBER(11,0) DEFAULT 0 NOT NULL, ScripAction NUMBER(11,0) DEFAULT 0 NOT NULL, ConditionRules CLOB, ActionRules CLOB, CustomIsApplicableCode CLOB, CustomPrepareCode CLOB, CustomCommitCode CLOB, Stage VARCHAR2(32), AssetType NUMBER(11,0) DEFAULT 0 NOT NULL, Template NUMBER(11,0) DEFAULT 0 NOT NULL, Creator NUMBER(11,0) DEFAULT 0 NOT NULL, Created DATE, LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL, LastUpdated DATE ); CREATE SEQUENCE AT_SCRIPACTIONS_seq; CREATE TABLE AT_ScripActions ( id NUMBER(11,0) CONSTRAINT AT_ScripActions_Key PRIMARY KEY, Name VARCHAR2(200), Description VARCHAR2(255), ExecModule VARCHAR2(60), Argument VARCHAR2(255), Creator NUMBER(11,0) DEFAULT 0 NOT NULL, Created DATE, LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL, LastUpdated DATE ); CREATE SEQUENCE AT_TEMPLATES_seq; CREATE TABLE AT_Templates ( id NUMBER(11,0) CONSTRAINT AT_Templates_Key PRIMARY KEY, AssetType NUMBER(11,0) DEFAULT 0 NOT NULL, Name VARCHAR2(200) NOT NULL, Description VARCHAR2(255), Type VARCHAR2(16), Language VARCHAR2(16), TranslationOf NUMBER(11,0) DEFAULT 0 NOT NULL, Content CLOB, LastUpdated DATE, LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL, Creator NUMBER(11,0) DEFAULT 0 NOT NULL, Created DATE ); rt-extension-assettracker-3.0.0/etc/schema.Pg000066400000000000000000000101771222742774700211710ustar00rootroot00000000000000# Courtesy of Rolf Schaufelberger # {{{ AT_Types CREATE TABLE AT_Types ( id SERIAL, Name varchar(200) NOT NULL , Description varchar(255) NULL , DefaultAdmin integer NULL DEFAULT 0 , Lifecycle varchar(32) NULL , Creator integer NOT NULL DEFAULT 0 , Created timestamp NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated timestamp NULL , Disabled integer NOT NULL DEFAULT 0 , PRIMARY KEY (id) ) ; CREATE UNIQUE INDEX AT_Types1 ON AT_Types (Name) ; CREATE INDEX AT_Types2 ON AT_Types (Disabled) ; # }}} # {{{ Assets CREATE TABLE AT_Assets ( id SERIAL, Type integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Status varchar(64) NULL , URI varchar(255), LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated timestamp NULL , Creator integer NOT NULL DEFAULT 0 , Created timestamp NULL , PRIMARY KEY (id) ) ; CREATE INDEX AT_Assets1 ON AT_Assets (Type, Status) ; CREATE INDEX AT_Assets2 ON AT_Assets (Name, Type, Status) ; # }}} # {{{ AT_IPs CREATE TABLE AT_IPs ( id SERIAL, IP char(15) NOT NULL , MAC char(12) NULL , Interface varchar(25) NULL DEFAULT 0 , Asset integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created timestamp NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated timestamp NULL , PRIMARY KEY (id) ) ; CREATE UNIQUE INDEX AT_IPs1 ON AT_IPs (IP) ; CREATE INDEX AT_IPs2 ON AT_IPs (Asset) ; # }}} # {{{ AT_Ports CREATE TABLE AT_Ports ( id SERIAL, Transport char(15) NOT NULL , Port char(12) NULL , IP integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created timestamp NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated timestamp NULL , PRIMARY KEY (id) ) ; CREATE INDEX AT_Ports1 ON AT_Ports (Port) ; CREATE INDEX AT_Ports2 ON AT_Ports (Transport,Port) ; CREATE INDEX AT_Ports3 ON AT_Ports (IP) ; # }}} -- -- Sequences for table SCRIPCONDITIONS -- CREATE SEQUENCE at_scripconditions_id_seq; CREATE TABLE AT_ScripConditions ( id INTEGER DEFAULT nextval('at_scripconditions_id_seq'), Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) NULL , Argument varchar(255) NULL , ApplicableTransTypes varchar(60) NULL , Creator integer NOT NULL DEFAULT 0 , Created TIMESTAMP NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated TIMESTAMP NULL , PRIMARY KEY (id) ); -- -- Sequences for table SCRIPS -- CREATE SEQUENCE at_scrips_id_seq; CREATE TABLE AT_Scrips ( id INTEGER DEFAULT nextval('at_scrips_id_seq'), Description varchar(255), ScripCondition integer NOT NULL DEFAULT 0 , ScripAction integer NOT NULL DEFAULT 0 , ConditionRules text NULL , ActionRules text NULL , CustomIsApplicableCode text NULL , CustomPrepareCode text NULL , CustomCommitCode text NULL , Stage varchar(32) NULL , AssetType integer NOT NULL DEFAULT 0 , Template integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created TIMESTAMP NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated TIMESTAMP NULL , PRIMARY KEY (id) ); -- -- Sequences for table SCRIPACTIONS -- CREATE SEQUENCE at_scripactions_id_seq; CREATE TABLE AT_ScripActions ( id INTEGER DEFAULT nextval('at_scripactions_id_seq'), Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) NULL , Argument varchar(255) NULL , Creator integer NOT NULL DEFAULT 0 , Created TIMESTAMP NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated TIMESTAMP NULL , PRIMARY KEY (id) ); -- -- Sequences for table TEMPLATES -- CREATE SEQUENCE at_templates_id_seq; CREATE TABLE AT_Templates ( id INTEGER DEFAULT nextval('at_templates_id_seq'), AssetType integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Type varchar(16) NULL , Language varchar(16) NULL , TranslationOf integer NOT NULL DEFAULT 0 , Content text NULL , LastUpdated TIMESTAMP NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created TIMESTAMP NULL , PRIMARY KEY (id) ); rt-extension-assettracker-3.0.0/etc/schema.SQLite000066400000000000000000000067771222742774700217770ustar00rootroot00000000000000# {{{ AT_Types CREATE TABLE AT_Types ( id INTEGER PRIMARY KEY , Name varchar(200) NOT NULL , Description varchar(255) NULL , DefaultAdmin integer NULL DEFAULT 0 , Lifecycle varchar(32) NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , Disabled int2 NOT NULL DEFAULT 0 ) ; CREATE UNIQUE INDEX AT_Types1 ON AT_Types (Name) ; CREATE INDEX AT_Types2 ON AT_Types (Disabled) ; # }}} # {{{ Assets CREATE TABLE AT_Assets ( id INTEGER PRIMARY KEY , Type integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Status varchar(64) NULL , URI varchar(255), LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL ) ; CREATE INDEX AT_Assets1 ON AT_Assets (Type, Status) ; CREATE INDEX AT_Assets2 ON AT_Assets (Name, Type, Status) ; # }}} # {{{ AT_IPs CREATE TABLE AT_IPs ( id INTEGER PRIMARY KEY , IP char(15) NOT NULL , MAC char(12) NULL , Interface varchar(25) NULL DEFAULT 0 , Asset integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL ) ; CREATE UNIQUE INDEX AT_IPs1 ON AT_IPs (IP) ; CREATE INDEX AT_IPs2 ON AT_IPs (Asset) ; # }}} # {{{ AT_Ports CREATE TABLE AT_Ports ( id INTEGER PRIMARY KEY , Transport char(15) NOT NULL , Port char(12) NULL , IP integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL ) ; CREATE INDEX AT_Ports1 ON AT_Ports (Port) ; CREATE UNIQUE INDEX AT_Ports2 ON AT_Ports (Transport,Port,IP) ; CREATE INDEX AT_Ports3 ON AT_Ports (IP) ; # }}} --- {{{ ScripConditions CREATE TABLE AT_ScripConditions ( id INTEGER PRIMARY KEY , Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) NULL , Argument varchar(255) NULL , ApplicableTransTypes varchar(60) NULL , Creator integer NULL , Created DATETIME NULL , LastUpdatedBy integer NULL , LastUpdated DATETIME NULL ) ; --- }}} --- {{{ Scrips CREATE TABLE AT_Scrips ( id INTEGER PRIMARY KEY , Description varchar(255), ScripCondition integer NULL , ScripAction integer NULL , ConditionRules text NULL , ActionRules text NULL , CustomIsApplicableCode text NULL , CustomPrepareCode text NULL , CustomCommitCode text NULL , Stage varchar(32) NULL , AssetType integer NULL , Template integer NULL , Creator integer NULL , Created DATETIME NULL , LastUpdatedBy integer NULL , LastUpdated DATETIME NULL ) ; --- }}} --- {{{ ScripActions CREATE TABLE AT_ScripActions ( id INTEGER PRIMARY KEY , Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) NULL , Argument varchar(255) NULL , Creator integer NULL , Created DATETIME NULL , LastUpdatedBy integer NULL , LastUpdated DATETIME NULL ) ; --- }}} --- {{{ Templates CREATE TABLE AT_Templates ( id INTEGER PRIMARY KEY , AssetType integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Type varchar(16) NULL , Language varchar(16) NULL , TranslationOf integer NULL , Content blob NULL , LastUpdated DATETIME NULL , LastUpdatedBy integer NULL , Creator integer NULL , Created DATETIME NULL ) ; --- }}} rt-extension-assettracker-3.0.0/etc/schema.mysql000066400000000000000000000103221222742774700217600ustar00rootroot00000000000000# {{{ AT_Types CREATE TABLE AT_Types ( id INTEGER NOT NULL AUTO_INCREMENT, Name varchar(200) NOT NULL , Description varchar(255) NULL , DefaultAdmin integer NULL DEFAULT 0 , Lifecycle varchar(32) CHARACTER SET ascii NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , Disabled int2 NOT NULL DEFAULT 0 , PRIMARY KEY (id) ) ENGINE=InnoDB; CREATE UNIQUE INDEX AT_Types1 ON AT_Types (Name) ; CREATE INDEX AT_Types2 ON AT_Types (Disabled) ; # }}} # {{{ Assets CREATE TABLE AT_Assets ( id INTEGER NOT NULL AUTO_INCREMENT, Type integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Status varchar(64) NULL , URI varchar(255), LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , PRIMARY KEY (id) ) ENGINE=InnoDB; CREATE INDEX AT_Assets1 ON AT_Assets (Type, Status) ; CREATE INDEX AT_Assets2 ON AT_Assets (Name, Type, Status) ; # }}} # {{{ AT_IPs CREATE TABLE AT_IPs ( id INTEGER NOT NULL AUTO_INCREMENT, IP char(15) NOT NULL , MAC char(12) NULL , Interface varchar(25) NULL DEFAULT 0 , Asset integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , PRIMARY KEY (id) ) ENGINE=InnoDB; CREATE UNIQUE INDEX AT_IPs1 ON AT_IPs (IP) ; CREATE INDEX AT_IPs2 ON AT_IPs (Asset) ; # }}} # {{{ AT_Ports CREATE TABLE AT_Ports ( id INTEGER NOT NULL AUTO_INCREMENT, Transport char(15) NOT NULL , Port char(12) NULL , IP integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , PRIMARY KEY (id) ) ENGINE=InnoDB; CREATE INDEX AT_Ports1 ON AT_Ports (Port) ; CREATE UNIQUE INDEX AT_Ports2 ON AT_Ports (Transport,Port,IP) ; CREATE INDEX AT_Ports3 ON AT_Ports (IP) ; # }}} # {{{ ScripConditions CREATE TABLE AT_ScripConditions ( id INTEGER NOT NULL AUTO_INCREMENT, Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) CHARACTER SET ascii NULL, Argument VARBINARY(255) NULL , ApplicableTransTypes varchar(60) CHARACTER SET ascii NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , PRIMARY KEY (id) ) ENGINE=InnoDB CHARACTER SET utf8; # }}} # {{{ Scrips CREATE TABLE AT_Scrips ( id INTEGER NOT NULL AUTO_INCREMENT, Description varchar(255), ScripCondition integer NOT NULL DEFAULT 0 , ScripAction integer NOT NULL DEFAULT 0 , ConditionRules text NULL , ActionRules text NULL , CustomIsApplicableCode text NULL , CustomPrepareCode text NULL , CustomCommitCode text NULL , Stage varchar(32) CHARACTER SET ascii NULL , AssetType integer NOT NULL DEFAULT 0 , Template integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , PRIMARY KEY (id) ) ENGINE=InnoDB CHARACTER SET utf8; # }}} # {{{ ScripActions CREATE TABLE AT_ScripActions ( id INTEGER NOT NULL AUTO_INCREMENT, Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) CHARACTER SET ascii NULL, Argument VARBINARY(255) NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , PRIMARY KEY (id) ) ENGINE=InnoDB CHARACTER SET utf8; # }}} # {{{ Templates CREATE TABLE AT_Templates ( id INTEGER NOT NULL AUTO_INCREMENT, AssetType integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Type varchar(16) CHARACTER SET ascii NULL , Language varchar(16) CHARACTER SET ascii NULL , TranslationOf integer NOT NULL DEFAULT 0 , Content TEXT NULL , LastUpdated DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , PRIMARY KEY (id) ) ENGINE=InnoDB CHARACTER SET utf8; # }}} rt-extension-assettracker-3.0.0/etc/upgrade/000077500000000000000000000000001222742774700210625ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/etc/upgrade/1.2.4/000077500000000000000000000000001222742774700215245ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/etc/upgrade/1.2.4/content000066400000000000000000000035621222742774700231270ustar00rootroot00000000000000# Initial data for a fresh AT Installation. use RTx::AssetTracker; use RTx::AssetTracker::ScripCondition; use RTx::AssetTracker::ScripAction; use RTx::AssetTracker::Template; @Final = ( \&AssetScripConditions, \&AssetScripActions, \&AssetTemplates, ); my $CurrentUser = RT::CurrentUser->new(); $CurrentUser->LoadByName('RT_System'); sub AssetScripConditions { $DB::single=1; print "Creating default asset scrips conditions..."; my @Conditions = ( { Name => 'User Defined', Description => 'Whenever a user-defined condition occurs', ExecModule => 'UserDefined', Argument => undef, ApplicableTransTypes => 'Any' }, ); for $item (@Conditions) { my $new_entry = new RTx::AssetTracker::ScripCondition($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } sub AssetScripActions { print "Creating default asset scrips actions..."; my @Conditions = ( { Name => 'User Defined', Description => 'Perform a user-defined action', ExecModule => 'UserDefined', Argument => undef }, ); for $item (@Conditions) { my $new_entry = new RTx::AssetTracker::ScripAction($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } sub AssetTemplates { print "Creating default asset templates..."; my @Conditions = ( { AssetType => 0, Name => 'Blank', Description => 'A blank template', Content => undef }, ); for $item (@Conditions) { my $new_entry = new RTx::AssetTracker::Template($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } rt-extension-assettracker-3.0.0/etc/upgrade/1.2.4/schema.SQLite000066400000000000000000000032401222742774700240460ustar00rootroot00000000000000--- {{{ ScripConditions CREATE TABLE AT_ScripConditions ( id INTEGER PRIMARY KEY , Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) NULL , Argument varchar(255) NULL , ApplicableTransTypes varchar(60) NULL , Creator integer NULL , Created DATETIME NULL , LastUpdatedBy integer NULL , LastUpdated DATETIME NULL ) ; --- }}} --- {{{ Scrips CREATE TABLE AT_Scrips ( id INTEGER PRIMARY KEY , Description varchar(255), ScripCondition integer NULL , ScripAction integer NULL , ConditionRules text NULL , ActionRules text NULL , CustomIsApplicableCode text NULL , CustomPrepareCode text NULL , CustomCommitCode text NULL , Stage varchar(32) NULL , AssetType integer NULL , Template integer NULL , Creator integer NULL , Created DATETIME NULL , LastUpdatedBy integer NULL , LastUpdated DATETIME NULL ) ; --- }}} --- {{{ ScripActions CREATE TABLE AT_ScripActions ( id INTEGER PRIMARY KEY , Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) NULL , Argument varchar(255) NULL , Creator integer NULL , Created DATETIME NULL , LastUpdatedBy integer NULL , LastUpdated DATETIME NULL ) ; --- }}} --- {{{ Templates CREATE TABLE AT_Templates ( id INTEGER PRIMARY KEY , AssetType integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Type varchar(16) NULL , Language varchar(16) NULL , TranslationOf integer NULL , Content blob NULL , LastUpdated DATETIME NULL , LastUpdatedBy integer NULL , Creator integer NULL , Created DATETIME NULL ) ; --- }}} rt-extension-assettracker-3.0.0/etc/upgrade/1.2.4/schema.mysql000066400000000000000000000042471222742774700240620ustar00rootroot00000000000000 # {{{ ScripConditions CREATE TABLE AT_ScripConditions ( id INTEGER NOT NULL AUTO_INCREMENT, Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) CHARACTER SET ascii NULL, Argument VARBINARY(255) NULL , ApplicableTransTypes varchar(60) CHARACTER SET ascii NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , PRIMARY KEY (id) ) TYPE=InnoDB CHARACTER SET utf8; # }}} # {{{ Scrips CREATE TABLE AT_Scrips ( id INTEGER NOT NULL AUTO_INCREMENT, Description varchar(255), ScripCondition integer NOT NULL DEFAULT 0 , ScripAction integer NOT NULL DEFAULT 0 , ConditionRules text NULL , ActionRules text NULL , CustomIsApplicableCode text NULL , CustomPrepareCode text NULL , CustomCommitCode text NULL , Stage varchar(32) CHARACTER SET ascii NULL , AssetType integer NOT NULL DEFAULT 0 , Template integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , PRIMARY KEY (id) ) TYPE=InnoDB CHARACTER SET utf8; # }}} # {{{ ScripActions CREATE TABLE AT_ScripActions ( id INTEGER NOT NULL AUTO_INCREMENT, Name varchar(200) NULL , Description varchar(255) NULL , ExecModule varchar(60) CHARACTER SET ascii NULL, Argument VARBINARY(255) NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , PRIMARY KEY (id) ) TYPE=InnoDB CHARACTER SET utf8; # }}} # {{{ Templates CREATE TABLE AT_Templates ( id INTEGER NOT NULL AUTO_INCREMENT, AssetType integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Type varchar(16) CHARACTER SET ascii NULL , Language varchar(16) CHARACTER SET ascii NULL , TranslationOf integer NOT NULL DEFAULT 0 , Content TEXT NULL , LastUpdated DATETIME NULL , LastUpdatedBy integer NOT NULL DEFAULT 0 , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL , PRIMARY KEY (id) ) TYPE=InnoDB CHARACTER SET utf8; # }}} rt-extension-assettracker-3.0.0/etc/upgrade/2.9.0/000077500000000000000000000000001222742774700215305ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/etc/upgrade/2.9.0/content000066400000000000000000000033221222742774700231250ustar00rootroot00000000000000use RTx::AssetTracker::Types; @Initial = ( sub { use strict; $RT::Logger->debug( 'Going to update empty Type Lifecycle column to "at_default"'); my $types = RTx::AssetTracker::Types->new( RT->SystemUser ); $types->FindAllRows; $types->Limit( FIELD => 'Lifecycle', OPERATOR => 'IS', VALUE => 'NULL', ); $types->Limit( FIELD => 'Lifecycle', VALUE => '', ENTRYAGGREGATOR => 'OR', ); $types->Limit( FIELD => 'Lifecycle', VALUE => 0, ENTRYAGGREGATOR => 'OR', ); while ( my $q = $types->Next ) { $q->SetLifecycle('at_default'); } }, sub { use strict; $RT::Logger->debug( 'Going to update global rights from RTx::AssetTracker::System to RT::System'); my $acl = RT::ACL->new( RT->SystemUser ); $acl->FindAllRows; $acl->Limit( FIELD => 'ObjectType', OPERATOR => '=', VALUE => 'RTx::AssetTracker::System', ); while ( my $ace = $acl->Next ) { my $new_ace = RT::ACE->new( RT->SystemUser ); my ( $ok, $msg ) = $new_ace->Create( PrincipalId => $ace->__Value('PrincipalId'), PrincipalType => $ace->__Value('PrincipalType'), RightName => $ace->__Value('RightName'), Object => $RT::System, ); if ( !$ok ) { $RT::Logger->warn( "Unable to create updated ACE " . $ace->id . ": " . $msg); } } }, ); rt-extension-assettracker-3.0.0/etc/upgrade/2.9.0/schema.Oracle000066400000000000000000000001501222742774700241130ustar00rootroot00000000000000ALTER TABLE AT_Types ADD Lifecycle VARCHAR2(32) NULL; ALTER TABLE AT_Assets MODIFY Status VARCHAR2(64); rt-extension-assettracker-3.0.0/etc/upgrade/2.9.0/schema.Pg000066400000000000000000000001611222742774700232560ustar00rootroot00000000000000ALTER TABLE AT_Types ADD COLUMN Lifecycle VARCHAR(32) NULL; ALTER TABLE AT_Assets ALTER Status TYPE varchar(64); rt-extension-assettracker-3.0.0/etc/upgrade/2.9.0/schema.SQLite000066400000000000000000000020741222742774700240560ustar00rootroot00000000000000ALTER TABLE AT_Types ADD COLUMN Lifecycle VARCHAR(32) NULL; BEGIN TRANSACTION; CREATE TEMPORARY TABLE AT_Assets_backup ( id INTEGER PRIMARY KEY , Type integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Status varchar(64) NULL , URI varchar(255), LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL ); INSERT INTO AT_Assets_backup SELECT * FROM AT_Assets; DROP TABLE AT_Assets; CREATE TABLE AT_Assets ( id INTEGER PRIMARY KEY , Type integer NOT NULL DEFAULT 0 , Name varchar(200) NOT NULL , Description varchar(255) NULL , Status varchar(64) NULL , URI varchar(255), LastUpdatedBy integer NOT NULL DEFAULT 0 , LastUpdated DATETIME NULL , Creator integer NOT NULL DEFAULT 0 , Created DATETIME NULL ); CREATE INDEX AT_Assets1 ON AT_Assets (Type, Status) ; CREATE INDEX AT_Assets2 ON AT_Assets (Name, Type, Status) ; INSERT INTO AT_Assets SELECT * FROM AT_Assets_backup; DROP TABLE AT_Assets_backup; COMMIT; rt-extension-assettracker-3.0.0/etc/upgrade/2.9.0/schema.mysql000066400000000000000000000000741222742774700240600ustar00rootroot00000000000000ALTER TABLE AT_Types ADD COLUMN Lifecycle VARCHAR(32) NULL; rt-extension-assettracker-3.0.0/html/000077500000000000000000000000001222742774700176245ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Admin/000077500000000000000000000000001222742774700206545ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Admin/Tools/000077500000000000000000000000001222742774700217545ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Admin/Tools/Shredder/000077500000000000000000000000001222742774700235145ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Admin/Tools/Shredder/Elements/000077500000000000000000000000001222742774700252705ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Admin/Tools/Shredder/Elements/Object/000077500000000000000000000000001222742774700264765ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Admin/Tools/Shredder/Elements/Object/RTx--AssetTracker--Asset000066400000000000000000000043521222742774700327420ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Object => undef <% loc('Asset') %>(<% loc('id') %>:<% $Object->id %>, <% loc('Name') %>: <% substr($Object->Name, 0, 30) %>...) rt-extension-assettracker-3.0.0/html/AssetTracker/000077500000000000000000000000001222742774700222175ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/000077500000000000000000000000001222742774700232475ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/000077500000000000000000000000001222742774700250235ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/EditScrip000066400000000000000000000156061222742774700266440ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/ListActions, actions => \@actions &>
<&| /Widgets/TitleBox, title => loc('Scrip Fields') &>
<&|/l&>Description:\ Description || '' %>" />
<&|/l&>Condition:\ <& /AssetTracker/Admin/Elements/SelectScripCondition, Name => "Scrip-$id-ScripCondition", Default => $ARGS{"Scrip-$id-ScripCondition"} || $scrip->ConditionObj->Id, &>
<&|/l&>Action:\ <& /AssetTracker/Admin/Elements/SelectScripAction, Name => "Scrip-$id-ScripAction", Default => $ARGS{"Scrip-$id-ScripAction"} || $scrip->ActionObj->Id, &>
<&|/l&>Template:\ <& /AssetTracker/Admin/Elements/SelectTemplate, Name => "Scrip-$id-Template", Default => $ARGS{"Scrip-$id-Template"} || $scrip->TemplateObj->Id, AssetType => $AssetType, &>
<&|/l&>Stage:\ <& /Admin/Elements/SelectStage, Name => "Scrip-$id-Stage", Default => $ARGS{"Scrip-$id-Stage"} || $scrip->Stage, &>
% if ($session{CurrentUser}->HasRight(Object => $RT::System, Right => 'ExecuteCode')) { <& /Elements/Submit, Label => $SubmitLabel, Reset => 1, &>
<&| /Widgets/TitleBox, title => loc('User Defined conditions and actions') &>
<&|/l&>(Use these fields when you choose 'User Defined' for a condition or action)
<&|/l&>Custom condition: % my $code = $ARGS{"Scrip-$id-CustomIsApplicableCode"} || $scrip->CustomIsApplicableCode || ''; % my $lines = @{[ $code =~ /\n/gs ]} + 3; % $lines = $min_lines if $lines < $min_lines;
<&|/l&>Custom action preparation code: % $code = $ARGS{"Scrip-$id-CustomPrepareCode"} || $scrip->CustomPrepareCode || ''; % $lines = @{[ $code =~ /\n/gs ]} + 3; % $lines = $min_lines if $lines < $min_lines;
<&|/l&>Custom action cleanup code: % $code = $ARGS{"Scrip-$id-CustomCommitCode"} || $scrip->CustomCommitCode || ''; % $lines = @{[ $code =~ /\n/gs ]} + 3; % $lines = $min_lines if $lines < $min_lines;
% } <& /Elements/Submit, Label => $SubmitLabel, Reset => 1, &>
<%init> my (@actions, $SubmitLabel); my $scrip = RTx::AssetTracker::Scrip->new( $session{'CurrentUser'} ); if ( $id ) { $scrip->Load( $id ); unless ( $id = $scrip->id ) { push @actions, loc("Couldn't load scrip #[_1]", $id); } $SubmitLabel = loc('Save Changes'); } unless ( $id ) { $id = 'new'; $SubmitLabel = loc('Create'); } my $min_lines = 10; my ($ok, $msg) = $scrip->CompileCheck; push @actions, $msg if !$ok; <%ARGS> $id => undef $title => undef $AssetType => 0 <%METHOD Process> <%ARGS> $id => undef $AssetType => undef <%INIT> return ($id) unless $id; my $scrip = RTx::AssetTracker::Scrip->new( $session{'CurrentUser'} ); if ( $id eq 'new' ) { return $scrip->Create( AssetType => $AssetType, ScripAction => $ARGS{"Scrip-new-ScripAction"}, ScripCondition => $ARGS{"Scrip-new-ScripCondition"}, Template => $ARGS{"Scrip-new-Template"}, Description => $ARGS{"Scrip-new-Description"}, CustomPrepareCode => $ARGS{"Scrip-new-CustomPrepareCode"}, CustomCommitCode => $ARGS{"Scrip-new-CustomCommitCode"}, CustomIsApplicableCode => $ARGS{"Scrip-new-CustomIsApplicableCode"}, Stage => $ARGS{"Scrip-new-Stage"}, ); } else { $scrip->Load( $id ); return (undef, loc("Couldn't load scrip #[_1]", $id)) unless $scrip->id; my @attribs = qw(AssetType ScripAction ScripCondition Template Stage Description CustomPrepareCode CustomCommitCode CustomIsApplicableCode); my @results = UpdateRecordObject( AttributesRef => \@attribs, AttributePrefix => 'Scrip-'.$scrip->Id, Object => $scrip, ARGSRef => \%ARGS ); return ($scrip->id, @results); } rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/EditScrips000066400000000000000000000075001222742774700270210ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/ListActions, actions => \@actions &>

<&|/l&>Current Scrips

<& /Elements/CollectionList, OrderBy => 'Description', Order => 'ASC', Rows => 100, %ARGS, Format => $Format, DisplayFormat => "__CheckBox.{DeleteScrip}__, $Format", Collection => $Scrips, AllowSorting => 1, PassArguments => [ qw(Query Format Rows Page Order OrderBy id) ], &> % if ( $Scrips->Count ) {

<&|/l&>(Check box to delete)

% } else {

<&|/l&>(No scrips)

% } <& /Elements/Submit, Caption => loc("Delete selected scrips"), Label => loc("Delete") &>
<%init> my (@actions); my $Scrips = RTx::AssetTracker::Scrips->new($session{'CurrentUser'}); my $AssetTypeObj = RTx::AssetTracker::Type->new($session{'CurrentUser'}); if ( $id ) { $AssetTypeObj->Load( $id ); unless ( $AssetTypeObj->id ) { push @actions, loc("Couldn't load asset type #[_1]", $id) } } if ($AssetTypeObj->id) { $Scrips->LimitToAssetType($id); $Format ||= RT->Config->Get('AdminSearchResultFormat')->{'AT_Scrips'}; } else { $Scrips->LimitToGlobal(); $Format ||= RT->Config->Get('AdminSearchResultFormat')->{'AT_GlobalScrips'}; } # deal with modifying and deleting existing scrips # we still support DeleteScrip-id format but array is preferred foreach my $id ( grep $_, @DeleteScrip, map /^DeleteScrip-(\d+)/, keys %ARGS ) { my $scrip = RTx::AssetTracker::Scrip->new($session{'CurrentUser'}); $scrip->Load( $id ); my ($retval, $msg) = $scrip->Delete; if ($retval) { push @actions, loc("Scrip deleted"); } else { push @actions, $msg; } } <%ARGS> $id => undef $title => undef $Format => undef @DeleteScrip => () rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/EditTemplates000066400000000000000000000075721222742774700275250ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/ListActions, actions => \@actions &>
% unless ( $Templates->Count ) {

<&|/l&>(No templates)

% } else { <& /Elements/CollectionList, OrderBy => 'id', Order => 'ASC', %ARGS, DisplayFormat => '__CheckBox.{DeleteTemplates}__,'. $Format, Format => $Format, Collection => $Templates, AllowSorting => 1, PassArguments => [qw(Format Rows Page Order OrderBy FindDisabledTypes)], &> <&|/l&>(Check box to delete) % } <& /Elements/Submit, Label => loc('Delete Template') &>
<%INIT> $Format ||= RT->Config->Get('AdminSearchResultFormat')->{'AT_Templates'}; my $TypeObj = RTx::AssetTracker::Type->new( $session{'CurrentUser'} ); $TypeObj->Load( $id ) if $id; my $Templates = RTx::AssetTracker::Templates->new($session{'CurrentUser'}); if ( $TypeObj->id ) { $Templates->LimitToAssetType( $id ); } else { $Templates->LimitToGlobal; } # Now let callbacks add their extra limits $m->callback( %ARGS, Templates => $Templates ); $Templates->RedoSearch; # deal with deleting existing templates my @actions; # backwards compatibility, use DeleteTemplates array for this foreach my $key (keys %ARGS) { next unless $key =~ /^DeleteTemplate-(\d+)/; push @DeleteTemplates, $1; } foreach my $id( @DeleteTemplates ) { my $TemplateObj = RTx::AssetTracker::Template->new( $session{'CurrentUser'} ); $TemplateObj->Load( $id ); unless ( $TemplateObj->id ) { push @actions, loc("Couldn't load template #[_1]", $id); next; } my ($retval, $msg) = $TemplateObj->Delete; if ( $retval ) { push @actions, loc("Template #[_1] deleted", $id); } else { push @actions, $msg; } } <%ARGS> $id => 0 $Format => undef @DeleteTemplates => () rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/EditTypeWatcherGroup000066400000000000000000000042321222742774700310310ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Label $TypeObj $Watchers <% $Label %>: <& /AssetTracker/Admin/Elements/EditTypeWatchers, TypeObj => $TypeObj, Watchers => $Watchers &> rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/EditTypeWatchers000066400000000000000000000054751222742774700302110ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %if ($Members->Count == 0 ) {
  • <&|/l&>none % } else { <&|/l&>(Check box to delete)

    <%INIT> my $Members = $Watchers->MembersObj; <%ARGS> $QueueObj => undef $Watchers => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/ListGlobalScrips000066400000000000000000000046461222742774700302000ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} % unless ( $Scrips->Count ) {

    <&|/l&>(No scrips)

    % } else { <& /Elements/CollectionList, OrderBy => 'Description', Order => 'ASC', Rows => 0, %ARGS, Format => $Format, Collection => $Scrips, &> % } <%init> my $Format = RT->Config->Get('AdminSearchResultFormat')->{'AT_GlobalScrips'}; my $Scrips = RTx::AssetTracker::Scrips->new( $session{'CurrentUser'} ); $Scrips->LimitToGlobal; rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/SelectScripAction000066400000000000000000000055011222742774700303250ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my $ScripActions = RTx::AssetTracker::ScripActions->new($session{'CurrentUser'}); # hide user-defined if the user can't execute code if ($session{CurrentUser}->HasRight(Object => $RT::System, Right => 'ExecuteCode')) { $ScripActions->UnLimit; } else { $ScripActions->Limit( FIELD => 'ExecModule', OPERATOR => '!=', VALUE => 'UserDefined', ); } $ScripActions->OrderBy(FIELD => 'Name'); <%ARGS> $Default => undef $Name => 'ScripAction' rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/SelectScripCondition000066400000000000000000000055341222742774700310440ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my $ScripConditions = RTx::AssetTracker::ScripConditions->new($session{'CurrentUser'}); # hide user-defined if the user can't execute code if ($session{CurrentUser}->HasRight(Object => $RT::System, Right => 'ExecuteCode')) { $ScripConditions->UnLimit; } else { $ScripConditions->Limit( FIELD => 'ExecModule', OPERATOR => '!=', VALUE => 'UserDefined', ); } $ScripConditions->OrderBy(FIELD => 'Name'); <%ARGS> $Default => undef $Name => 'ScripCondition' rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Elements/SelectTemplate000066400000000000000000000057621222742774700276730ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my $PrimaryTemplates = RTx::AssetTracker::Templates->new($session{'CurrentUser'}); if ($AssetType != 0) { $PrimaryTemplates->LimitToAssetType($AssetType); $PrimaryTemplates->OrderBy(FIELD => 'Name'); } my $OtherTemplates = RTx::AssetTracker::Templates->new($session{'CurrentUser'}); $OtherTemplates->LimitToGlobal; $OtherTemplates->OrderBy(FIELD => 'Name'); <%ARGS> $AssetType => undef $Default => 'none' $Name => 'Template' rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Global/000077500000000000000000000000001222742774700244475ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Global/CustomFields/000077500000000000000000000000001222742774700270505ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Global/CustomFields/Type-Assets.html000066400000000000000000000045671222742774700321330ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Admin/Elements/EditCustomFields, %ARGS, title => $title, ObjectType => 'RTx::AssetTracker::Type', Object=> $object, SubType => 'RTx::AssetTracker::Asset' &> <%INIT> my $title = loc( 'Edit Custom Fields for assets of all types'); my $object = RTx::AssetTracker::Type->new($session{'CurrentUser'}); rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Global/CustomFields/Types.html000066400000000000000000000045141222742774700310460ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Admin/Elements/EditCustomFields, %ARGS, title => $title, ObjectType => 'RTx::AssetTracker::Type', Object=> $object &> <%INIT> my $title = loc( 'Edit Custom Fields for all asset types'); my $object = RTx::AssetTracker::Type->new($session{'CurrentUser'}); rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Global/Scrip.html000066400000000000000000000047231222742774700264230ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &> <& /AssetTracker/Admin/Elements/EditScrip, title => $title, %ARGS, id => $id &> <%init> my ($id, @results) = $m->comp( '/AssetTracker/Admin/Elements/EditScrip:Process', %ARGS ); my ($title); if ( $id ) { $title = loc("Modify a scrip that applies to all asset types"); } else { $title = loc("Add a scrip which will apply to all asset types"); } rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Global/Scrips.html000066400000000000000000000044121222742774700266010ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /AssetTracker/Admin/Elements/EditScrips, title => $title, id => $id, %ARGS &> <%init> my $title = loc("Modify scrips which apply to all asset types"); my (@actions); <%ARGS> $id => 0 rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Global/Template.html000066400000000000000000000074761222742774700271260ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>
    %if ($Create ) { % } else { % } %# hang onto the type id <& /Admin/Elements/ModifyTemplate, Name => $TemplateObj->Name, Description => $TemplateObj->Description, Content => $TemplateObj->Content, Type => $TemplateObj->Type &> <& /Elements/Submit, Label => $SubmitLabel, Reset => 1 &>
    <%INIT> my $TemplateObj = RTx::AssetTracker::Template->new($session{'CurrentUser'}); my ($title, @results, $SubmitLabel); if ($Create) { $title = loc("Create a template"); $SubmitLabel = loc('Create'); } else { if (defined ($Template) && $Template eq 'new') { my ($val, $msg) = $TemplateObj->Create(AssetType => $AssetType, Name => $Name, Type => $Type); Abort(loc("Could not create template: [_1]", $msg)) unless ($val); push @results, $msg; } else { $TemplateObj->Load($Template) || Abort(loc('No Template')); } $title = loc('Modify template [_1]', loc($TemplateObj->Name())); $SubmitLabel = loc('Save Changes'); } if ($TemplateObj->Id()) { my @attribs = qw( Name Description AssetType Type Content ); my @aresults = UpdateRecordObject( AttributesRef => \@attribs, Object => $TemplateObj, ARGSRef => \%ARGS); push @results, @aresults; my ($ok, $msg) = $TemplateObj->CompileCheck; push @results, $msg if !$ok; } <%ARGS> $AssetType => '' $Template => '' $Create => '' $Name => '' $Type => '' rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Global/Templates.html000066400000000000000000000044401222742774700272750ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title, FeedURI => 'templates' &> <& /Elements/Tabs &> <& /AssetTracker/Admin/Elements/EditTemplates, title => $title, %ARGS &> <%init> my $title = loc("Modify templates which apply to all asset types"); my (@actions); <%ARGS> $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/000077500000000000000000000000001222742774700243535ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/CustomFields.html000066400000000000000000000051721222742774700276470ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Admin/Elements/EditCustomFields, %ARGS, title => $title, Object => $Object, ObjectType => 'RTx::AssetTracker::Type' &> <%INIT> my $Object = RTx::AssetTracker::Type->new( $session{'CurrentUser'} ); $Object->Load($id) || Abort( loc( "Couldn't load object [_1]", $id ) ); my $FriendlySubTypes = RT::CustomField->new( $session{'CurrentUser'} ) ->FriendlyLookupType( $Object->CustomFieldLookupType ); my $title = loc( 'Custom Fields for asset type [_1]', $Object->Name ); <%ARGS> $id => undef $SubType => 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset' rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/GroupRights.html000066400000000000000000000057201222742774700275220ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => loc('Modify group rights for asset type [_1]', $TypeObj->Name) &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>
    % $m->callback( %ARGS, TypeObj => $TypeObj, results => \@results, principals => \@principals ); <& /Admin/Elements/EditRights, Context => $TypeObj, Principals => \@principals &> <& /Elements/Submit, Label => loc('Save Changes') &>
    <%INIT> # Update the acls. my @results = ProcessACLs(\%ARGS); if (!defined $id) { Abort(loc("No Asset Type defined")); } my $TypeObj = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $TypeObj->Load($id) || Abort(loc("Couldn't load type [_1]",$id)); # Principal collections my @principals = GetPrincipalsMap($TypeObj, qw(System Roles Groups)); <%ARGS> $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/History.html000066400000000000000000000046261222742774700267120ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Ticket/Elements/ShowHistory, Ticket => $TypeObj, ShowDisplayModes => 0, &> <%INIT> my $TypeObj = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $TypeObj->Load($id) || Abort("Couldn't load asset type '$id'"); my $title = loc("History of the asset type [_1]", $TypeObj->Name); <%ARGS> $id => '' unless defined $id rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/Modify.html000066400000000000000000000136301222742774700264730ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>
    % my $CFs = $TypeObj->CustomFields; % while (my $CF = $CFs->Next) { % }
    <&|/l&>Type Name: Name || $Name %>" />
    <&|/l&>Description: Description || $Description || '' %>" size="60" />
    <&|/l&>Lifecycle: <& /Widgets/Form/Select:InputOnly, Name => 'Lifecycle', Values => [ sort { loc($a) cmp loc($b) } RT::Lifecycle->List ], CurrentValue => $Create ? "at_default" : $TypeObj->Lifecycle || $ARGS{'Lifecycle'}, Default => 0, &>
    <% loc($CF->Name) %>: <& /Elements/EditCustomField, CustomField => $CF, Object => $TypeObj, ($Create ? (NamePrefix => 'Object-RTx::AssetTracker::Type--CustomField-') : () )&>
    /> <&|/l&>Enabled (Unchecking this box disables this type)
    % $m->callback( %ARGS, TypeObj => $TypeObj, results => \@results );
    % if ( $Create ) { <& /Elements/Submit, Label => loc('Create') &> % } else { <& /Elements/Submit, Label => loc('Save Changes') &> % }
    <%INIT> my ($title, @results, @no_redirect_results, $Disabled, $EnabledChecked); my $TypeObj = RTx::AssetTracker::Type->new( $session{'CurrentUser'} ); $TypeObj->Load( $id ) if !$id || $id eq 'new'; $EnabledChecked = 'checked="checked"'; unless ($Create) { if ( defined $id && $id eq 'new' ) { my ($val, $msg) = $TypeObj->Create( Name => $Name ); if (!$val) { $Create = 1; # Create failed, so bring us back to step 1 } push @results, $msg; } else { $TypeObj->Load($id) || $TypeObj->Load($Name) || Abort(loc("Couldn't load asset type '[_1]'", $Name)); } } if ( $TypeObj->Id ) { $title = loc('Configuration for asset type [_1]', $TypeObj->Name ); my @attribs= qw(Description Name Lifecycle); # we're asking about enabled on the web page but really care about disabled if ( $SetEnabled ) { $Disabled = $ARGS{'Disabled'} = $Enabled? 0: 1; } $m->callback( CallbackName => 'BeforeUpdate', Type => $TypeObj, AttributesRef => \@attribs, ARGSRef => \%ARGS, ); push @results, UpdateRecordObject( AttributesRef => \@attribs, Object => $TypeObj, ARGSRef => \%ARGS ); $Disabled = $ARGS{'Disabled'} = $Enabled? 0: 1; $EnabledChecked = "" if $TypeObj->Disabled; my @linkresults; $m->callback( results => \@linkresults, RecordObj => $TypeObj, ARGSRef => \%ARGS, CallbackName => 'ProcessLinks' ); push @results, @linkresults; push @results, ProcessObjectCustomFieldUpdates( ARGSRef => \%ARGS, Object => $TypeObj ); } else { $title = loc("Create a type"); } # This code does automatic redirection if any updates happen. MaybeRedirectForResults( Actions => \@results, Arguments => { id => $TypeObj->Id }, ) if $TypeObj->id; push @results, @no_redirect_results; <%ARGS> $id => undef $result => undef $Name => undef $Create => undef $Description => undef $SetEnabled => undef $Enabled => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/People.html000066400000000000000000000136401222742774700264710ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>

    <&|/l&>Current watchers

    % for my $Name (RTx::AssetTracker::Type->RoleGroupTypes) { % my $role_group_method = $Name . 'RoleGroup'; <& /AssetTracker/Admin/Elements/EditTypeWatcherGroup, Label => loc($Name), TypeObj => $TypeObj, Watchers => $TypeObj->$role_group_method &> % } % $m->callback(CallbackName => 'CurrentWatchers', TypeObj => $TypeObj);

    <&|/l&>New watchers

    <&|/l&>Find people whose
    <& /Elements/SelectUsers &>
    <&|/l&>Find groups whose
    <& /Elements/SelectGroups &>

    <&|/l&>Add new watchers:

    <&|/l&>Users % if ($user_msg) {
    <%$user_msg%> % } elsif ($Users) {

      % while (my $u = $Users->Next ) {
    • <& /AssetTrackler/Elements/SelectWatcherType, Scope => 'type', Name => "Type-AddWatcher-Principal-". $u->PrincipalId, &> <& /Elements/ShowUser, User => $u &>
    • % }
    % }

    <&|/l&>Groups % if ($group_msg) {
    <%$group_msg%> % } elsif ($Groups) {

      % while (my $g = $Groups->Next ) {
    • <&/AssetTracker/Elements/SelectWatcherType, Scope=>'type', Name => "Type-AddWatcher-Principal-".$g->PrincipalId &> <%$g->Name%> (<%$g->Description%>) % }
    % }
    <& /Elements/Submit, Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to"), Reset => 1 &>
    <%INIT> my ($field, @results, $User, $Users, $Groups, $watcher, $user_msg, $group_msg); my $TypeObj = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $TypeObj->Load($id) || Abort(loc("Couldn't load type", $id)); unless ($OnlySearchForPeople or $OnlySearchForGroup) { # Delete deletable watchers foreach my $key (keys %ARGS) { my $id = $TypeObj->Id; if (($key =~ /^Type-$id-DeleteWatcher-Type-(.*?)-Principal-(\d*)$/)) {; my ($code, $msg) = $TypeObj->DeleteWatcher(Type => $1, PrincipalId => $2); push @results, $msg; } } # Add new watchers foreach my $key (keys %ARGS) { my $type = $ARGS{$key}; next unless $key =~ /^Type-AddWatcher-Principal-(\d*)$/; my $id = $1; next unless RTx::AssetTracker::Type->IsRoleGroupType($type); my ($code, $msg) = $TypeObj->AddWatcher( Type => $type, PrincipalId => $id, ); push @results, $msg; } } if ( $ARGS{'UserString'} ) { $Users = RT::Users->new( $session{'CurrentUser'} ); $Users->Limit( FIELD => $ARGS{'UserField'}, VALUE => $ARGS{'UserString'}, OPERATOR => $ARGS{'UserOp'} ); } else { $user_msg = loc("No principals selected."); } if ( $ARGS{'GroupString'} ) { $Groups = RT::Groups->new( $session{'CurrentUser'} ); $Groups->Limit( FIELD => 'Domain', OPERATOR => '=', VALUE => 'UserDefined' ); $Groups->Limit( FIELD => $ARGS{'GroupField'}, VALUE => $ARGS{'GroupString'}, OPERATOR => $ARGS{'GroupOp'} ); } else { $group_msg = loc("No principals selected."); } my $title = loc('People related to type [_1]', $TypeObj->Name); <%ARGS> $OnlySearchForPeople => undef $OnlySearchForGroup => undef $UserField => 'Name' $UserOp => '=' $UserString => undef $GroupField => 'Name' $GroupOp => '=' $GroupString => undef $Type => undef $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/Scrip.html000066400000000000000000000053041222742774700263230ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &> <& /AssetTracker/Admin/Elements/EditScrip, title => $title, %ARGS, id => $id &> <%init> my $TypeObj = RTx::AssetTracker::Type->new( $session{'CurrentUser'} ); $TypeObj->Load( $AssetType ); unless( $TypeObj->id ) { Abort(loc("Asset Type [_1] not found", $id)); } my ($title); ($id, my @results) = $m->comp( '/AssetTracker/Admin/Elements/EditScrip:Process', %ARGS ); if ( $id ) { $title = loc("Modify a scrip for asset type [_1]", $TypeObj->Name); } else { $title = loc("Create a scrip for asset type [_1]", $TypeObj->Name); } <%ARGS> $id => undef $AssetType => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/Scrips.html000066400000000000000000000052421222742774700265070ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> % unless ( $TypeObj->Disabled ) { # Global scrips does not apply to disabled types

    <&|/l&>Scrips which apply to all asset types

    <& /AssetTracker/Admin/Elements/ListGlobalScrips &>
    % } <& /AssetTracker/Admin/Elements/EditScrips, title => $title, %ARGS &> <%init> my $TypeObj = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $TypeObj->Load($id); my $title; if ($TypeObj->id) { $title = loc("Modify scrips for asset type [_1]", $TypeObj->Name); } else { Abort(loc("Asset Type [_1] not found",$id)); } <%ARGS> $id => undef #some identifier that a Type could rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/Template.html000066400000000000000000000104041222742774700270130ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>
    %if ($Create ) { % } else { % } %# hang onto the type id <& /Admin/Elements/ModifyTemplate, Name => $TemplateObj->Name, Description => $TemplateObj->Description, Content => $TemplateObj->Content, Type => $TemplateObj->Type &> <& /Elements/Submit, Label => $SubmitLabel, Reset => 1 &>
    <%INIT> my $TemplateObj = RTx::AssetTracker::Template->new( $session{'CurrentUser'} ); my $TypeObj; my $SubmitLabel; my $title; my @results; if ( !$Create ) { if ( $Template eq 'new' ) { my ( $val, $msg ) = $TemplateObj->Create( AssetType => $AssetType, Name => $Name, Type => $Type ); Abort( loc( "Could not create template: [_1]", $msg ) ) unless ($val); push @results, $msg; } else { $TemplateObj->Load($Template) || Abort( loc('No Template') ); } } if ( $TemplateObj->Id() ) { $AssetType = $TemplateObj->Type; $TypeObj = $TemplateObj->TypeObj; my @attribs = qw( Name Description AssetType Type Content ); my @aresults = UpdateRecordObject( AttributesRef => \@attribs, Object => $TemplateObj, ARGSRef => \%ARGS ); push @results, @aresults; my ( $ok, $msg ) = $TemplateObj->CompileCheck; push @results, $msg if !$ok; } else { $TypeObj = RTx::AssetTracker::Type->new( $session{'CurrentUser'} ); $TypeObj->Load($AssetType); } if ($Create) { $title = loc( 'Create a new template for asset type [_1]', $TypeObj->Name ); $SubmitLabel = loc('Create'); } else { $title = loc( 'Modify template [_1] for asset type [_2]', loc( $TemplateObj->Name()), $TypeObj->Name ); $SubmitLabel = loc('Save Changes'); } <%ARGS> $AssetType => '' $Template => '' $Create => undef $Name => undef $Type => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/Templates.html000066400000000000000000000047121222742774700272030ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Admin/Elements/EditTemplates, title => $title, %ARGS &> <%INIT> my $AssetTypeObj = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $AssetTypeObj->Load($id); if (!$AssetTypeObj->id) { Abort(loc("Asset Type [_1] not found",$id)); } my $title = loc("Templates for queue [_1]", $AssetTypeObj->Name); <%ARGS> $id => undef #some identifier that an Asset Type could rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/UserRights.html000066400000000000000000000060001222742774700273340ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => loc('Modify user rights for asset type [_1]', $TypeObj->Name) &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>
    %# XXX TODO put this somewhere more reasonable % $m->callback( %ARGS, TypeObj => $TypeObj, results => \@results ); <& /Admin/Elements/EditRights, Context => $TypeObj, Principals => \@principals &> <& /Elements/Submit, Label => loc('Save Changes') &>
    <%INIT> # Update the acls. my @results = ProcessACLs(\%ARGS); if (!defined $id) { Abort(loc("No Asset Type defined")); } my $TypeObj = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $TypeObj->Load($id) || Abort(loc("Couldn't load asset type [_1]",$id)); my @principals = GetPrincipalsMap($TypeObj, 'Users'); <%ARGS> $id => undef $UserString => undef $UserOp => undef $UserField => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Admin/Types/index.html000066400000000000000000000106161222742774700263540ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Admin/Elements/Header, Title => loc("Admin asset types") &> <& /Elements/Tabs &>

    <%$caption%>

    <&|/l&>Select an asset type:

    % unless ( $types->Count ) { <&|/l&>No asset types matching search criteria found. % } else { <& /Elements/CollectionList, OrderBy => 'Name', Order => 'ASC', Rows => 50, %ARGS, Format => $Format, Collection => $types, AllowSorting => 1, PassArguments => [qw( Format Rows Page Order OrderBy FindDisabledTypes TypeString TypeOp TypeField )], &> % }
    % foreach my $field( qw(Format Rows Page Order OrderBy) ) { % next unless defined $ARGS{ $field } && length $ARGS{ $field }; % } <& /Elements/SelectMatch, Name => 'TypeOp', Default => $TypeOp &>
    /> <&|/l&>Include disabled asset types in listing.
    <%INIT> my $types = RTx::AssetTracker::Types->new($session{'CurrentUser'}); $types->FindAllRows if $FindDisabledTypes; my ($caption); if ( defined $TypeString && length $TypeString ) { $caption = $FindDisabledTypes ? loc("All asset types matching search criteria") : loc("Enabled asset types matching search criteria"); $types->Limit( FIELD => $TypeField, OPERATOR => $TypeOp, VALUE => $TypeString, ); RT::Interface::Web::Redirect(RT->Config->Get('WebURL')."AssetTracker/Admin/Types/Modify.html?id=".$types->First->id) if $types->Count == 1; } else { $types->UnLimit; $caption = $FindDisabledTypes ? loc("All Asset Types") : loc("Enabled Asset Types"); } $Format ||= RT->Config->Get('AdminSearchResultFormat')->{'AT_Types'}; <%ARGS> $FindDisabledTypes => 0 $Format => undef $TypeField => 'Name' $TypeOp => '=' $TypeString => '' rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/000077500000000000000000000000001222742774700232765ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Create.html000066400000000000000000000221361222742774700253730ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>
    % $m->callback( CallbackName => 'FormStart', TypeObj => $TypeObj, ARGSRef => \%ARGS );
    <&| /Widgets/TitleBox, title => $title, class=>'asset-info-basics' &> <& /AssetTracker/Asset/Elements/EditBasics, InTable => 1, fields => [ { name => 'Name', html => '', }, { name => 'Type', comp => '/AssetTracker/Asset/Elements/ShowType', args => { TypeObj => $TypeObj, }, }, { name => 'Status', comp => '/AssetTracker/Elements/SelectStatus', args => { Name => "Status", Default => $ARGS{Status} || $TypeObj->Lifecycle->DefaultOnCreate, DefaultValue => 0, SkipDeleted => 1, TypeObj => $TypeObj, }, }, { name => 'Description', html => '', }, ] &> %foreach my $role ( $TypeObj->RoleGroupTypes() ) { %} % $m->callback( CallbackName => 'AfterRoles', ARGSRef => \%ARGS ); <& /AssetTracker/Asset/Elements/EditCustomFields, %ARGS, TypeObj => $TypeObj, InTable => 1 &> %# <& /AssetTracker/Asset/Elements/EditTransactionCustomFields, %ARGS, TypeObj => $TypeObj, InTable => 1 &>
    <% $role %>: <& /Elements/EmailInput, Name => $role, Size => undef, Default => $ARGS{$role} &>
    % $m->callback( CallbackName => 'AfterBasics', TypeObj => $TypeObj, ARGSRef => \%ARGS );

    <& /Elements/Submit, Label => loc("Create"), id => 'SubmitAsset' &>
    <%INIT> $m->callback( CallbackName => "Init", ARGSRef => \%ARGS ); my $Type = $ARGS{Type}; $session{DefaultType} = $Type; my %link_types = %{ RTx::AssetTracker::Asset::LINKTYPEMAP() }; my @link_order = RTx::AssetTracker::Asset::LINKORDER(); if ($CloneAsset) { my $CloneAssetObj = RTx::AssetTracker::Asset->new( $session{CurrentUser} ); $CloneAssetObj->Load($CloneAsset) or Abort( loc("Asset could not be loaded") ); my $clone = { }; foreach my $role ( $CloneAssetObj->TypeObj->RoleGroupTypes() ) { my $role_method = $role . 'RoleGroup'; $clone->{$role} = join( ',', $CloneAssetObj->$role_method->MemberEmailAddressesAsString ); } $clone->{$_} = $CloneAssetObj->$_() for qw/Name Description Status/; if (0) { # Temporarily disabled my $members = $CloneAssetObj->Members; my ( @members, @members_of, @refers, @refers_by, @depends, @depends_by ); my $refers = $CloneAssetObj->RefersTo; while ( my $refer = $refers->Next ) { push @refers, $refer->LocalTarget; } $clone->{'new-RefersTo'} = join ' ', @refers; my $refers_by = $CloneAssetObj->ReferredToBy; while ( my $refer_by = $refers_by->Next ) { push @refers_by, $refer_by->LocalBase; } $clone->{'RefersTo-new'} = join ' ', @refers_by; my $depends = $CloneAssetObj->DependsOn; while ( my $depend = $depends->Next ) { push @depends, $depend->LocalTarget; } $clone->{'new-DependsOn'} = join ' ', @depends; my $depends_by = $CloneAssetObj->DependedOnBy; while ( my $depend_by = $depends_by->Next ) { push @depends_by, $depend_by->LocalBase; } $clone->{'DependsOn-new'} = join ' ', @depends_by; while ( my $member = $members->Next ) { push @members, $member->LocalBase; } $clone->{'MemberOf-new'} = join ' ', @members; my $members_of = $CloneAssetObj->MemberOf; while ( my $member_of = $members_of->Next ) { push @members_of, $member_of->LocalTarget; } $clone->{'new-MemberOf'} = join ' ', @members_of; } my $cfs = $CloneAssetObj->TypeObj->AssetCustomFields(); while ( my $cf = $cfs->Next ) { my $cf_id = $cf->id; my $cf_values = $CloneAssetObj->CustomFieldValues( $cf->id ); my @cf_values; while ( my $cf_value = $cf_values->Next ) { push @cf_values, $cf_value->Content; } $clone->{"Object-RTx::AssetTracker::Asset--CustomField-$cf_id-Value"} = join "\n", @cf_values; } $clone->{'ChangeComment'} = "Cloned from asset #$CloneAsset."; for ( keys %$clone ) { $ARGS{$_} = $clone->{$_} if not defined $ARGS{$_}; } } my @results; my $title = loc("Create a new asset"); my $TypeObj = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $TypeObj->Load($Type) || Abort(loc("Type could not be loaded.")); $m->callback( TypeObj => $TypeObj, title => \$title, results => \@results, ARGSRef => \%ARGS ); $TypeObj->Disabled && Abort(loc("Cannot create assets of a disabled type.")); my $CFs = $TypeObj->AssetCustomFields(); my $ValidCFs = $m->comp( '/Elements/ValidateCustomFields', CustomFields => $CFs, ARGSRef => \%ARGS, NamePrefix => "Object-RTx::AssetTracker::Asset--CustomField-" ); my $skip_create = 0; $m->callback( CallbackName => 'BeforeCreate', ARGSRef => \%ARGS, skip_create => \$skip_create, results => \@results ); if (defined($ARGS{'id'}) and $ARGS{'id'} eq 'new') { # new asset? if ( $ValidCFs && !$skip_create) { $m->comp('Display.html', %ARGS); $RT::Logger->crit("After display call; error is $@"); $m->abort(); } elsif ( !$ValidCFs ) { # Invalid CFs while (my $CF = $CFs->Next) { my $msg = $m->notes('InvalidField-' . $CF->Id) or next; push @results, $CF->Name . ': ' . $msg; } } } <%ARGS> $CloneAsset => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Display.html000066400000000000000000000156041222742774700255770ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title, LinkRel => \%link_rel &> <& /Elements/Tabs &> % $m->callback(CallbackName => 'BeforeActionList', %ARGS, Actions => \@Actions, ARGSRef => \%ARGS, Asset => $AssetObj); <& /Elements/ListActions, actions => \@Actions &> % $m->callback( %ARGS, Asset => $AssetObj, Transactions => $transactions, CallbackName => 'BeforeShowSummary' );
    <&| /Widgets/TitleBox, title => loc('Asset metadata') &> <& /AssetTracker/Asset/Elements/ShowSummary, Asset => $AssetObj &>

    % $m->callback( %ARGS, Asset => $AssetObj, CallbackName => 'BeforeShowTicketLinks' );
    % $m->callback( Asset => $AssetObj, %ARGS, Transactions => $transactions, CallbackName => 'BeforeShowHistory' ); % if ( $RT::ShowAssetHistory ) { <& /Ticket/Elements/ShowHistory , Ticket => $AssetObj, Tickets => $Assets, Transactions => $transactions, ShowDisplayModes => 0, DisplayPath => RT->Config->Get('WebPath')."/AssetTracker/Asset/Display.html?id=".$AssetObj->id &> % } % $m->callback( %ARGS, % Asset => $AssetObj, % Transactions => $transactions, % CallbackName => 'AfterShowHistory', % ); <%ARGS> $id => undef $Create => undef $ShowHeaders => 0 $Collapsed => undef $AssetObj => undef $Actions => undef <%INIT> $m->callback( AssetObj => $AssetObj, ARGSRef => \%ARGS, CallbackName => 'Initial' ); #if ( ! $ARGS{'NoRedirect'} && RT::Interface::Web->MobileClient()) { # $id ||= $AssetObj->id if $AssetObj; # RT::Interface::Web::Redirect(RT->Config->Get('WebURL').'m/asset/show?id='.$id); # $m->abort; #} my (@Actions, $Assets, $title); push(@Actions, @$Actions) if $Actions; unless ($id || $AssetObj) { Abort('No asset specified'); } if ($ARGS{'id'} eq 'new') { # {{{ Create a new asset my $Type = RTx::AssetTracker::Type->new( $session{'CurrentUser'} ); $Type->Load($ARGS{'Type'}); unless ($Type->Id) { Abort('Type not found'); } unless ( $Type->CurrentUserHasRight('CreateAsset') ) { Abort('You have no permission to create assets of that type.'); } ($AssetObj, @Actions) = CreateAsset( %ARGS, ); unless ( $AssetObj->CurrentUserHasRight('ShowAsset') ) { Abort("No permission to view newly created asset #".$AssetObj->id."."); } } else { $AssetObj ||= LoadAsset($ARGS{'id'}); $AssetObj->CurrentUser->PrincipalObj->HasRights( Object => $AssetObj ); my $SkipProcessing; $m->callback( CallbackName => 'BeforeProcessArguments', AssetObj => $AssetObj, Assets => $Assets, ActionsRef => \@Actions, ARGSRef => \%ARGS, SkipProcessing => \$SkipProcessing ); if ( !$SkipProcessing ) { $m->callback(CallbackName => 'ProcessArguments', Asset => $AssetObj, ARGSRef => \%ARGS, Actions => \@Actions); # push @Actions, ProcessAssetWatchers(ARGSRef => \%ARGS, AssetObj => $AssetObj ); push @Actions, ProcessAssetBasics( ARGSRef => \%ARGS, AssetObj => $AssetObj ); push @Actions, ProcessAssetLinks( ARGSRef => \%ARGS, AssetObj => $AssetObj ); # push @Actions, ProcessObjectCustomFieldUpdates(ARGSRef => \%ARGS, AssetObj => $AssetObj ); unless ($AssetObj->CurrentUserHasRight('ShowAsset')) { if (@Actions) { Abort("A change was applied successfully, but you no longer have permissions to view the asset", Actions => \@Actions); } else { Abort("No permission to view asset"); } } } } $title = loc("Asset #[_1]: [_2]", $AssetObj->Id, $AssetObj->Name || ''); $m->callback( CallbackName => 'BeforeDisplay', AssetObj => \$AssetObj, Assets => \$Assets, Actions => \@Actions, title => \$title, ARGSRef => \%ARGS, ); # This code does automatic redirection if any updates happen. MaybeRedirectForResults( Actions => \@Actions, Path => "/AssetTracker/Asset/Display.html", Anchor => $ARGS{'Anchor'}, Arguments => { id => $AssetObj->id }, ); my $transactions = $m->comp('Elements/FindTransactions',Asset => $AssetObj, Assets => $Assets || undef); my %link_rel; if (defined $session{'assets'} and ($ARGS{'Query'} or $session{'CurrentAssetSearchHash'}->{'Query'})) { my $item_map = $session{'assets'}->ItemMap; $link_rel{first} = "AssetTracker/Asset/Display.html?id=" . $item_map->{first} if $item_map->{$AssetObj->Id}{prev}; $link_rel{prev} = "AssetTracker/Asset/Display.html?id=" . $item_map->{$AssetObj->Id}{prev} if $item_map->{$AssetObj->Id}{prev}; $link_rel{next} = "AssetTracker/Asset/Display.html?id=" . $item_map->{$AssetObj->Id}{next} if $item_map->{$AssetObj->Id}{next}; $link_rel{last} = "AssetTracker/Asset/Display.html?id=" . $item_map->{last} if $item_map->{$AssetObj->Id}{next}; } rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/000077500000000000000000000000001222742774700250525ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/BulkLinks000066400000000000000000000112441222742774700266750ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}}

    <&|/l&>Current Links

    %foreach my $type ( @link_order ) { %}
    <&|/l&><% $link_types{$type}{Name} %>: % if ( $hash{$type} && $link_types{$type}{Mode} eq 'Target' ) { % for my $link ( values %{$hash{$type}} ) { <& /Elements/ShowLink, URI => $link->TargetURI &>
    % } } % elsif ( $hash{$type} && $link_types{$type}{Mode} eq 'Base' ) { % for my $link ( values %{$hash{$type}} ) { <& /Elements/ShowLink, URI => $link->BaseURI &>
    % } }
    <&|/l&>(Check box to delete)

    <&|/l&>New Links

    <&|/l&>Enter tickets or URIs to link assets to. Separate multiple entries with spaces.
    %foreach my $type ( @link_order ) { % if ( $link_types{$type}{Mode} eq 'Target' ) { % } elsif ( $link_types{$type}{Mode} eq 'Base' ) { % } %}
    <&|/l&><% $link_types{$type}{Name} %>:" />" />
    <%ARGS> $Assets => undef <%INIT> my %link_types = %{ RTx::AssetTracker::Asset::LINKTYPEMAP() }; my @link_order = RTx::AssetTracker::Asset::LINKORDER(); my %hash; if ( $Assets && $Assets->Count ) { my $first_asset = $Assets->Next; # we only show current links that exist on all the assets for my $type ( @link_order ) { my $target_or_base = $link_types{$type}{Mode}; while ( my $link = $first_asset->$type->Next ) { $hash{$type}{$link->$target_or_base} = $link; } } while ( my $asset = $Assets->Next ) { for my $type ( @link_order ) { my $target_or_base = $link_types{$type}{Mode}; # if $hash{$type} is empty, no need to check any more next unless $hash{$type} && keys %{$hash{$type}}; my %exists; while ( my $link = $asset->$type->Next ) { $exists{$link->$target_or_base}++; } for ( keys %{$hash{$type}} ) { delete $hash{$type}{$_} unless $exists{$_}; } } } } rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/ChangeComment000066400000000000000000000002621222742774700275050ustar00rootroot00000000000000<%$Text%>: <%ARGS> $Text => "Change Comment" $Name => "ChangeComment" $SIZE => 75 $MAXLENGTH => 255 rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/EditBasics000066400000000000000000000075461222742774700270230ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $AssetObj => undef @fields => () $InTable => 0 %defaults => () <%INIT> unless ( @fields ) { @fields = ( { name => 'Name', html => '', }, { name => 'Status', comp => '/AssetTracker/Elements/SelectStatus', args => { Name => 'Status', DefaultLabel => loc("[_1] (Unchanged)",loc($AssetObj->Status)), Default => $defaults{'Status'} || undef, AssetObj => $AssetObj, TypeObj => $AssetObj->TypeObj, }, }, { name => 'Type', comp => '/AssetTracker/Elements/SelectType', args => { Name => 'Type', Default => $defaults{'Type'} || $AssetObj->TypeObj->Id, ShowNullOption => 0, } }, { name => 'Description', html => '', }, ); } $m->callback( CallbackName => 'MassageFields', %ARGS, AssetObj => $AssetObj, Fields => \@fields ); # Process the field list, skipping if html is provided and running the # components otherwise for my $field (@fields) { next if defined $field->{'html'}; if ( $field->{'comp'} ) { $field->{'html'} = $m->scomp($field->{'comp'}, %{$field->{'args'} || {}}); } } % unless ($InTable) { % } % for my $field (@fields) { \ \ \ % } % $m->callback( CallbackName => 'EndOfList', AssetObj => $AssetObj, %ARGS, Fields => \@fields ); % unless ($InTable) {
    <&|/l&><% $field->{'name'} %>:<% $field->{'html'} |n %>
    % } rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/EditCustomField000066400000000000000000000077531222742774700300350ustar00rootroot00000000000000%# {{{ BEGIN BPS TAGGED BLOCK %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# }}} END BPS TAGGED BLOCK % my $Values; % if ($AssetObj) { % $Values = $AssetObj->CustomFieldValues($CustomField->id); % } % if ($CustomField->Type eq 'Freeform' and $CustomField->MaxValues == 1) { % } elsif ($CustomField->Type eq 'Freeform' and $CustomField->MaxValues != 1) { % my $content; % if ($AssetObj) { % while (my $value = $Values->Next ) { % $content .= "\n" . $value->Content; % } % } elsif ($Default) { value="<%$Default ? $Default : ''%>" % } %# } elsif ($CustomField->Type eq 'Select' and $CustomField->MaxValues == 1) { % } elsif ($CustomField->Type eq 'Select' ) { -Magic" value="1"> % } <%ARGS> $AssetObj => undef $CustomField => undef $NamePrefix => undef $NameSuffix => undef $Rows => 5 $Cols=> 20 $Default => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/EditCustomFields000066400000000000000000000075251222742774700302150ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}}
    % my @entry_fields; % my $i = 0; % my $cfcount = $CustomFields->Count; % $cfcount++ if ($cfcount % 2) ; # if we have an odd number of % #custom fields, fudge it so we know where to put in the table break % while (my $CustomField = $CustomFields->Next()) { % if ($cfcount == 2 * $i) {
    % } % $i++; % if ($CustomField->CurrentUserHasRight("ModifyCustomField")) { % }
    <%$CustomField->Name%>
    <%$CustomField->FriendlyType%>
    % my $default = $m->notes('Field-' . $CustomField->Id); % $default ||= $ARGS{"CustomField-". $CustomField->Id }; <& /Elements/EditCustomField, %ARGS, Object => $AssetObj, CustomField => $CustomField, NamePrefix => $NamePrefix, Default => $default, &> % } % elsif ($CustomField->CurrentUserHasRight("SeeCustomField")) { % my $Values = $Object->CustomFieldValues($CustomField->Id);
      % while (my $Value = $Values->Next()) {
    • % if ($CustomField->Type eq 'Image') { <& /Elements/ShowCustomFieldImage, Object => $Value &> % } else { <%$Value->Content%> % }
    • % } % unless ($Values->Count()) {
    • <&|/l&>(no value)
    • % }
    % }
    <%INIT> my $CustomFields; my $NamePrefix; my $Object; if ($AssetObj) { $CustomFields = $AssetObj->CustomFields(); $NamePrefix = "Object-RTx::AssetTracker::Asset-".$AssetObj->Id."-CustomField-"; $Object = $AssetObj; } else { $CustomFields = $TypeObj->AssetCustomFields(); $NamePrefix = "Object-RTx::AssetTracker::Asset--CustomField-"; $Object = $TypeObj; } <%ARGS> $AssetObj => undef $TypeObj => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/EditIPs000066400000000000000000000026231222742774700263010ustar00rootroot00000000000000

    <&|/l&>Add IPs

    % my $i = 1; % while ($i <= $blanks) { % $i++; % }
    InterfaceIP AddressMAC AddressTCP Ports (space separated)UDP Ports

    <&|/l&>Current IPs

    <&|/l&>(Check box to delete)
    % while(my $ip = $ips->Next) { % }
    <%$ip->Interface%>: <%$ip->IP%> (<%$ip->MAC%>) TCP:
    UDP:
    <%init> my $ips = $AssetObj->IPs; <%args> $AssetObj => undef $blanks => 5 rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/EditPeople000066400000000000000000000045051222742774700270330ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}}

    <&|/l&>Current watchers

    % for my $role ( RTx::AssetTracker::Type->RoleGroupTypes() ) {
    <&|/l&><% RTx::AssetTracker::Type->RoleLabel( $role ) %>: <& EditWatchers, AssetObj => $AssetObj, TypeObj => $TypeObj, Role => $role, %ARGS &>
    % } <%ARGS> $AssetObj => undef $TypeObj => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/EditWatchers000066400000000000000000000115371222742774700273720ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} % if (ref $AssetObj) { % } % elsif (ref $TypeObj) { % }
    Current Members:
    (check to delete)
      %# Print out a placeholder if there are none. % unless ( $Members->Count ) {
    • <&|/l&>none
    • % } % while ( my $watcher = $Members->Next ) { % my $member = $watcher->MemberObj->Object;
    • % if ( $member->isa( 'RT::User' ) ) { % if ( $session{CurrentUser}->HasRight( Right => 'AdminUsers', Object => $RT::System ) && % $session{CurrentUser}->HasRight( Right => 'ShowConfigTab', Object =>$RT::System ) ) { <& /Elements/ShowUser, User => $member &> % } else { <& /Elements/ShowUser, User => $member &> % } } % else { % if ( $session{CurrentUser}->HasRight( Right => 'AdminGroup', Object => $RT::System ) && % $session{CurrentUser}->HasRight( Right => 'ShowConfigTab', Object =>$RT::System ) ) { <% $member->Name %> % } else { <% $member->Name %> % } }
    • % }
      Add Groups:
    <&/AssetTracker/Elements/SelectGroupsWithRight, Right => $Right, Name => "AddWatcher-Type-$Role", AssetObj => $AssetObj &>
    Add Users:
    <&/AssetTracker/Elements/SelectUsersWithRight, Right => $Right, Name => "AddWatcher-Type-$Role", IncludeSuperusers=>1,Verbose => 1, SortFriendly => 1, AssetObj => $AssetObj &>
    Add Groups:
    <&/AssetTracker/Elements/SelectGroupsWithRight, Right => $Right, Name => "AddWatcher-Type-$Role", TypeObj => $TypeObj &>
    Add Users:
    <&/AssetTracker/Elements/SelectUsersWithRight, Right => $Right, Name => "AddWatcher-Type-$Role", IncludeSuperusers=>1,Verbose => 1, SortFriendly => 1, TypeObj => $TypeObj &>
    <%INIT> my $Members; my $Watchers; my $Right; if ($Asset && !$AssetObj && !ref($Asset)) { $AssetObj = RTx::AssetTracker::Asset->new($session{'CurrentUser'}); $AssetObj->Load($Asset); $AssetObj->Id or Abort("Asset: $Asset could not be loaded."); } elsif (ref($Asset) eq 'RTx::AssetTracker::Asset') { $AssetObj = $Asset; } if ($AssetObj) { unless (!$AssetObj->Id || $AssetObj->CurrentUserHasRight('ModifyAsset')) { Abort("No permission to modify asset"); } my $group_method = $Role . 'RoleGroup'; $Watchers = $AssetObj->$group_method(); $Members = $Watchers->MembersObj; } else { $Watchers = RT::Group->new($session{'CurrentUser'}); $Members = RT::GroupMembers->new($session{'CurrentUser'}); } $Right = RTx::AssetTracker::Type->RoleRight( $Role ); <%ARGS> $AssetObj => undef $Asset => undef $TypeObj => undef $Role $IncludeUsers => 1 $IncludeGroups => 1 rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/FindTransactions000066400000000000000000000053511222742774700302520ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my $Transactions = RT::Transactions->new($session{'CurrentUser'}); if ($Assets) { while (my $t = $Assets->Next) { $Transactions->LimitToAsset($t->id); } } else { $Transactions = $Asset->Transactions; } my $OldestFirst = RT->Config->Get( 'OldestTransactionsFirst', $session{'CurrentUser'} ); my $SortOrder = $OldestFirst? 'ASC': 'DESC'; $Transactions->OrderByCols( { FIELD => 'Created', ORDER => $SortOrder }, { FIELD => 'id', ORDER => $SortOrder }, ); $Transactions->Next(); $Transactions->GotoFirstItem(); # actually do the search return ($Transactions); <%ARGS> $Asset => undef $Assets => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/ShowBasics000066400000000000000000000056121222742774700270460ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# This will check SeeType at the asset role level, queue level, and global level % if ($Asset->CurrentUserHasRight('SeeType')) { % } % $m->callback( %ARGS, CallbackName => 'EndOfList', AssetObj => $Asset );
    <&|/l&>Id: <% $Asset->Id %>
    <&|/l&>Name: <% $Asset->Name%>
    <&|/l&>Description: <% $Asset->Description%>
    <&|/l&>Status: <% loc($Asset->Status) %>
    <&|/l&>Asset Type: <& ShowType, Asset => $Asset, TypeObj => $Asset->TypeObj &>
    <%ARGS> $Asset => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/ShowCustomFields000066400000000000000000000041061222742774700302400ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/ShowCustomFields, Object => $Asset &> <%ARGS> $Asset => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/ShowIP000066400000000000000000000004731222742774700261520ustar00rootroot00000000000000 % while( my $ip = $ips->Next ) { % }
    <%$ip->Interface%>: <%$ip->IP%> (<%$ip->MAC%>)
    TCP: <% join(', ', sort {$a<=>$b} $ip->TCPPorts) %>
    UDP: <% join(', ', sort {$a<=>$b} $ip->UDPPorts) %>
    <%INIT> my $ips = $Asset->IPs; <%ARGS> $Asset rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/ShowPeople000066400000000000000000000053361222742774700270710ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} % for my $role ( RTx::AssetTracker::Type->RoleGroupTypes() ) { % my $group = $role . 'RoleGroup'; % }
    <&|/l&><% RTx::AssetTracker::Type->RoleLabel($role) %>: % my $GroupMembersCount = $Asset->$group->MembersObj->Count(); % my $TypeMembersCount = $Asset->TypeObj->$group->MembersObj->Count(); % if ( $GroupMembersCount ) { <& /Ticket/Elements/ShowGroupMembers, Group => $Asset->$group(), Recursively => RT->Config->Get('ShowGroupMembers') &> % } % if ( $ShowTypeWatchers and $TypeMembersCount ) { <& /Ticket/Elements/ShowGroupMembers, Group => $Asset->TypeObj->$group(), Recursively => RT->Config->Get('ShowGroupMembers') &> % }
    <%ARGS> $Asset => undef $ShowTypeWatchers => $RT::ShowTypeWatchersInAsset rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/ShowSummary000066400000000000000000000111121222742774700272670ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}}
    % $m->callback( %ARGS, CallbackName => 'LeftColumnTop' ); <&| /Widgets/TitleBox, title => loc('The Basics'), ($can_modify ? (title_href => RT->Config->Get('WebPath')."/AssetTracker/Asset/Modify.html?id=".$Asset->Id) : ()), class=> 'asset-info-basics', &> <& /AssetTracker/Asset/Elements/ShowBasics, Asset => $Asset &> % if ( RT->Config->Get('EnableIP') ) { <&| /Widgets/TitleBox, title => loc('IP Addresses'), ($can_modify ? (title_href => RT->Config->Get('WebPath')."/AssetTracker/Asset/ModifyIPs.html?id=".$Asset->Id) : ()), class => 'asset-info-IPs', &> <& /AssetTracker/Asset/Elements/ShowIP, Asset => $Asset &> % } <&| /Widgets/TitleBox, title => loc('People'), (($can_modify || $can_modify_owner || $can_modify_people ) ? (title_href => RT->Config->Get('WebPath')."/AssetTracker/Asset/ModifyPeople.html?id=".$Asset->Id) : ()), class=> 'asset-info-people', &> <& /AssetTracker/Asset/Elements/ShowPeople, Asset => $Asset &> % my (@extra); % push @extra, titleright_raw => ''.loc('Graph').'' unless RT->Config->Get('DisableGraphViz'); % $m->callback( %ARGS, CallbackName => 'LinksExtra', extra => \@extra ); <&| /Widgets/TitleBox, title => loc('Links'), ($can_modify ? (title_href => RT->Config->Get('WebPath')."/AssetTracker/Asset/ModifyLinks.html?id=".$Asset->Id) : ()), class => 'asset-info-links', @extra &><& /AssetTracker/Elements/ShowLinks, Asset => $Asset, TicketLinks => 0 &> % $m->callback( %ARGS, CallbackName => 'LeftColumn' ); % $m->callback( %ARGS, CallbackName => 'RightColumnTop' ); <&| /Widgets/TitleBox, title => loc('Custom Fields'), (($can_modify || $can_modify_cf) ? (title_href => RT->Config->Get('WebPath')."/AssetTracker/Asset/ModifyFields.html?id=".$Asset->Id) : ()), class => 'asset-info-cfs', hide_empty => 1, &><& /AssetTracker/Asset/Elements/ShowCustomFields, Asset => $Asset &> % $m->callback( %ARGS, CallbackName => 'RightColumn' );
    <%ARGS> $Asset => undef <%INIT> my $can_modify = $Asset->CurrentUserHasRight('ModifyAsset'); my $can_modify_cf = $Asset->CurrentUserHasRight('ModifyCustomField'); my $can_modify_owner = $Asset->CurrentUserHasRight('OwnAsset'); my $can_modify_people = $Asset->CurrentUserHasRight('WatchAsAdmin'); rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Elements/ShowType000066400000000000000000000045601222742774700265640ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <% $value %> <%ARGS> $Asset => undef $TypeObj <%INIT> my $value = $TypeObj->Name; if ( $Asset and $Asset->CurrentUserHasRight('SeeType') ) { # Grab the type name anyway if the current user can # see the type based on his role for this asset $value = $TypeObj->__Value('Name'); } $value = '#'. $TypeObj->id unless defined $value && length $value; rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Graphs/000077500000000000000000000000001222742774700245225ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Graphs/Elements/000077500000000000000000000000001222742774700262765ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Graphs/Elements/EditGraphProperties000066400000000000000000000133331222742774700321500ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <&| /Widgets/TitleBox, title => loc('Graph Properties') &> <% loc('Direction') %>
    <% loc('Main type of links') %> <% loc('maximum depth') %>
    <% loc('Show as well') %>: % foreach my $type ( @link_types ) { % my $checked = ''; % $checked = 'checked="checked"' if grep $type eq $_, @ShowLinks; /><% loc($type) %> % }
    % my @properties = RT::Graph::AssetTracker::Assets->AssetProperties( $session{'CurrentUser'} ); <% loc('Fill boxes with color using') %>:
    % if ( RT::Link->can('Description' ) ) { % my $checked = ''; % $checked = 'checked="checked"' if $ShowLinkDescriptions; <% loc('Show link descriptions') %>: />
    % } <%PERL> for my $i ( 1..($MaxDepth||6) ) { my @default; if ( my $tmp = $ARGS{ 'Level-'. $i .'-Properties' } ) { @default = ref $tmp? @$tmp : ($tmp); } $m->comp('SELF:Properties', Level => $i, Available => \@properties, Default => \@default, ); } <& /Elements/Submit, Label => loc('Update Graph'), Name => 'Update' &> <%ARGS> $id => undef $Direction => 'TB' $LeadingLink => 'Members' @ShowLinks => RTx::AssetTracker::Asset::LINKMAP() $MaxDepth => 3 $FillUsing => '' $ShowLinkDescriptions => 0 <%INIT> require RT::Graph::AssetTracker::Assets; require RT::Link; my @link_types = RTx::AssetTracker::Asset::LINKORDER(); @ShowLinks = grep $_ ne $LeadingLink, @ShowLinks; <%METHOD Properties> <%ARGS> @Available => () @Default => () $Level => 1, <%INIT> my $id = "graph-properties-box-$Level"; my $class = ''; $class = 'class="hidden"' if $Level != 1 && !@Default; <% loc('Show Assets Properties on [_1] level', $Level) %> (<% loc('open/close') %>): > % while ( my ($group, $list) = (splice @Available, 0, 2) ) { % }
    <% loc($group) %>: % foreach my $prop ( @$list ) { % my $checked = ''; % $checked = 'checked="checked"' if grep $_ eq $prop, @Default; /><% loc($prop) %> % }

    rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Graphs/Elements/ShowGraph000066400000000000000000000053531222742774700301310ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}}
    <% safe_run_child { Encode::decode_utf8( $graph->as_cmapx ) } |n %>
    <& ShowLegends, %ARGS, Asset => $asset &> <%ARGS> $id => undef <%INIT> use RT::Util 'safe_run_child'; my $asset = RTx::AssetTracker::Asset->new( $session{'CurrentUser'} ); $asset->Load( $id ); unless ( $asset->id ) { $RT::Logger->error("Couldn't load asset $id"); return; } $ARGS{'id'} = $id = $asset->id; require RT::Graph::AssetTracker::Assets; my $graph = RT::Graph::AssetTracker::Assets->AssetLinks( %ARGS, Graph => undef, Asset => $asset, ); rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Graphs/Elements/ShowLegends000066400000000000000000000055401222742774700304470ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <&| /Widgets/TitleBox, title => loc('Legends'), hideable => $hideable &> % if ( $FillUsing ) { % }
    <% loc('Status') %>: % foreach my $status ( sort keys %RT::Graph::AssetTracker::Assets::asset_status_style ) { % my $style = $RT::Graph::AssetTracker::Assets::asset_status_style{ $status }; <% loc($status) %> % }
    <% loc($FillUsing) %>: % foreach my $value ( sort keys %RT::Graph::AssetTracker::Assets::fill_cache ) { % my $color = $RT::Graph::AssetTracker::Assets::fill_cache{ $value }; <% loc($value) %> % }
    <%ARGS> $FillUsing => '' $hideable => 1 rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Graphs/dhandler000066400000000000000000000052041222742774700262270ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my $arg = $m->dhandler_arg; my $id; if ( $arg =~ m{^(\d+)$}i ) { ($id) = ($1); } else { return $m->abort( 404 ); } my $asset = RTx::AssetTracker::Asset->new($session{'CurrentUser'} ); $asset->Load( $id ); unless ( $asset->id ) { $RT::Logger->error("Couldn't load asset #$id"); return $m->abort( 404 ); } require RT::Graph::AssetTracker::Assets; my $graph = RT::Graph::AssetTracker::Assets->AssetLinks( %ARGS, Graph => undef, Asset => $asset, ); $r->content_type( 'image/png' ); $m->clear_buffer; my $png; use RT::Util 'safe_run_child'; safe_run_child { $graph->as_png(\$png) }; $m->out( $png ); $m->abort; rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Graphs/index.html000066400000000000000000000076531222742774700265320ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &> <& Elements/ShowGraph, %ARGS, Asset => $asset &>
    <& Elements/EditGraphProperties, %ARGS, Asset => $asset &> <& /Search/Elements/EditSearches, %$saved_search, Title => loc('Manage saved graphs'), Type => 'AssetGraph', SearchFields => \@save_arguments, CurrentSearch => { map { $_ => $ARGS{$_} } @save_arguments }, AllowCopy => 0, &>
    <%ARGS> <%INIT> use RT::Graph::AssetTracker::Assets; my @results; my @save_arguments = qw(id Direction LeadingLink ShowLinks MaxDepth FillUsing ShowLinkDescriptions); foreach my $level ( 0 .. 6 ) { push @save_arguments, "Level-". $level ."-Properties"; } my $saved_search = { Type => 'AssetGraph' }; push @results, $m->comp( '/Search/Elements/EditSearches:Init', %ARGS, Query => \%ARGS, SavedSearch => $saved_search, SearchFields => \@save_arguments, ); my $id = $ARGS{'id'}; my $asset = LoadAsset( $id ); $ARGS{'id'} = $id = $asset->id; $ARGS{'LeadingLink'} ||= 'Members'; if ( $ARGS{'ShowLinks'} && !ref $ARGS{'ShowLinks'} ) { $ARGS{'ShowLinks'} = [$ARGS{'ShowLinks'}]; } elsif ( !$ARGS{'ShowLinks'} ) { $ARGS{'ShowLinks'} = [ qw(RunsOn DependsOn RefersTo ComponentOf) ]; } $ARGS{'ShowLinks'} = [ grep $_ ne $ARGS{'LeadingLink'}, @{ $ARGS{'ShowLinks'} } ]; $ARGS{'MaxDepth'} = 3 unless defined $ARGS{'MaxDepth'} && length $ARGS{'MaxDepth'}; push @results, $m->comp( '/Search/Elements/EditSearches:Save', %ARGS, Query => \%ARGS, SavedSearch => $saved_search, SearchFields => \@save_arguments, ); my $title = loc( "Asset #[_1] relationships graph", $id ); rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/History.html000066400000000000000000000052061222742774700256300ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => loc("History of asset #[_1]: [_2]", $Asset->Id, $Asset->Name) &> <& /Elements/Tabs &> % $m->callback( %ARGS, Asset => $Asset, CallbackName => 'BeforeActionList' );
    <& /Ticket/Elements/ShowHistory , Ticket => $Asset, ShowDisplayModes => 0, DisplayPath => RT->Config->Get('WebPath')."/AssetTracker/Asset/Display.html?id=".$Asset->id &> % $m->callback( %ARGS, CallbackName => 'AfterShowHistory', Asset => $Asset ); <%ARGS> $id => undef <%INIT> my $Asset = LoadAsset ($id); unless ($Asset->CurrentUserHasRight('ShowAsset')) { Abort("No permission to view asset"); } rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/Modify.html000066400000000000000000000073271222742774700254240ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => loc('Modify asset #[_1]: [_2]', $AssetObj->id, $AssetObj->Name) &> <& /Elements/Tabs &> % $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Asset => $AssetObj); <& /Elements/ListActions, actions => \@results &>
    % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS ); <&| /Widgets/TitleBox, title => loc('Modify asset #[_1]', $AssetObj->Id), class => 'asset-info-basics' &> <& Elements/EditBasics, AssetObj => $AssetObj &> % $m->callback( CallbackName => 'AfterBasics', Asset => $AssetObj ); <&| /Widgets/TitleBox, title => loc('Comments'), class => 'asset-info-comments' &> <& /AssetTracker/Asset/Elements/ChangeComment, Name => "BasicComment" &> %#<& Elements/EditTransactionCustomFields, AssetObj => $AssetObj, TypeObj => $AssetObj->TypeObj &>
    <& /Elements/Submit, Name => 'SubmitAsset', Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to") &>
    <%INIT> my $AssetObj = LoadAsset($id); # Now let callbacks have a chance at editing %ARGS $m->callback( AssetObj => $AssetObj, ARGSRef => \%ARGS ); my @results = ProcessAssetBasics( AssetObj => $AssetObj, ARGSRef => \%ARGS); #$AssetObj->ApplyTransactionBatch; unless ($AssetObj->CurrentUserHasRight('ShowAsset')) { if (@results) { Abort("A change was applied successfully, but you no longer have permissions to view the asset", Actions => \@results); } else { Abort("No permission to view asset"); } } <%ARGS> $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/ModifyAll.html000066400000000000000000000116231222742774700260470ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => loc("Update asset #[_1]: [_2]", $Asset->Id, $Asset->Name) &> <& /Elements/Tabs &> % $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Asset => $Asset); <& /Elements/ListActions, actions => \@results &>
    % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS ); <&| /Widgets/TitleBox, title => loc('Basics'), class => 'asset-info-basics' &> <& Elements/EditBasics, AssetObj => $Asset &> % $m->callback(CallbackName => 'AfterBasics', Asset => $Asset);
    <&| /Widgets/TitleBox, title => loc('People'), class => 'asset-info-people' &> <& Elements/EditPeople, AssetObj => $Asset &>
    <&| /Widgets/TitleBox, title => loc('Fields'), class => 'asset-info-cfs' &> <& Elements/EditCustomFields, AssetObj => $Asset &>
    <&| /Widgets/TitleBox, title => loc('Links'), class => 'asset-info-links' &> <& /AssetTracker/Elements/EditLinks, AssetObj => $Asset, ShowFind => 0 &>
    % if ($RT::EnableIP) { <&| /Widgets/TitleBox, title => loc('IPs'), class => 'asset-info-IPs' &> <& Elements/EditIPs, AssetObj => $Asset &> % }
    <&| /Widgets/TitleBox, title => 'Change Comment', class => 'asset-info-comment' &> <& /AssetTracker/Asset/Elements/ChangeComment, Name => "GlobalComment" &> <& /Elements/Submit, Name => 'SubmitAsset', Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to") &>
    <%INIT> my $Asset = LoadAsset($id); my $CustomFields = $Asset->CustomFields; # call this to show up hints of valid cf values. $m->comp( '/Elements/ValidateCustomFields', CustomFields => $CustomFields, ARGSRef => {}, ); $m->callback( AssetObj => $Asset, ARGSRef => \%ARGS ); my @results; push @results, ProcessAssetWatchers( AssetObj => $Asset, ARGSRef => \%ARGS); push @results, ProcessATObjectCustomFieldUpdates( Object => $Asset, ARGSRef => \%ARGS); push @results, ProcessAssetBasics( AssetObj => $Asset, ARGSRef => \%ARGS); push @results, ProcessAssetLinks( AssetObj => $Asset, ARGSRef => \%ARGS); push @results, ProcessAssetIPs( AssetObj => $Asset, ARGSRef => \%ARGS); push @results, ProcessAssetPorts( AssetObj => $Asset, ARGSRef => \%ARGS); #$Asset->ApplyTransactionBatch; MaybeRedirectForResults( Actions => \@results, Path => "/AssetTracker/Asset/ModifyAll.html", Arguments => { id => $Asset->id }, ); # If they've gone and moved the ticket to somewhere they can't see, etc... unless ($Asset->CurrentUserHasRight('ShowAsset')) { if (@results) { Abort("A change was applied successfully, but you no longer have permissions to view the asset", Actions => \@results); } else { Abort("No permission to view asset"); } } <%ARGS> $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/ModifyFields.html000066400000000000000000000071031222742774700265430ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => loc('Modify custom fields for asset #[_1]', $AssetObj->id) &> <& /Elements/Tabs &> % $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Asset => $AssetObj); <& /Elements/ListActions, actions => \@results &>
    % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS ); <&| /Widgets/TitleBox, title => loc('Modify custom fields for asset #[_1]', $AssetObj->Id), class => 'asset-info-cfs' &> <& Elements/EditCustomFields, AssetObj => $AssetObj, DefaultsFromTopArguments => 0 &> <&| /Widgets/TitleBox, title => loc('Comments'), class => 'asset-info-comments' &> <& /AssetTracker/Asset/Elements/ChangeComment, Name => "FieldComment" &> <& /Elements/Submit, Name => 'SubmitAsset', Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to") &>
    <%INIT> my $AssetObj = LoadAsset($id); my $CustomFields = $AssetObj->CustomFields; # call this to show up hints of valid cf values. $m->comp( '/Elements/ValidateCustomFields', CustomFields => $CustomFields, ARGSRef => {}, ); # Now let callbacks have a chance at editing %ARGS $m->callback( AssetObj => $AssetObj, CustomFields => $CustomFields, ARGSRef => \%ARGS ); my @results = ProcessATObjectCustomFieldUpdates(Object => $AssetObj, ARGSRef => \%ARGS); #$AssetObj->ApplyTransactionBatch; <%ARGS> $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/ModifyIPs.html000066400000000000000000000063351222742774700260360ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => loc('Modify IP addresses related to asset #[_1]', $Asset->id) &> <& /Elements/Tabs &> % $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Asset => $Asset); <& /Elements/ListActions, actions => \@results &>
    % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS ); <&| /Widgets/TitleBox, title => loc('Modify IP addresses related to asset #[_1]', $Asset->Id), class => 'asset-info-IPs' &> <& Elements/EditIPs, AssetObj => $Asset &> % $m->callback( CallbackName => 'AfterIPs', Asset => $Asset ); <&| /Widgets/TitleBox, title => loc('Comments'), class => 'asset-info-comment' &> <& /AssetTracker/Asset/Elements/ChangeComment, Name => "IPComment" &> <& /Elements/Submit, Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to") &>
    <%INIT> my @results; my $Asset = LoadAsset($id); push @results, ProcessAssetIPs( AssetObj => $Asset, ARGSRef => \%ARGS); push @results, ProcessAssetPorts( AssetObj => $Asset, ARGSRef => \%ARGS); #$AssetObj->ApplyTransactionBatch; <%ARGS> $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/ModifyLinks.html000066400000000000000000000070531222742774700264210ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => loc('Modify links related to asset #[_1]', $Asset->id) &> <& /Elements/Tabs &> % $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Asset => $Asset); <& /Elements/ListActions, actions => \@results &>
    % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS ); % my (@extra); % push @extra, titleright_raw => ''.loc('Graph').'' unless RT->Config->Get('DisableGraphViz'); <&| /Widgets/TitleBox, title => loc('Edit Links'), class => 'asset-info-links', @extra &> <& /AssetTracker/Elements/EditLinks, AssetObj => $Asset, AssetField => $AssetField, AssetString => $AssetString, AssetOp => $AssetOp &> <&| /Widgets/TitleBox, title => loc('Comments'), class => 'asset-info-comment' &> <& /AssetTracker/Asset/Elements/ChangeComment, Name => "LinkComment" &> <& /Elements/Submit, Name => 'SubmitTicket', Label => loc('Save Changes') &>
    <%INIT> my $Asset = LoadAsset($id); my @results; $m->callback( AssetObj => $Asset, ARGSRef => \%ARGS, Results => \@results ); # if we're trying to search for watchers and nothing else unless ($OnlySearchForAsset) { @results = ProcessAssetLinks( AssetObj => $Asset, ARGSRef => \%ARGS); } <%ARGS> $OnlySearchForAsset => undef $AssetField => undef $AssetOp => undef $AssetString => undef $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Asset/ModifyPeople.html000066400000000000000000000072301222742774700265620ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => loc('Modify people related to asset #[_1]', $Asset->id) &> <& /Elements/Tabs &> % $m->callback(CallbackName => 'BeforeActionList', Actions => \@results, ARGSRef => \%ARGS, Asset => $Asset); <& /Elements/ListActions, actions => \@results &>
    % $m->callback( CallbackName => 'FormStart', ARGSRef => \%ARGS ); <&| /Widgets/TitleBox, title => loc('Modify people related to asset #[_1]', $Asset->Id), class => 'asset-info-people' &> <& Elements/EditPeople, Asset => $Asset, UserField => $UserField, UserString => $UserString, UserOp => $UserOp, GroupString => $GroupString, GroupOp => $GroupOp, GroupField => $GroupField &> <&| /Widgets/TitleBox, title => loc('Comments'), class => 'asset-info-comment' &> <& /AssetTracker/Asset/Elements/ChangeComment, Name => "PeopleComment" &> <& /Elements/Submit, Name => 'SubmitAsset', Label => loc('Save Changes'), Caption => loc("If you've updated anything above, be sure to") &>
    <%INIT> my (@results, @wresults); my $Asset = LoadAsset($id); # if we're trying to search for watchers and nothing else unless ($OnlySearchForPeople or $OnlySearchForGroup) { @results = ProcessAssetBasics( AssetObj => $Asset, ARGSRef => \%ARGS); @wresults = ProcessAssetWatchers( AssetObj => $Asset, ARGSRef => \%ARGS); #$Asset->ApplyTransactionBatch; } push @results, @wresults; <%ARGS> $OnlySearchForPeople => undef $OnlySearchForGroup => undef $UserField => undef $UserOp => undef $UserString => undef $GroupField => undef $GroupOp => undef $GroupString => undef $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/000077500000000000000000000000001222742774700237735ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/AddLinks000066400000000000000000000040421222742774700254070ustar00rootroot00000000000000% if ($Assets and $Assets->Count and $ShowFind) {

    <&|/l&>Add new links to Assets:
    % while (my $a = $Assets->Next ) { % }
    <&|/l&>Type <&|/l&>Asset
    % if ($TicketLinkTypes) { <&/AssetTracker/Elements/SelectTicketLinkType, Name => "AddLink-Asset-".$a->URI &> % } else { <&/AssetTracker/Elements/SelectLinkType, Name => "AddLink-Asset-".$a->URI &> % } <%$a->Name%> (<% substr($a->Description,0,35) %>)
    % } % if (!$SearchOnly) {

    <&|/l&>Add new links to:
    <&|/l&>Type <&|/l&>URI
    <&/AssetTracker/Elements/SelectLinkType, Name => "AddLink-Other" &>
    % } <%init> my ($Assets); if ($AssetString) { $Assets = RTx::AssetTracker::Assets->new($session{'CurrentUser'}); if ( $AssetField eq 'Type' ) { my $Types = RTx::AssetTracker::Types->new($session{'CurrentUser'}); $Types->Limit(FIELD => 'Name', OPERATOR => $AssetOp, VALUE => $AssetString); while (my $type = $Types->Next) { $Assets->Limit(FIELD => 'Type', VALUE => $type->Id) if $type->Id; } } elsif (($AssetField eq 'Name') or ($AssetField eq 'Description') or ($AssetField eq 'Status') ) { $Assets->Limit(FIELD => $AssetField, VALUE => $AssetString, OPERATOR => $AssetOp); } else { $Assets->LimitCustomField(CUSTOMFIELD => $AssetField, VALUE => $AssetString, OPERATOR => $AssetOp ); } $Assets->Limit(FIELD => 'Status', VALUE => 'retired', OPERATOR => '!=') unless $AllowRetired; #eliminate the assets own id $Assets->Limit(FIELD => 'id', VALUE => $AssetObj->Id, OPERATOR => '!=') if ref $AssetObj; } <%ARGS> $AssetField => 'Name' $AssetOp => '=' $AssetString => undef $AllowRetired => 0 $AssetObj => undef $ShowFind => 1 $SearchOnly => 0 $TicketLinkTypes => 0 rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/CollectionAsTSV/000077500000000000000000000000001222742774700267475ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/CollectionAsTSV/AssetList000066400000000000000000000041601222742774700306060ustar00rootroot00000000000000% if ($ShowHeader) { <& /AssetTracker/Elements/CollectionAsTSV/Header, Format => \@Format, AllowSorting => $AllowSorting, Order => $Order, Query => $Query, Rows => $Rows, Page => $Page, OrderBy => $OrderBy , BaseURL => $BaseURL, maxitems => $maxitems &>\ % } % my $i; % while (my $record = $Collection->Next) { % $i++; % # Every ten rows, flush the buffer and put something on the page. % $m->flush_buffer() unless ($i % 10); <& /AssetTracker/Elements/CollectionAsTSV/Row, Format => \@Format, i => $i, record => $record, maxitems => $maxitems &>\ % } % $m->abort(); <%INIT> $r->content_type('application/vnd.ms-excel'); my $maxitems = 0; $Format ||= RT->Config->Get('DefaultAssetSearchResultFormat'); # Scrub the html of the format string to remove any potential nasties. $Format = $m->comp('/Elements/ScrubHTML', Content => $Format); unless ($Collection) { $Collection = RTx::AssetTracker::Assets->new($session{'CurrentUser'}); $Collection->FromSQL($Query); } my (@Format) = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $Format); # Find the maximum number of items in any row, so we can pad the table. my $item = 0; foreach my $col (@Format) { $item++; if ( defined $col->{title} && $col->{title} eq 'NEWLINE' ) { $item = 0; } else { $maxitems = $item if $item > $maxitems; } } if ( $OrderBy =~ /\|/ ) { # Multiple Sorts my @OrderBy = split /\|/, $OrderBy; my @Order = split /\|/, $Order; $Collection->OrderByCols( map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0 .. $#OrderBy ) ); } else { $Collection->OrderBy( FIELD => $OrderBy, ORDER => $Order ); } #$Collection->RowsPerPage($Rows) if ($Rows); #$Collection->GotoPage($Page-1); # SB uses page 0 as the first page my $TotalFound = $Collection->CountAll(); <%ARGS> $Query => undef $Rows => 0 $Page => 1 $Title => 'Asset Search' $Collection => undef $AllowSorting => undef $Order => undef $OrderBy => undef $BaseURL => undef $Format => $RT::DefaultAssetSearchResultFormat $ShowNavigation => 1 $ShowHeader => 1 rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/CollectionAsTSV/Header000066400000000000000000000060501222742774700300630ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Class => 'RTx__AssetTracker__Asset' @Format => undef $AllowSorting => undef $Order=>undef $BaseURL => undef $Query => undef $Rows => undef $Page => undef $maxitems => undef <%perl> $Rows = 0; my %generic_query_args = ( Query => $Query, Rows => $Rows, Page => $Page ); foreach my $col (@Format) { my $title = $col->{'title'} || ''; if ( $title eq 'NEWLINE' ) { $m->out(qq{\n}); next; } elsif ( $title eq 'NBSP' ) { $m->out("\t"); next; } # if title is not defined then use defined attribute or last # one we saw in the format unless ( defined $col->{'title'} ) { my $attr = $col->{'attribute'} || $col->{'last_attribute'}; my $tmp = $m->comp( '/Elements/ColumnMap', Class => $Class, Name => $attr, Attr => 'title', ); $title = ProcessColumnMapValue( $tmp, Arguments => [ $attr ], Escape => 0 ); } $title =~ s/(?:\n|\r)//g; $title =~ s{\t}{ }g; $m->out( loc($title) ); $m->out("\t"); } rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/CollectionAsTSV/Row000066400000000000000000000026701222742774700274460ustar00rootroot00000000000000<%ARGS> $i => undef @Format => undef $record => undef $maxitems => undef $Depth => undef $Warning => undef $ColumnMap => {} $Class => 'RTx__AssetTracker__Asset' $Classes => '' $TrailingNewline => 1 <%PERL> foreach my $column (@Format) { if ( defined $column->{title} && $column->{title} eq 'NEWLINE' ) { $m->out( "\n" ); next; } my @out; foreach my $subcol ( @{ $column->{output} } ) { my ($col) = ($subcol =~ /^__(.*?)__$/); unless ( $col ) { push @out, $subcol; next; } unless ( exists $ColumnMap->{$col}{'value'} ) { $ColumnMap->{$col}{'value'} = $m->comp( "/Elements/ColumnMap", Class => $Class, Name => $col, Attr => 'value' ); } push @out, ProcessColumnMapValue( $ColumnMap->{$col}{'value'}, Arguments => [$record, $i], Escape => 0, ); } s/\t/ \[TAB\] /gs for @out; $m->out(@out) if @out; $m->out( "\t" ); } #$m->out("\n"); <%FILTER> # replace line breaks with comma-space unless it is the last one, which will get scrubbed s/\([^\t])/, $1/ig; # no HTML my $scrubber = HTML::Scrubber->new( deny => [ qw(*) ] ); #$scrubber->comment(0); $_ = ( $scrubber->scrub($_) ); # The only newline should be at the end of the line s/\n//g; $_ .= "\n" if $TrailingNewline; rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/CreateAsset000066400000000000000000000051441222742774700261250ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}}

    callback(CallbackName => 'InFormElement'); > % my $button_start = ''; % my $type_selector = $m->scomp('/AssetTracker/Elements/SelectNewAssetType', OnChange => 'document.CreateAssetOfType.submit()', SendTo => $SendTo ); <&|/l_unsafe, $button_start, $button_end, $type_selector &>[_1]New asset of type [_2] [_3] % $m->callback(CallbackName => 'BeforeFormEnd');
    <%ARGS> $SendTo => '/AssetTracker/Asset/Create.html', rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/DeleteLinkPair000066400000000000000000000020441222742774700265520ustar00rootroot00000000000000 <& ShowRelationLabel, uri => $uri, Label => loc($Base_name), Relation => $Base &>: % while (my $link = $Object->$Base->Next) { <& /Elements/ShowLink, URI => $link->TargetURI &>
    % } <& ShowRelationLabel, uri => $uri, Label => loc($Target_name), Relation => $Target &>: % while (my $link = $Object->$Target->Next) { <& /Elements/ShowLink, URI => $link->BaseURI &>
    % } <%INIT> my $uri; if ($Object && $Object->Id) { $uri = $Object->URI; } else { $uri = 'new'; } my ($Target, $Base_name, $Target_name); $Base_name = $Map->{$Base}{Name}; $Target = $Map->{$Base}{Mate}; $Target_name = $Map->{$Target}{Name}; <%ARGS> $Object $Base $Map rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/DeleteLinks000066400000000000000000000011371222742774700261230ustar00rootroot00000000000000

    <&|/l&>Current Links

    % for my $link ( keys %$link_dir ) { <& DeleteLinkPair, Object => $Object, Base => $link, Map => $link_types &> % }
    <&|/l&>(Check box to delete)
    <%INIT> my $id; if ($Object && $Object->Id) { $id = $Object->Id; } else { $id = 'new'; } my $link_types = RTx::AssetTracker::Asset::LINKTYPEMAP(); my $link_dir = RTx::AssetTracker::Asset::LINKDIRMAP(); <%ARGS> $Object => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/EditLinks000066400000000000000000000011321222742774700256010ustar00rootroot00000000000000
    %#

    <&|/l&>New links

    % if ($ShowFind) { <&|/l&>Find assets whose
    <& /AssetTracker/Elements/SelectAssets &> % } <& AddLinks, %ARGS &>
    % if (ref $AssetObj) { <& DeleteLinks, Object => $AssetObj &> % }
    <%ARGS> $AssetField => undef $AssetOp => undef $AssetString => undef $AssetObj => undef $ShowFind => 1 $SearchOnly => 0 $TicketLinkTypes => 0 rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/Quicksearch000066400000000000000000000051271222742774700261650ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}}
    <&|/Widgets/TitleBox, title => loc("Quick search"), bodyclass => "", titleright => loc("Edit"), titleright_href => RT->Config->Get('WebPath').'/Prefs/AssetQuicksearch.html', &> <& $comp, type_filter => sub { $_->CurrentUserHasRight('ShowAsset') && !exists $unwanted->{$_->Name} }, &>
    <%INIT> my $unwanted = $session{'CurrentUser'}->UserObj->Preferences('AssetQuickSearch', {}); my $comp = $SplitByLifecycle? '/AssetTracker/Elements/TypeSummaryByLifecycle' : '/AssetTracker/Elements/TypeSummaryByStatus'; <%ARGS> $SplitByLifecycle => 1 rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/RecentAssets000066400000000000000000000053131222742774700263230ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <&| /Widgets/TitleBox, title => loc('[_1] Most recently updated assets', $rows), title_href => "Search/Results.html".$QueryString &> <& /Elements/CollectionList, Collection => $Assets, Format => "'__Name__/TITLE:Name', Description, TypeName, Status", Query => $Query, OrderBy => 'LastUpdated', Order => 'DESC', Rows => $rows, ShowNavigation => 0 &> <%init> my $rows = 10; my $Query = " id > 0"; my $QueryString = '?' . $m->comp('/Elements/QueryString', Query => $Query, Order => 'DESC', OrderBy => 'LastUpdated') if ($Query); my $Assets = RTx::AssetTracker::Assets->new($session{'CurrentUser'}); $Assets->FromSQL($Query); rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectAssets000066400000000000000000000061261222742774700263250ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/SelectMatch, Name => 'AssetOp', Default => $AssetOp &> <%INIT> my $CFs = RT::CustomFields->new($session{'CurrentUser'}); $CFs->LimitToChildType('RTx::AssetTracker::Type-RTx::AssetTracker::Asset'); $CFs->OrderBy( FIELD => 'Name' ); my @fields = RTx::AssetTracker::Asset->BasicColumns; if ( $Fields and ref $Fields eq 'ARRAY' ) { if ( ref $Fields->[0] eq 'ARRAY' ) { @fields = @$Fields; } else { # make the name equal the label @fields = [ @$Fields, @$Fields ]; } } <%ARGS> $AssetField => '' $AssetOp => '' $AssetString => '' $Fields => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectAttachmentField000066400000000000000000000043571222742774700301230ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name => 'AttachmentField' rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectDateType000066400000000000000000000042541222742774700266020ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name => 'DateType' rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectGroupsWithRight000066400000000000000000000071571222742774700302010ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my @objects; my @equiv_objects; my @groups; if ($AssetObj) { @objects = ($AssetObj); @equiv_objects = $AssetObj->TypeObj; } elsif ($TypeObj) { @objects = ($TypeObj); } elsif ($cftypes) { @objects = keys %{$cftypes}; } else { my $Types = RTx::AssetTracker::Types->new($session{CurrentUser}); $Types->UnLimit(); while (my $Type = $Types->Next()) { push( @objects, $Type ); } } my %group_uniq_hash; foreach my $object (@objects) { my $Groups = RT::Groups->new($session{CurrentUser}); $Groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => $Domain); $Groups->WithRight(Right => $Right, Object => $object, EquivObjects => [ @equiv_objects ], IncludeSystemRights => $IncludeSystemRights, IncludeSuperusers => $IncludeSuperusers, ); while (my $Group = $Groups->Next()) { $group_uniq_hash{$Group->Id()} = $Group; } } @groups = sort { uc($a->Name) cmp uc($b->Name) } values %group_uniq_hash; <%ARGS> $Name => undef $Default => 0 $TypeObj => undef $AssetObj => undef $Right $DefaultValue => 0 $DefaultLabel => "-" $cftypes => undef $IncludeSystemRights => 1 $IncludeSuperusers => 0 $Size => 5 $Domain => 'UserDefined'; rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectIPField000066400000000000000000000043301222742774700263320ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name => 'IPField' rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectLinkType000066400000000000000000000046041222742774700266210ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name => "LinkType" $Default => undef <%INIT> # TODO handle Default my %link_types = %{ RTx::AssetTracker::Asset::LINKTYPEMAP() }; my @link_order = RTx::AssetTracker::Asset::LINKORDER(); rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectNewAssetType000066400000000000000000000047121222742774700274550ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my $type = RT->Config->Get("DefaultType", $session{'CurrentUser'}); if (RT->Config->Get("RememberDefaultType", $session{'CurrentUser'})) { if (my $session_default = $session{'DefaultType'}) { $type = $session_default; } } $m->callback(Type => \$type, CallbackName => 'DefaultType'); rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectProtocol000066400000000000000000000006041222742774700266570ustar00rootroot00000000000000 <%ARGS> $Name => undef $Default => '' $DefaultValue => 1 $DefaultLabel => "-" rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectStatus000066400000000000000000000116221222742774700263430ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> ### XXX: no cover for Tools/MyDay.html my %statuses_by_lifecycle; if ( @Statuses ) { $statuses_by_lifecycle{''} = \@Statuses; } elsif ( $AssetObj ) { my @status; my $current = $AssetObj->Status; push @status, $current; my $lifecycle = $AssetObj->TypeObj->Lifecycle; my %has = (); foreach my $next ( $lifecycle->Transitions( $current ) ) { my $check = $lifecycle->CheckRight( $current => $next ); $has{ $check } = $AssetObj->CurrentUserHasRight( $check ) unless exists $has{ $check }; push @status, $next if $has{ $check }; } $statuses_by_lifecycle{$lifecycle->Name} = \@status; } elsif ( $TypeObj ) { my $lifecycle = $TypeObj->Lifecycle; $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Transitions('') ]; } elsif ( %Types ) { for my $id (keys %Types) { my $type = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $type->Load($id); if ($type->id) { my $lifecycle = $type->Lifecycle; $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Valid ]; } } } else { for my $lifecycle (map { RT::Lifecycle->Load($_) } RT::Lifecycle->List) { $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Valid ]; } } if (keys %statuses_by_lifecycle) { my %simplified; my $key = sub { join "\0", sort @{$_[0]}; }; for my $name (sort keys %statuses_by_lifecycle) { my $matched; my $statuses = $statuses_by_lifecycle{$name}; for my $simple (sort keys %simplified) { if ($key->($statuses) eq $key->($simplified{$simple})) { # Statuses are the same, join 'em! $simplified{"$simple, $name"} = delete $simplified{$simple}; $matched++; last; } } unless ($matched) { $simplified{$name} = $statuses; } } %statuses_by_lifecycle = %simplified; } my $group_by_lifecycle = keys %statuses_by_lifecycle > 1; <%ARGS> $Name => undef @Statuses => () $AssetObj => undef $TypeObj => undef %Types => () $Default => '' $SkipDeleted => 0 $DefaultValue => 1 $DefaultLabel => "-" $Multiple => 0 $Size => 6 rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectTicketLinkType000066400000000000000000000047061222742774700277700ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name => "LinkType" $Default => undef <%INIT> # TODO handle Default my %link_types = ( MemberOf => 'Parent', HasMember => 'Child', RefersTo => 'Refers To', ReferredToBy => 'Referred To By', DependsOn => 'Depends On', DependedOnBy => 'Depended On By'); rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectTransport000066400000000000000000000006051222742774700270530ustar00rootroot00000000000000 <%ARGS> $Name => undef $Default => '' $DefaultValue => 1 $DefaultLabel => "-" rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectType000066400000000000000000000077241222742774700260110ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} % if ($Lite) { % my $d = RTx::AssetTracker::Type->new($session{'CurrentUser'}); % $d->Load($Default); % } % else { % } <%args> $CheckTypeRight => 'CreateAsset' $ShowNullOption => 1 $ShowAllTypes => 1 $Name => undef $Verbose => undef $NamedValues => 0 $DefaultLabel => "-" $Default => 0 $Lite => 0 $OnChange => undef $Multiple => 0 $Size => 6 $Class => 'select-type' <%init> my $cache_key = "SelectType---" . $session{'CurrentUser'}->Id . "---$CheckTypeRight---$ShowAllTypes"; if ( defined $session{$cache_key} && ref $session{$cache_key} eq 'ARRAY') { delete $session{$cache_key}; } if ( defined $session{$cache_key} && $session{$cache_key}{lastupdated} <= RT->System->QueueCacheNeedsUpdate ) { delete $session{$cache_key}; } if ( not defined $session{$cache_key} and not $Lite ) { my $t = RTx::AssetTracker::Types->new($session{'CurrentUser'}); $t->UnLimit; while (my $type = $t->Next) { if ($ShowAllTypes || $type->CurrentUserHasRight($CheckTypeRight)) { push @{$session{$cache_key}{types}}, { Id => $type->Id, Name => $type->Name, Description => $type->Description, }; } } $session{$cache_key}{lastupdated} = time(); } rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectUsers000066400000000000000000000066301222742774700261640ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my @objects; my @users; if ($AssetObj) { @objects = ($AssetObj); } elsif ($TypeObj) { @objects = ($TypeObj); } elsif ($cfqueues) { @objects = keys %{$cfqueues}; } else { my $Types = RTx::AssetTracker::Types->new($session{CurrentUser}); $Types->UnLimit(); while (my $Type = $Types->Next()) { push( @objects, $Type ); } } my %user_uniq_hash; foreach my $object (@objects) { my $Users = RT::Users->new($session{CurrentUser}); $Users->WhoHaveRight(Right => $Right, Object => $object, IncludeSystemRights => 1, IncludeSuperusers => 0); while (my $User = $Users->Next()) { $user_uniq_hash{$User->Id()} = $User; } } @users = sort { uc($a->Name) cmp uc($b->Name) } values %user_uniq_hash; <%ARGS> $TypeObj => undef $Name => undef $Default => 0 $User => undef $AssetObj => undef $Right $DefaultValue => 1 $DefaultLabel => "-" $cfqueues => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectUsersWithRight000066400000000000000000000073011222742774700300120ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my @objects; my @equiv_objects; my @users; if ($AssetObj) { @objects = ($AssetObj); @equiv_objects = $AssetObj->TypeObj; } elsif ($TypeObj) { @objects = ($TypeObj); } elsif ($cftypes) { @objects = keys %{$cftypes}; } else { my $Types = RTx::AssetTracker::Types->new($session{CurrentUser}); $Types->UnLimit(); while (my $Type = $Types->Next()) { push( @objects, $Type ); } } my %user_uniq_hash; foreach my $object (@objects) { my $Users = RT::Users->new($session{CurrentUser}); $Users->WhoHaveRight(Right => $Right, Object => $object, EquivObjects => [ @equiv_objects ], IncludeSystemRights => $IncludeSystemRights, IncludeSuperusers => $IncludeSuperusers, ); while (my $User = $Users->Next()) { $user_uniq_hash{$User->Id()} = $User; } } @users = sort { uc($a->Name) cmp uc($b->Name) } values %user_uniq_hash; <%ARGS> $Name => undef $Default => 0 $TypeObj => undef $AssetObj => undef $Right $DefaultValue => 0 $DefaultLabel => "-" $cftypes => undef $IncludeSystemRights => 1 $IncludeSuperusers => 0 $Verbose => 0 $SortFriendly => 0 $Size => 5 rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SelectWatcherType000066400000000000000000000046131222742774700273210ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my @types = RTx::AssetTracker::Type->RoleGroupTypes; <%ARGS> $AllowNull => 1 $Default=>undef $Scope => 'asset' $Name => 'WatcherType' rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/ShowAsset000066400000000000000000000011031222742774700256310ustar00rootroot00000000000000 <%$Asset->TypeObj->Name%>: <%$Asset->Name%> [<%$Asset->Status%>] <%INIT> unless ($Asset) { Abort("No asset specified!") unless $id; Abort("Bad id: $id") unless ($id =~ /\d+/); $Asset = RTx::AssetTracker::Asset->new($session{CurrentUser}); $Asset->Load($id); Abort("Asset could not be loaded!") unless $Asset->Id; } Abort("No permission to view asset!") unless $Asset->CurrentUserHasRight('ShowAsset'); <%ARGS> $Asset => undef $id => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/ShowLinkPair000066400000000000000000000024541222742774700262750ustar00rootroot00000000000000% for ( \%Base, \%Target ) { % no strict 'refs'; <&|/l&><% $_->{Name} %>:
      % my $method = $_->{Method}; % while (my $Link = $Asset->$method->Next) { % my $uri_method = $_->{Mode} . 'URI'; % if ( ( $SkipTickets || $ShowOnlyTickets ) && $Link->$uri_method->IsLocal ) { % my $object = $_->{Mode} eq 'Base' ? $Link->TargetObj : $Link->BaseObj; % if ( $SkipTickets && $method eq 'ReferredToBy' && ref( $Link->BaseObj ) eq 'RT::Ticket' ) { next; } % if ( $ShowOnlyTickets && ref( $object ) ne 'RT::Ticket' ) { next; } % if ( $SkipResolvedTickets && ref( $object ) eq 'RT::Ticket' % && $object->QueueObj->IsInactiveStatus( $object->Status ) ) { next; } % }
    • <& /Elements/ShowLink, URI => $_->{Mode} eq 'Base' ? $Link->TargetURI : $Link->BaseURI &> % }
    % } <%INIT> my ($Target, $Base_name, $Target_name); $Base_name = $Map->{$Base}{Name}; $Target = $Map->{$Base}{Mate}; $Target_name = $Map->{$Target}{Name}; my %Base = ( Name => $Base_name, Method => $Base, Mode => 'Base' ); my %Target = ( Name => $Target_name, Method => $Target, Mode => 'Target' ); <%ARGS> $Asset $Base $Map $SkipTickets => 1 $SkipResolvedTickets => 0 $ShowOnlyTickets => 0 rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/ShowLinks000066400000000000000000000046151222742774700256450ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} % for my $link ( keys %$link_dir ) { <& ShowLinkPair, Asset => $Asset, Base => $link, Map => $link_types &> % } % # Allow people to add more rows to the table % $m->callback( %ARGS );
    <%INIT> my $link_types = RTx::AssetTracker::Asset::LINKTYPEMAP(); my $link_dir = RTx::AssetTracker::Asset::LINKDIRMAP(); <%ARGS> $Asset => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/ShowRelationLabel000066400000000000000000000047571222742774700273110ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%$Label%> <%INIT> my $typemap = RTx::AssetTracker::Asset->LINKTYPEMAP->{$Relation}; my $search_mode = $typemap->{Mode}; my $search_type = $typemap->{Type}; my $search_relation = RTx::AssetTracker::Asset->LINKDIRMAP->{$search_type}{$search_mode}; my $Query = $search_relation . " = '$uri'"; my $SearchURL = RT->Config->Get('WebPath') . '/AssetTracker/Search/Results.html?' . $m->comp('/Elements/QueryString', Query => $Query); <%ARGS> $uri $Label $Relation rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/ShowTicketLinks000066400000000000000000000026451222742774700270120ustar00rootroot00000000000000<&| /Widgets/TitleBox, title => loc('[_1] Most recent tickets', $Rows), title_href => RT->Config->Get('WebPath').'/Search/Results.html?' . $m->comp('/Elements/QueryString', %query_args), class => 'asset-info-tickets' &> <& /Elements/CollectionList, Class => 'RT::Tickets', ShowNavigation => 0, %query_args, &>
    <&|/l_unsafe, $m->scomp('/Elements/SelectNewTicketQueue' )&> [_1]
    <%init> my $user = $session{'CurrentUser'}->UserObj; my $Rows = $user->Preferences( 'SummaryRows', ( RT->Config->Get('DefaultSummaryRows') || 10 ) ); my %query_args = ( Query => "RefersTo = '" . $Asset->URI . "'", Format => qq{ '__id__/TITLE:#', '__Subject__/TITLE:Subject', '__QueueName__', '__Status__', '__OwnerName__', '__LastUpdatedRelative__'}, OrderBy => 'LastUpdated', Order => 'DESC', Rows => $Rows, ); <%args> $Asset => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/SimpleSearch000066400000000000000000000044711222742774700263030ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $SendTo => '/AssetTracker/index.html' rt-extension-assettracker-3.0.0/html/AssetTracker/Elements/TypeSummaryByLifecycle000066400000000000000000000122411222742774700303300ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%perl> foreach my $lifecycle ( map $lifecycle{$_}, sort keys %lifecycle ) { my @cur_statuses = grep $lifecycle->IsValid($_), @statuses; next unless @cur_statuses; % for my $status ( @cur_statuses ) { % } <%PERL> my $i = 0; for my $type (@types) { next if lc($type->{Lifecycle} || '') ne lc $lifecycle->Name; $i++; % for my $status (@cur_statuses) { % } % }
    <&|/l&>Type<% loc($status) %>
    <% $type->{Name} %> <% $data->{$type->{id}}->{lc $status } || '-' %>
    % } <%INIT> my $build_search_link = sub { my ($type_name, $extra_query) = @_; $type_name =~ s/(['\\])/\\$1/g; #' return RT->Config->Get('WebPath') . "/AssetTracker/Search/Results.html?Query=" . $m->interp->apply_escapes("Type = '$type_name' AND $extra_query", 'u'); }; my $link_all = sub { my ($type, $all_statuses) = @_; return $build_search_link->($type->{Name}, "(".join(" OR ", map "Status = '$_'", @$all_statuses).")"); }; my $link_status = sub { my ($type, $status) = @_; return $build_search_link->($type->{Name}, "Status = '$status'"); }; $m->callback( CallbackName => 'LinkBuilders', build_search_link => \$build_search_link, link_all => \$link_all, link_status => \$link_status, ); my $Types = RTx::AssetTracker::Types->new( $session{'CurrentUser'} ); $Types->UnLimit(); $m->callback( CallbackName => 'SQLFilter', Types => $Types ); my @types = grep $type_filter->($_), @{ $Types->ItemsArrayRef }; $m->callback( CallbackName => 'Filter', Types => \@types ); @types = map { { id => $_->Id, Name => $_->Name, Description => $_->Description || '', Lifecycle => $_->Lifecycle->Name, } } grep $_, @types; my %lifecycle; for my $type (@types) { my $cycle = RT::Lifecycle->Load( $type->{'Lifecycle'} ); $lifecycle{ lc $cycle->Name } = $cycle; } unless (@statuses) { my %seen; foreach my $set ( 'initial', 'active' ) { foreach my $lifecycle ( map $lifecycle{$_}, sort keys %lifecycle ) { push @statuses, grep !$seen{ lc $_ }++, $lifecycle->Valid($set); } } } my $data = {}; my $statuses = {}; use RT::Report::Assets; my $report = RT::Report::Assets->new( RT->SystemUser ); my $query = "(". join(" OR ", map {s{(['\\])}{\\$1}g; "Status = '$_'"} @statuses) #' .") AND (". join(' OR ', map "Type = ".$_->{id}, @types) .")"; $query = 'id < 0' unless @types; $report->SetupGroupings( Query => $query, GroupBy => [qw(Status Type)] ); while ( my $entry = $report->Next ) { $data->{ $entry->__Value("Type") }->{ $entry->__Value("Status") } = $entry->__Value('id'); $statuses->{ $entry->__Value("Status") } = 1; } <%ARGS> $type_filter => undef @statuses => () rt-extension-assettracker-3.0.0/html/AssetTracker/Search/000077500000000000000000000000001222742774700234245ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Build.html000066400000000000000000000255641222742774700253650ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# %# Data flow here: %# The page receives a Query from the previous page, and maybe arguments %# corresponding to actions. (If it doesn't get a Query argument, it pulls %# one out of the session hash. Also, it could be getting just a raw query from %# Build/Edit.html (Advanced).) %# %# After doing some stuff with default arguments and saved searches, the ParseQuery %# function (which is similar to, but not the same as, _parser in lib/RT/Tickets_SQL.pm) %# converts the Query into a RT::Interface::Web::QueryBuilder::Tree. This mason file %# then adds stuff to or modifies the tree based on the actions that had been requested %# by clicking buttons. It then calls GetQueryAndOptionList on the tree to generate %# the SQL query (which is saved as a hidden input) and the option list for the Clauses %# box in the top right corner. %# %# Worthwhile refactoring: the tree manipulation code for the actions could use some cleaning %# up. The node-adding code is different in the "add" actions from in ParseQuery, which leads %# to things like ParseQuery correctly not quoting numbers in numerical fields, while the "add" %# action does quote it (this breaks SQLite). %# <& /Elements/Header, Title => $title &> <& /Elements/Tabs, %TabArgs &>
    <& Elements/PickCriteria, query => $query{'Query'}, types => $types &>
    <& /Elements/Submit, Label => loc('Add these terms'), SubmitId => 'AddClause', Name => 'AddClause'&> <& /Elements/Submit, Label => loc('Add these terms and Search'), SubmitId => 'DoSearch', Name => 'DoSearch'&>
    <& /Search/Elements/EditQuery, %ARGS, actions => \@actions, optionlist => $optionlist, Description => $saved_search{'Description'}, &>
    <& /Search/Elements/EditSearches, %saved_search, CurrentSearch => \%query, Type => 'Asset' &>
    <& Elements/DisplayOptions, %ARGS, %query, AvailableColumns => $AvailableColumns, CurrentFormat => $CurrentFormat, &> <& /Elements/Submit, Label => loc('Update format and Search'), Name => 'DoSearch', id=>"formatbuttons"&>
    <%INIT> use RT::Interface::Web::QueryBuilder; use RT::Interface::Web::QueryBuilder::Tree; $ARGS{SavedChartSearchId} ||= 'new'; my $title = loc("Asset Query Builder"); my %query; for( qw(Query Format OrderBy Order RowsPerPage) ) { $query{$_} = $ARGS{$_}; } my %saved_search = ( Type => 'Asset' ); my @actions = $m->comp( '/Search/Elements/EditSearches:Init', %ARGS, Query => \%query, SavedSearch => \%saved_search); if ( $NewQuery ) { # Wipe all data-carrying variables clear if we want a new # search, or we're deleting an old one.. %query = (); %saved_search = ( Id => 'new', Type => 'Asset' ); # ..then wipe the session out.. delete $session{'CurrentAssetSearchHash'}; # ..and the search results. $session{'assets'}->CleanSlate if defined $session{'assets'}; } { # Attempt to load what we can from the session and preferences, set defaults my $current = $session{'CurrentAssetSearchHash'}; my $prefs = $session{'CurrentUser'}->UserObj->Preferences("AssetSearchDisplay") || {}; my $default = { Query => '', Format => '', OrderBy => 'id', Order => 'ASC', RowsPerPage => 50 }; for( qw(Query Format OrderBy Order RowsPerPage) ) { $query{$_} = $current->{$_} unless defined $query{$_}; $query{$_} = $prefs->{$_} unless defined $query{$_}; $query{$_} = $default->{$_} unless defined $query{$_}; } for( qw(Order OrderBy) ) { if (ref $query{$_} eq "ARRAY") { $query{$_} = join( '|', @{ $query{$_} } ); } } if ( $query{'Format'} ) { # Clean unwanted junk from the format $query{'Format'} = $m->comp( '/Elements/ScrubHTML', Content => $query{'Format'} ); } } my $ParseQuery = sub { my ($string, $results) = @_; my $tree = RT::Interface::Web::QueryBuilder::Tree->new('AND'); @$results = $tree->ParseAssetSQL( Query => $string, CurrentUser => $session{'CurrentUser'} ); return $tree; }; my @parse_results; my $tree = $ParseQuery->( $query{'Query'}, \@parse_results ); # if parsing went poorly, send them to the edit page to fix it if ( @parse_results ) { push @actions, @parse_results; return $m->comp( "Edit.html", Query => $query{'Query'}, Format => $query{'Format'}, SavedSearchId => $saved_search{'Id'}, SavedChartSearchId => $ARGS{'SavedChartSearchId'}, actions => \@actions, ); } my @options = $tree->GetDisplayedNodes; my @current_values = grep defined, @options[@clauses]; my @new_values = (); # Try to find if we're adding a clause foreach my $arg ( keys %ARGS ) { next unless $arg =~ m/^ValueOf(\w+|'\w*CF\.\{.*?\}')$/ && ( ref $ARGS{$arg} eq "ARRAY" ? grep $_ ne '', @{ $ARGS{$arg} } : $ARGS{$arg} ne '' ); # We're adding a $1 clause my $field = $1; my ($op, $value); #figure out if it's a grouping my $keyword = $ARGS{ $field . "Field" } || $field; my ( @ops, @values ); if ( ref $ARGS{ 'ValueOf' . $field } eq "ARRAY" ) { # we have many keys/values to iterate over, because there is # more than one CF with the same name. @ops = @{ $ARGS{ $field . 'Op' } }; @values = @{ $ARGS{ 'ValueOf' . $field } }; } else { @ops = ( $ARGS{ $field . 'Op' } ); @values = ( $ARGS{ 'ValueOf' . $field } ); } $RT::Logger->error("Bad Parameters passed into Query Builder") unless @ops == @values; for ( my $i = 0; $i < @ops; $i++ ) { my ( $op, $value ) = ( $ops[$i], $values[$i] ); next if !defined $value || $value eq ''; if ( $value =~ /^NULL$/i && $op =~ /=/ ) { if ( $op eq '=' ) { $op = "IS"; } elsif ( $op eq '!=' ) { $op = "IS NOT"; } } elsif ($value =~ /\D/) { $value =~ s/(['\\])/\\$1/g; $value = "'$value'"; } if ($keyword =~ /^'(\w*CF)\.\{(.*)\}'/) { my ($field, $cf) = ($1, $2); $cf =~ s/(['\\])/\\$1/g; $keyword = "'$field.{$cf}'"; } my $clause = { Key => $keyword, Op => $op, Value => $value }; push @new_values, RT::Interface::Web::QueryBuilder::Tree->new($clause); } } push @actions, $m->comp('/Search/Elements/EditQuery:Process', %ARGS, Tree => $tree, Selected => \@current_values, New => \@new_values, ); # Rebuild $Query based on the additions / movements my $optionlist_arrayref; ($query{'Query'}, $optionlist_arrayref) = $tree->GetQueryAndOptionList(\@current_values); my $optionlist = join "\n", map { qq() } @$optionlist_arrayref; my $types = $tree->GetReferencedTypes; # Deal with format changes my ( $AvailableColumns, $CurrentFormat ); ( $query{'Format'}, $AvailableColumns, $CurrentFormat ) = $m->comp( 'Elements/BuildFormatString', %ARGS, types => $types, Format => $query{'Format'}, ); # if we're asked to save the current search, save it push @actions, $m->comp( '/Search/Elements/EditSearches:Save', %ARGS, Query => \%query, SavedSearch => \%saved_search); # Populate the "query" context with saved search data if ($ARGS{SavedSearchSave}) { $query{'SavedSearchId'} = $saved_search{'Id'}; } # Push the updates into the session so we don't lose 'em $session{'CurrentAssetSearchHash'} = { %query, SearchId => $saved_search{'Id'}, Object => $saved_search{'Object'}, Description => $saved_search{'Description'}, }; # Show the results, if we were asked. if ( $ARGS{'DoSearch'} ) { my $redir_query_string = $m->comp( '/Elements/QueryString', %query, SavedChartSearchId => $ARGS{'SavedChartSearchId'}, SavedSearchId => $saved_search{'Id'}, ); RT::Interface::Web::Redirect(RT->Config->Get('WebURL') . 'AssetTracker/Search/Results.html?' . $redir_query_string); $m->abort; } # Build a querystring for the tabs my %TabArgs = (); if ($NewQuery) { $TabArgs{QueryString} = 'NewQuery=1'; } elsif ( $query{'Query'} ) { $TabArgs{QueryArgs} = \%query; } <%ARGS> $NewQuery => 0 @clauses => () rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Bulk.html000066400000000000000000000265551222742774700252240ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>
    % foreach my $var (qw(Query Format OrderBy Order Rows Page SavedChartSearchId)) { %} <& /Elements/CollectionList, Collection => $Assets, Query => $Query, DisplayFormat => $Format, Format => $ARGS{'Format'}, Verbatim => 1, AllowSorting => 1, OrderBy => $OrderBy, Order => $Order, Rows => $Rows, Page => $Page, BaseURL => RT->Config->Get('WebPath')."/AssetTracker/Search/Bulk.html?", Class => 'RTx::AssetTracker::Assets' &> % $m->callback(CallbackName => 'AfterAssetList', ARGSRef => \%ARGS);
    <& /Elements/Submit, Label => loc('Update'), CheckboxNameRegex => '/^UpdateAsset\d+$/', CheckAll => 1, ClearAll => 1 &>
    <&|/Widgets/TitleBox, title => $title &>
    % for my $role ( RTx::AssetTracker::Type->RoleGroupTypes() ) { % }
    <&|/l&>Add <% $role %>: " />
    <&|/l&>Remove <% $role %>: " />
    (Username or Email separated by spaces)
    <&|/l&>Make Description:
    <&|/l&>Make Type: <& /AssetTracker/Elements/SelectType, Name => "Type", Default => $ARGS{Type} &>
    <&|/l&>Make Status: <& /AssetTracker/Elements/SelectStatus, Name => "Status", Default => $ARGS{Status}, Types => $seen_types &>
    <%perl> my $cfs = RT::CustomFields->new($session{'CurrentUser'}); $cfs->LimitToGlobalAsset(); $cfs->LimitToAssetType($_) for keys %$seen_types; % if ($cfs->Count) { <&|/Widgets/TitleBox, title => loc('Edit Custom Fields'), color => "#336633"&> % while (my $cf = $cfs->Next()) { % my $rows = 5; % my $cf_id = $cf->id; % my @add = (NamePrefix => 'Bulk-Add-CustomField-', CustomField => $cf, Rows => $rows, % Multiple => ($cf->MaxValues ==1 ? 0 : 1) , Cols => 25, % Default => $ARGS{"Bulk-Add-CustomField-$cf_id-Values"} || $ARGS{"Bulk-Add-CustomField-$cf_id-Value"}, ); % my @del = (NamePrefix => 'Bulk-Delete-CustomField-', CustomField => $cf, % Rows => $rows, Multiple => 1, Cols => 25, % Default => $ARGS{"Bulk-Delete-CustomField-$cf_id-Values"} || $ARGS{"Bulk-Delete-CustomField-$cf_id-Value"}, ); % if ($cf->Type eq 'Select') { % } elsif ($cf->Type eq 'Combobox') { % } elsif ($cf->Type eq 'Freeform') { % } elsif ($cf->Type eq 'Text') { % } else { % $RT::Logger->crit("Unknown CustomField type: " . $cf->Type); % } % }
    <&|/l&>Name <&|/l&>Add values <&|/l&>Delete values
    <% loc($cf->Name) %>
    (<%$cf->FriendlyType%>)
    <& /Elements/EditCustomFieldSelect, @add &> <& /Elements/EditCustomFieldSelect, @del &><& /Elements/EditCustomFieldCombobox, @add &> <& /Elements/EditCustomFieldCombobox, @del &><& /Elements/EditCustomFieldFreeform, @add &> <& /Elements/EditCustomFieldFreeform, @del &><& /Elements/EditCustomFieldText, @add &>  
    % } <&|/Widgets/TitleBox, title => loc('Edit Links'), color => "#336633"&> <& /AssetTracker/Asset/Elements/BulkLinks, Assets => $Assets &> <&|/Widgets/TitleBox, title => 'Change Comment', color=> "#999900", width => "100%" &> <& /AssetTracker/Asset/Elements/ChangeComment, Name => "GlobalComment" &> <& /Elements/Submit, Label => loc('Update') &>
    <%INIT> unless ($session{'CurrentUser'}->HasRight( Object => $RT::System, Right => 'BulkUpdate')) { Abort(loc("No permission to perform bulk updates of assets.")); } unless ( defined $Rows ) { $Rows = $RowsPerPage; $ARGS{Rows} = $RowsPerPage; } my $title = loc("Update multiple assets"); # Iterate through the ARGS hash and remove anything with a null value. map ( $ARGS{$_} =~ /^$/ && ( delete $ARGS{$_} ), keys %ARGS ); my (@results); $Page ||= 1; $Format ||= RT->Config->Get('DefaultAssetSearchResultFormat'); # inject _CHECKBOX to the first field. $Format =~ s/'?([^']+)'?,/'___CHECKBOX__$1',/; my $Assets = RTx::AssetTracker::Assets->new($session{'CurrentUser'}); $Assets->FromSQL($Query); if ( $OrderBy =~ /\|/ ) { # Multiple Sorts my @OrderBy = split /\|/, $OrderBy; my @Order = split /\|/, $Order; $Assets->OrderByCols( map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0 .. $#OrderBy ) ); } else { $Assets->OrderBy( FIELD => $OrderBy, ORDER => $Order ); } $Assets->RowsPerPage($Rows) if ($Rows); $Assets->GotoPage( $Page - 1 ); # SB uses page 0 as the first page Abort( loc("No search to operate on.") ) unless ($Assets); # build up a list of all custom fields for assets that we're displaying, so # we can display sane edit widgets. my $fields = {}; my $seen_types = {}; while ( my $asset = $Assets->Next ) { next if $seen_types->{ $asset->Type }++; my $custom_fields = $asset->CustomFields; while ( my $field = $custom_fields->Next ) { $fields->{ $field->id } = $field; } } #Iterate through each asset we've been handed my @linkresults; $Assets->RedoSearch(); # pull out the labels for any custom fields we want to update my $cf_del_keys; @$cf_del_keys = grep { /^Bulk-Delete-CustomField/ } keys %ARGS; my $cf_add_keys; @$cf_add_keys = grep { /^Bulk-Add-CustomField/ } keys %ARGS; while ( my $Asset = $Assets->Next ) { next unless ( $ARGS{ "UpdateAsset" . $Asset->Id } ); #Update the links $ARGS{'id'} = $Asset->id; #Update the basics. my @basicresults = ProcessAssetBasics( AssetObj => $Asset, ARGSRef => \%ARGS ); #Update the watchers my @watchresults = ProcessAssetWatchers( AssetObj => $Asset, ARGSRef => \%ARGS ); foreach my $type ( keys %{ RTx::AssetTracker::Asset::LINKDIRMAP() } ) { $ARGS{ $Asset->id . "-" . $type } = $ARGS{"Asset-$type"}; $ARGS{ $type . "-" . $Asset->id } = $ARGS{"$type-Asset"}; } @linkresults = ProcessAssetLinks( AssetObj => $Asset, ARGSRef => \%ARGS ); foreach my $type ( keys %{ RTx::AssetTracker::Asset::LINKDIRMAP() } ) { delete $ARGS{ $type . "-" . $Asset->id }; delete $ARGS{ $Asset->id . "-" . $type }; } my @cfresults; foreach my $list ( $cf_add_keys, $cf_del_keys ) { next unless $list->[0]; my $op; if ( $list->[0] =~ /Add/ ) { $op = 'add'; } elsif ( $list->[0] =~ /Del/ ) { $op = 'del'; } else { $RT::Logger->crit( "Got an op that was neither add nor delete. can never happen" . $list->[0] ); last; } foreach my $key (@$list) { my ( $cfid, $cf ); next if $key =~ /CustomField-(\d+)-Category$/; if ( $key =~ /CustomField-(\d+)-/ ) { $cfid = $1; $cf = RT::CustomField->new( $session{'CurrentUser'} ); $cf->Load($cfid); } else {next} my @values = ref( $ARGS{$key} ) eq 'ARRAY' ? @{ $ARGS{$key} } : ( $ARGS{$key} ); map { s/(\r\n|\r)/\n/g; } @values; # fix the newlines # now break the multiline values into multivalues @values = map { split( /\n/, $_ ) } @values unless ( $cf->SingleValue ); my $current_values = $Asset->CustomFieldValues($cfid); foreach my $value (@values) { if ( $op eq 'del' && $current_values->HasEntry($value) ) { my ( $id, $msg ) = $Asset->DeleteCustomFieldValue( Field => $cfid, Value => $value ); push @cfresults, $msg; } elsif ( $op eq 'add' && !$current_values->HasEntry($value) ) { my ( $id, $msg ) = $Asset->AddCustomFieldValue( Field => $cfid, Value => $value ); push @cfresults, $msg; } } } } my @tempresults = ( @watchresults, @basicresults, @linkresults, @cfresults ); @tempresults = map { loc( "Asset #[_1] [_2]: [_3]", $Asset->Id, $Asset->Name, $_ ) } @tempresults; @results = ( @results, @tempresults ); } #my $TxnCFs = RT::CustomFields->new( $session{CurrentUser} ); #$TxnCFs->LimitToLookupType( "RTx::AssetTracker::Type-RTx::AssetTracker::Asset-RT::Transaction" ); #$TxnCFs->LimitToGlobalOrObjectId( sort keys %seen_types ); <%args> $Format => undef $Page => 1 $Rows => undef $RowsPerPage => undef $Order => 'ASC' $OrderBy => 'id' $Query => undef $SavedSearchId => undef $SavedChartSearchId => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Edit.html000066400000000000000000000065271222742774700252110ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title&> <& /Elements/Tabs &> <& /Search/Elements/NewListActions, actions => \@actions &>
    <&|/Widgets/TitleBox, title => loc('Query'), &> <&|/Widgets/TitleBox, title => loc('Format'), &> <& /Elements/Submit, Label => loc("Apply"), Reset => 1, Caption => loc("Apply your changes")&>
    <%INIT> my $title = loc("Edit Query"); $Format = $m->comp('/Elements/ScrubHTML', Content => $Format); my $QueryString = $m->comp('/Elements/QueryString', Query => $Query, Format => $Format, RowsPerPage => $Rows, OrderBy => $OrderBy, Order => $Order, ); <%ARGS> $SavedSearchId => 'new' $SavedChartSearchId => 'new' $Query => '' $Format => '' $Rows => '50' $OrderBy => 'id' $Order => 'ASC' @actions => () rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/000077500000000000000000000000001222742774700252005ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/BuildFormatString000066400000000000000000000152531222742774700305300ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Format => RT->Config->Get('DefaultAssetSearchResultFormat') %types => () $Face => undef $Size => undef $Link => undef $Title => undef $AddCol => undef $RemoveCol => undef $ColUp => undef $ColDown => undef $SelectDisplayColumns => undef $CurrentDisplayColumns => undef <%init> # This can't be in a block, because otherwise we return the # same \@fields every request, and keep tacking more CustomFields onto # it -- and it grows per request. # All the things we can display in the format string by default my @fields = qw( id TypeName Name Description Status ExtendedStatus ); push @fields, RTx::AssetTracker::Type->RoleGroupTypes(); push @fields, qw( CreatedBy LastUpdatedBy Created CreatedRelative LastUpdated LastUpdatedRelative ); push @fields, RTx::AssetTracker::Asset::LINKORDER(); push @fields, qw( IP ) if RT->Config->Get('EnableIP'); push @fields, qw( NEWLINE NBSP ); # loc_qw $m->callback( CallbackOnce => 1, CallbackName => 'SetFieldsOnce', Fields => \@fields ); my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'}); foreach my $id (keys %types) { # Gotta load up the $type object, since types get stored by name now. my $type = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $type->Load($id); $CustomFields->LimitToAssetType($type->Id) if $type->Id; } $CustomFields->LimitToGlobalAsset; while ( my $CustomField = $CustomFields->Next ) { push @fields, "CustomField.{" . $CustomField->Name . "}"; } $m->callback( Fields => \@fields, ARGSRef => \%ARGS ); my ( @seen); $Format ||= RT->Config->Get('DefaultAssetSearchResultFormat'); my @format = split( /,\s*/, $Format ); foreach my $field (@format) { my %column = (); $field =~ s/'(.*)'/$1/; my ( $prefix, $suffix ); if ( $field =~ m/(.*)__(.*)__(.*)/ ) { $prefix = $1; $suffix = $3; $field = $2; } $field = "" if !$field; $column{Prefix} = $prefix; $column{Suffix} = $suffix; $field =~ s/\s*(.*)\s*/$1/; $column{Column} = $field; push @seen, \%column; } if ( $RemoveCol ) { # we do this regex match to avoid a non-numeric warning my ($index) = $CurrentDisplayColumns =~ /^(\d+)/; my $column = $seen[$index]; if ( defined($index) ) { delete $seen[$index]; my @temp = @seen; @seen = (); foreach my $element (@temp) { next unless $element; push @seen, $element; } } } elsif ( $AddCol ) { if ( defined $SelectDisplayColumns ) { my $selected = $SelectDisplayColumns; my @columns; if (ref($selected) eq 'ARRAY') { @columns = @$selected; } else { push @columns, $selected; } foreach my $col (@columns) { my %column = (); $column{Column} = $col; if ( $Face eq "Bold" ) { $column{Prefix} .= ""; $column{Suffix} .= ""; } if ( $Face eq "Italic" ) { $column{Prefix} .= ""; $column{Suffix} .= ""; } if ($Size) { $column{Prefix} .= "<" . $m->interp->apply_escapes( $Size, 'h' ) . ">"; $column{Suffix} .= "interp->apply_escapes( $Size, 'h' ) . ">"; } if ( $Link eq "Display" ) { $column{Prefix} .= q{}; $column{Suffix} .= ""; } if ($Title) { $column{Suffix} .= "/TITLE:" . $m->interp->apply_escapes( $Title, 'h' ); } push @seen, \%column; } } } elsif ( $ColUp ) { my $index = $CurrentDisplayColumns; if ( defined $index && ( $index - 1 ) >= 0 ) { my $column = $seen[$index]; $seen[$index] = $seen[ $index - 1 ]; $seen[ $index - 1 ] = $column; $CurrentDisplayColumns = $index - 1; } } elsif ( $ColDown ) { my $index = $CurrentDisplayColumns; if ( defined $index && ( $index + 1 ) < scalar @seen ) { my $column = $seen[$index]; $seen[$index] = $seen[ $index + 1 ]; $seen[ $index + 1 ] = $column; $CurrentDisplayColumns = $index + 1; } } my @format_string; foreach my $field (@seen) { next unless $field; my $row = "'"; $row .= $field->{'Prefix'} if defined $field->{'Prefix'}; $row .= "__$field->{'Column'}__" unless ( $field->{'Column'} eq "" ); $row .= $field->{'Suffix'} if defined $field->{'Suffix'}; $row .= "'"; push( @format_string, $row ); } $Format = join(",\n", @format_string); return($Format, \@fields, \@seen); rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/DisplayOptions000066400000000000000000000042701222742774700301070ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <&| /Widgets/TitleBox, title => loc("Sorting"), id => 'sorting' &> <& EditSort, %ARGS &> <&| /Widgets/TitleBox, title => loc("Display Columns"), id => 'columns' &> <& EditFormat, %ARGS &> rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/EditFormat000066400000000000000000000075231222742774700271700ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}}
    <&|/l&>Add Columns: <&|/l&>Format: <&|/l&>Show Columns:
    <&|/l&>Link:
    <&|/l&>Title:
    <&|/l&>Size:
    <&|/l&>Style:

    <%ARGS> $CurrentFormat => undef $AvailableColumns => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/EditSort000066400000000000000000000076021222742774700266650ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} % for my $o (0..3) { % $Order[$o] ||= ''; $OrderBy[$o] ||= ''; % }
    % if ($o == 0) { <&|/l&>Order by: % }
    <&|/l&>Rows per page: <& /Elements/SelectResultsPerPage, Name => "RowsPerPage", Default => $RowsPerPage &>
    <%INIT> my $assets = RTx::AssetTracker::Assets->new($session{'CurrentUser'}); my %FieldDescriptions = %{$assets->FIELDS}; my %fields; for my $field (keys %FieldDescriptions) { next unless $FieldDescriptions{$field}->[0] =~ /^(?:ENUM|INT|DATE|STRING|ID)$/; $fields{$field} = $field; } $fields{ $_ . '.EmailAddress' } = $_ . '.EmailAddress' for RTx::AssetTracker::Type->RoleGroupTypes(); # Add all available CustomFields to the list of sortable columns. my @cfs = grep /^CustomField/, @{$ARGS{AvailableColumns}}; $fields{$_} = $_ for @cfs; $m->callback(CallbackName => 'MassageSortFields', Fields => \%fields ); my @Order = split /\|/, $Order; my @OrderBy = split /\|/, $OrderBy; if ($Order =~ /\|/) { @Order = split /\|/, $Order; } else { @Order = ( $Order ); } <%ARGS> $Order => '' $OrderBy => '' $RowsPerPage => undef $Format => undef $GroupBy => 'id' rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/PickAssetCFs000066400000000000000000000050451222742774700274110ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> %types => () <%init> my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'}); foreach my $id (keys %types) { # Gotta load up the $type object, since types get stored by name now. my $type = RTx::AssetTracker::Type->new($session{'CurrentUser'}); $type->Load($id); $CustomFields->LimitToAssetType($type->Id) if $type->Id; } $CustomFields->LimitToGlobalAsset; $CustomFields->OrderBy( FIELD => 'Name', ORDER => 'ASC' ); <& /Search/Elements/PickCFs, %ARGS, TicketSQLField => 'CF', CustomFields => $CustomFields &> rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/PickBasics000066400000000000000000000163331222742774700271440ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} % foreach( @lines ) { <& /Search/Elements/ConditionRow, Condition => $_ &> % } <%INIT> my @lines = ( { Name => 'id', Field => loc('id'), Op => { Type => 'component', Path => '/Elements/SelectEqualityOperator', }, Value => { Type => 'text', Size => 5 } }, { Name => 'Attachment', Field => { Type => 'component', Path => '/AssetTracker/Elements/SelectAttachmentField', }, Op => { Type => 'component', Path => '/Elements/SelectBoolean', Arguments => { True => loc("matches"), False => loc("doesn't match"), TrueVal => 'LIKE', FalseVal => 'NOT LIKE', }, }, Value => { Type => 'text', Size => 20 }, }, { Name => 'Type', Field => loc('Type'), Op => { Type => 'component', Path => '/Elements/SelectBoolean', Arguments => { TrueVal=> '=', FalseVal => '!=' }, }, Value => { Type => 'component', Path => '/AssetTracker/Elements/SelectType', Arguments => { NamedValues => 1, CheckTypeRight => 'ShowAsset' }, }, }, { Name => 'Status', Field => loc('Status'), Op => { Type => 'component', Path => '/Elements/SelectBoolean', Arguments => { TrueVal=> '=', FalseVal => '!=' }, }, Value => { Type => 'component', Path => '/AssetTracker/Elements/SelectStatus', Arguments => { SkipDeleted => 1, Types => \%types }, }, }, { Name => 'Actor', Field => { Type => 'select', Options => [ Creator => loc('Creator'), LastUpdatedBy => loc('Last updated by'), ], }, Op => { Type => 'component', Path => '/Elements/SelectBoolean', Arguments => { TrueVal=> '=', FalseVal => '!=' }, }, Value => { Type => 'component', Path => '/Elements/SelectOwner', Arguments => { ValueAttribute => 'Name', Types => \%types }, }, }, { Name => 'Watcher', Field => { Type => 'component', Path => '/AssetTracker/Search/Elements/SelectPersonType', Arguments => { Default => 'Owner' }, }, Op => { Type => 'component', Path => '/Elements/SelectMatch', }, Value => { Type => 'text', Size => 20 } }, { Name => 'WatcherGroup', Field => { Type => 'component', Path => '/AssetTracker/Search/Elements/SelectPersonType', Arguments => { Default => 'Owner', Suffix => 'Group' }, }, Op => { Type => 'component', Path => '/Elements/SelectBoolean', Arguments => { True => loc("belongs to"), False => loc("does not belong to"), TrueVal => '=', FalseVal => '!=', }, }, Value => { Type => 'component', Path => '/Search/Elements/SelectGroup', Arguments => { }, }, }, { Name => 'Date', Field => { Type => 'component', Path => '/AssetTracker/Elements/SelectDateType', }, Op => { Type => 'component', Path => '/Elements/SelectDateRelation', }, Value => { Type => 'component', Path => '/Elements/SelectDate', Arguments => { ShowTime => 0, Default => '' }, }, }, { Name => 'Links', Field => { Type => 'component', Path => '/AssetTracker/Search/Elements/SelectLinks' }, Op => { Type => 'component', Path => '/Elements/SelectBoolean', Arguments => { TrueVal => '=', FalseVal => '!=' }, }, Value => { Type => 'text', Size => 5 } }, ); if ( RT->Config->Get('EnableIP') ) { push @lines, { Name => 'IP', Field => { Type => 'component', Path => '/AssetTracker/Elements/SelectIPField' }, Op => { Type => 'component', Path => '/Elements/SelectCustomFieldOperator', Arguments => { Name => 'IPOp' }, }, Value => { Type => 'text', Size => 20 } }, { Name => 'Port', Field => loc('Port'), Op => { Type => 'component', Path => '/Elements/SelectEqualityOperator', Arguments => { Name => 'PortOp' }, }, Value => { Type => 'text', Size => 5 } }, { Name => 'Transport', Field => loc('Transport'), Op => { Type => 'component', Path => '/Elements/SelectBoolean', Arguments => { TrueVal => '=', FalseVal => '!=' }, }, Value => { Type => 'component', Path => '/AssetTracker/Elements/SelectTransport', Arguments => { Name => "ValueOfTransport" }, }, } } $m->callback( Conditions => \@lines ); <%ARGS> %types => () rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/PickCriteria000066400000000000000000000050721222742774700275000ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <&| /Widgets/TitleBox, title => loc('Add Criteria')&> % $m->callback( %ARGS, CallbackName => "BeforeBasics" ); <& PickBasics, types => \%types &> <& PickAssetCFs, types => \%types &> % $m->callback( %ARGS, CallbackName => "AfterCFs" );

    <&|/l&>Aggregator <& /Search/Elements/SelectAndOr, Name => "AndOr" &>
    <%ARGS> $addquery => 0 $query => undef %types => () rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/ResultsRSSView000066400000000000000000000114671222742774700300200ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> use Encode (); my $old_current_user; if ( $m->request_comp->path =~ RT->Config->Get('WebNoAuthRegex') ) { my $path = $m->dhandler_arg; my $notfound = sub { my $mesg = shift; $r->headers_out->{'Status'} = '404 Not Found'; $RT::Logger->info("Error encountered in rss generation: $mesg"); $m->clear_and_abort; }; $notfound->("Invalid path: $path") unless $path =~ m!^([^/]+)/([^/]+)/?!; my ( $name, $auth ) = ( $1, $2 ); # Unescape parts $name =~ s/\%([0-9a-z]{2})/chr(hex($1))/gei; # convert to perl strings $name = Encode::decode_utf8($name); my $user = RT::User->new(RT->SystemUser); $user->Load($name); $notfound->("Invalid user: $user") unless $user->id; $notfound->("Invalid authstring") unless $user->ValidateAuthString( $auth, $ARGS{Query} . $ARGS{Order} . $ARGS{OrderBy} ); $old_current_user = $session{'CurrentUser'}; my $cu = RT::CurrentUser->new; $cu->Load($user); $session{'CurrentUser'} = $cu; } my $Assets = RTx::AssetTracker::Assets->new($session{'CurrentUser'}); $Assets->FromSQL($ARGS{'Query'}); if ($OrderBy =~ /\|/) { # Multiple Sorts my @OrderBy = split /\|/,$OrderBy; my @Order = split /\|/,$Order; $Assets->OrderByCols( map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0 .. $#OrderBy ) );; } else { $Assets->OrderBy(FIELD => $OrderBy, ORDER => $Order); } $r->content_type('application/rss+xml'); # create an RSS 1.0 file (http://purl.org/rss/1.0/) use XML::RSS; my $rss = XML::RSS->new(version => '1.0'); $rss->channel( title => RT->Config->Get('rtname').": Search " . $ARGS{'Query'}, link => RT->Config->Get('WebURL'), description => "", dc => { }, generator => "RT v" . $RT::VERSION, syn => { updatePeriod => "hourly", updateFrequency => "1", updateBase => "1901-01-01T00:00+00:00", }, ); while ( my $Asset = $Assets->Next()) { my $creator_str = $m->scomp('/Elements/ShowUser', User => $Asset->CreatorObj); $creator_str =~ s/[\r\n]//g; $rss->add_item( title => $Asset->Name || loc('No name'), link => RT->Config->Get('WebURL')."AssetTracker/Asset/Display.html?id=".$Asset->id, description => $Asset->Description, dc => { creator => $creator_str, date => $Asset->CreatedObj->RFC2822, }, guid => $Asset->Type . '_' . $Asset->id, ); } $m->out($rss->as_string); $session{'CurrentUser'} = $old_current_user if $old_current_user; $m->abort(); <%ARGS> $OrderBy => 'Created' $Order => 'ASC' rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/SelectLinks000066400000000000000000000043331222742774700273460ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name => 'LinksField' <%INIT> my @fields = ( RTx::AssetTracker::Asset::LINKORDER(), 'LinkedTo' ); rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Elements/SelectPersonType000066400000000000000000000060421222742774700303750ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%INIT> my @types; if ($Scope =~ /type/) { @types = RTx::AssetTracker::Type->RoleGroupTypes(); } elsif ($Suffix eq 'Group') { @types = ( RTx::AssetTracker::Type->RoleGroupTypes(), qw(Watcher) ); } else { @types = ( RTx::AssetTracker::Type->RoleGroupTypes(), qw(Watcher) ); #, map( { "Type$_" } RTx::AssetTracker::Type->RoleGroupTypes() ), qw(TypeWatcher) ); } my @subtypes = @{ $RTx::AssetTracker::Assets::SEARCHABLE_SUBFIELDS{'User'} }; <%ARGS> $AllowNull => 1 $Suffix => '' $Default=>undef $Scope => 'asset' $Name => 'WatcherType' rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Export.html000066400000000000000000000010001222742774700255620ustar00rootroot00000000000000<%INIT> my $Assets = RTx::AssetTracker::Assets->new($session{CurrentUser}); $Assets->FromSQL($Query); my $DisplayFormat = $Format || RT->Config->Get('DefaultAssetSearchResultFormat'); my @Format = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $DisplayFormat); $r->headers_out->set( "Content-Disposition" => "attachment; Content-type=application/vnd.ms-excel; filename=Assets.xls" ); $m->out($Assets->ExportExcel(\@Format)); $m->flush_buffer; $m->abort; <%ARGS> $Query $Format rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Grid.html000066400000000000000000000151271222742774700252050ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &>
    % foreach my $var (qw(Query Format OrderBy Order Rows Page SavedChartSearchId)) { %} <& /Elements/CollectionList, Collection => $Assets, Query => $Query, DisplayFormat => $Format, Format => $ARGS{'Format'}, Verbatim => 1, AllowSorting => 1, OrderBy => $OrderBy, Order => $Order, Rows => $Rows, Page => $Page, BaseURL => RT->Config->Get('WebPath')."/AssetTracker/Search/Grid.html?", Class => 'RTx::AssetTracker::Assets' &> % $m->callback(CallbackName => 'AfterAssetList', ARGSRef => \%ARGS);
    <& /Elements/Submit, Label => loc('Update'), CheckboxNameRegex => '/^UpdateAsset\d+$/', CheckAll => 1, ClearAll => 1 &>
    <%INIT> unless ($session{'CurrentUser'}->HasRight( Object => $RT::System, Right => 'BulkUpdate')) { Abort(loc("No permission to perform bulk updates of assets.")); } unless ( defined $Rows ) { $Rows = $RowsPerPage; $ARGS{Rows} = $RowsPerPage; } my $title = loc("Grid asset update"); # Iterate through the ARGS hash and remove anything with a null value. map ( $ARGS{$_} =~ /^$/ && ( delete $ARGS{$_} ), keys %ARGS ); my (@results); $Page ||= 1; $Format ||= RT->Config->Get('DefaultAssetSearchResultFormat'); # Process the updates my @UpdateIDs; foreach my $arg (keys %ARGS) { if ($arg =~ /^UpdateAsset(\d+)$/) { push @UpdateIDs, $1; delete $ARGS{$arg}; } } foreach my $id (@UpdateIDs) { my $Asset = RTx::AssetTracker::Asset->new( $session{CurrentUser} ); my ($rv, $msg) = $Asset->Load($id); unless ($Asset->Id) { push @results, "Unable to load asset: $msg"; next; } # update the Asset basics my %basics; $basics{id} = $id; foreach my $arg (keys %ARGS) { next unless $arg =~ /^Asset-$id-(.*)$/; $basics{$1} = $ARGS{$arg}; #delete $ARGS{$arg}; } push @results, ProcessAssetBasics( AssetObj => $Asset, ARGSRef => \%basics); # update watchers? push @results, ProcessAssetWatchers(AssetObj => $Asset, ARGSRef => \%ARGS); # update the Assets custom fields my %cfs; $cfs{id} = $id; foreach my $arg (keys %ARGS) { next unless $arg =~ /Asset-$id-CustomField-/; $cfs{$arg} = $ARGS{$arg}; delete $ARGS{$arg}; } push @results, ProcessObjectCustomFieldUpdates( AssetObj => $Asset, ARGSRef => \%cfs); } my $Assets = RTx::AssetTracker::Assets->new($session{'CurrentUser'}); $Assets->FromSQL($Query); if ( $OrderBy =~ /\|/ ) { # Multiple Sorts my @OrderBy = split /\|/, $OrderBy; my @Order = split /\|/, $Order; $Assets->OrderByCols( map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0 .. $#OrderBy ) ); } else { $Assets->OrderBy( FIELD => $OrderBy, ORDER => $Order ); } $Assets->RowsPerPage($Rows) if ($Rows); $Assets->GotoPage( $Page - 1 ); # SB uses page 0 as the first page Abort( loc("No search to operate on.") ) unless ($Assets); # build up a list of all custom fields for assets that we're displaying, so # we can display sane edit widgets. while ( my $asset = $Assets->Next ) { next if $m->notes->{seen_types}{ $asset->Type }++; my $custom_fields = $asset->CustomFields; while ( my $field = $custom_fields->Next ) { next if $m->notes->{fields}{ $asset->Type }{ $field->Name }++; } } #Iterate through each asset we've been handed $Assets->RedoSearch(); while (my $Asset = $Assets->Next) { $RT::Logger->debug( "Checking Asset ".$Asset->Id ."\n"); next unless ($ARGS{"UpdateAsset".$Asset->Id}); $RT::Logger->debug ("Matched\n"); my @updateresults; #Update the basics. my @basicresults = ProcessAssetBasics(AssetObj => $Asset, ARGSRef => \%ARGS); my @cfresults = ProcessBulkCustomFieldUpdates(Object => $Asset, ARGSRef => \%ARGS); #Update the watchers $RT::Logger->debug( "About to update watchers"); my @watchresults = ProcessAssetWatchers(AssetObj => $Asset, ARGSRef => \%ARGS); push @results, @watchresults; $ARGS{'id'} = $Asset; my @tempresults = ( @basicresults, @cfresults, @updateresults); @tempresults = map { loc("Asset #[_1] [_2]: [_3]",$Asset->Id,$Asset->Name,$_) } @tempresults; push @results, @tempresults; } <%ARGS> $Format => undef $Page => 1 $Rows => undef $RowsPerPage => undef $Order => 'ASC' $OrderBy => 'id' $Query => undef $SavedSearchId => undef rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Results.html000066400000000000000000000175301222742774700257610ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title, Refresh => $refresh, LinkRel => \%link_rel &> <& /Elements/Tabs &> % $m->callback( ARGSRef => \%ARGS, CallbackName => 'BeforeResults' ); % unless ($ok) { % $msg =~ s{ at .*? line .*}{}s; <&| /Widgets/TitleBox, title => loc("Error"), class => "error" &> <&|/l_unsafe, "".$m->interp->apply_escapes($msg, "h")."" &>There was an error parsing your search query: [_1]. Your RT admin can find more information in the error logs. % } else { <& /Elements/CollectionList, Collection => $session{'assets'}, Query => $Query, TotalFound => $assetcount, AllowSorting => 1, OrderBy => $OrderBy, Order => $Order, Rows => $Rows, Page => $Page, Format => $Format, Class => 'RTx::AssetTracker::Assets', BaseURL => $BaseURL &> % } % $m->callback( ARGSRef => \%ARGS, CallbackName => 'AfterResults' ); % my %hiddens = (Query => $Query, Format => $Format, Rows => $Rows, OrderBy => $OrderBy, Order => $Order, HideResults => $HideResults, Page => $Page, SavedChartSearchId => $SavedChartSearchId );
    % foreach my $key (keys(%hiddens)) { % } <& /Elements/Refresh, Name => 'AssetsRefreshInterval', Default => $session{'assets_refresh_interval'}||RT->Config->Get('SearchResultsRefreshInterval', $session{'CurrentUser'}) &>
    <%INIT> $m->callback( ARGSRef => \%ARGS, CallbackName => 'Initial' ); # Read from user preferences my $prefs = $session{'CurrentUser'}->UserObj->Preferences("AssetSearchDisplay") || {}; # These variables are what define a search_hash; this is also # where we give sane defaults. $Format ||= $prefs->{'Format'} || RT->Config->Get('DefaultAssetSearchResultFormat'); $Order ||= $prefs->{'Order'} || 'ASC'; $OrderBy ||= $prefs->{'OrderBy'} || 'id'; # Some forms pass in "RowsPerPage" rather than "Rows" # We call it RowsPerPage everywhere else. if ( !defined($Rows) ) { if (defined $ARGS{'RowsPerPage'} ) { $Rows = $ARGS{'RowsPerPage'}; } elsif ( defined $prefs->{'RowsPerPage'} ) { $Rows = $prefs->{'RowsPerPage'}; } else { $Rows = 50; } } $Page = 1 unless $Page && $Page > 0; my ($title, $assetcount); $session{'i'}++; $session{'assets'} = RTx::AssetTracker::Assets->new($session{'CurrentUser'}); my ($ok, $msg) = $Query ? $session{'assets'}->FromSQL($Query) : (1, "Vacuously OK"); # Provide an empty search if parsing failed $session{'assets'}->FromSQL("id < 0") unless ($ok); if ($OrderBy =~ /\|/) { # Multiple Sorts my @OrderBy = split /\|/,$OrderBy; my @Order = split /\|/,$Order; $session{'assets'}->OrderByCols( map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } } ( 0 .. $#OrderBy ) );; } else { $session{'assets'}->OrderBy(FIELD => $OrderBy, ORDER => $Order); } $session{'assets'}->RowsPerPage( $Rows ) if $Rows; $session{'assets'}->GotoPage( $Page - 1 ); $session{'CurrentAssetSearchHash'} = { Format => $Format, Query => $Query, Page => $Page, Order => $Order, OrderBy => $OrderBy, RowsPerPage => $Rows, AutoForwardSingleResult => 0, }; if ( $session{'assets'}->Query()) { $assetcount = $session{assets}->CountAll(); $title = loc('Found [quant,_1,asset]', $assetcount); } else { $title = loc("Find assets"); } my $QueryString = "?".$m->comp('/Elements/QueryString', Query => $Query, Format => $Format, Rows => $Rows, OrderBy => $OrderBy, Order => $Order, Page => $Page); if ($ARGS{'AssetsRefreshInterval'}) { $session{'assets_refresh_interval'} = $ARGS{'AssetsRefreshInterval'}; } my $refresh = $session{'assets_refresh_interval'} || RT->Config->Get('SearchResultsRefreshInterval', $session{'CurrentUser'} ); # Check $m->request_args, not $DECODED_ARGS, to avoid creating a new CSRF token on each refresh if (RT->Config->Get('RestrictReferrer') and $refresh and not $m->request_args->{CSRF_Token}) { my $token = RT::Interface::Web::StoreRequestToken( $session{'CurrentAssetSearchHash'} ); $m->notes->{RefreshURL} = RT->Config->Get('WebURL') . "AssetTracker/Search/Results.html?CSRF_Token=" . $token; } my %link_rel; my $genpage = sub { return $m->comp( '/Elements/QueryString', Query => $Query, Format => $Format, Rows => $Rows, OrderBy => $OrderBy, Order => $Order, Page => shift(@_), ); }; if ( $AutoForwardSingleResult && $assetcount == 1 && $session{assets}->First ) { # $assetcount is not always precise unless $UseSQLForACLChecks is set to true, # check $session{assets}->First here is to make sure the asset is there. RT::Interface::Web::Redirect( RT->Config->Get('WebURL') ."AssetTracker/Asset/Display.html?id=". $session{assets}->First->id ); } my $BaseURL = RT->Config->Get('WebPath')."/AssetTracker/Search/Results.html?"; $link_rel{first} = $BaseURL . $genpage->(1) if $Page > 1; $link_rel{prev} = $BaseURL . $genpage->($Page - 1) if $Page > 1; $link_rel{next} = $BaseURL . $genpage->($Page + 1) if ($Page * $Rows) < $assetcount; $link_rel{last} = $BaseURL . $genpage->(POSIX::ceil($assetcount/$Rows)) if $Rows and ($Page * $Rows) < $assetcount; <%CLEANUP> $session{'assets'}->PrepForSerialization(); <%ARGS> $Query => undef $Format => undef $HideResults => 0 $Rows => undef $Page => 1 $OrderBy => undef $Order => undef $SavedSearchId => undef $SavedChartSearchId => undef $AutoForwardSingleResult => 0 rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Results.rdf000066400000000000000000000040541222742774700255650ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /AssetTracker/Search/Elements/ResultsRSSView, %ARGS &> rt-extension-assettracker-3.0.0/html/AssetTracker/Search/Results.tsv000066400000000000000000000006721222742774700256300ustar00rootroot00000000000000<& /AssetTracker/Elements/CollectionAsTSV/AssetList, Query => $Query, AllowSorting => 1, OrderBy => $OrderBy, Order => $Order, Rows => $Rows, Page => $Page, Format => $Format, BaseURL => RT->Config->Get('WebPath')."/AssetTracker/Search/Results.html?" &> <%ARGS> $Query => undef $Format => undef $HideResults => 0 $Rows => 50 $Page => 1 $OrderBy => 'id' $Order => 'ASC' $AutoForwardSingleResult => 0 rt-extension-assettracker-3.0.0/html/AssetTracker/Tools/000077500000000000000000000000001222742774700233175ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/AssetTracker/Tools/Import.html000066400000000000000000000041501222742774700254570ustar00rootroot00000000000000<& /Elements/Header, Title => loc("Asset Import") &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => $results &>
    Run scrips: Detailed transactions:

    The format of the import file is very important. The best way to get the correct format is to do an asset search and click "Importable Spreadsheet." Make all changes in this spreadsheet and import it.

    To create new assets enter 'new' in the 'id' column. An ID number will be assigned automatically.

    Run scrips: Will run scrips for every transaction. Don't run scrips if you don't have to. If there is an error during import the database will be rolled back (changes undone), but if a scrip happens to do a database commit then transactions cannot be rolled back on error.

    Detailed transactions: When an existing asset is updated an "Update" transaction will be recorded, unless this option is selected which will cause a transaction to be recorded for every change in the asset. <%INIT> my $results = []; unless ( $session{CurrentUser}->HasRight( Object => $RT::System, Right => 'AssetImport') ) { Abort(loc("No permission to perform asset import.")); } if ($Import && $XML) { my $cgi_object = $m->cgi_object; my $fh = $cgi_object->upload('XML'); my $filename = "$fh"; my ($buffer, $xml_data); while ( my $bytesread = read( $fh, $buffer, 4096 ) ) { $xml_data .= $buffer; } my $assets = RTx::AssetTracker::Assets->new($session{CurrentUser}); my $rv; eval { ($rv, $results) = $assets->ImportXML($xml_data, $RunScrips, $Detailed); unshift @$results, loc("Asset import errors. No changes were made.") unless $rv; }; if ($@) { unshift @$results, loc("Asset import exception: [_1]", $@) unless $rv; } } <%ARGS> $Import => undef $XML => '' $RunScrips => 0 $Detailed => 0 rt-extension-assettracker-3.0.0/html/AssetTracker/index.html000066400000000000000000000026251222742774700242210ustar00rootroot00000000000000<& /Elements/Header, Title => loc("Assets at a glance"), Refresh => $session{'home_refresh_interval'} &> <& /Elements/Tabs &>
    <& /AssetTracker/Elements/Quicksearch &> %#
    %#
    %#<& /Elements/Refresh, Name => 'HomeRefreshInterval', Default => $session {'home_refresh_interval'} &> %#
    %#
    <& /AssetTracker/Elements/RecentAssets &>
    <%init> if ( $ARGS{'q'} ) { my $query = $ARGS{'q'}; if ( RT->Config->Get('EnableIP') && $query =~ m/^(\d+\.\d+\.\d+\.\d+)$/ ) { $m->comp("/AssetTracker/Search/Results.html", Query => "IP = '$query'", AutoForwardSingleResult => 1); return(); } elsif ( $query =~ /^#?(\d+)$/ ) { RT::Interface::Web::Redirect(RT->Config->Get('WebURL')."AssetTracker/Asset/Display.html?id=".$1); } if ($m->callback( query => $query, %ARGS, CallbackName => 'SearchPreFilter' )) { return(); } $m->comp("/AssetTracker/Search/Results.html", Query => "Name LIKE '$query' AND Status != 'retired'", AutoForwardSingleResult => 1); return(); } if ($ARGS{'HomeRefreshInterval'}) { $session{'home_refresh_interval'} = $ARGS{'HomeRefreshInterval'}; } rt-extension-assettracker-3.0.0/html/Callbacks/000077500000000000000000000000001222742774700215035ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/000077500000000000000000000000001222742774700240765ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Admin/000077500000000000000000000000001222742774700251265ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Admin/Global/000077500000000000000000000000001222742774700263265ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Admin/Global/CustomFields/000077500000000000000000000000001222742774700307275ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Admin/Global/CustomFields/index.html/000077500000000000000000000000001222742774700330015ustar00rootroot00000000000000Default000066400000000000000000000007151222742774700342340ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Admin/Global/CustomFields/index.html<%INIT> $tabs->{'ZZ'} = { title => loc('Assets'), text => loc('Select custom fields for all assets'), path => RT->Config->Get('WebPath').'/AssetTracker/Admin/Global/CustomFields/Type-Assets.html', }; $tabs->{'ZZZ'} = { title => loc('Asset Types'), text => loc('Select custom fields for asset types'), path => RT->Config->Get('WebPath').'/AssetTracker/Admin/Global/CustomFields/Types.html', }; <%ARGS> $tabs rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Elements/000077500000000000000000000000001222742774700256525ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Elements/CollectionList/000077500000000000000000000000001222742774700306015ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Elements/CollectionList/EachRow000066400000000000000000000057111222742774700320600ustar00rootroot00000000000000<%perl> my $request_path = $HTML::Mason::Commands::r->path_info; if ( $request_path =~ m{^/AssetTracker/Search/Grid.html} ) { foreach my $column (@Format) { my $col = $column->{'attribute'} || $column->{'last_attribute'}; if ( $col eq 'Name' ) { $column->{output} = [ '' ]; } elsif ( $col eq 'Description' ) { $column->{output} = [ '' ]; } elsif ( $col eq 'Status' ) { $column->{output} = [ $m->scomp( '/AssetTracker/Elements/SelectStatus', Name => "Asset-".$Record->Id."-Status", Default => $Record->Status, Types => $m->notes->{seen_types} ) ]; $column->{output}->[0] =~ s/\n//g; } elsif ( $col eq 'TypeName' ) { $column->{output} = [ $m->scomp( '/AssetTracker/Elements/SelectType', Name => "Asset-".$Record->Id."-Type", Default => $Record->Type ) ]; $column->{output}->[0] =~ s/\n//g; } elsif ( RTx::AssetTracker::Type->IsRoleGroupType( $col ) ) { $column->{output} = [ ''; } elsif ( $col =~ /^CustomField\.\{(.+)\}$/ ) { my $cfname = $1; # See if it is a CF for this Asset type if( $m->notes->{fields}{$Record->Type}{$cfname} ) { my $CustomField = RT::CustomField->new($session{CurrentUser}); my ($rv, $msg) = $CustomField->LoadByNameAndAssetType( Name => $cfname, Type => $Record->Type ); # This must be a global CF $CustomField->LoadByNameAndAssetType( Name => $cfname, Type => 0 ) unless $CustomField->Id; if ( $CustomField->CurrentUserHasRight("ModifyCustomField") ) { $column->{output} = [ $m->scomp( '/Elements/EditCustomField', Object => $Record, CustomField => $CustomField ) ]; $column->{output}->[0] =~ s/\n//g; } } else { $column->{output} = [ 'n/a' ]; } } } unshift @{$Format[0]{output}}, '___CHECKBOX__' unless $Format[0]{output}->[0] eq '___CHECKBOX__'; } <%args> $Record => undef $Warning => undef $Classes => undef @Format => undef rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Elements/Header/000077500000000000000000000000001222742774700270425ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Elements/Header/Head000066400000000000000000000045261222742774700276350ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2008 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/copyleft/gpl.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Elements/Tabs/000077500000000000000000000000001222742774700265435ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Elements/Tabs/Privileged000066400000000000000000000410361222742774700305640ustar00rootroot00000000000000<%INIT> my $request_path = $HTML::Mason::Commands::r->path_info; $request_path =~ s!/{2,}!/!g; my $query_string = sub { my %args = @_; my $u = URI->new(); $u->query_form(map { $_ => $args{$_} } sort keys %args); return $u->query; }; #warn keys %session; my $assets; eval { $assets = Menu->child( assets => title => loc('Assets'), path => '/AssetTracker/index.html', sort_order => 3 ); }; if ($@) { return; } $assets->child( new => title => loc('New Search') => path => "/AssetTracker/Search/Build.html?NewQuery=1" ); if ( $request_path =~ m{^/AssetTracker/} ) { PageWidgets()->delete('simple_search'); PageWidgets()->delete('create_ticket'); PageWidgets()->child( AT_simple_search => raw_html => $m->scomp('/AssetTracker/Elements/SimpleSearch') ); PageWidgets()->child( create_asset => raw_html => $m->scomp('/AssetTracker/Elements/CreateAsset') ); } if ( $request_path =~ m{^/AssetTracker/Asset/} ) { if ( ( $DECODED_ARGS->{'id'} || '' ) =~ /^(\d+)$/ ) { my $id = $1; my $obj = RTx::AssetTracker::Asset->new( $session{'CurrentUser'} ); $obj->Load($id); if ( $obj and $obj->id ) { my $actions = PageMenu()->child( actions => title => loc('Actions'), sort_order => 95 ); my $tabs = PageMenu(); $tabs->child( display => title => loc('Display') => path => "/AssetTracker/Asset/Display.html?id=" . $id ); $tabs->child( history => title => loc('History') => path => "/AssetTracker/Asset/History.html?id=" . $id ); my %can = %{ $obj->CurrentUser->PrincipalObj->HasRights( Object => $obj ) }; $can{'_ModifyOwner'} = $can{'OwnAsset'}; my $can = sub { unless ($_[0] eq 'ExecuteCode') { return $can{$_[0]} || $can{'SuperUser'}; } else { return !RT->Config->Get('DisallowExecuteCode') && ( $can{'ExecuteCode'} || $can{'SuperUser'} ); } }; # comment out until we can do it for an individual custom field #if ( $can->('ModifyAsset') || $can->('ModifyCustomField') ) { $tabs->child( basics => title => loc('Basics'), path => "/AssetTracker/Asset/Modify.html?id=" . $id, ); #} if ( $can->('ModifyAsset') || $can->('_ModifyOwner') || $can->('Watch') || $can->('WatchAsAdminCc') ) { $tabs->child( people => title => loc('People'), path => "/AssetTracker/Asset/ModifyPeople.html?id=" . $id,); } if ( $can->('ModifyAsset') && RT->Config->Get('EnableIP') ) { $tabs->child( ips => title => loc('IPs'), path => "/AssetTracker/Asset/ModifyIPs.html?id=" . $id, ); } if ( $can->('ModifyAsset') ) { $tabs->child( fields => title => loc('Fields'), path => "/AssetTracker/Asset/ModifyFields.html?id=" . $id, ); $tabs->child( links => title => loc('Links'), path => "/AssetTracker/Asset/ModifyLinks.html?id=" . $id, ); } #if ( $can->('ModifyAsset') || $can->('ModifyCustomField') || $can->('_ModifyOwner') ) { $tabs->child( jumbo => title => loc('Jumbo'), path => "/AssetTracker/Asset/ModifyAll.html?id=" . $id, ); #} my $hide_resolve_with_deps = RT->Config->Get('HideResolveActionsWithDependencies') && $obj->HasUnresolvedDependencies; my $current = $obj->Status; my $lifecycle = $obj->TypeObj->Lifecycle; my $i = 1; foreach my $info ( $lifecycle->Actions($current) ) { my $next = $info->{'to'}; next unless $lifecycle->IsTransition( $current => $next ); my $check = $lifecycle->CheckRight( $current => $next ); next unless $can->($check); next if $hide_resolve_with_deps && $lifecycle->IsInactive($next) && !$lifecycle->IsInactive($current); my $url = '/AssetTracker/Asset/Display.html?' . $query_string->( Status => $next, id => $id, ); my $key = $info->{'label'} || ucfirst($next); $actions->child( $key => title => loc( $key ), path => $url); } #AT removed ticket taking/stealing if ( defined $session{"assets"} ) { # we have to update session data if we get new ItemMap my $updatesession = 1 unless ( $session{"assets"}->{'item_map'} ); my $item_map = $session{"assets"}->ItemMap; if ($updatesession) { $session{"assets"}->PrepForSerialization(); } my $search = $assets; #Menu()->child('search'); # Don't display prev links if we're on the first ticket if ( $item_map->{$id}->{prev} ) { $search->child( first => title => '<< ' . loc('First'), class => "nav", path => "/AssetTracker/Asset/Display.html?id=" . $item_map->{first}); $search->child( prev => title => '< ' . loc('Prev'), class => "nav", path => "/AssetTracker/Asset/Display.html?id=" . $item_map->{$id}->{prev}); } # Don't display next links if we're on the last ticket if ( $item_map->{$id}->{next} ) { $search->child( next => title => loc('Next') . ' >', class => "nav", path => "/AssetTracker/Asset/Display.html?id=" . $item_map->{$id}->{next}); $search->child( last => title => loc('Last') . ' >>', class => "nav", path => "/AssetTracker/Asset/Display.html?id=" . $item_map->{last}); } } } } } if ( $request_path =~ m{^/AssetTracker/(?:Asset|Search)/} || ( $request_path =~ m{^/AssetTracker/index\.html} && $DECODED_ARGS->{'q'} ) ) { my $search = $assets; #->child('search'); my $args = ''; my $has_query = ''; my $current_search = $session{"CurrentAssetSearchHash"} || {}; my $search_id = $DECODED_ARGS->{'SavedSearchLoad'} || $DECODED_ARGS->{'SavedSearchId'} || $search->{'SearchId'} || ''; $has_query = 1 if ( $DECODED_ARGS->{'Query'} or $current_search->{'Query'} ); my %query_args = ( SavedSearchId => ( $search_id eq 'new' ) ? undef : $search_id, SavedChartSearchId => $DECODED_ARGS->{'SavedChartSearchId'} || $current_search->{SavedChartSearchId}, Query => $DECODED_ARGS->{'Query'} || $current_search->{'Query'}, Format => $DECODED_ARGS->{'Format'} || $current_search->{'Format'}, OrderBy => $DECODED_ARGS->{'OrderBy'} || $current_search->{'OrderBy'} ||'', Order => $DECODED_ARGS->{'Order'} || $current_search->{'Order'} ||'', Page => $DECODED_ARGS->{'Page'} || $current_search->{'Page'}, RowsPerPage => ( defined $DECODED_ARGS->{'RowsPerPage'} ? $DECODED_ARGS->{'RowsPerPage'} : $current_search->{'RowsPerPage'}) ); for my $field (qw(Order OrderBy)) { if ( ref( $query_args{$field} ) eq 'ARRAY' ) { $query_args{$field} = join( "|", @{ $query_args{$field} } ); } elsif (not defined $query_args{$field}) { delete $query_args{$field}; } } $args = "?" . ($QueryString || $query_string->(%query_args)); my $current_search_menu; if ( $request_path =~ m{^/AssetTracker/Asset} ) { $current_search_menu = $search->child( current_search => title => loc('Current Search') ); $current_search_menu->path("/AssetTracker/Search/Results.html$args") if $has_query; } else { $current_search_menu = PageMenu(); } $current_search_menu->child( edit_search => title => loc('Edit Search'), path => "/AssetTracker/Search/Build.html" . ( ($has_query) ? $args : '' ) ); $current_search_menu->child( advanced => title => loc('Advanced'), path => "/AssetTracker/Search/Edit.html$args" ); if ($has_query) { $current_search_menu->child( results => title => loc('Show Results'), path => "/AssetTracker/Search/Results.html$args" ); } if ( $has_query ) { my $bulk = $current_search_menu->child( bulk => title => loc('Bulk Update'), path => "/AssetTracker/Search/Bulk.html$args" ); $bulk->child( batch => title => loc('Batch Update multiple assets'), path => "/AssetTracker/Search/Bulk.html$args" ); $bulk->child( grid => title => loc('Grid Update multiple assets'), path => "/AssetTracker/Search/Grid.html$args" ); my $more = $current_search_menu->child( more => title => loc('Feeds') ); $more->child( spreadsheet => title => loc('Spreadsheet'), path => "/AssetTracker/Search/Results.tsv$args" ); $more->child( importable => title => loc('Importable Spreadsheet'), path => "/AssetTracker/Search/Export.html$args" ); if ($request_path =~ m{^/AssetTracker/Search/Results.html} && #XXX TODO better abstraction $session{'CurrentUser'}->HasRight( Right => 'SuperUser', Object => RT->System )) { my $shred_args = $query_string->( Search => 1, Plugin => 'Assets', 'Assets:query' => $query_args{'Query'}, 'Assets:limit' => $query_args{'Rows'}, ); $more->child( shredder => title => loc('Shredder'), path => '/Admin/Tools/Shredder/?' . $shred_args); } } } if ( $request_path =~ m{^/AssetTracker/Admin/Types} ) { my $type = 'Types'; my $tabs = PageMenu(); my $section; if ( $request_path =~ m{^/AssetTracker/Admin/$type/?(?:index.html)?$} || ( $request_path =~ m{^/AssetTracker/Admin/$type/(?:Modify.html)$} && $DECODED_ARGS->{'Create'} ) ) { $section = $tabs; } else { $section = $tabs->child( select => title => loc('Asset Types'), path => "/AssetTracker/Admin/$type/" ); } $section->child( select => title => loc('Select'), path => "/AssetTracker/Admin/$type/" ); $section->child( create => title => loc('Create'), path => "/AssetTracker/Admin/$type/Modify.html?Create=1" ); } if ( $request_path =~ m{^/AssetTracker/Admin/Types} ) { if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ || $DECODED_ARGS->{'Type'} && $DECODED_ARGS->{'Type'} =~ /^\d+$/ || $DECODED_ARGS->{'AssetType'} && $DECODED_ARGS->{'AssetType'} =~ /^\d+$/ ) { my $id = $DECODED_ARGS->{'Type'} || $DECODED_ARGS->{'AssetType'} || $DECODED_ARGS->{'id'}; my $type_obj = RTx::AssetTracker::Type->new( $session{'CurrentUser'} ); $type_obj->Load($id); if ( $type_obj and $type_obj->id ) { my $type = PageMenu(); $type->child( basics => title => loc('Basics'), path => "/AssetTracker/Admin/Types/Modify.html?id=" . $id ); $type->child( people => title => loc('Watchers'), path => "/AssetTracker/Admin/Types/People.html?id=" . $id ); my $templates = $type->child(templates => title => loc('Templates'), path => "/AssetTracker/Admin/Types/Templates.html?id=" . $id); $templates->child( select => title => loc('Select'), path => "/AssetTracker/Admin/Types/Templates.html?id=".$id); $templates->child( create => title => loc('Create'), path => "/AssetTracker/Admin/Types/Template.html?Create=1;AssetType=".$id); my $scrips = $type->child( scrips => title => loc('Scrips'), path => "/AssetTracker/Admin/Types/Scrips.html?id=" . $id); $scrips->child( select => title => loc('Select'), path => "/AssetTracker/Admin/Types/Scrips.html?id=" . $id ); $scrips->child( create => title => loc('Create'), path => "/AssetTracker/Admin/Types/Scrip.html?Create=1;AssetType=" . $id); my $ticket_cfs = $type->child( 'asset-custom-fields' => title => loc('Asset Custom Fields'), path => '/AssetTracker/Admin/Types/CustomFields.html?SubType=RTx::AssetTracker::Asset&id=' . $id ); # my $txn_cfs = $type->child( 'transaction-custom-fields' => title => loc('Transaction Custom Fields'), # path => '/AssetTracker/Admin/Types/CustomFields.html?SubType=RTx::AssetTracker::Asset-RT::Transaction&id='.$id ); $type->child( 'group-rights' => title => loc('Group Rights'), path => "/AssetTracker/Admin/Types/GroupRights.html?id=".$id ); $type->child( 'user-rights' => title => loc('User Rights'), path => "/AssetTracker/Admin/Types/UserRights.html?id=" . $id ); $type->child( 'history' => title => loc('History'), path => "/AssetTracker/Admin/Types/History.html?id=" . $id ); } } } if ( $request_path =~ m{^/AssetTracker/Admin/Global/(Scrip|Template)s?\.html} ) { my $type = $1; my $tabs = PageMenu(); # With only two elements, swapping between dropdown and menu is kinda dumb # In the glorious future this should be cleaner. $tabs->child( select => title => loc('Select'), path => "/AssetTracker/Admin/Global/${type}s.html" ); $tabs->child( create => title => loc('Create'), path => "/AssetTracker/Admin/Global/${type}.html?Create=1" ); } my $tools = Menu->child("tools"); if ( $session{'CurrentUser'}->HasRight( Right => 'AssetImport', Object => RT->System ) ) { $tools->child( asset_import => title => loc('Asset Import'), description => loc('Import assets from an Excel 94 XML formatted file'), path => '/AssetTracker/Tools/Import.html', ); } if ( my $admin = $tools->child("config") ) { my $types = $admin->child( 'asset-types' => title => loc('Asset Types'), path => '/AssetTracker/Admin/Types/', description => loc('Manage asset types and asset-specific properties') ); $types->child( select => title => loc('Select'), path => "/AssetTracker/Admin/Types/index.html" ); $types->child( create => title => loc('Create'), path => "/AssetTracker/Admin/Types/Modify.html?Create=1" ); my $admin_global = $admin->child("global"); my $scrips = $admin_global->child( at_scrips => title => loc('AT Scrips'), description => loc('Modify scrips which apply to all asset types'), path => '/AssetTracker/Admin/Global/Scrips.html', ); $scrips->child( select => title => loc('Select'), path => "/AssetTracker/Admin/Global/Scrips.html" ); $scrips->child( create => title => loc('Create'), path => "/AssetTracker/Admin/Global/Scrip.html?Create=1" ); my $templates = $admin_global->child( at_templates => title => loc('AT Templates'), description => loc('Edit system templates for asset tracker'), path => '/AssetTracker/Admin/Global/Templates.html', ); $templates->child( select => title => loc('Select'), path => "/AssetTracker/Admin/Global/Templates.html" ); $templates->child( create => title => loc('Create'), path => "/AssetTracker/Admin/Global/Template.html?Create=1" ); my $cfadmin = $admin_global->child("custom-fields"); $cfadmin->child( types => title => loc('Asset Types'), description => loc('Select custom fields for all asset types'), path => '/AssetTracker/Admin/Global/CustomFields/Types.html'); $cfadmin->child( assets => title => loc('Assets'), description => loc('Select custom fields for assets of all types'), path => '/AssetTracker/Admin/Global/CustomFields/Type-Assets.html', ); } <%ARGS> $show_menu => 1 $QueryString => '' rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/000077500000000000000000000000001222742774700252745ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/000077500000000000000000000000001222742774700260645ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/3.4-compat/000077500000000000000000000000001222742774700276515ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/3.4-compat/main.css/000077500000000000000000000000001222742774700313645ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/3.4-compat/main.css/End000066400000000000000000000000261222742774700320130ustar00rootroot00000000000000@import "webat.css"; rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/3.5-default/000077500000000000000000000000001222742774700300135ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/3.5-default/main.css/000077500000000000000000000000001222742774700315265ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/3.5-default/main.css/End000066400000000000000000000000261222742774700321550ustar00rootroot00000000000000@import "webat.css"; rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/aileron/000077500000000000000000000000001222742774700275155ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/aileron/main.css/000077500000000000000000000000001222742774700312305ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/aileron/main.css/End000066400000000000000000000001001222742774700316500ustar00rootroot00000000000000@import "../web2/webat.css"; @import "../web2/asset-lists.css"; rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/ballard/000077500000000000000000000000001222742774700274655ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/ballard/main.css/000077500000000000000000000000001222742774700312005ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/ballard/main.css/End000066400000000000000000000001001222742774700316200ustar00rootroot00000000000000@import "../web2/webat.css"; @import "../web2/asset-lists.css"; rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/base/000077500000000000000000000000001222742774700267765ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/base/main.css/000077500000000000000000000000001222742774700305115ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/base/main.css/End000066400000000000000000000000321222742774700311350ustar00rootroot00000000000000@import "asset-form.css"; rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/web2/000077500000000000000000000000001222742774700267235ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/web2/main.css/000077500000000000000000000000001222742774700304365ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/NoAuth/css/web2/main.css/End000066400000000000000000000000601222742774700310630ustar00rootroot00000000000000@import "webat.css"; @import "asset-lists.css"; rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Ticket/000077500000000000000000000000001222742774700253215ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Ticket/Elements/000077500000000000000000000000001222742774700270755ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Ticket/Elements/ShowTransaction/000077500000000000000000000000001222742774700322235ustar00rootroot00000000000000ModifyDisplay000066400000000000000000000003041222742774700346410ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Ticket/Elements/ShowTransaction<%args> $Transaction $text => undef <%init> if ( $Transaction->Data && $Transaction->ObjectType eq 'RTx::AssetTracker::Asset' ) { $$text .= ' (' . $Transaction->Data . ')' } rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Ticket/ModifyLinks.html/000077500000000000000000000000001222742774700305145ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Ticket/ModifyLinks.html/BeforeActionList000066400000000000000000000025171222742774700336400ustar00rootroot00000000000000<%INIT> if ($ARGSRef->{SubmitTicket}) { my $link_args; my $id = $ARGSRef->{id}; my $Ticket = LoadTicket($id); for my $key ( keys %$ARGSRef ) { next unless $key =~ /^AddLink-Asset-(.*)/; my $uri = $1; my $link_type = delete $ARGSRef->{$key}; next unless $link_type; if ($link_type eq 'MemberOf') { $link_args->{"$id-MemberOf"} = join(" ", $link_args->{"$id-MemberOf"}, $uri); } elsif ($link_type eq 'HasMember') { $link_args->{"MemberOf-$id"} = join(" ", $link_args->{"MemberOf-$id"}, $uri); } elsif ($link_type eq 'DependsOn') { $link_args->{"$id-DependsOn"} = join(" ", $link_args->{"$id-DependsOn"}, $uri); } elsif ($link_type eq 'DependedOnBy') { $link_args->{"DependsOn-$id"} = join(" ", $link_args->{"DependsOn-$id"}, $uri); } elsif ($link_type eq 'RefersTo') { $link_args->{"$id-RefersTo"} = join(" ", $link_args->{"$id-RefersTo"}, $uri); } elsif ($link_type eq 'ReferredToBy') { $link_args->{"RefersTo-$id"} = join(" ", $link_args->{"RefersTo-$id"}, $uri); } } my @results = ProcessTicketLinks( TicketObj => $Ticket, ARGSRef => $link_args ); push @$Actions, @results; } <%ARGS> $ARGSRef $Actions rt-extension-assettracker-3.0.0/html/Callbacks/AssetTracker/Ticket/ModifyLinks.html/FormStart000066400000000000000000000005251222742774700323620ustar00rootroot00000000000000<&| /Widgets/TitleBox, title => loc('Asset Search'), width => "100%", class => 'asset-info-links' &> <& /AssetTracker/Elements/EditLinks, AssetObj => undef, AssetField => $ARGSRef->{AssetField}, AssetString => $ARGSRef->{AssetString}, AssetOp => $ARGSRef->{AssetOp}, SearchOnly => 1, TicketLinkTypes => 1 &> <%ARGS> $ARGSRef rt-extension-assettracker-3.0.0/html/Elements/000077500000000000000000000000001222742774700214005ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Elements/AssetQuickSearch000066400000000000000000000000511222742774700245210ustar00rootroot00000000000000<& /AssetTracker/Elements/Quicksearch &> rt-extension-assettracker-3.0.0/html/Elements/RTx__AssetTracker__Asset/000077500000000000000000000000001222742774700262255ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Elements/RTx__AssetTracker__Asset/ColumnMap000066400000000000000000000162231222742774700300470ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name => undef $Attr => undef <%ONCE> my $COLUMN_MAP; my $LinkCallback = sub { my $method = shift; my $mode = $RTx::AssetTracker::Asset::LINKTYPEMAP{$method}{Mode}; my $type = $RTx::AssetTracker::Asset::LINKTYPEMAP{$method}{Type}; my $other_mode = ($mode eq "Target" ? "Base" : "Target"); my $mode_uri = $mode.'URI'; my $local_type = 'Local'.$mode; { export_style => 's24', export_value => sub { my @values = map { $_->$mode_uri->Resolver->URI, "\n", } @{ $_[0]->Links($other_mode,$type)->ItemsArrayRef }; pop @values; # Remove that last \n return @values; }, value => sub { map { \'', $_->$mode_uri->Resolver->AsString, \'
    ', } @{ $_[0]->Links($other_mode,$type)->ItemsArrayRef } } } }; $COLUMN_MAP = { IP => { attribute => 'IP', title => 'IP Address', value => sub { return map { ($_, \'
    ') } $_[0]->IPsAsList }, export_value => sub { $_[0]->_export_formatted_IPs() }, }, TypeName => { attribute => 'Type', title => 'Type', value => sub { return $_[0]->TypeObj->Name } }, Name => { attribute => 'Name', title => 'Name', value => sub { return $_[0]->Name } }, Status => { attribute => 'Status', title => 'Status', value => sub { return loc($_[0]->Status) } }, Description => { attribute => 'Description', title => 'Description', value => sub { return $_[0]->Description || "(" . loc('No description') . ")" } }, ExtendedStatus => { title => 'Status', attribute => 'Status', value => sub { my $Asset = shift; if ( $Asset->HasUnresolvedDependencies ) { if ( $Asset->HasUnresolvedDependencies( Type => 'approval' ) or $Asset->HasUnresolvedDependencies( Type => 'code' ) ) { return \'', loc('(pending approval)'), \''; } else { return \'', loc('(pending other Collection)'), \''; } } else { return loc( $Asset->Status ); } } }, LastTransaction => { attribute => 'LastTransaction', title => 'Last Transaction', value => sub { return $_[0]->Transactions->Last->BriefDescription } }, # Falls through to generic ColumnMap for other attributes id => { export_type => 'Number', export_style => 's23', }, Created => { export_type => 'DateTime', export_style => 's21', export_value => sub { my $date = $_[0]->Created . '.000'; $date =~ s/ /T/; $date } }, LastUpdated => { export_type => 'DateTime', export_style => 's21', export_value => sub { my $date = $_[0]->Created . '.000'; $date =~ s/ /T/; $date } }, CustomField => { export_style => 's24', export_value => sub { # Cache the CF object on a per-request basis, to avoid # having to load it for every row my $key = join("-","CF", $_[0]->CustomFieldLookupType, $_[0]->CustomFieldLookupId, $_[-1]); my $cf = $m->notes($key); unless ($cf) { $cf = $_[0]->LoadCustomFieldByIdentifier($_[-1]); $m->notes($key, $cf); } # Display custom field contents, separated by newlines. my $values = $cf->ValuesForObject( $_[0] ); my @values = map { $_->Content, "\n", } @{ $values->ItemsArrayRef }; pop @values; # Remove that last \n return @values; }, }, # Everything from LINKTYPEMAP (map { $_ => $LinkCallback->( $_ ) } keys %RTx::AssetTracker::Asset::LINKTYPEMAP), '_CLASS' => { value => sub { return $_[1] % 2 ? 'oddline' : 'evenline' } }, '_CHECKBOX' => { attribute => 'checkbox', title => 'Update', align => 'right', value => sub { return \('') } }, }; for my $role ( RTx::AssetTracker::Type->RoleGroupTypes() ) { my $group_method = $role . 'RoleGroup'; my $export_method = $role . 'RoleGroupExportString'; $COLUMN_MAP->{$role} = { attribute => "$role.EmailAddress", value => sub { return $_[0]->$group_method->MemberEmailAddressesAsString }, export_value => sub { return $_[0]->$export_method }, }; } <%init> $m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap' ); return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr ); rt-extension-assettracker-3.0.0/html/Elements/RTx__AssetTracker__Scrip/000077500000000000000000000000001222742774700262265ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Elements/RTx__AssetTracker__Scrip/ColumnMap000066400000000000000000000073511222742774700300520ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name $Attr => undef <%ONCE> my $COLUMN_MAP = { id => { title => '#', # loc attribute => 'id', align => 'right', value => sub { return $_[0]->id }, }, AssetType => { title => 'Asset Type', # loc value => sub { return $_[0]->AssetTypeObj->Name if $_[0]->AssetType; return $_[0]->loc('Global'); }, }, AssetTypeId => { title => 'Queue', # loc value => sub { $_[0]->AssetType }, }, Condition => { title => 'Condition', # loc value => sub { return $_[0]->loc( $_[0]->ScripConditionObj->Name ) }, }, Action => { title => 'Action', # loc value => sub { return $_[0]->loc( $_[0]->ScripActionObj->Name ) }, }, Template => { title => 'Template', # loc value => sub { return $_[0]->loc( $_[0]->TemplateObj->Name ) }, }, AutoDescription => { title => 'Condition, Action and Template', # loc value => sub { return $_[0]->loc( "[_1] [_2] with template [_3]", $_[0]->loc($_[0]->ConditionObj->Name), $_[0]->loc($_[0]->ActionObj->Name), $_[0]->loc($_[0]->TemplateObj->Name), ) }, }, Description => { title => 'Description', # loc attribute => 'Description', value => sub { return $_[0]->Description() }, }, Stage => { title => 'Stage', # loc attribute => 'Stage', value => sub { return $_[0]->Stage() }, }, }; <%INIT> $m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 ); return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr ); rt-extension-assettracker-3.0.0/html/Elements/RTx__AssetTracker__Template/000077500000000000000000000000001222742774700267215ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Elements/RTx__AssetTracker__Template/ColumnMap000066400000000000000000000057601222742774700305470ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name $Attr => undef <%ONCE> my $COLUMN_MAP = { id => { title => '#', # loc attribute => 'id', align => 'right', value => sub { return $_[0]->id }, }, Name => { title => 'Name', # loc attribute => 'Name', value => sub { return $_[0]->Name() }, }, Description => { title => 'Description', # loc attribute => 'Description', value => sub { return $_[0]->Description() }, }, AssetType => { title => 'AssetType', # loc value => sub { return $_[0]->AssetTypeObj->Name if $_[0]->AssetType; return $_[0]->loc('Global'); }, }, AssetTypeId => { title => 'AssetType', # loc value => sub { $_[0]->AssetType }, }, }; <%INIT> $m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 ); return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr ); rt-extension-assettracker-3.0.0/html/Elements/RTx__AssetTracker__Type/000077500000000000000000000000001222742774700260675ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Elements/RTx__AssetTracker__Type/ColumnMap000066400000000000000000000056621222742774700277160ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <%ARGS> $Name => undef $Attr => undef <%ONCE> my $COLUMN_MAP = { id => { title => '#', # loc attribute => 'id', align => 'right', value => sub { return $_[0]->id } }, Disabled => { title => \' ', attribute => 'Disabled', value => sub { return $_[0]->Disabled? $_[0]->loc('Disabled'): $_[0]->loc('Enabled') }, }, Lifecycle => { title => 'Lifecycle', attribute => 'Lifecycle', value => sub { return $_[0]->Lifecycle->Name }, }, }; foreach my $field (qw( Name Description )) { $COLUMN_MAP->{$field} = { title => $field, attribute => $field, value => sub { return $_[0]->$field() }, }, } <%INIT> $m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 ); return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr ); rt-extension-assettracker-3.0.0/html/Helpers/000077500000000000000000000000001222742774700212265ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Helpers/Autocomplete/000077500000000000000000000000001222742774700236675ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Helpers/Autocomplete/Assets000066400000000000000000000033621222742774700250600ustar00rootroot00000000000000% $r->content_type('application/json'); <% JSON::to_json( \@suggestions ) |n %> % $m->abort; <%ARGS> $return => 'URI' $term => undef $delim => undef $max => 10 $op => undef <%INIT> require JSON; require RTx::AssetTracker::Assets; require RTx::AssetTracker::Type; $m->abort unless defined $return and defined $term and length $term; # Use our delimeter if we have one if ( defined $delim and length $delim ) { if ( $delim eq ',' ) { $delim = qr/,\s*/; } else { $delim = qr/\Q$delim\E/; } # If the field handles multiple values, pop the last one off $term = (split $delim, $term)[-1] if $term =~ $delim; } my $CurrentUser = $session{'CurrentUser'}; # Require privileged users or overriding config $m->abort unless $CurrentUser->Privileged or RT->Config->Get('AllowAssetAutocompleteForUnprivileged'); my %fields = %{ { Name => 'LIKE' } }; # If an operator is provided, check against only the returned field # using that operator %fields = ( $return => $op ) if $op; my $Assets = RTx::AssetTracker::Assets->new( $CurrentUser ); my $Types = RTx::AssetTracker::Types->new( $CurrentUser ); $Types->UnLimit(); while (my ($name, $op) = each %fields) { $op = 'STARTSWITH' unless $op =~ /^(?:LIKE|(?:START|END)SWITH|=|!=)$/i; $Assets->Limit( FIELD => $name, OPERATOR => $op, VALUE => $term, ENTRYAGGREGATOR => 'OR', SUBCLAUSE => 'autocomplete', ); } my @suggestions; while ( my $Asset = $Assets->Next ) { my $formatted = $m->scomp('/AssetTracker/Elements/ShowAsset', Asset => $Asset); $formatted =~ s/\n//g; $formatted =~ s|<.+?>||g; push @suggestions, { label => $formatted, value => $Asset->$return }; } rt-extension-assettracker-3.0.0/html/NoAuth/000077500000000000000000000000001222742774700210225ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/NoAuth/css/000077500000000000000000000000001222742774700216125ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/NoAuth/css/3.4-compat/000077500000000000000000000000001222742774700233775ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/NoAuth/css/3.4-compat/webat.css000066400000000000000000000054401222742774700252160ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2008 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} .asset-info-cfs .label { vertical-align: top; } .asset-info-cfs ul { margin: 0; padding: 0; margin-left: 0.5em; list-style: none; } .asset-summary .asset-info-basics .titlebox-content { border-left: none: } .asset-summary .ticket-info-basics .titlebox-title { background: #9c3031; } .asset-summary .asset-info-people .titlebox-content { border-left: none; } .asset-summary .ticket-info-people .titlebox-title { background: #31309c; } %# light green - #ad8 .asset-summary .asset-info-links .titlebox-content { border-left: none; } .asset-summary .ticket-info-links .titlebox-title { background: #316531; } %# orange - #d71 .asset-summary .asset-info-dates .titlebox-content { border-left: none; } .asset-summary .ticket-info-dates .titlebox-title { background: #633063; } rt-extension-assettracker-3.0.0/html/NoAuth/css/3.5-default/000077500000000000000000000000001222742774700235415ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/NoAuth/css/3.5-default/webat.css000066400000000000000000000050461222742774700253620ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2008 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} .asset-info-cfs .label { vertical-align: top; } .asset-info-cfs ul { margin: 0; padding: 0; margin-left: 0.5em; list-style: none; } .asset-summary .asset-info-basics .titlebox-content { border-left: 0.5em solid #b32; } .asset-summary .asset-info-people .titlebox-content { border-left: 0.5em solid #48c; } %# light green - #ad8 .asset-summary .asset-info-links .titlebox-content { border-left: 0.5em solid #316531; } %# orange - #d71 .asset-summary .asset-info-dates .titlebox-content { border-left: 0.5em solid #633063; } rt-extension-assettracker-3.0.0/html/NoAuth/css/base/000077500000000000000000000000001222742774700225245ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/NoAuth/css/base/asset-form.css000066400000000000000000000051471222742774700253250ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} .asset-info-basics input[name="Name"] { width: 20em; } .asset-info-basics input[name="Description"] { width: 40em; } .sidebyside #asset-create-metadata, .sidebyside #asset-update-metadata { float: left; width: 58%; clear: left; } .sidebyside #asset-info-links { float: right; width: 40%; clear: right; } @media (max-width: 950px) { /* Revert to a single column when we're less than 1000px wide */ .sidebyside #asset-create-metadata, .sidebyside #asset-update-metadata, .sidebyside #asset-info-links { float: none; width: auto; clear: both; } } rt-extension-assettracker-3.0.0/html/NoAuth/css/web2/000077500000000000000000000000001222742774700224515ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/NoAuth/css/web2/asset-lists.css000066400000000000000000000053211222742774700254370ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} table.type-summary { margin-top: 0.75em; font-size: 0.9em; border: 1px solid #aaa; border-bottom: 2px solid #999; border-right: 2px solid #999; border-spacing: 0; width: 100%; } table.type-summary tr>*:first-child { padding-left: 1em; } table.type-summary tr>*:last-child { padding-right: 1em; } table.type-summary a { font-weight: bold; } table.type-summary th.collection-as-table { font-size: 0.9em; margin-bottom: 0.5em; text-align: right; } table.type-summary th.collection-as-table:first-child { text-align: left; } table.type-summary td { border-bottom: 1px solid #ccc; } /* full-page asset lists */ #comp-AssetTracker-Search-Results #body>table.collection { margin-bottom: 2em; } rt-extension-assettracker-3.0.0/html/NoAuth/css/web2/webat.css000066400000000000000000000071221222742774700242670ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2008 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} .quick-create .select-assettype { width: 12em; } #CreateAssetOfType { text-align: right; } #CreateAssetOfType input.button { width: 10em; } div.asset-info-basics div.titlebox-content .labeltop{ width: 10em; } .asset-info-IPs .titlebox-title .left { background-color: #369; color: #fff;} .asset-info-tickets .titlebox-title .left { background-color: #316531; color: #fff;} .asset-info-comment .titlebox-title .left { background-color: #999; color: #fff;} .asset-info-cfs .titlebox-title .left { background-color: #b32; color: #fff;} .asset-info-basics .titlebox-title .left { background-color: #b32; color: #fff;} .asset-info-people .titlebox-title .left { background-color: #48c; color: #fff;} .asset-info-requestor .titlebox-title .left { white-space: nowrap; background-color: #48c; color: #fff;} .asset-info-links .titlebox-title .left { background-color: #316531; color: #fff;} .asset-info-reminders .titlebox-title .left { background-color: #369; color: #fff;} .asset-info-dates .titlebox-title .left { background-color: #633063; color: #fff;} .asset-info-attachments .titlebox-title .left { background-color: #993366; color: #fff;} .asset-summary .titlebox-title a, div#body .asset-summary .titlebox-title a:visited { color: #fff;} div#body .ticketlinks .titlebox-title .left a { color: #fff; } div#body .ticketlinks .titlebox-title .left a:visited { color: #fff; } .asset-inactive { text-decoration: line-through; color: #666 } #comp-AssetTracker-Search-Build .submit { width: 58%; } #display-options .submit { width: 100%; } .edit-role { float: left; padding-left: 2em; } rt-extension-assettracker-3.0.0/html/NoAuth/css/web2/webatmsie.css000066400000000000000000000040531222742774700251450ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} .topaction .select-assettype { margin-top: 0.2em; } rt-extension-assettracker-3.0.0/html/NoAuth/css/web2/webatmsie6.css000066400000000000000000000040471222742774700252360ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} .topaction .select-assettype { margin-top: 0; } rt-extension-assettracker-3.0.0/html/NoAuth/js/000077500000000000000000000000001222742774700214365ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/NoAuth/js/assetautocomplete.js000066400000000000000000000034771222742774700255500ustar00rootroot00000000000000jQuery(function() { // inputs that accept multiple asset urls var multipleCompletion = new Array("([0-9]|new)+-RefersTo"); // inputs with only a single email address allowed var singleCompletion = new Array(); // inputs for only privileged users var privilegedCompletion = new Array(); // build up the regexps we'll use to match var applyto = new RegExp('^(' + multipleCompletion.concat(singleCompletion, privilegedCompletion).join('|') + ')$'); var acceptsMultiple = new RegExp('^(' + multipleCompletion.join('|') + ')$'); var onlyPrivileged = new RegExp('^(' + privilegedCompletion.join('|') + ')$'); var inputs = document.getElementsByTagName("input"); for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; var inputName = input.getAttribute("name"); if (!inputName || !inputName.match(applyto)) continue; var options = { source: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Assets" }; var queryargs = []; if (inputName.match(onlyPrivileged)) { queryargs.push("privileged=1"); } if (inputName.match(acceptsMultiple)) { queryargs.push("delim=,"); options.focus = function () { // prevent value inserted on focus return false; } options.select = function(event, ui) { var terms = this.value.split(/,\s*/); terms.pop(); // remove current input terms.push( ui.item.value ); // add selected item this.value = terms.join(", "); return false; } } if (queryargs.length) options.source += "?" + queryargs.join("&"); jQuery(input).autocomplete(options); } }); rt-extension-assettracker-3.0.0/html/Prefs/000077500000000000000000000000001222742774700207035ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Prefs/AssetQuicksearch.html000066400000000000000000000072321222742774700250370ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@actions &>

    <&|/l&>Select asset types to be displayed on the "RT at a glance" page

      % for my $type (@types) {
    • {$type->Name}) { checked="checked" % } /><%$type->Name%><% $type->Description ? ': '.$type->Description : '' %>
    • % }
    <& /Elements/Submit, Caption => loc("Save Changes"), Label => loc('Save'), Name => 'Save', Reset => 1, CheckAll => 1, ClearAll => 1, CheckboxNameRegex => '/^Want-/', &>
    <%INIT> my @actions; my $title = loc("Customize").' '.loc("Asset quick search"); my $user = $session{'CurrentUser'}->UserObj; my $unwanted = $user->Preferences('AssetQuickSearch', {}); my $Types = RTx::AssetTracker::Types->new($session{'CurrentUser'}); $Types->UnLimit; my $right = 'ShowAsset'; $m->callback( CallbackName => 'ModifyTypes', Types => \$Types, Right => \$right, Unwanted => $unwanted, ); my @types = grep { $right ? $_->CurrentUserHasRight($right) : 1 } @{$Types->ItemsArrayRef}; if ($ARGS{'Save'}) { for my $type (@types) { if ($ARGS{"Want-".$type->Name}) { delete $unwanted->{$type->Name}; } else { ++$unwanted->{$type->Name}; } } my ($ok, $msg) = $user->SetPreferences('AssetQuickSearch', $unwanted); push @actions, $ok ? loc('Preferences saved.') : $msg; } rt-extension-assettracker-3.0.0/html/REST/000077500000000000000000000000001222742774700204015ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/REST/1.0/000077500000000000000000000000001222742774700206775ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/000077500000000000000000000000001222742774700217655ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/asset/000077500000000000000000000000001222742774700231045ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/asset/default000066400000000000000000000365271222742774700244700ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/Forms/asset/default %# <%ARGS> $id $changes => {} $fields => undef $args => undef <%INIT> use MIME::Entity; use RT::Interface::REST; my $cf_spec = RT::Interface::REST->custom_field_spec(1); my @comments; my ($c, $o, $k, $e) = ("", [], {}, 0); my %data = %$changes; my $asset = RTx::AssetTracker::Asset->new($session{CurrentUser}); my @dates = qw(Created LastUpdated); my @people = RTx::AssetTracker::Type->RoleGroupTypes(); my @create = ( qw(Type Name Description Status), RTx::AssetTracker::Type->RoleGroupTypes() ); my @simple = qw(Name Status); my %dates = map {lc $_ => $_} @dates; my %people = map {lc $_ => $_} @people; my %create = map {lc $_ => $_} @create; my %simple = map {lc $_ => $_} @simple; # Are we dealing with an existing asset? if ($id ne 'new') { $asset->Load($id); if (!$asset->Id) { return [ "# Asset $id does not exist.", [], {}, 1 ]; } elsif ( %data ) { if ( $data{status} && lc $data{status} eq 'deleted' && ! grep { $_ ne 'id' && $_ ne 'status' } keys %data ) { if ( !$asset->CurrentUserHasRight('DeleteAsset') ) { return [ "# You are not allowed to delete asset $id.", [], {}, 1 ]; } } elsif ( !$asset->CurrentUserHasRight('ModifyAsset') ) { return [ "# You are not allowed to modify asset $id.", [], {}, 1 ]; } } elsif (!$asset->CurrentUserHasRight('ShowAsset')) { return [ "# You are not allowed to display asset $id.", [], {}, 1 ]; } } else { if (!keys(%data)) { # GET asset/new: Return a suitable default form. # We get defaults from assettype/1 (XXX: What if it isn't there?). my $assettype = RTx::AssetTracker::Type->new($session{CurrentUser}); $assettype->Load(1); return [ "# Required: id, Type", [ ( qw(id Type Name Description Status TransactionData), RTx::AssetTracker::Type->RoleGroupTypes() ) ], { id => "asset/new", Type => $assettype->Name, Name => "", Description => "", Status => "production", TransactionData => "", map { $_ => [] } RTx::AssetTracker::Type->RoleGroupTypes(), }, 0 ]; } else { # We'll create a new asset, and fall through to set fields that # can't be set in the call to Create(). my (%v, $transactiondata); foreach my $k (keys %data) { # flexibly parse any dates if ($dates{lc $k}) { my $time = RT::Date->new($session{CurrentUser}); $time->Set(Format => 'unknown', Value => $data{$k}); $data{$k} = $time->ISO; } if (exists $create{lc $k}) { $v{$create{lc $k}} = delete $data{$k}; } # Set custom field elsif ($k =~ /^$cf_spec/) { my $key = $1 || $2; my $cf = RT::CustomField->new( $session{CurrentUser} ); $cf->LoadByNameAndAssetType( Name => $key, Type => $data{Type} || $v{Type} ); unless ( $cf->id ) { $cf->LoadByNameAndAssetType( Name => $key, Type => 0 ); } if (not $cf->id) { push @comments, "# Invalid custom field name ($key)"; delete $data{$k}; next; } $v{"CustomField-".$cf->Id()} = delete $data{$k}; } elsif (lc $k eq 'transactiondata') { $transactiondata = delete $data{$k}; } } if ( $e ) { unshift @comments, "# Could not create asset."; $k = \%data; goto DONE; } # people fields allow multiple values $v{$_} = vsplit($v{$_}) foreach ( grep $create{lc $_}, @people ); if ($transactiondata) { $v{TransactionData} = $transactiondata; } my($tid,$trid,$terr) = $asset->Create(%v); unless ($tid) { push(@comments, "# Could not create asset."); push(@comments, "# " . $terr); goto DONE; } delete $data{id}; $id = $asset->Id; push(@comments, "# Asset $id created."); # see if the hash is empty goto DONE if ! keys(%data); } } # Now we know we're dealing with an existing asset. if (!keys(%data)) { my ($time, $key, $val, @data); push @data, [ id => "asset/".$asset->Id ]; push @data, [ Type => $asset->TypeObj->Name ] if (!%$fields || exists $fields->{lc 'Type'}); push @data, [ Creator => $asset->CreatorObj->Name ] if (!%$fields || exists $fields->{lc 'Creator'}); foreach (qw(Name Description Status)) { next unless (!%$fields || (exists $fields->{lc $_})); push @data, [$_ => $asset->$_ ]; } foreach $key (@people) { my $group = $key . 'RoleGroup'; next unless (!%$fields || (exists $fields->{$group})); push @data, [ $key => [ $asset->$group->MemberEmailAddresses ] ]; } $time = new RT::Date ($session{CurrentUser}); foreach $key (@dates) { next unless (!%$fields || (exists $fields->{lc $key})); $time->Set(Format => 'sql', Value => $asset->$key); push @data, [ $key => $time->AsString ]; } # Display custom fields my $CustomFields = $asset->CustomFields; while (my $cf = $CustomFields->Next()) { next unless !%$fields || exists $fields->{"cf.{".lc($cf->Name)."}"} || exists $fields->{"cf-".lc $cf->Name}; my $vals = $asset->CustomFieldValues($cf->Id()); my @out = (); if ( $cf->SingleValue ) { my $v = $vals->Next; push @out, $v->Content if $v; } else { while (my $v = $vals->Next()) { my $content = $v->Content; $content =~ s/'/\\'/g; if ( $v->Content =~ /,/ ) { push @out, q{'} . $content . q{'}; } else { push @out, $content; } } } push @data, [ ('CF.{' . $cf->Name . '}') => join ',', @out ]; } my %k = map {@$_} @data; $o = [ map {$_->[0]} @data ]; $k = \%k; } else { my ($get, $set, $key, $val, $n, $s); my $updated; foreach $key (keys %data) { $val = $data{$key}; $key = lc $key; $n = 1; if (ref $val eq 'ARRAY') { unless ( $people{$key} ) { $n = 0; $s = "$key may have only one value."; goto SET; } } if ($key =~ /^type$/i) { next if $val eq $asset->TypeObj->Name; ($n, $s) = $asset->SetType($val); } elsif (exists $simple{$key}) { $key = $simple{$key}; $set = "Set$key"; my $current = $asset->$key; $current = '' unless defined $current; next if ($val eq $current) or ($current =~ /^\d+$/ && $val =~ /^\d+$/ && $val == $current); ($n, $s) = $asset->$set("$val"); } elsif (exists $dates{$key}) { $key = $dates{$key}; # We try to detect whether it should update a field by checking # whether its current value equals the entered value. Since the # LastUpdated field is automatically updated as other columns are # changed, it is not properly skipped. Users cannot update this # field anyway. next if $key eq 'LastUpdated'; $set = "Set$key"; my $time = RT::Date->new($session{CurrentUser}); $time->Set(Format => 'sql', Value => $asset->$key); next if ($val =~ /^not set$/i || $val eq $time->AsString); $time->Set(Format => 'unknown', Value => $val); ($n, $s) = $asset->$set($time->ISO); } elsif (exists $people{$key}) { $key = $people{$key}; my ($p, @msgs); my %new = map {$_=>1} @{ vsplit($val) }; my %old = map {$_=>1} $asset->$key->MemberEmailAddresses; my $type = $key; foreach $p (keys %old) { unless (exists $new{$p}) { ($s, $n) = $asset->DeleteWatcher(Type => $type, Email => $p); push @msgs, [ $s, $n ]; } } foreach $p (keys %new) { unless ($asset->IsWatcher(Type => $type, Email => $p)) { ($s, $n) = $asset->AddWatcher(Type => $type, Email => $p); push @msgs, [ $s, $n ]; } } $n = 1; if (@msgs = grep {$_->[0] == 0} @msgs) { $n = 0; $s = join "\n", map {"# ".$_->[1]} @msgs; $s =~ s/^# //; } } # Set custom field elsif ($key =~ /^$cf_spec/) { $key = $1 || $2; my $cf = RT::CustomField->new( $session{CurrentUser} ); $cf->LoadByNameAndAssetType( Name => $key, Type => $asset->Type ); unless ( $cf->id ) { $cf->LoadByNameAndAssetType( Name => $key, Type => 0 ); } if (not $cf->id) { $n = 0; $s = "Unknown custom field."; } else { my $vals = $asset->CustomFieldValues($cf->id); if ( !defined $val || !length $val ) { while ( my $val = $vals->Next ) { ($n, $s) = $asset->DeleteCustomFieldValue( Field => $cf, ValueId => $val->id, ); $s =~ s/^# // if defined $s; } } elsif ( $cf->SingleValue ) { my $old = $vals->Next; if ( $old ) { if ( $val ne $old->Content ) { ($n, $s) = $asset->AddCustomFieldValue( Field => $cf, Value => $val ); $s =~ s/^# // if defined $s; } } else { ($n, $s) = $asset->AddCustomFieldValue( Field => $cf, Value => $val ); $s =~ s/^# // if defined $s; } } else { my @new; my ( $a, $b ) = split /\s*,\s*/, $val, 2; while ($a) { no warnings 'uninitialized'; if ( $a =~ /^'/ ) { my $s = $a; while ( $a !~ /'$/ || ( $a !~ /(\\\\)+'$/ && $a =~ /(\\)+'$/ ) ) { ( $a, $b ) = split /\s*,\s*/, $b, 2; $s .= ',' . $a; } $s =~ s/^'//; $s =~ s/'$//; $s =~ s/\\'/'/g; push @new, $s; } elsif ( $a =~ /^q\{/ ) { my $s = $a; while ( $a !~ /\}$/ ) { ( $a, $b ) = split /\s*,\s*/, $b, 2; $s .= ',' . $a; } $s =~ s/^q\{//; $s =~ s/\}//; push @new, $s; } else { push @new, $a; } ( $a, $b ) = split /\s*,\s*/, $b, 2; } my %new; $new{$_}++ for @new; while (my $v = $vals->Next()) { my $c = $v->Content; if ( $new{$c} ) { $new{$c}--; } else { $asset->DeleteCustomFieldValue( Field => $cf, ValueId => $v->id ); } } for ( @new ) { while ( $new{$_} && $new{$_}-- ) { ($n, $s) = $asset->AddCustomFieldValue( Field => $cf, Value => $_ ); $s =~ s/^# // if defined $s; } } } } } elsif ($key ne 'id' && $key ne 'type' && $key ne 'creator' && $key ne 'content-type' ) { $n = 0; $s = "Unknown field."; } SET: if ($n == 0) { $e = 1; push @comments, "# $key: $s"; unless (@$o) { # move id forward @$o = ("id", grep { $_ ne 'id' } keys %$changes); $k = $changes; } } else { $updated ||= 1; } } push(@comments, "# Asset ".$asset->id." updated.") if $updated; } DONE: $c ||= join("\n", @comments) if @comments; return [$c, $o, $k, $e]; rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/asset/history000066400000000000000000000143501222742774700245330ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/Forms/asset/history %# <%ARGS> $id $args => undef $format => undef $fields => undef <%INIT> my $asset = RTx::AssetTracker::Asset->new($session{CurrentUser}); my ($c, $o, $k, $e) = ("", [], {}, ""); $asset->Load($id); unless ($asset->Id) { return [ "# Asset $id does not exist.", [], {}, 1 ]; } my $trans = $asset->Transactions(); my $total = $trans->Count(); if ( $args ) { chomp $args; } else { $args = ''; } my @arglist = split('/', $args ); my ($type, $tid); if (defined $arglist[0] && $arglist[0] eq 'type') { $type = $arglist[1]; } elsif ( defined $arglist[0] && $arglist[0] eq 'id') { $tid = $arglist[1]; } else { $type = $args; } if ($type) { # Create, Set, Status # CustomField, AddLink, DeleteLink, AddWatcher, DelWatcher if ($args =~ /^links?$/) { $trans->Limit(FIELD => 'Type', OPERATOR => 'LIKE', VALUE => '%Link'); } elsif ($args =~ /^watchers?$/) { $trans->Limit(FIELD => 'Type', OPERATOR => 'LIKE', VALUE => '%Watcher'); } else { $trans->Limit(FIELD => 'Type', OPERATOR => '=', VALUE => $type); } } elsif ($tid) { $trans->Limit(FIELD => 'Id', OPERATOR => '=', VALUE => $tid); } if ($tid) { my @data; my $t = RT::Transaction->new($session{CurrentUser}); # this paragraph limits the transaction ID query to transactions on this asset. # Otherwise you can query any transaction from any asset, which makes no sense. my $Transactions = $asset->Transactions; my $tok=0; while (my $T = $Transactions->Next()) { $tok=1 if ($T->Id == $tid) } if ($tok) { $t->Load($tid); } else { return [ "# Transaction $tid is not related to Asset $id", [], {}, 1 ]; } push @data, [ id => $t->Id ]; push @data, [ Asset => $t->Asset ] if (!%$fields || exists $fields->{lc 'Asset'}); push @data, [ TimeTaken => $t->TimeTaken ] if (!%$fields || exists $fields->{lc 'TimeTaken'}); push @data, [ Type => $t->Type ] if (!%$fields || exists $fields->{lc 'Type'}); push @data, [ Field => $t->Field ] if (!%$fields || exists $fields->{lc 'Field'}); push @data, [ OldValue => $t->OldValue ] if (!%$fields || exists $fields->{lc 'OldValue'}); push @data, [ NewValue => $t->NewValue ] if (!%$fields || exists $fields->{lc 'NewValue'}); push @data, [ Data => $t->Data ] if (!%$fields || exists $fields->{lc 'Data'}); push @data, [ Description => $t->Description ] if (!%$fields || exists $fields->{lc 'Description'}); push @data, [ Content => $t->Content ] if (!%$fields || exists $fields->{lc 'Content'}); if (!%$fields || exists $fields->{lc 'Content'}) { my $creator = RT::User->new($session{CurrentUser}); $creator->Load($t->Creator); push @data, [ Creator => $creator->Name ]; } push @data, [ Created => $t->Created ] if (!%$fields || exists $fields->{lc 'Created'}); if (!%$fields || exists $fields->{lc 'Attachments'}) { my $attachlist; my $attachments = $t->Attachments; while (my $a = $attachments->Next) { my $size = length($a->Content||''); if ($size > 1024) { $size = int($size/102.4)/10 . "k" } else { $size .= "b" } my $name = (defined $a->Filename and length $a->Filename) ? $a->Filename : "untitled"; $attachlist .= "\n" . $a->Id.": $name ($size)"; } push @data, [Attachments => $attachlist]; } my %k = map {@$_} @data; $o = [ map {$_->[0]} @data ]; $k = \%k; } else { my (@data, $tids); $format ||= "s"; $format = "l" if (%$fields); while (my $t = $trans->Next) { my $tid = $t->Id; if ($format eq "l") { $tids .= "," if $tids; $tids .= $tid; } else { push @$o, $tid; $k->{$tid} = $t->Description; } } if ($format eq "l") { my @tid; push @tid, "asset/$id/history/id/$tids"; my $fieldstring; foreach my $key (keys %$fields) { $fieldstring .= "," if $fieldstring; $fieldstring .= $key; } my ($content, $forms); $m->subexec("/REST/1.0/show", id => \@tid, format => $format, fields => $fieldstring); return [ $c, $o, $k, $e ]; } } if (!$c) { my $sub = $trans->Count(); $c = "# $sub/$total ($args/total)"; } return [ $c, $o, $k, $e ]; rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/asset/links000066400000000000000000000122221222742774700241460ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/Forms/asset/links %# <%ARGS> $id $format => 's' $changes => undef <%INIT> my @data; my $asset = RTx::AssetTracker::Asset->new($session{CurrentUser}); $asset->Load($id); if (!$asset->Id) { return [ "# Asset $id does not exist.", [], {}, 1 ]; } my ($c, $o, $k, $e) = ("", [], {}, 0); my @fields = RT->Config->Get('AssetLinkTypes'); my %fields = map { lc $_ => $_ } @fields; my %lfields = %RT::AssetTracker::LINKTYPEMAP; if ($changes) { my ($get, $set, $key, $val, $n, $s); my %data = %$changes; my @comments; foreach $key (keys %data) { $val = $data{$key}; $key = lc $key; $n = 1; if (exists $fields{$key}) { $key = $fields{$key}; my %old; my $field = $lfields{$key}->{Mode}; while (my $link = $asset->$key->Next) { $old{$link->$field} = 1; } my %new; foreach my $nkey (@{vsplit($val)}) { if ($nkey =~ /^\d+$/) { my $uri = RT::URI->new($session{CurrentUser}); my $as = RTx::AssetTracker::Asset->new($session{CurrentUser}); $as->Load($nkey); if ($as->Id) { $uri->FromObject($as); $nkey = $uri->URI; } else { $n = 0; $s = "Asset $nkey does not exist."; goto SET; } } $new{$nkey} = 1; } foreach my $u (keys %old) { if (exists $new{$u}) { delete $new{$u}; } else { my $type = $lfields{$key}->{Type}; my $mode = $lfields{$key}->{Mode}; ($n, $s) = $asset->DeleteLink(Type => $type, $mode => $u); goto SET; } } foreach my $u (keys %new) { my $type = $lfields{$key}->{Type}; my $mode = $lfields{$key}->{Mode}; ($n, $s) = $asset->AddLink(Type => $type, $mode => $u); goto SET; } } elsif ($key ne 'id' && $key ne 'type') { $n = 0; $s = "Unknown field: $key"; } SET: if ($n == 0) { $e = 1; push @comments, "# $key: $s"; unless (@$o) { @$o = ("id", @fields); %$k = %data; } } } push(@comments, "# Links for asset $id updated.") unless @comments; $c = join("\n", @comments) if @comments; } else { my @data; push @data, [ id => "asset/".$asset->Id."/links" ]; foreach my $key (@fields) { my @val; my $field = $lfields{$key}->{Mode}; while (my $link = $asset->$key->Next) { push @val, $link->$field; } push(@val, "") if (@val == 0 && defined $format && $format eq 'l'); push @data, [ $key => [ @val ] ] if @val; } my %k = map {@$_} @data; $o = [ map {$_->[0]} @data ]; $k = \%k; } return [ $c, $o, $k, $e ]; rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/asset/ns000066400000000000000000000044361222742774700234560ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/Forms/asset/ns %# <%ARGS> $id <%perl> my $asset = RTx::AssetTracker::Asset->new($session{CurrentUser}); # Actually, $id here isn't an ID, it's an Asset Name $asset->Load($id); if (!$asset->Id) { return (0, "No Asset of name \"$id\" exists."); } return $asset->Id; rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/assettype/000077500000000000000000000000001222742774700240065ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/assettype/assetcustomfields000066400000000000000000000062011222742774700274710ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/Forms/assettype/assetcustomfields %# <%ARGS> $id $format => 's' $changes => {} <%perl> my @comments; my ($c, $o, $k, $e) = ("", [], {}, 0); my $assettype = RTx::AssetTracker::Type->new($session{CurrentUser}); $assettype->Load($id); if (!$assettype->Id) { return [ "# Asset type $id does not exist.", [], {}, 1 ]; } if (%$changes) { $e = 1; $c = "Cannot modify Asset type CF definitions via REST"; goto DONE; } my @data; push @data, [ id => "assettype/" . $assettype->Id ]; my $qcfs = RT::CustomFields->new($session{CurrentUser});; $qcfs->LimitToGlobalOrAssetType($id); while ( my $qcf = $qcfs->Next() ) { if ( $format eq "l" ) { my $cfadmin = ( $qcf->SingleValue ? 'SingleValue' : 'MultiValue' ) . $qcf->Type . ( $qcf->Pattern ? ( ' ' . $qcf->FriendlyPattern ) : '' ); push @data, [ $qcf->Name . ' (' . $qcf->Description . ')' => $cfadmin ]; } else { push @data, [ $qcf->Name => "" ]; } } my %k = map {@$_} @data; $o = [ map {$_->[0]} @data ]; $k = \%k; DONE: $c ||= join("\n", @comments) if @comments; return [ $c, $o, $k, $e ]; rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/assettype/customfields000066400000000000000000000060771222742774700264440ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/Forms/assettype/customfields %# <%ARGS> $id $format => 's' $changes => {} <%perl> my @comments; my ($c, $o, $k, $e) = ("", [], {}, 0); my $assettype = new RTx::AssetTracker::Type $session{CurrentUser}; $assettype->Load($id); if (!$assettype->Id) { return [ "# Asset Type $id does not exist.", [], {}, 1 ]; } if (%$changes) { $e = 1; $c = "Cannot modify Asset Type CF definitions via REST"; goto DONE; } my @data; push @data, [ id => "assettype/".$assettype->Id ]; my $qcfs = $assettype->CustomFields; while ( my $qcf = $qcfs->Next() ) { if ( $format eq "l" ) { my $cfadmin = ( $qcf->SingleValue ? 'SingleValue' : 'MultiValue' ) . $qcf->Type . ( $qcf->Pattern ? ( ' ' . $qcf->FriendlyPattern ) : '' ); push @data, [ $qcf->Name . ' (' . $qcf->Description . ')' => $cfadmin ]; } else { push @data, [ $qcf->Name => "" ]; } } my %k = map {@$_} @data; $o = [ map { $_->[0] } @data ]; $k = \%k; DONE: $c ||= join("\n", @comments) if @comments; return [ $c, $o, $k, $e ]; rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/assettype/default000066400000000000000000000125621222742774700253630ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/Forms/assettype/default %# <%ARGS> $id $format => 's' $fields => undef # these are the fields passed to the rt "-f" flag. $changes => {} <%perl> my @comments; my ($c, $o, $k, $e) = ("", [], {}, 0); my %data = %$changes; my $assettype = RTx::AssetTracker::Type->new($session{CurrentUser}); my @fields = qw(Name Description); my %fields = map { lc $_ => $_ } @fields; if ($id ne 'new') { $assettype->Load($id); if (!$assettype->Id) { return [ "# Asset type $id does not exist.", [], {}, 1 ]; } } else { if (keys %data == 0) { return [ "# Required: Name", [ "id", @fields ], { id => 'assettype/new', Name => '', Description => "", }, 0 ]; } else { my %v; my %create = %fields; foreach my $k (keys %data) { if (exists $create{lc $k}) { $v{$create{lc $k}} = delete $data{$k}; } } if ($v{Name} eq '') { my %o = keys %$changes; delete @o{"id", @fields}; return [ "# Please set the asset type name.", [ "id", @fields, keys %o ], $changes, 1 ]; } $assettype->Create(%v); unless ($assettype->Id) { return [ "# Could not create asset type.", [], {}, 1 ]; } delete $data{id}; $id = $assettype->Id; push(@comments, "# Asset type $id created."); goto DONE if keys %data == 0; } } if ( keys %data == 0) { my @data; push @data, [ id => "assettype/".$assettype->Id ]; foreach my $key (@fields) { push @data, [ $key => $assettype->$key ]; } # Custom fields my $CustomFields = $assettype->CustomFields; while ( my $CustomField = $CustomFields->Next() ) { next unless ( !%$fields || exists $fields->{ lc "CF-" . $CustomField->Name } ); next unless $CustomField->CurrentUserHasRight('SeeCustomField'); my $CFvalues = $assettype->CustomFieldValues( $CustomField->Id ); my @CFvalues; while ( my $CFvalue = $CFvalues->Next() ) { push @CFvalues, $CFvalue->Content; } push @data, [ "CF-" . $CustomField->Name => \@CFvalues ]; } my %k = map {@$_} @data; $o = [ map {$_->[0]} @data ]; $k = \%k; } else { my ($get, $set, $key, $val, $n, $s); my $updated; foreach $key (keys %data) { $val = $data{$key}; $key = lc $key; $n = 1; if (exists $fields{$key}) { $key = $fields{$key}; $set = "Set$key"; next if $val eq $assettype->$key; ($n, $s) = $assettype->$set($val); } elsif ($key ne 'id') { $n = 0; $s = "Unknown field."; } SET: if ($n == 0) { $e = 1; push @comments, "# $key: $s"; unless (@$o) { my %o = keys %$changes; delete @o{"id", @fields}; @$o = ("id", @fields, keys %o); $k = $changes; } } else { $updated ||= 1; } } push(@comments, "# Asset type $id updated.") if $updated; } DONE: $c ||= join("\n", @comments) if @comments; return [ $c, $o, $k, $e ]; rt-extension-assettracker-3.0.0/html/REST/1.0/Forms/assettype/ns000066400000000000000000000045261222742774700243600ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/Forms/assettype/ns %# <%ARGS> $id <%perl> use RTx::AssetTracker::Types; my $assettypes = RTx::AssetTracker::Types->new($session{CurrentUser}); $assettypes->Limit(FIELD => 'Name', OPERATOR => '=', VALUE => $id); if ($assettypes->Count == 0) { return (0, "No asset type named $id exists."); } return $assettypes->Next->Id; rt-extension-assettracker-3.0.0/html/REST/1.0/asset/000077500000000000000000000000001222742774700220165ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/REST/1.0/asset/link000066400000000000000000000070031222742774700226760ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/asset/link %# <%ARGS> $id => undef $del => 0 $rel $to <%INIT> use RT::Interface::REST; my $output; my $status = "200 Ok"; my $asset = RTx::AssetTracker::Asset->new($session{CurrentUser}); my $object = $r->path_info; my @fields = RT->Config->Get('AssetLinkTypes'); my %fields = map { lc $_ => $_ } @fields; my %lfields = %RTx::AssetTracker::Asset::LINKTYPEMAP; # http://.../REST/1.0/asset/link/1 $object =~ s#^/REST/1.0/asset/link##; if ($id && $object && $id != $object) { $output = "Different ids in URL (`$object') and submitted form (`$id').\n"; $status = "400 Bad Request"; goto OUTPUT; } $id ||= $object; unless ($id =~ /^\d+$/) { $output = $r->path_info. "\n"; $output .= "Invalid asset id: '$id'.\n"; $status = "400 Bad Request"; goto OUTPUT; } unless (exists $fields{lc $rel}) { $output = "Invalid link: '$rel'.\n"; $status = "400 Bad Request"; goto OUTPUT; } $rel = $fields{lc $rel}; $asset->Load($id); unless ($asset->Id) { $output = "Couldn't load asset id: '$id'.\n"; $status = "404 Asset not found"; goto OUTPUT; } my $type = $lfields{$rel}->{Type}; my $mode = $lfields{$rel}->{Mode}; my $n = 1; my $op = $del ? "DeleteLink" : "AddLink"; ($n, $output) = $asset->$op(Type => $type, $mode => "at://$RT::Organization/asset/$to"); if ($n == 0) { $status = "500 Error"; } else { my $action = $del ? "Deleted" : "Created"; $output .= " $action link " . $asset->Id . " $rel $to"; } OUTPUT: RT/<% $RT::VERSION %> <% $status %> <% $output |n %> rt-extension-assettracker-3.0.0/html/REST/1.0/search/000077500000000000000000000000001222742774700221445ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/REST/1.0/search/asset000066400000000000000000000112331222742774700232060ustar00rootroot00000000000000%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} %# REST/1.0/search/asset %# <%ARGS> $query $format => undef $orderby => undef $fields => undef <%INIT> use RT::Interface::REST; my $output = ""; my $status = "200 Ok"; my $assets = RTx::AssetTracker::Assets->new($session{CurrentUser}); # Parse and validate any field specifications. my $field = RT::Interface::REST->field_spec; my (%fields, @fields); if ($fields) { $format ||= "l"; unless ($fields =~ /^(?:$field,)*$field$/) { $status = "400 Bad Request"; $output = "Invalid field specification: $fields"; goto OUTPUT; } @fields = map lc, split /\s*,\s*/, $fields; @fields{@fields} = (); unless (exists $fields{id}) { unshift @fields, "id"; $fields{id} = (); } } $format ||= "s"; if ($format !~ /^[isl]$/) { $status = "400 Bad request"; $output = "Unknown listing format: $format. (Use i, s, or l.)\n"; goto OUTPUT; } my ($n, $s); eval { ($n, $s) = $assets->FromSQL($query); }; if ($orderby) { my %args; my $order = substr($orderby, 0, 1); if ($order eq '+' || $order eq '-') { # remove the +/- sorting sigil substr($orderby, 0, 1, ''); if ($order eq '+') { $args{ORDER} = 'ASC'; } elsif ($order eq '-') { $args{ORDER} = 'DESC'; } } $assets->OrderBy( FIELD => $orderby, %args, ); } if ($@ || $n == 0) { $s ||= $@; $status = "400 Bad request"; $output = "Invalid query: '$s'.\n"; goto OUTPUT; } $n = 0; my @output; while (my $asset = $assets->Next) { $n++; my $id = $asset->Id; if ($format eq "i") { $output .= "asset/" . $id . "\n"; } elsif ($format eq "s") { if ($fields) { my $result = $m->comp("/REST/1.0/Forms/asset/default", id => $id, format => $format, fields => \%fields); my ($notes, $order, $key_values, $errors) = @$result; # If it's the first time through, add our header if ($n == 1) { $output .= join("\t",@$order)."\n"; } # Cut off the annoying asset/ before the id; $key_values->{'id'} = $id; $output .= join("\t", map { ref $key_values->{$_} eq 'ARRAY' ? join( ', ', @{$key_values->{$_}} ) : $key_values->{$_} } @$order)."\n"; } else { $output .= $asset->Id . ": ". $asset->Name . "\n"; } } else { my $d = $m->comp("/REST/1.0/Forms/asset/default", id => $id, format => $format, fields => \%fields); my ($c, $o, $k, $e) = @$d; push @output, [ $c, $o, $k ]; } } if ($n == 0 && $format ne "i") { $output = "No matching results.\n"; } $output = form_compose(\@output) if @output; OUTPUT: $m->out("RT/". $RT::VERSION . " " . $status ."\n\n"); $m->out($output ); return(); rt-extension-assettracker-3.0.0/html/Search/000077500000000000000000000000001222742774700210315ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Search/Asset.html000066400000000000000000000003521222742774700227760ustar00rootroot00000000000000<%init> RT::Interface::Web::Redirect(RT->Config->Get('WebURL'). 'AssetTracker/Search/Results.html?' . $m->comp( '/Elements/QueryString', %ARGS, Load => 'Load', CurrentSearch => $ARGS{LoadSavedSearch} ) ); rt-extension-assettracker-3.0.0/html/Search/Elements/000077500000000000000000000000001222742774700226055ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/html/Search/Elements/Asset000066400000000000000000000005511222742774700236100ustar00rootroot00000000000000<& /Elements/CollectionList, Collection => $Assets, %ARGS, Class => 'RTx::AssetTracker::Assets', BaseURL => $RT::WebPath."/AssetTracker/Search/Results.html?" &> <%init> my $QueryString = '?' . $m->comp('/Elements/QueryString', %ARGS); my $Assets = RTx::AssetTracker::Assets->new($session{'CurrentUser'}); $Assets->FromSQL($ARGS{'Query'}); rt-extension-assettracker-3.0.0/html/Search/Elements/AssetGraph000066400000000000000000000001321222742774700245650ustar00rootroot00000000000000<%INIT> return $m->comp('/AssetTracker/Asset/Graphs/Elements/ShowGraph', %ARGS); rt-extension-assettracker-3.0.0/inc/000077500000000000000000000000001222742774700174315ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/inc/Module/000077500000000000000000000000001222742774700206565ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/inc/Module/Install.pm000066400000000000000000000301351222742774700226240ustar00rootroot00000000000000#line 1 package Module::Install; # For any maintainers: # The load order for Module::Install is a bit magic. # It goes something like this... # # IF ( host has Module::Install installed, creating author mode ) { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install # 3. The installed version of inc::Module::Install loads # 4. inc::Module::Install calls "require Module::Install" # 5. The ./inc/ version of Module::Install loads # } ELSE { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install # 3. The ./inc/ version of Module::Install loads # } use 5.005; use strict 'vars'; use Cwd (); use File::Find (); use File::Path (); use vars qw{$VERSION $MAIN}; BEGIN { # All Module::Install core packages now require synchronised versions. # This will be used to ensure we don't accidentally load old or # different versions of modules. # This is not enforced yet, but will be some time in the next few # releases once we can make sure it won't clash with custom # Module::Install extensions. $VERSION = '1.06'; # Storage for the pseudo-singleton $MAIN = undef; *inc::Module::Install::VERSION = *VERSION; @inc::Module::Install::ISA = __PACKAGE__; } sub import { my $class = shift; my $self = $class->new(@_); my $who = $self->_caller; #------------------------------------------------------------- # all of the following checks should be included in import(), # to allow "eval 'require Module::Install; 1' to test # installation of Module::Install. (RT #51267) #------------------------------------------------------------- # Whether or not inc::Module::Install is actually loaded, the # $INC{inc/Module/Install.pm} is what will still get set as long as # the caller loaded module this in the documented manner. # If not set, the caller may NOT have loaded the bundled version, and thus # they may not have a MI version that works with the Makefile.PL. This would # result in false errors or unexpected behaviour. And we don't want that. my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; unless ( $INC{$file} ) { die <<"END_DIE" } Please invoke ${\__PACKAGE__} with: use inc::${\__PACKAGE__}; not: use ${\__PACKAGE__}; END_DIE # This reportedly fixes a rare Win32 UTC file time issue, but # as this is a non-cross-platform XS module not in the core, # we shouldn't really depend on it. See RT #24194 for detail. # (Also, this module only supports Perl 5.6 and above). eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006; # If the script that is loading Module::Install is from the future, # then make will detect this and cause it to re-run over and over # again. This is bad. Rather than taking action to touch it (which # is unreliable on some platforms and requires write permissions) # for now we should catch this and refuse to run. if ( -f $0 ) { my $s = (stat($0))[9]; # If the modification time is only slightly in the future, # sleep briefly to remove the problem. my $a = $s - time; if ( $a > 0 and $a < 5 ) { sleep 5 } # Too far in the future, throw an error. my $t = time; if ( $s > $t ) { die <<"END_DIE" } Your installer $0 has a modification time in the future ($s > $t). This is known to create infinite loops in make. Please correct this, then run $0 again. END_DIE } # Build.PL was formerly supported, but no longer is due to excessive # difficulty in implementing every single feature twice. if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } Module::Install no longer supports Build.PL. It was impossible to maintain duel backends, and has been deprecated. Please remove all Build.PL files and only use the Makefile.PL installer. END_DIE #------------------------------------------------------------- # To save some more typing in Module::Install installers, every... # use inc::Module::Install # ...also acts as an implicit use strict. $^H |= strict::bits(qw(refs subs vars)); #------------------------------------------------------------- unless ( -f $self->{file} ) { foreach my $key (keys %INC) { delete $INC{$key} if $key =~ /Module\/Install/; } local $^W; require "$self->{path}/$self->{dispatch}.pm"; File::Path::mkpath("$self->{prefix}/$self->{author}"); $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); $self->{admin}->init; @_ = ($class, _self => $self); goto &{"$self->{name}::import"}; } local $^W; *{"${who}::AUTOLOAD"} = $self->autoload; $self->preload; # Unregister loader and worker packages so subdirs can use them again delete $INC{'inc/Module/Install.pm'}; delete $INC{'Module/Install.pm'}; # Save to the singleton $MAIN = $self; return 1; } sub autoload { my $self = shift; my $who = $self->_caller; my $cwd = Cwd::cwd(); my $sym = "${who}::AUTOLOAD"; $sym->{$cwd} = sub { my $pwd = Cwd::cwd(); if ( my $code = $sym->{$pwd} ) { # Delegate back to parent dirs goto &$code unless $cwd eq $pwd; } unless ($$sym =~ s/([^:]+)$//) { # XXX: it looks like we can't retrieve the missing function # via $$sym (usually $main::AUTOLOAD) in this case. # I'm still wondering if we should slurp Makefile.PL to # get some context or not ... my ($package, $file, $line) = caller; die <<"EOT"; Unknown function is found at $file line $line. Execution of $file aborted due to runtime errors. If you're a contributor to a project, you may need to install some Module::Install extensions from CPAN (or other repository). If you're a user of a module, please contact the author. EOT } my $method = $1; if ( uc($method) eq $method ) { # Do nothing return; } elsif ( $method =~ /^_/ and $self->can($method) ) { # Dispatch to the root M:I class return $self->$method(@_); } # Dispatch to the appropriate plugin unshift @_, ( $self, $1 ); goto &{$self->can('call')}; }; } sub preload { my $self = shift; unless ( $self->{extensions} ) { $self->load_extensions( "$self->{prefix}/$self->{path}", $self ); } my @exts = @{$self->{extensions}}; unless ( @exts ) { @exts = $self->{admin}->load_all_extensions; } my %seen; foreach my $obj ( @exts ) { while (my ($method, $glob) = each %{ref($obj) . '::'}) { next unless $obj->can($method); next if $method =~ /^_/; next if $method eq uc($method); $seen{$method}++; } } my $who = $self->_caller; foreach my $name ( sort keys %seen ) { local $^W; *{"${who}::$name"} = sub { ${"${who}::AUTOLOAD"} = "${who}::$name"; goto &{"${who}::AUTOLOAD"}; }; } } sub new { my ($class, %args) = @_; delete $INC{'FindBin.pm'}; { # to suppress the redefine warning local $SIG{__WARN__} = sub {}; require FindBin; } # ignore the prefix on extension modules built from top level. my $base_path = Cwd::abs_path($FindBin::Bin); unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { delete $args{prefix}; } return $args{_self} if $args{_self}; $args{dispatch} ||= 'Admin'; $args{prefix} ||= 'inc'; $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); $args{bundle} ||= 'inc/BUNDLES'; $args{base} ||= $base_path; $class =~ s/^\Q$args{prefix}\E:://; $args{name} ||= $class; $args{version} ||= $class->VERSION; unless ( $args{path} ) { $args{path} = $args{name}; $args{path} =~ s!::!/!g; } $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; $args{wrote} = 0; bless( \%args, $class ); } sub call { my ($self, $method) = @_; my $obj = $self->load($method) or return; splice(@_, 0, 2, $obj); goto &{$obj->can($method)}; } sub load { my ($self, $method) = @_; $self->load_extensions( "$self->{prefix}/$self->{path}", $self ) unless $self->{extensions}; foreach my $obj (@{$self->{extensions}}) { return $obj if $obj->can($method); } my $admin = $self->{admin} or die <<"END_DIE"; The '$method' method does not exist in the '$self->{prefix}' path! Please remove the '$self->{prefix}' directory and run $0 again to load it. END_DIE my $obj = $admin->load($method, 1); push @{$self->{extensions}}, $obj; $obj; } sub load_extensions { my ($self, $path, $top) = @_; my $should_reload = 0; unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { unshift @INC, $self->{prefix}; $should_reload = 1; } foreach my $rv ( $self->find_extensions($path) ) { my ($file, $pkg) = @{$rv}; next if $self->{pathnames}{$pkg}; local $@; my $new = eval { local $^W; require $file; $pkg->can('new') }; unless ( $new ) { warn $@ if $@; next; } $self->{pathnames}{$pkg} = $should_reload ? delete $INC{$file} : $INC{$file}; push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); } $self->{extensions} ||= []; } sub find_extensions { my ($self, $path) = @_; my @found; File::Find::find( sub { my $file = $File::Find::name; return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; my $subpath = $1; return if lc($subpath) eq lc($self->{dispatch}); $file = "$self->{path}/$subpath.pm"; my $pkg = "$self->{name}::$subpath"; $pkg =~ s!/!::!g; # If we have a mixed-case package name, assume case has been preserved # correctly. Otherwise, root through the file to locate the case-preserved # version of the package name. if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { my $content = Module::Install::_read($subpath . '.pm'); my $in_pod = 0; foreach ( split //, $content ) { $in_pod = 1 if /^=\w/; $in_pod = 0 if /^=cut/; next if ($in_pod || /^=cut/); # skip pod text next if /^\s*#/; # and comments if ( m/^\s*package\s+($pkg)\s*;/i ) { $pkg = $1; last; } } } push @found, [ $file, $pkg ]; }, $path ) if -d $path; @found; } ##################################################################### # Common Utility Functions sub _caller { my $depth = 0; my $call = caller($depth); while ( $call eq __PACKAGE__ ) { $depth++; $call = caller($depth); } return $call; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _read { local *FH; open( FH, '<', $_[0] ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_NEW sub _read { local *FH; open( FH, "< $_[0]" ) or die "open($_[0]): $!"; my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } END_OLD sub _readperl { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; return $string; } sub _readpod { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; return $string if $_[0] =~ /\.pod\z/; $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; $string =~ s/^\n+//s; return $string; } # Done in evals to avoid confusing Perl::MinimumVersion eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; sub _write { local *FH; open( FH, '>', $_[0] ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_NEW sub _write { local *FH; open( FH, "> $_[0]" ) or die "open($_[0]): $!"; foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } END_OLD # _version is for processing module versions (eg, 1.03_05) not # Perl versions (eg, 5.8.1). sub _version ($) { my $s = shift || 0; my $d =()= $s =~ /(\.)/g; if ( $d >= 2 ) { # Normalise multipart versions $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; } $s =~ s/^(\d+)\.?//; my $l = $1 || 0; my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g; $l = $l . '.' . join '', @v if @v; return $l + 0; } sub _cmp ($$) { _version($_[1]) <=> _version($_[2]); } # Cloned from Params::Util::_CLASS sub _CLASS ($) { ( defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s ) ? $_[0] : undef; } 1; # Copyright 2008 - 2012 Adam Kennedy. rt-extension-assettracker-3.0.0/inc/Module/Install/000077500000000000000000000000001222742774700222645ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/inc/Module/Install/Base.pm000066400000000000000000000021471222742774700235000ustar00rootroot00000000000000#line 1 package Module::Install::Base; use strict 'vars'; use vars qw{$VERSION}; BEGIN { $VERSION = '1.06'; } # Suspend handler for "redefined" warnings BEGIN { my $w = $SIG{__WARN__}; $SIG{__WARN__} = sub { $w }; } #line 42 sub new { my $class = shift; unless ( defined &{"${class}::call"} ) { *{"${class}::call"} = sub { shift->_top->call(@_) }; } unless ( defined &{"${class}::load"} ) { *{"${class}::load"} = sub { shift->_top->load(@_) }; } bless { @_ }, $class; } #line 61 sub AUTOLOAD { local $@; my $func = eval { shift->_top->autoload } or return; goto &$func; } #line 75 sub _top { $_[0]->{_top}; } #line 90 sub admin { $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new; } #line 106 sub is_admin { ! $_[0]->admin->isa('Module::Install::Base::FakeAdmin'); } sub DESTROY {} package Module::Install::Base::FakeAdmin; use vars qw{$VERSION}; BEGIN { $VERSION = $Module::Install::Base::VERSION; } my $fake; sub new { $fake ||= bless(\@_, $_[0]); } sub AUTOLOAD {} sub DESTROY {} # Restore warning handler BEGIN { $SIG{__WARN__} = $SIG{__WARN__}->(); } 1; #line 159 rt-extension-assettracker-3.0.0/inc/Module/Install/Can.pm000066400000000000000000000061571222742774700233340ustar00rootroot00000000000000#line 1 package Module::Install::Can; use strict; use Config (); use ExtUtils::MakeMaker (); use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # check if we can load some module ### Upgrade this to not have to load the module if possible sub can_use { my ($self, $mod, $ver) = @_; $mod =~ s{::|\\}{/}g; $mod .= '.pm' unless $mod =~ /\.pm$/i; my $pkg = $mod; $pkg =~ s{/}{::}g; $pkg =~ s{\.pm$}{}i; local $@; eval { require $mod; $pkg->VERSION($ver || 0); 1 }; } # Check if we can run some command sub can_run { my ($self, $cmd) = @_; my $_cmd = $cmd; return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { next if $dir eq ''; require File::Spec; my $abs = File::Spec->catfile($dir, $cmd); return $abs if (-x $abs or $abs = MM->maybe_command($abs)); } return; } # Can our C compiler environment build XS files sub can_xs { my $self = shift; # Ensure we have the CBuilder module $self->configure_requires( 'ExtUtils::CBuilder' => 0.27 ); # Do we have the configure_requires checker? local $@; eval "require ExtUtils::CBuilder;"; if ( $@ ) { # They don't obey configure_requires, so it is # someone old and delicate. Try to avoid hurting # them by falling back to an older simpler test. return $self->can_cc(); } # Do we have a working C compiler my $builder = ExtUtils::CBuilder->new( quiet => 1, ); unless ( $builder->have_compiler ) { # No working C compiler return 0; } # Write a C file representative of what XS becomes require File::Temp; my ( $FH, $tmpfile ) = File::Temp::tempfile( "compilexs-XXXXX", SUFFIX => '.c', ); binmode $FH; print $FH <<'END_C'; #include "EXTERN.h" #include "perl.h" #include "XSUB.h" int main(int argc, char **argv) { return 0; } int boot_sanexs() { return 1; } END_C close $FH; # Can the C compiler access the same headers XS does my @libs = (); my $object = undef; eval { local $^W = 0; $object = $builder->compile( source => $tmpfile, ); @libs = $builder->link( objects => $object, module_name => 'sanexs', ); }; my $result = $@ ? 0 : 1; # Clean up all the build files foreach ( $tmpfile, $object, @libs ) { next unless defined $_; 1 while unlink; } return $result; } # Can we locate a (the) C compiler sub can_cc { my $self = shift; my @chunks = split(/ /, $Config::Config{cc}) or return; # $Config{cc} may contain args; try to find out the program part while (@chunks) { return $self->can_run("@chunks") || (pop(@chunks), next); } return; } # Fix Cygwin bug on maybe_command(); if ( $^O eq 'cygwin' ) { require ExtUtils::MM_Cygwin; require ExtUtils::MM_Win32; if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) { *ExtUtils::MM_Cygwin::maybe_command = sub { my ($self, $file) = @_; if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) { ExtUtils::MM_Win32->maybe_command($file); } else { ExtUtils::MM_Unix->maybe_command($file); } } } } 1; __END__ #line 236 rt-extension-assettracker-3.0.0/inc/Module/Install/Fetch.pm000066400000000000000000000046271222742774700236640ustar00rootroot00000000000000#line 1 package Module::Install::Fetch; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub get_file { my ($self, %args) = @_; my ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) { $args{url} = $args{ftp_url} or (warn("LWP support unavailable!\n"), return); ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; } $|++; print "Fetching '$file' from $host... "; unless (eval { require Socket; Socket::inet_aton($host) }) { warn "'$host' resolve failed!\n"; return; } return unless $scheme eq 'ftp' or $scheme eq 'http'; require Cwd; my $dir = Cwd::getcwd(); chdir $args{local_dir} or return if exists $args{local_dir}; if (eval { require LWP::Simple; 1 }) { LWP::Simple::mirror($args{url}, $file); } elsif (eval { require Net::FTP; 1 }) { eval { # use Net::FTP to get past firewall my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); $ftp->login("anonymous", 'anonymous@example.com'); $ftp->cwd($path); $ftp->binary; $ftp->get($file) or (warn("$!\n"), return); $ftp->quit; } } elsif (my $ftp = $self->can_run('ftp')) { eval { # no Net::FTP, fallback to ftp.exe require FileHandle; my $fh = FileHandle->new; local $SIG{CHLD} = 'IGNORE'; unless ($fh->open("|$ftp -n")) { warn "Couldn't open ftp: $!\n"; chdir $dir; return; } my @dialog = split(/\n/, <<"END_FTP"); open $host user anonymous anonymous\@example.com cd $path binary get $file $file quit END_FTP foreach (@dialog) { $fh->print("$_\n") } $fh->close; } } else { warn "No working 'ftp' program available!\n"; chdir $dir; return; } unless (-f $file) { warn "Fetching failed: $@\n"; chdir $dir; return; } return if exists $args{size} and -s $file != $args{size}; system($args{run}) if exists $args{run}; unlink($file) if $args{remove}; print(((!exists $args{check_for} or -e $args{check_for}) ? "done!" : "failed! ($!)"), "\n"); chdir $dir; return !$?; } 1; rt-extension-assettracker-3.0.0/inc/Module/Install/Makefile.pm000066400000000000000000000274371222742774700243540ustar00rootroot00000000000000#line 1 package Module::Install::Makefile; use strict 'vars'; use ExtUtils::MakeMaker (); use Module::Install::Base (); use Fcntl qw/:flock :seek/; use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub Makefile { $_[0] } my %seen = (); sub prompt { shift; # Infinite loop protection my @c = caller(); if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) { die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])"; } # In automated testing or non-interactive session, always use defaults if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) { local $ENV{PERL_MM_USE_DEFAULT} = 1; goto &ExtUtils::MakeMaker::prompt; } else { goto &ExtUtils::MakeMaker::prompt; } } # Store a cleaned up version of the MakeMaker version, # since we need to behave differently in a variety of # ways based on the MM version. my $makemaker = eval $ExtUtils::MakeMaker::VERSION; # If we are passed a param, do a "newer than" comparison. # Otherwise, just return the MakeMaker version. sub makemaker { ( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0 } # Ripped from ExtUtils::MakeMaker 6.56, and slightly modified # as we only need to know here whether the attribute is an array # or a hash or something else (which may or may not be appendable). my %makemaker_argtype = ( C => 'ARRAY', CONFIG => 'ARRAY', # CONFIGURE => 'CODE', # ignore DIR => 'ARRAY', DL_FUNCS => 'HASH', DL_VARS => 'ARRAY', EXCLUDE_EXT => 'ARRAY', EXE_FILES => 'ARRAY', FUNCLIST => 'ARRAY', H => 'ARRAY', IMPORTS => 'HASH', INCLUDE_EXT => 'ARRAY', LIBS => 'ARRAY', # ignore '' MAN1PODS => 'HASH', MAN3PODS => 'HASH', META_ADD => 'HASH', META_MERGE => 'HASH', PL_FILES => 'HASH', PM => 'HASH', PMLIBDIRS => 'ARRAY', PMLIBPARENTDIRS => 'ARRAY', PREREQ_PM => 'HASH', CONFIGURE_REQUIRES => 'HASH', SKIP => 'ARRAY', TYPEMAPS => 'ARRAY', XS => 'HASH', # VERSION => ['version',''], # ignore # _KEEP_AFTER_FLUSH => '', clean => 'HASH', depend => 'HASH', dist => 'HASH', dynamic_lib=> 'HASH', linkext => 'HASH', macro => 'HASH', postamble => 'HASH', realclean => 'HASH', test => 'HASH', tool_autosplit => 'HASH', # special cases where you can use makemaker_append CCFLAGS => 'APPENDABLE', DEFINE => 'APPENDABLE', INC => 'APPENDABLE', LDDLFLAGS => 'APPENDABLE', LDFROM => 'APPENDABLE', ); sub makemaker_args { my ($self, %new_args) = @_; my $args = ( $self->{makemaker_args} ||= {} ); foreach my $key (keys %new_args) { if ($makemaker_argtype{$key}) { if ($makemaker_argtype{$key} eq 'ARRAY') { $args->{$key} = [] unless defined $args->{$key}; unless (ref $args->{$key} eq 'ARRAY') { $args->{$key} = [$args->{$key}] } push @{$args->{$key}}, ref $new_args{$key} eq 'ARRAY' ? @{$new_args{$key}} : $new_args{$key}; } elsif ($makemaker_argtype{$key} eq 'HASH') { $args->{$key} = {} unless defined $args->{$key}; foreach my $skey (keys %{ $new_args{$key} }) { $args->{$key}{$skey} = $new_args{$key}{$skey}; } } elsif ($makemaker_argtype{$key} eq 'APPENDABLE') { $self->makemaker_append($key => $new_args{$key}); } } else { if (defined $args->{$key}) { warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n}; } $args->{$key} = $new_args{$key}; } } return $args; } # For mm args that take multiple space-seperated args, # append an argument to the current list. sub makemaker_append { my $self = shift; my $name = shift; my $args = $self->makemaker_args; $args->{$name} = defined $args->{$name} ? join( ' ', $args->{$name}, @_ ) : join( ' ', @_ ); } sub build_subdirs { my $self = shift; my $subdirs = $self->makemaker_args->{DIR} ||= []; for my $subdir (@_) { push @$subdirs, $subdir; } } sub clean_files { my $self = shift; my $clean = $self->makemaker_args->{clean} ||= {}; %$clean = ( %$clean, FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_), ); } sub realclean_files { my $self = shift; my $realclean = $self->makemaker_args->{realclean} ||= {}; %$realclean = ( %$realclean, FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_), ); } sub libs { my $self = shift; my $libs = ref $_[0] ? shift : [ shift ]; $self->makemaker_args( LIBS => $libs ); } sub inc { my $self = shift; $self->makemaker_args( INC => shift ); } sub _wanted_t { } sub tests_recursive { my $self = shift; my $dir = shift || 't'; unless ( -d $dir ) { die "tests_recursive dir '$dir' does not exist"; } my %tests = map { $_ => 1 } split / /, ($self->tests || ''); require File::Find; File::Find::find( sub { /\.t$/ and -f $_ and $tests{"$File::Find::dir/*.t"} = 1 }, $dir ); $self->tests( join ' ', sort keys %tests ); } sub write { my $self = shift; die "&Makefile->write() takes no arguments\n" if @_; # Check the current Perl version my $perl_version = $self->perl_version; if ( $perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; } # Make sure we have a new enough MakeMaker require ExtUtils::MakeMaker; if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) { # This previous attempted to inherit the version of # ExtUtils::MakeMaker in use by the module author, but this # was found to be untenable as some authors build releases # using future dev versions of EU:MM that nobody else has. # Instead, #toolchain suggests we use 6.59 which is the most # stable version on CPAN at time of writing and is, to quote # ribasushi, "not terminally fucked, > and tested enough". # TODO: We will now need to maintain this over time to push # the version up as new versions are released. $self->build_requires( 'ExtUtils::MakeMaker' => 6.59 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.59 ); } else { # Allow legacy-compatibility with 5.005 by depending on the # most recent EU:MM that supported 5.005. $self->build_requires( 'ExtUtils::MakeMaker' => 6.36 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.36 ); } # Generate the MakeMaker params my $args = $self->makemaker_args; $args->{DISTNAME} = $self->name; $args->{NAME} = $self->module_name || $self->name; $args->{NAME} =~ s/-/::/g; $args->{VERSION} = $self->version or die <<'EOT'; ERROR: Can't determine distribution version. Please specify it explicitly via 'version' in Makefile.PL, or set a valid $VERSION in a module, and provide its file path via 'version_from' (or 'all_from' if you prefer) in Makefile.PL. EOT if ( $self->tests ) { my @tests = split ' ', $self->tests; my %seen; $args->{test} = { TESTS => (join ' ', grep {!$seen{$_}++} @tests), }; } elsif ( $Module::Install::ExtraTests::use_extratests ) { # Module::Install::ExtraTests doesn't set $self->tests and does its own tests via harness. # So, just ignore our xt tests here. } elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) { $args->{test} = { TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ), }; } if ( $] >= 5.005 ) { $args->{ABSTRACT} = $self->abstract; $args->{AUTHOR} = join ', ', @{$self->author || []}; } if ( $self->makemaker(6.10) ) { $args->{NO_META} = 1; #$args->{NO_MYMETA} = 1; } if ( $self->makemaker(6.17) and $self->sign ) { $args->{SIGN} = 1; } unless ( $self->is_admin ) { delete $args->{SIGN}; } if ( $self->makemaker(6.31) and $self->license ) { $args->{LICENSE} = $self->license; } my $prereq = ($args->{PREREQ_PM} ||= {}); %$prereq = ( %$prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->requires) ); # Remove any reference to perl, PREREQ_PM doesn't support it delete $args->{PREREQ_PM}->{perl}; # Merge both kinds of requires into BUILD_REQUIRES my $build_prereq = ($args->{BUILD_REQUIRES} ||= {}); %$build_prereq = ( %$build_prereq, map { @$_ } # flatten [module => version] map { @$_ } grep $_, ($self->configure_requires, $self->build_requires) ); # Remove any reference to perl, BUILD_REQUIRES doesn't support it delete $args->{BUILD_REQUIRES}->{perl}; # Delete bundled dists from prereq_pm, add it to Makefile DIR my $subdirs = ($args->{DIR} || []); if ($self->bundles) { my %processed; foreach my $bundle (@{ $self->bundles }) { my ($mod_name, $dist_dir) = @$bundle; delete $prereq->{$mod_name}; $dist_dir = File::Basename::basename($dist_dir); # dir for building this module if (not exists $processed{$dist_dir}) { if (-d $dist_dir) { # List as sub-directory to be processed by make push @$subdirs, $dist_dir; } # Else do nothing: the module is already present on the system $processed{$dist_dir} = undef; } } } unless ( $self->makemaker('6.55_03') ) { %$prereq = (%$prereq,%$build_prereq); delete $args->{BUILD_REQUIRES}; } if ( my $perl_version = $self->perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; if ( $self->makemaker(6.48) ) { $args->{MIN_PERL_VERSION} = $perl_version; } } if ($self->installdirs) { warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS}; $args->{INSTALLDIRS} = $self->installdirs; } my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_} ) } keys %$args; my $user_preop = delete $args{dist}->{PREOP}; if ( my $preop = $self->admin->preop($user_preop) ) { foreach my $key ( keys %$preop ) { $args{dist}->{$key} = $preop->{$key}; } } my $mm = ExtUtils::MakeMaker::WriteMakefile(%args); $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile'); } sub fix_up_makefile { my $self = shift; my $makefile_name = shift; my $top_class = ref($self->_top) || ''; my $top_version = $self->_top->VERSION || ''; my $preamble = $self->preamble ? "# Preamble by $top_class $top_version\n" . $self->preamble : ''; my $postamble = "# Postamble by $top_class $top_version\n" . ($self->postamble || ''); local *MAKEFILE; open MAKEFILE, "+< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; eval { flock MAKEFILE, LOCK_EX }; my $makefile = do { local $/; }; $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m; $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m; # Module::Install will never be used to build the Core Perl # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m; #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m; # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well. $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g; # XXX - This is currently unused; not sure if it breaks other MM-users # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg; seek MAKEFILE, 0, SEEK_SET; truncate MAKEFILE, 0; print MAKEFILE "$preamble$makefile$postamble" or die $!; close MAKEFILE or die $!; 1; } sub preamble { my ($self, $text) = @_; $self->{preamble} = $text . $self->{preamble} if defined $text; $self->{preamble}; } sub postamble { my ($self, $text) = @_; $self->{postamble} ||= $self->admin->postamble; $self->{postamble} .= $text if defined $text; $self->{postamble} } 1; __END__ #line 544 rt-extension-assettracker-3.0.0/inc/Module/Install/Metadata.pm000066400000000000000000000432771222742774700243570ustar00rootroot00000000000000#line 1 package Module::Install::Metadata; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } my @boolean_keys = qw{ sign }; my @scalar_keys = qw{ name module_name abstract version distribution_type tests installdirs }; my @tuple_keys = qw{ configure_requires build_requires requires recommends bundles resources }; my @resource_keys = qw{ homepage bugtracker repository }; my @array_keys = qw{ keywords author }; *authors = \&author; sub Meta { shift } sub Meta_BooleanKeys { @boolean_keys } sub Meta_ScalarKeys { @scalar_keys } sub Meta_TupleKeys { @tuple_keys } sub Meta_ResourceKeys { @resource_keys } sub Meta_ArrayKeys { @array_keys } foreach my $key ( @boolean_keys ) { *$key = sub { my $self = shift; if ( defined wantarray and not @_ ) { return $self->{values}->{$key}; } $self->{values}->{$key} = ( @_ ? $_[0] : 1 ); return $self; }; } foreach my $key ( @scalar_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} = shift; return $self; }; } foreach my $key ( @array_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} ||= []; push @{$self->{values}->{$key}}, @_; return $self; }; } foreach my $key ( @resource_keys ) { *$key = sub { my $self = shift; unless ( @_ ) { return () unless $self->{values}->{resources}; return map { $_->[1] } grep { $_->[0] eq $key } @{ $self->{values}->{resources} }; } return $self->{values}->{resources}->{$key} unless @_; my $uri = shift or die( "Did not provide a value to $key()" ); $self->resources( $key => $uri ); return 1; }; } foreach my $key ( grep { $_ ne "resources" } @tuple_keys) { *$key = sub { my $self = shift; return $self->{values}->{$key} unless @_; my @added; while ( @_ ) { my $module = shift or last; my $version = shift || 0; push @added, [ $module, $version ]; } push @{ $self->{values}->{$key} }, @added; return map {@$_} @added; }; } # Resource handling my %lc_resource = map { $_ => 1 } qw{ homepage license bugtracker repository }; sub resources { my $self = shift; while ( @_ ) { my $name = shift or last; my $value = shift or next; if ( $name eq lc $name and ! $lc_resource{$name} ) { die("Unsupported reserved lowercase resource '$name'"); } $self->{values}->{resources} ||= []; push @{ $self->{values}->{resources} }, [ $name, $value ]; } $self->{values}->{resources}; } # Aliases for build_requires that will have alternative # meanings in some future version of META.yml. sub test_requires { shift->build_requires(@_) } sub install_requires { shift->build_requires(@_) } # Aliases for installdirs options sub install_as_core { $_[0]->installdirs('perl') } sub install_as_cpan { $_[0]->installdirs('site') } sub install_as_site { $_[0]->installdirs('site') } sub install_as_vendor { $_[0]->installdirs('vendor') } sub dynamic_config { my $self = shift; my $value = @_ ? shift : 1; if ( $self->{values}->{dynamic_config} ) { # Once dynamic we never change to static, for safety return 0; } $self->{values}->{dynamic_config} = $value ? 1 : 0; return 1; } # Convenience command sub static_config { shift->dynamic_config(0); } sub perl_version { my $self = shift; return $self->{values}->{perl_version} unless @_; my $version = shift or die( "Did not provide a value to perl_version()" ); # Normalize the version $version = $self->_perl_version($version); # We don't support the really old versions unless ( $version >= 5.005 ) { die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n"; } $self->{values}->{perl_version} = $version; } sub all_from { my ( $self, $file ) = @_; unless ( defined($file) ) { my $name = $self->name or die( "all_from called with no args without setting name() first" ); $file = join('/', 'lib', split(/-/, $name)) . '.pm'; $file =~ s{.*/}{} unless -e $file; unless ( -e $file ) { die("all_from cannot find $file from $name"); } } unless ( -f $file ) { die("The path '$file' does not exist, or is not a file"); } $self->{values}{all_from} = $file; # Some methods pull from POD instead of code. # If there is a matching .pod, use that instead my $pod = $file; $pod =~ s/\.pm$/.pod/i; $pod = $file unless -e $pod; # Pull the different values $self->name_from($file) unless $self->name; $self->version_from($file) unless $self->version; $self->perl_version_from($file) unless $self->perl_version; $self->author_from($pod) unless @{$self->author || []}; $self->license_from($pod) unless $self->license; $self->abstract_from($pod) unless $self->abstract; return 1; } sub provides { my $self = shift; my $provides = ( $self->{values}->{provides} ||= {} ); %$provides = (%$provides, @_) if @_; return $provides; } sub auto_provides { my $self = shift; return $self unless $self->is_admin; unless (-e 'MANIFEST') { warn "Cannot deduce auto_provides without a MANIFEST, skipping\n"; return $self; } # Avoid spurious warnings as we are not checking manifest here. local $SIG{__WARN__} = sub {1}; require ExtUtils::Manifest; local *ExtUtils::Manifest::manicheck = sub { return }; require Module::Build; my $build = Module::Build->new( dist_name => $self->name, dist_version => $self->version, license => $self->license, ); $self->provides( %{ $build->find_dist_packages || {} } ); } sub feature { my $self = shift; my $name = shift; my $features = ( $self->{values}->{features} ||= [] ); my $mods; if ( @_ == 1 and ref( $_[0] ) ) { # The user used ->feature like ->features by passing in the second # argument as a reference. Accomodate for that. $mods = $_[0]; } else { $mods = \@_; } my $count = 0; push @$features, ( $name => [ map { ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_ } @$mods ] ); return @$features; } sub features { my $self = shift; while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) { $self->feature( $name, @$mods ); } return $self->{values}->{features} ? @{ $self->{values}->{features} } : (); } sub no_index { my $self = shift; my $type = shift; push @{ $self->{values}->{no_index}->{$type} }, @_ if $type; return $self->{values}->{no_index}; } sub read { my $self = shift; $self->include_deps( 'YAML::Tiny', 0 ); require YAML::Tiny; my $data = YAML::Tiny::LoadFile('META.yml'); # Call methods explicitly in case user has already set some values. while ( my ( $key, $value ) = each %$data ) { next unless $self->can($key); if ( ref $value eq 'HASH' ) { while ( my ( $module, $version ) = each %$value ) { $self->can($key)->($self, $module => $version ); } } else { $self->can($key)->($self, $value); } } return $self; } sub write { my $self = shift; return $self unless $self->is_admin; $self->admin->write_meta; return $self; } sub version_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->version( ExtUtils::MM_Unix->parse_version($file) ); # for version integrity check $self->makemaker_args( VERSION_FROM => $file ); } sub abstract_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->abstract( bless( { DISTNAME => $self->name }, 'ExtUtils::MM_Unix' )->parse_abstract($file) ); } # Add both distribution and module name sub name_from { my ($self, $file) = @_; if ( Module::Install::_read($file) =~ m/ ^ \s* package \s* ([\w:]+) \s* ; /ixms ) { my ($name, $module_name) = ($1, $1); $name =~ s{::}{-}g; $self->name($name); unless ( $self->module_name ) { $self->module_name($module_name); } } else { die("Cannot determine name from $file\n"); } } sub _extract_perl_version { if ( $_[0] =~ m/ ^\s* (?:use|require) \s* v? ([\d_\.]+) \s* ; /ixms ) { my $perl_version = $1; $perl_version =~ s{_}{}g; return $perl_version; } else { return; } } sub perl_version_from { my $self = shift; my $perl_version=_extract_perl_version(Module::Install::_read($_[0])); if ($perl_version) { $self->perl_version($perl_version); } else { warn "Cannot determine perl version info from $_[0]\n"; return; } } sub author_from { my $self = shift; my $content = Module::Install::_read($_[0]); if ($content =~ m/ =head \d \s+ (?:authors?)\b \s* ([^\n]*) | =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s* .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s* ([^\n]*) /ixms) { my $author = $1 || $2; # XXX: ugly but should work anyway... if (eval "require Pod::Escapes; 1") { # Pod::Escapes has a mapping table. # It's in core of perl >= 5.9.3, and should be installed # as one of the Pod::Simple's prereqs, which is a prereq # of Pod::Text 3.x (see also below). $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $Pod::Escapes::Name2character_number{$1} ? chr($Pod::Escapes::Name2character_number{$1}) : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) { # Pod::Text < 3.0 has yet another mapping table, # though the table name of 2.x and 1.x are different. # (1.x is in core of Perl < 5.6, 2.x is in core of # Perl < 5.9.3) my $mapping = ($Pod::Text::VERSION < 2) ? \%Pod::Text::HTML_Escapes : \%Pod::Text::ESCAPES; $author =~ s{ E<( (\d+) | ([A-Za-z]+) )> } { defined $2 ? chr($2) : defined $mapping->{$1} ? $mapping->{$1} : do { warn "Unknown escape: E<$1>"; "E<$1>"; }; }gex; } else { $author =~ s{E}{<}g; $author =~ s{E}{>}g; } $self->author($author); } else { warn "Cannot determine author info from $_[0]\n"; } } #Stolen from M::B my %license_urls = ( perl => 'http://dev.perl.org/licenses/', apache => 'http://apache.org/licenses/LICENSE-2.0', apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1', artistic => 'http://opensource.org/licenses/artistic-license.php', artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php', lgpl => 'http://opensource.org/licenses/lgpl-license.php', lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php', lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html', bsd => 'http://opensource.org/licenses/bsd-license.php', gpl => 'http://opensource.org/licenses/gpl-license.php', gpl2 => 'http://opensource.org/licenses/gpl-2.0.php', gpl3 => 'http://opensource.org/licenses/gpl-3.0.html', mit => 'http://opensource.org/licenses/mit-license.php', mozilla => 'http://opensource.org/licenses/mozilla1.1.php', open_source => undef, unrestricted => undef, restrictive => undef, unknown => undef, ); sub license { my $self = shift; return $self->{values}->{license} unless @_; my $license = shift or die( 'Did not provide a value to license()' ); $license = __extract_license($license) || lc $license; $self->{values}->{license} = $license; # Automatically fill in license URLs if ( $license_urls{$license} ) { $self->resources( license => $license_urls{$license} ); } return 1; } sub _extract_license { my $pod = shift; my $matched; return __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ L(?i:ICEN[CS]E|ICENSING)\b.*?) (=head \d.*|=cut.*|)\z /xms ) || __extract_license( ($matched) = $pod =~ m/ (=head \d \s+ (?:C(?i:OPYRIGHTS?)|L(?i:EGAL))\b.*?) (=head \d.*|=cut.*|)\z /xms ); } sub __extract_license { my $license_text = shift or return; my @phrases = ( '(?:under )?the same (?:terms|license) as (?:perl|the perl (?:\d )?programming language)' => 'perl', 1, '(?:under )?the terms of (?:perl|the perl programming language) itself' => 'perl', 1, 'Artistic and GPL' => 'perl', 1, 'GNU general public license' => 'gpl', 1, 'GNU public license' => 'gpl', 1, 'GNU lesser general public license' => 'lgpl', 1, 'GNU lesser public license' => 'lgpl', 1, 'GNU library general public license' => 'lgpl', 1, 'GNU library public license' => 'lgpl', 1, 'GNU Free Documentation license' => 'unrestricted', 1, 'GNU Affero General Public License' => 'open_source', 1, '(?:Free)?BSD license' => 'bsd', 1, 'Artistic license 2\.0' => 'artistic_2', 1, 'Artistic license' => 'artistic', 1, 'Apache (?:Software )?license' => 'apache', 1, 'GPL' => 'gpl', 1, 'LGPL' => 'lgpl', 1, 'BSD' => 'bsd', 1, 'Artistic' => 'artistic', 1, 'MIT' => 'mit', 1, 'Mozilla Public License' => 'mozilla', 1, 'Q Public License' => 'open_source', 1, 'OpenSSL License' => 'unrestricted', 1, 'SSLeay License' => 'unrestricted', 1, 'zlib License' => 'open_source', 1, 'proprietary' => 'proprietary', 0, ); while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) { $pattern =~ s#\s+#\\s+#gs; if ( $license_text =~ /\b$pattern\b/i ) { return $license; } } return ''; } sub license_from { my $self = shift; if (my $license=_extract_license(Module::Install::_read($_[0]))) { $self->license($license); } else { warn "Cannot determine license info from $_[0]\n"; return 'unknown'; } } sub _extract_bugtracker { my @links = $_[0] =~ m#L<( https?\Q://rt.cpan.org/\E[^>]+| https?\Q://github.com/\E[\w_]+/[\w_]+/issues| https?\Q://code.google.com/p/\E[\w_\-]+/issues/list )>#gx; my %links; @links{@links}=(); @links=keys %links; return @links; } sub bugtracker_from { my $self = shift; my $content = Module::Install::_read($_[0]); my @links = _extract_bugtracker($content); unless ( @links ) { warn "Cannot determine bugtracker info from $_[0]\n"; return 0; } if ( @links > 1 ) { warn "Found more than one bugtracker link in $_[0]\n"; return 0; } # Set the bugtracker bugtracker( $links[0] ); return 1; } sub requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+(v?[\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->requires( $module => $version ); } } sub test_requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->test_requires( $module => $version ); } } # Convert triple-part versions (eg, 5.6.1 or 5.8.9) to # numbers (eg, 5.006001 or 5.008009). # Also, convert double-part versions (eg, 5.8) sub _perl_version { my $v = $_[-1]; $v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e; $v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e; $v =~ s/(\.\d\d\d)000$/$1/; $v =~ s/_.+$//; if ( ref($v) ) { # Numify $v = $v + 0; } return $v; } sub add_metadata { my $self = shift; my %hash = @_; for my $key (keys %hash) { warn "add_metadata: $key is not prefixed with 'x_'.\n" . "Use appopriate function to add non-private metadata.\n" unless $key =~ /^x_/; $self->{values}->{$key} = $hash{$key}; } } ###################################################################### # MYMETA Support sub WriteMyMeta { die "WriteMyMeta has been deprecated"; } sub write_mymeta_yaml { my $self = shift; # We need YAML::Tiny to write the MYMETA.yml file unless ( eval { require YAML::Tiny; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.yml\n"; YAML::Tiny::DumpFile('MYMETA.yml', $meta); } sub write_mymeta_json { my $self = shift; # We need JSON to write the MYMETA.json file unless ( eval { require JSON; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.json\n"; Module::Install::_write( 'MYMETA.json', JSON->new->pretty(1)->canonical->encode($meta), ); } sub _write_mymeta_data { my $self = shift; # If there's no existing META.yml there is nothing we can do return undef unless -f 'META.yml'; # We need Parse::CPAN::Meta to load the file unless ( eval { require Parse::CPAN::Meta; 1; } ) { return undef; } # Merge the perl version into the dependencies my $val = $self->Meta->{values}; my $perl = delete $val->{perl_version}; if ( $perl ) { $val->{requires} ||= []; my $requires = $val->{requires}; # Canonize to three-dot version after Perl 5.6 if ( $perl >= 5.006 ) { $perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e } unshift @$requires, [ perl => $perl ]; } # Load the advisory META.yml file my @yaml = Parse::CPAN::Meta::LoadFile('META.yml'); my $meta = $yaml[0]; # Overwrite the non-configure dependency hashs delete $meta->{requires}; delete $meta->{build_requires}; delete $meta->{recommends}; if ( exists $val->{requires} ) { $meta->{requires} = { map { @$_ } @{ $val->{requires} } }; } if ( exists $val->{build_requires} ) { $meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } }; } return $meta; } 1; rt-extension-assettracker-3.0.0/inc/Module/Install/RTx.pm000066400000000000000000000153351222742774700233460ustar00rootroot00000000000000#line 1 package Module::Install::RTx; use 5.008; use strict; use warnings; no warnings 'once'; use Module::Install::Base; use base 'Module::Install::Base'; our $VERSION = '0.31'; use FindBin; use File::Glob (); use File::Basename (); my @DIRS = qw(etc lib html static bin sbin po var); my @INDEX_DIRS = qw(lib bin sbin); sub RTx { my ( $self, $name ) = @_; my $original_name = $name; my $RTx = 'RTx'; $RTx = $1 if $name =~ s/^(\w+)-//; my $fname = $name; $fname =~ s!-!/!g; $self->name("$RTx-$name") unless $self->name; $self->all_from( -e "$name.pm" ? "$name.pm" : "lib/$RTx/$fname.pm" ) unless $self->version; $self->abstract("RT $name Extension") unless $self->abstract; my @prefixes = (qw(/opt /usr/local /home /usr /sw )); my $prefix = $ENV{PREFIX}; @ARGV = grep { /PREFIX=(.*)/ ? ( ( $prefix = $1 ), 0 ) : 1 } @ARGV; if ($prefix) { $RT::LocalPath = $prefix; $INC{'RT.pm'} = "$RT::LocalPath/lib/RT.pm"; } else { local @INC = ( $ENV{RTHOME} ? ( $ENV{RTHOME}, "$ENV{RTHOME}/lib" ) : (), @INC, map { ( "$_/rt4/lib", "$_/lib/rt4", "$_/rt3/lib", "$_/lib/rt3", "$_/lib" ) } grep $_, @prefixes ); until ( eval { require RT; $RT::LocalPath } ) { warn "Cannot find the location of RT.pm that defines \$RT::LocalPath in: @INC\n"; $_ = $self->prompt("Path to directory containing your RT.pm:") or exit; $_ =~ s/\/RT\.pm$//; push @INC, $_, "$_/rt3/lib", "$_/lib/rt3", "$_/lib"; } } my $lib_path = File::Basename::dirname( $INC{'RT.pm'} ); my $local_lib_path = "$RT::LocalPath/lib"; print "Using RT configuration from $INC{'RT.pm'}:\n"; unshift @INC, "$RT::LocalPath/lib" if $RT::LocalPath; unshift @INC, $lib_path; $RT::LocalVarPath ||= $RT::VarPath; $RT::LocalPoPath ||= $RT::LocalLexiconPath; $RT::LocalHtmlPath ||= $RT::MasonComponentRoot; $RT::LocalStaticPath ||= $RT::StaticPath; $RT::LocalLibPath ||= "$RT::LocalPath/lib"; my $with_subdirs = $ENV{WITH_SUBDIRS}; @ARGV = grep { /WITH_SUBDIRS=(.*)/ ? ( ( $with_subdirs = $1 ), 0 ) : 1 } @ARGV; my %subdirs; %subdirs = map { $_ => 1 } split( /\s*,\s*/, $with_subdirs ) if defined $with_subdirs; unless ( keys %subdirs ) { $subdirs{$_} = 1 foreach grep -d "$FindBin::Bin/$_", @DIRS; } # If we're running on RT 3.8 with plugin support, we really wany # to install libs, mason templates and po files into plugin specific # directories my %path; if ( $RT::LocalPluginPath ) { die "Because of bugs in RT 3.8.0 this extension can not be installed.\n" ."Upgrade to RT 3.8.1 or newer.\n" if $RT::VERSION =~ /^3\.8\.0/; $path{$_} = $RT::LocalPluginPath . "/$original_name/$_" foreach @DIRS; } else { foreach ( @DIRS ) { no strict 'refs'; my $varname = "RT::Local" . ucfirst($_) . "Path"; $path{$_} = ${$varname} || "$RT::LocalPath/$_"; } $path{$_} .= "/$name" for grep $path{$_}, qw(etc po var); } my %index = map { $_ => 1 } @INDEX_DIRS; $self->no_index( directory => $_ ) foreach grep !$index{$_}, @DIRS; my $args = join ', ', map "q($_)", map { ($_, $path{$_}) } grep $subdirs{$_}, keys %path; print "./$_\t=> $path{$_}\n" for sort keys %subdirs; if ( my @dirs = map { ( -D => $_ ) } grep $subdirs{$_}, qw(bin html sbin) ) { my @po = map { ( -o => $_ ) } grep -f, File::Glob::bsd_glob("po/*.po"); $self->postamble(<< ".") if @po; lexicons :: \t\$(NOECHO) \$(PERL) -MLocale::Maketext::Extract::Run=xgettext -e \"xgettext(qw(@dirs @po))\" . } my $postamble = << "."; install :: \t\$(NOECHO) \$(PERL) -MExtUtils::Install -e \"install({$args})\" . if ( $subdirs{var} and -d $RT::MasonDataDir ) { my ( $uid, $gid ) = ( stat($RT::MasonDataDir) )[ 4, 5 ]; $postamble .= << "."; \t\$(NOECHO) chown -R $uid:$gid $path{var} . } my %has_etc; if ( File::Glob::bsd_glob("$FindBin::Bin/etc/schema.*") ) { $has_etc{schema}++; } if ( File::Glob::bsd_glob("$FindBin::Bin/etc/acl.*") ) { $has_etc{acl}++; } if ( -e 'etc/initialdata' ) { $has_etc{initialdata}++; } $self->postamble("$postamble\n"); unless ( $subdirs{'lib'} ) { $self->makemaker_args( PM => { "" => "" }, ); } else { $self->makemaker_args( INSTALLSITELIB => $path{'lib'} ); $self->makemaker_args( INSTALLARCHLIB => $path{'lib'} ); } $self->makemaker_args( INSTALLSITEMAN1DIR => "$RT::LocalPath/man/man1" ); $self->makemaker_args( INSTALLSITEMAN3DIR => "$RT::LocalPath/man/man3" ); $self->makemaker_args( INSTALLSITEARCH => "$RT::LocalPath/man" ); if (%has_etc) { $self->load('RTxInitDB'); print "For first-time installation, type 'make initdb'.\n"; my $initdb = ''; $initdb .= <<"." if $has_etc{schema}; \t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxInitDB(qw(schema \$(NAME) \$(VERSION)))" . $initdb .= <<"." if $has_etc{acl}; \t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxInitDB(qw(acl \$(NAME) \$(VERSION)))" . $initdb .= <<"." if $has_etc{initialdata}; \t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxInitDB(qw(insert \$(NAME) \$(VERSION)))" . $self->postamble("initdb ::\n$initdb\n"); $self->postamble("initialize-database ::\n$initdb\n"); } } # stolen from RT::Handle so we work on 3.6 (cmp_versions came in with 3.8) { my %word = ( a => -4, alpha => -4, b => -3, beta => -3, pre => -2, rc => -1, head => 9999, ); sub cmp_version($$) { my ($a, $b) = (@_); my @a = grep defined, map { /^[0-9]+$/? $_ : /^[a-zA-Z]+$/? $word{$_}|| -10 : undef } split /([^0-9]+)/, $a; my @b = grep defined, map { /^[0-9]+$/? $_ : /^[a-zA-Z]+$/? $word{$_}|| -10 : undef } split /([^0-9]+)/, $b; @a > @b ? push @b, (0) x (@a-@b) : push @a, (0) x (@b-@a); for ( my $i = 0; $i < @a; $i++ ) { return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i]; } return 0; }} sub requires_rt { my ($self,$version) = @_; # if we're exactly the same version as what we want, silently return return if ($version eq $RT::VERSION); my @sorted = sort cmp_version $version,$RT::VERSION; if ($sorted[-1] eq $version) { # should we die? warn "\nWarning: prerequisite RT $version not found. Your installed version of RT ($RT::VERSION) is too old.\n\n"; } } 1; __END__ #line 329 rt-extension-assettracker-3.0.0/inc/Module/Install/RTx/000077500000000000000000000000001222742774700230015ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/inc/Module/Install/RTx/Factory.pm000066400000000000000000000025601222742774700247510ustar00rootroot00000000000000#line 1 package Module::Install::RTx::Factory; use Module::Install::Base; @ISA = qw(Module::Install::Base); use strict; use File::Basename (); sub RTxInitDB { my ($self, $action, $name, $version) = @_; unshift @INC, substr(delete($INC{'RT.pm'}), 0, -5) if $INC{'RT.pm'}; require RT; unshift @INC, "$RT::LocalPath/lib" if $RT::LocalPath; $RT::SbinPath ||= $RT::LocalPath; $RT::SbinPath =~ s/local$/sbin/; foreach my $file ($RT::CORE_CONFIG_FILE, $RT::SITE_CONFIG_FILE) { next if !-e $file or -r $file; die "No permission to read $file\n-- please re-run $0 with suitable privileges.\n"; } RT::LoadConfig(); require RT::System; my $lib_path = File::Basename::dirname($INC{'RT.pm'}); my @args = ("-Ilib"); push @args, "-I$RT::LocalPath/lib" if $RT::LocalPath; push @args, ( "-I$lib_path", "$RT::SbinPath/rt-setup-database", "--action" => $action, "--datadir" => "etc", (($action eq 'insert') ? ("--datafile" => "etc/initialdata") : ()), "--dba" => $RT::DatabaseAdmin || $RT::DatabaseUser, "--prompt-for-dba-password" => '', (RT::System->can('AddUpgradeHistory') ? ("--package" => $name, "--ext-version" => $version) : ()), ); print "$^X @args\n"; (system($^X, @args) == 0) or die "...returned with error: $?\n"; } 1; rt-extension-assettracker-3.0.0/inc/Module/Install/Substitute.pm000066400000000000000000000056221222742774700250020ustar00rootroot00000000000000#line 1 package Module::Install::Substitute; use strict; use warnings; use 5.008; # I don't care much about earlier versions use Module::Install::Base; our @ISA = qw(Module::Install::Base); our $VERSION = '0.03'; require File::Temp; require File::Spec; require Cwd; #line 89 sub substitute { my $self = shift; $self->{__subst} = shift; $self->{__option} = {}; if( UNIVERSAL::isa( $_[0], 'HASH' ) ) { my $opts = shift; while( my ($k,$v) = each( %$opts ) ) { $self->{__option}->{ lc( $k ) } = $v || ''; } } $self->_parse_options; my @file = @_; foreach my $f (@file) { $self->_rewrite_file( $f ); } return; } sub _parse_options { my $self = shift; my $cwd = Cwd::getcwd(); foreach my $t ( qw(from to) ) { $self->{__option}->{$t} = $cwd unless $self->{__option}->{$t}; my $d = $self->{__option}->{$t}; die "Couldn't read directory '$d'" unless -d $d && -r _; } } sub _rewrite_file { my ($self, $file) = @_; my $source = File::Spec->catfile( $self->{__option}{from}, $file ); $source .= $self->{__option}{sufix} if $self->{__option}{sufix}; unless( -f $source && -r _ ) { print STDERR "Couldn't find file '$source'\n"; return; } my $dest = File::Spec->catfile( $self->{__option}{to}, $file ); return $self->__rewrite_file( $source, $dest ); } sub __rewrite_file { my ($self, $source, $dest) = @_; my $mode = (stat($source))[2]; open my $sfh, "<$source" or die "Couldn't open '$source' for read"; print "Open input '$source' file for substitution\n"; my ($tmpfh, $tmpfname) = File::Temp::tempfile('mi-subst-XXXX', UNLINK => 1); $self->__process_streams( $sfh, $tmpfh, ($source eq $dest)? 1: 0 ); close $sfh; seek $tmpfh, 0, 0 or die "Couldn't seek in tmp file"; open my $dfh, ">$dest" or die "Couldn't open '$dest' for write"; print "Open output '$dest' file for substitution\n"; while( <$tmpfh> ) { print $dfh $_; } close $dfh; chmod $mode, $dest or "Couldn't change mode on '$dest'"; } sub __process_streams { my ($self, $in, $out, $replace) = @_; my @queue = (); my $subst = $self->{'__subst'}; my $re_subst = join('|', map {"\Q$_"} keys %{ $subst } ); while( my $str = <$in> ) { if( $str =~ /^###\s*(before|replace|after)\:\s?(.*)$/s ) { my ($action, $nstr) = ($1,$2); $nstr =~ s/\@($re_subst)\@/$subst->{$1}/ge; die "Replace action is bad idea for situations when dest is equal to source" if $replace && $action eq 'replace'; if( $action eq 'before' ) { die "no line before 'before' action" unless @queue; # overwrite prev line; pop @queue; push @queue, $nstr; push @queue, $str; } elsif( $action eq 'replace' ) { push @queue, $nstr; } elsif( $action eq 'after' ) { push @queue, $str; push @queue, $nstr; # skip one line; <$in>; } } else { push @queue, $str; } while( @queue > 3 ) { print $out shift(@queue); } } while( scalar @queue ) { print $out shift(@queue); } } 1; rt-extension-assettracker-3.0.0/inc/Module/Install/Win32.pm000066400000000000000000000034031222742774700235240ustar00rootroot00000000000000#line 1 package Module::Install::Win32; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # determine if the user needs nmake, and download it if needed sub check_nmake { my $self = shift; $self->load('can_run'); $self->load('get_file'); require Config; return unless ( $^O eq 'MSWin32' and $Config::Config{make} and $Config::Config{make} =~ /^nmake\b/i and ! $self->can_run('nmake') ); print "The required 'nmake' executable not found, fetching it...\n"; require File::Basename; my $rv = $self->get_file( url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', local_dir => File::Basename::dirname($^X), size => 51928, run => 'Nmake15.exe /o > nul', check_for => 'Nmake.exe', remove => 1, ); die <<'END_MESSAGE' unless $rv; ------------------------------------------------------------------------------- Since you are using Microsoft Windows, you will need the 'nmake' utility before installation. It's available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe or ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe Please download the file manually, save it to a directory in %PATH% (e.g. C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to that directory, and run "Nmake15.exe" from there; that will create the 'nmake.exe' file needed by this module. You may then resume the installation process described in README. ------------------------------------------------------------------------------- END_MESSAGE } 1; rt-extension-assettracker-3.0.0/inc/Module/Install/WriteAll.pm000066400000000000000000000023761222742774700243550ustar00rootroot00000000000000#line 1 package Module::Install::WriteAll; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '1.06'; @ISA = qw{Module::Install::Base}; $ISCORE = 1; } sub WriteAll { my $self = shift; my %args = ( meta => 1, sign => 0, inline => 0, check_nmake => 1, @_, ); $self->sign(1) if $args{sign}; $self->admin->WriteAll(%args) if $self->is_admin; $self->check_nmake if $args{check_nmake}; unless ( $self->makemaker_args->{PL_FILES} ) { # XXX: This still may be a bit over-defensive... unless ($self->makemaker(6.25)) { $self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL'; } } # Until ExtUtils::MakeMaker support MYMETA.yml, make sure # we clean it up properly ourself. $self->realclean_files('MYMETA.yml'); if ( $args{inline} ) { $self->Inline->write; } else { $self->Makefile->write; } # The Makefile write process adds a couple of dependencies, # so write the META.yml files after the Makefile. if ( $args{meta} ) { $self->Meta->write; } # Experimental support for MYMETA if ( $ENV{X_MYMETA} ) { if ( $ENV{X_MYMETA} eq 'JSON' ) { $self->Meta->write_mymeta_json; } else { $self->Meta->write_mymeta_yaml; } } return 1; } 1; rt-extension-assettracker-3.0.0/lib/000077500000000000000000000000001222742774700174265ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/000077500000000000000000000000001222742774700177535ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/CustomFieldValues/000077500000000000000000000000001222742774700233515ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/CustomFieldValues/ATServers.pm000066400000000000000000000024531222742774700255710ustar00rootroot00000000000000package RT::CustomFieldValues::ATServers; # Activate this CFSource via RT_SiteConfig.pm # Set(@CustomFieldValuesSources, (qw(RT::CustomFieldValues::ATServers))); use base qw(RT::CustomFieldValues::External); use RT; use RTx::AssetTracker::Asset; use RTx::AssetTracker::Type; RT::LoadConfig(); RT::Init(); my $at = RTx::AssetTracker::Asset->new(RT->SystemUser); my $attype = RTx::AssetTracker::Type->new(RT->SystemUser); sub SourceDescription { return 'DC Servers from CMDB'; } sub ExternalValues { my $self = shift; my @res; my $i = 0; my $assets = RTx::AssetTracker::Assets->new($RT::SystemUser); my $query = "Status != 'retired' AND Type = 'Servers'"; my ($id, $msg) = $assets->FromSQL($query); while (my $asset = $assets->Next) { my $assdisc = $asset->Description; my $assname = $asset->Name; push @res, { name => $assname, description => $assdisc, sortorder => $i++, }; } return \@res; } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RT/CustomField_ATOverlay.pm000066400000000000000000000044261222742774700244630ustar00rootroot00000000000000 package RT::CustomField; use strict; no warnings qw(redefine); use RT::CustomField; use RT::CurrentUser; =head2 LoadByNameAndAssetType (Type => TYPEID, Name => NAME) Loads the Custom field named NAME. Will load a Disabled Custom Field even if there is a non-disabled Custom Field with the same Name. If a Type parameter is specified, only look for asset custom fields tied to that Asset Type. If the Type parameter is '0', look for global asset custom fields. If no type parameter is specified, look for any and all custom fields with this name. =cut sub LoadByNameAndAssetType { my $self = shift; my %args = ( Type => undef, Name => undef, @_, ); unless ( defined $args{'Name'} && length $args{'Name'} ) { $RT::Logger->error("Couldn't load Custom Field without Name"); return wantarray ? (0, $self->loc("No name provided")) : 0; } # if we're looking for an asset type by name, make it a number if ( defined $args{'Type'} && ($args{'Type'} =~ /\D/ || !$self->ContextObject) ) { my $TypeObj = RTx::AssetTracker::Type->new( $self->CurrentUser ); $TypeObj->Load( $args{'Type'} ); $args{'Type'} = $TypeObj->Id; $self->SetContextObject( $TypeObj ) unless $self->ContextObject; } # XXX - really naive implementation. Slow. - not really. still just one query my $CFs = RT::CustomFields->new( $self->CurrentUser ); $CFs->SetContextObject( $self->ContextObject ); my $field = $args{'Name'} =~ /\D/? 'Name' : 'id'; $CFs->Limit( FIELD => $field, VALUE => $args{'Name'}, CASESENSITIVE => 0); # Don't limit to asset type if asset type is 0. Trying to do so breaks # RT::Group type CFs. if (defined $args{'Type'}) { $CFs->LimitToAssetType( $args{'Type'} ); } # When loading by name, we _can_ load disabled fields, but prefer # non-disabled fields. $CFs->FindAllRows; $CFs->OrderByCols( { FIELD => "Disabled", ORDER => 'ASC' }, ); # We only want one entry. $CFs->RowsPerPage(1); # version before 3.8 just returns 0, so we need to test if wantarray to be # backward compatible. return wantarray ? (0, $self->loc("Not found")) : 0 unless my $first = $CFs->First; return $self->LoadById( $first->id ); } 1; rt-extension-assettracker-3.0.0/lib/RT/CustomFields_ATOverlay.pm000066400000000000000000000034771222742774700246530ustar00rootroot00000000000000 package RT::CustomFields; use strict; no warnings qw(redefine); =head2 LimitToGlobalOrAssetType ASSETTYPEID This is a copy of LimitToGlobalOrQueue which is DEPRECATED since CFs are applicable not only to tickets these days. Limits the set of custom fields found to global custom fields or those tied to the asset type with ID ASSETTYPEID =cut sub LimitToGlobalOrAssetType { my $self = shift; my $assettype = shift; $self->LimitToGlobalOrObjectId( $assettype ); $self->LimitToLookupType( 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset' ); } =head2 LimitToAssetType ASSETTYPEID This is a copy of LimitToQueue which is DEPRECATED since CFs are applicable not only to tickets these days. Takes an asset type id (numerical) as its only argument. Makes sure that Scopes it pulls out apply to this asset type (or another that you've selected with another call to this method =cut sub LimitToAssetType { my $self = shift; my $assettype = shift; $self->Limit (ALIAS => $self->_OCFAlias, ENTRYAGGREGATOR => 'OR', FIELD => 'ObjectId', VALUE => "$assettype") if defined $assettype; $self->LimitToLookupType( 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset' ); } =head2 LimitToGlobalAsset This is a copy of LimitToGlobal which is DEPRECATED since CFs are applicable not only to tickets these days. Makes sure that Scopes it pulls out apply to all asset types (or another that you've selected with another call to this method or LimitToAssetType) =cut sub LimitToGlobalAsset { my $self = shift; $self->Limit (ALIAS => $self->_OCFAlias, ENTRYAGGREGATOR => 'OR', FIELD => 'ObjectId', VALUE => 0); $self->LimitToLookupType( 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset' ); } 1; rt-extension-assettracker-3.0.0/lib/RT/Graph/000077500000000000000000000000001222742774700210145ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/Graph/AssetTracker/000077500000000000000000000000001222742774700234075ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/Graph/AssetTracker/Assets.pm000066400000000000000000000303231222742774700252100ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} package RT::Graph::AssetTracker::Assets; use strict; use warnings; =head1 NAME RT::Graph::AssetTracker::Assets - view relations between assets as graphs =cut unless ($RT::DisableGraphViz) { require IPC::Run; IPC::Run->import; require IPC::Run::SafeHandles; IPC::Run::SafeHandles->import; require GraphViz; GraphViz->import; } our %asset_status_style = ( production => { fontcolor => '#FF0000', fontsize => 10 }, development => { fontcolor => '#000000', fontsize => 10 }, qa => { fontcolor => '#DAA520', fontsize => 10 }, dr => { fontcolor => '#00FF00', fontsize => 10 }, pilot => { fontcolor => '#808080', fontsize => 10 }, test => { fontcolor => '#898989', fontsize => 10 }, retired => { fontcolor => '#A9A9A9', fontsize => 10 }, ); our %link_style = ( RunsOn => { style => 'solid' }, DependsOn => { style => 'dashed' }, RefersTo => { style => 'dotted' }, ComponentOf => { style => 'bold' }, ); # We don't use qw() because perl complains about "possible attempt to put comments in qw() list" our @fill_colors = split ' ',< sub { return $_[0]->TypeObj->Name || $_[0]->Type }, CF => sub { my $values = $_[0]->CustomFieldValues( $_[1] ); return join ', ', map $_->Content, @{ $values->ItemsArrayRef }; }, ); foreach my $field (qw(Name Description Status)) { $property_cb{ $field } = sub { return $_[0]->$field }, } foreach my $field (qw(Creator LastUpdatedBy)) { $property_cb{ $field } = sub { my $method = $field .'Obj'; return $_[0]->$method->Name; }; } foreach my $field (RTx::AssetTracker::Type->RoleGroupTypes) { $property_cb{ $field } = sub { my $method = $field .'RoleGroupExportString'; return $_[0]->$method; }; } foreach my $field (qw(LastUpdated Created)) { $property_cb{ $field } = sub { my $method = $field .'Obj'; return $_[0]->$method->AsString; }; } foreach my $field (map { $RT::Link::DIRMAP{$_}{'Target'} } keys %RT::Link::DIRMAP) { $property_cb{ $field } = sub { return join ', ', map $_->BaseObj->id, @{ $_[0]->$field->ItemsArrayRef }; }; } foreach my $field (map { $RT::Link::DIRMAP{$_}{'Base'} } keys %RT::Link::DIRMAP) { $property_cb{ $field } = sub { return join ', ', map $_->TargetObj->id, @{ $_[0]->$field->ItemsArrayRef }; }; } sub AssetProperties { my $self = shift; my $user = shift; my @res = ( Basics => [qw(Name Description Status Type )], # loc_qw People => [ RTx::AssetTracker::Type->RoleGroupTypes, qw(Creator LastUpdatedBy)], # loc_qw Dates => [qw(Created LastUpdated)], # loc_qw Links => [qw(RunsOn IsRunning DependsOn DependedOnBy RefersTo ReferredToBy ComponentOf HasComponent)], # loc_qw ); my $cfs = RT::CustomFields->new( $user ); $cfs->LimitToLookupType('RTx::AssetTracker::Type-RTx::AssetTracker::Asset'); $cfs->OrderBy( FIELD => 'Name' ); my ($first, %seen) = (1); while ( my $cf = $cfs->Next ) { next if $seen{ lc $cf->Name }++; next if $cf->Type eq 'Image'; if ( $first ) { push @res, 'CustomFields', []; $first = 0; } push @{ $res[-1] }, 'CF.{'. $cf->Name .'}'; } return @res; } sub _SplitProperty { my $self = shift; my $property = shift; my ($key, @subkeys) = split /\./, $property; foreach ( grep /^{.*}$/, @subkeys ) { s/^{//; s/}$//; } return $key, @subkeys; } sub _PropertiesToFields { my $self = shift; my %args = ( Asset => undef, Graph => undef, CurrentDepth => 1, @_ ); my @properties; if ( my $tmp = $args{ 'Level-'. $args{'CurrentDepth'} .'-Properties' } ) { @properties = ref $tmp? @$tmp : ($tmp); } my @fields; foreach my $property( @properties ) { my ($key, @subkeys) = $self->_SplitProperty( $property ); unless ( $property_cb{ $key } ) { $RT::Logger->error("Couldn't find property handler for '$key' and '@subkeys' subkeys"); next; } push @fields, ($subkeys[0] || $key) .': '. $property_cb{ $key }->( $args{'Asset'}, @subkeys ); } return @fields; } sub AddAsset { my $self = shift; my %args = ( Asset => undef, Properties => [], Graph => undef, CurrentDepth => 1, @_ ); my %node_style = ( style => 'filled,rounded', %{ $asset_status_style{ $args{'Asset'}->Status } || {} }, URL => $RT::WebPath .'/AssetTracker/Asset/Display.html?id='. $args{'Asset'}->id, tooltip => gv_escape( $args{'Asset'}->Name || '#'. $args{'Asset'}->id ), ); my @fields = $self->_PropertiesToFields( %args ); if ( @fields ) { unshift @fields, $args{'Asset'}->id; my $label = join ' | ', map { s/(?=[{}|><])/\\/g; $_ } @fields; $label = "{ $label }" if ($args{'Direction'} || 'TB') =~ /^(?:TB|BT)$/; $node_style{'label'} = gv_escape( $label ); $node_style{'shape'} = 'record'; } if ( $args{'FillUsing'} ) { my ($key, @subkeys) = $self->_SplitProperty( $args{'FillUsing'} ); my $value; if ( $property_cb{ $key } ) { $value = $property_cb{ $key }->( $args{'Asset'}, @subkeys ); } else { $RT::Logger->error("Couldn't find property callback for '$key'"); } if ( defined $value && length $value && $value =~ /\S/ ) { my $fill = $fill_cache{ $value }; $fill = $fill_cache{ $value } = shift @available_colors unless $fill; if ( $fill ) { $node_style{'fillcolor'} = $fill; $node_style{'style'} ||= ''; $node_style{'style'} = join ',', split( ',', $node_style{'style'} ), 'filled' unless $node_style{'style'} =~ /\bfilled\b/; } } } $args{'Graph'}->add_node( $args{'Asset'}->id, %node_style ); } sub AssetLinks { my $self = shift; my %args = ( Asset => undef, Graph => undef, Direction => 'TB', Seen => undef, SeenEdge => undef, LeadingLink => 'Members', ShowLinks => [], MaxDepth => 0, CurrentDepth => 1, ShowLinkDescriptions => 0, @_ ); my %valid_links = map { $_ => 1 } qw(RunsOn IsRunning DependsOn DependedOnBy RefersTo ReferredToBy ComponentOf HasComponent); # Validate our link types $args{ShowLinks} = [ grep { $valid_links{$_} } @{$args{ShowLinks}} ]; $args{LeadingLink} = 'Members' unless $valid_links{ $args{LeadingLink} }; unless ( $args{'Graph'} ) { $args{'Graph'} = GraphViz->new( name => 'asset_links_'. $args{'Asset'}->id, bgcolor => "transparent", # TODO: patch GraphViz to support all posible RDs rankdir => ($args{'Direction'} || "TB") eq "LR", node => { shape => 'box', style => 'filled,rounded', fillcolor => 'white' }, ); %fill_cache = (); @available_colors = @fill_colors; } $args{'Seen'} ||= {}; return $args{'Graph'} if $args{'Seen'}{ $args{'Asset'}->id }++; $self->AddAsset( %args ); return $args{'Graph'} if $args{'MaxDepth'} && $args{'CurrentDepth'} >= $args{'MaxDepth'}; $args{'SeenEdge'} ||= {}; my $show_link_descriptions = $args{'ShowLinkDescriptions'} && RT::Link->can('Description'); foreach my $type ( $args{'LeadingLink'}, @{ $args{'ShowLinks'} } ) { my $links = $args{'Asset'}->$type(); $links->GotoFirstItem; while ( my $link = $links->Next ) { next if $args{'SeenEdge'}{ $link->id }++; my $target = $link->TargetObj; next unless $target && $target->isa('RTx::AssetTracker::Asset'); my $base = $link->BaseObj; next unless $base && $base->isa('RTx::AssetTracker::Asset'); my $next = $target->id == $args{'Asset'}->id? $base : $target; $self->AssetLinks( %args, Asset => $next, $type eq $args{'LeadingLink'} ? ( CurrentDepth => $args{'CurrentDepth'} + 1 ) : ( MaxDepth => $args{'CurrentDepth'} + 1, CurrentDepth => $args{'CurrentDepth'} + 1 ), ); my $desc; $desc = $link->Description if $show_link_descriptions; $args{'Graph'}->add_edge( # we revers order of member links to get better layout $link->Type eq 'MemberOf' ? ($target->id => $base->id, dir => 'back') : ($base->id => $target->id), %{ $link_style{ $link->Type } || {} }, $desc? (label => gv_escape $desc): (), ); } } return $args{'Graph'}; } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RT/Interface/000077500000000000000000000000001222742774700216535ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/Interface/Web/000077500000000000000000000000001222742774700223705ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/Interface/Web/QueryBuilder/000077500000000000000000000000001222742774700250045ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/Interface/Web/QueryBuilder/Tree_ATOverlay.pm000066400000000000000000000046351222742774700301770ustar00rootroot00000000000000 package RT::Interface::Web::QueryBuilder::Tree; use strict; no warnings qw(redefine); =head2 GetReferencedTypes Returns a hash reference; each type referenced with an '=' operation will appear as a key whose value is 1. =cut sub GetReferencedTypes { my $self = shift; my $types = {}; $self->traverse( sub { my $node = shift; return if $node->isRoot; return unless $node->isLeaf; my $clause = $node->getNodeValue(); return unless $clause->{Key} eq 'Type'; return unless $clause->{Op} eq '='; $types->{ $clause->{RawValue} } = 1; } ); return $types; } sub ParseAssetSQL { my $self = shift; my %args = ( Query => '', CurrentUser => '', #XXX: Hack @_ ); my $string = $args{'Query'}; my @results; my %field = %{ RTx::AssetTracker::Assets->new( $args{'CurrentUser'} )->FIELDS }; my %lcfield = map { ( lc($_) => $_ ) } keys %field; my $node = $self; my %callback; $callback{'OpenParen'} = sub { $node = __PACKAGE__->new( 'AND', $node ); }; $callback{'CloseParen'} = sub { $node = $node->getParent }; $callback{'EntryAggregator'} = sub { $node->setNodeValue( $_[0] ) }; $callback{'Condition'} = sub { my ($key, $op, $value) = @_; my $rawvalue = $value; my ($main_key) = split /[.]/, $key; my $class; if ( exists $lcfield{ lc $main_key } ) { $key =~ s/^[^.]+/ $lcfield{ lc $main_key } /e; ($main_key) = split /[.]/, $key; # make the case right $class = $field{ $main_key }->[0]; } unless( $class ) { push @results, [ $args{'CurrentUser'}->loc("Unknown field: [_1]", $key), -1 ] } if ( lc $op eq 'is' || lc $op eq 'is not' ) { $value = 'NULL'; # just fix possible mistakes here } elsif ( $value !~ /^[+-]?[0-9]+$/ ) { $value =~ s/(['\\])/\\$1/g; $value = "'$value'"; } if ($key =~ s/(['\\])/\\$1/g or $key =~ /[^{}\w\.]/) { $key = "'$key'"; } my $clause = { Key => $key, Op => $op, Value => $value, RawValue => $rawvalue }; $node->addChild( __PACKAGE__->new( $clause ) ); }; $callback{'Error'} = sub { push @results, @_ }; require RT::SQL; RT::SQL::Parse($string, \%callback); return @results; } 1; rt-extension-assettracker-3.0.0/lib/RT/Interface/Web_ATOverlay.pm000066400000000000000000000766311222742774700246710ustar00rootroot00000000000000package RT::Interface::Web; use strict; no warnings qw(redefine); package HTML::Mason::Commands; use vars qw/$r $m %session/; # {{{ sub LoadAsset - loads an asset =head2 LoadAsset id Takes an asset id as its only variable. if it's handed an array, it takes the first value. Returns an RTx::AssetTracker::Asset object as the current user. =cut sub LoadAsset { my $id = shift; if ( ref($id) eq "ARRAY" ) { $id = $id->[0]; } unless ($id) { Abort("No asset specified"); } my $Asset = RTx::AssetTracker::Asset->new( $session{'CurrentUser'} ); $Asset->Load($id); unless ( $Asset->id ) { Abort("Could not load asset $id"); } return $Asset; } # }}} =head2 ProcessAssetBasics ( AssetObj => $Asset, ARGSRef => \%ARGS ); Returns an array of results messages. =cut sub ProcessAssetBasics { my %args = ( AssetObj => undef, ARGSRef => undef, TransactionData => undef, @_ ); my $AssetObj = $args{'AssetObj'}; my $ARGSRef = $args{'ARGSRef'}; # Set basic fields my @attribs = qw( Name Description Status Type ); # Canonicalize Type to its ID if it isn't numeric if ( $ARGSRef->{'Type'} and ( $ARGSRef->{'Type'} !~ /^(\d+)$/ ) ) { my $temp = RTx::AssetTracker::Type->new(RT->SystemUser); $temp->Load( $ARGSRef->{'Type'} ); if ( $temp->id ) { $ARGSRef->{'Type'} = $temp->id; } } # Status isn't a field that can be set to a null value. # RT core complains if you try delete $ARGSRef->{'Status'} unless $ARGSRef->{'Status'}; my @results = UpdateRecordObject( AttributesRef => \@attribs, Object => $AssetObj, ARGSRef => $ARGSRef ); # }}} return (@results); } # {{{ sub ProcessAssetIPs =head2 ProcessAssetIPs ( AssetObj => $Asset, ARGSRef => \%ARGS ); Returns an array of results messages. =cut sub ProcessAssetIPs { my %args = ( AssetObj => undef, ARGSRef => undef, IPComment => undef, @_ ); my $AssetObj = $args{'AssetObj'}; my $ARGSRef = $args{'ARGSRef'}; my @results; my %ips; foreach my $key ( keys %$ARGSRef ) { if ($key =~ /^Asset-(\d+)-DeleteIP-(\d+)$/) { my ($rc, $msg) = $AssetObj->DeleteIP(IP => $2, TransactionData => $ARGSRef->{IPComment} || $ARGSRef->{GlobalComment}); push @results, $msg; #delete $ARGSRef->{$key}; next; } next unless ($key =~ /^Asset-(\d+)-Add(Interface|IP|MAC|TCPPorts|UDPPorts)(\d+)$/); $ips{$3}{$2} = $ARGSRef->{$key}; delete $ARGSRef->{$key}; } foreach my $ip (keys %ips) { next unless ( $ips{$ip}{IP} or $ips{$ip}{Interface} or $ips{$ip}{MAC} ); $ips{$ip}{TCPPorts} =~ s/,/ /g; $ips{$ip}{UDPPorts} =~ s/,/ /g; my @TCPPorts = split(/\s+/, $ips{$ip}{TCPPorts}); my @UDPPorts = split(/\s+/, $ips{$ip}{UDPPorts}); # We should check for valid port numbers! my (undef, $msg) = $AssetObj->AddIP( IP => $ips{$ip}{IP}, Interface => $ips{$ip}{Interface}, MAC => $ips{$ip}{MAC}, TCPPorts => \@TCPPorts, UDPPorts => \@UDPPorts, TransactionData => $ARGSRef->{IPComment} || $ARGSRef->{GlobalComment}); push @results, $msg; } return (@results); } # }}} sub ProcessAssetPorts { my %args = ( AssetObj => undef, ARGSRef => undef, IPComment => undef, @_ ); my $AssetObj = $args{'AssetObj'}; my $ARGSRef = $args{'ARGSRef'}; my @results; my %ips; foreach my $key ( keys %$ARGSRef ) { next unless ($key =~ /^Asset-(\d+)-IP-(\d+)-(TCP|UDP)Ports$/); my ($asset, $ipid, $transport) = ($1, $2, $3); next if $ARGSRef->{"Asset-$asset-DeleteIP-$ipid"}; my $ports = $ARGSRef->{$key}; $ports =~ s/,/ /g; my @new_ports = split /\s+/, $ports; @new_ports = grep { /^\d+$/ } @new_ports; my $ip = RTx::AssetTracker::IP->new( $session{ CurrentUser } ); $ip->Load($ipid); my $method = $transport."Ports"; my @current_ports = $ip->$method(); $method = "Delete".$transport."Port"; foreach my $port (@current_ports) { next if grep { $_ eq $port } @new_ports; my ($rv, $msg) = $ip->$method($port, TransactionData => $ARGSRef->{IPComment} || $ARGSRef->{GlobalComment}); if ($rv) { push @results, "$transport port $port deleted from IP " . $ip->IP; } else { push @results, "$transport port $port could not be deleted from IP " . $ip->IP . ": $msg"; } } $method = "Add".$transport."Port"; foreach my $port (@new_ports) { next if grep { $_ eq $port } @current_ports; my ($rv, $msg) = $ip->$method($port, TransactionData => $ARGSRef->{IPComment} || $ARGSRef->{GlobalComment}); if ($rv) { push @results, "$transport port $port added to IP " . $ip->IP; } else { push @results, "$transport port $port could not be added to IP " . $ip->IP . ": $msg"; } } } return @results; } # {{{ sub ProcessAssetWatchers =head2 ProcessAssetWatchers ( AssetObj => $Asset, ARGSRef => \%ARGS ); Returns an array of results messages. =cut sub ProcessAssetWatchers { my %args = ( AssetObj => undef, ARGSRef => undef, @_ ); my (@results); $RT::Logger->debug("Processing asset watchers"); my $Asset = $args{'AssetObj'}; my $ARGSRef = $args{'ARGSRef'}; # {{{ Munge watchers foreach my $key ( keys %$ARGSRef ) { # {{{ Delete deletable watchers if ( ( $key =~ /^Asset-DeleteWatcher-Type-(.*)-Principal-(\d+)$/ ) ) { my ( $code, $msg ) = $Asset->DeleteWatcher(PrincipalId => $2, Type => $1, TransactionData => $ARGSRef->{PeopleComment} || $ARGSRef->{GlobalComment}); push @results, $msg; } # Delete watchers in the simple style demanded by the bulk manipulator elsif ( $key =~ /^Delete(\w+)$/ ) { my ( $code, $msg ) = $Asset->DeleteWatcher( Email => $ARGSRef->{$key}, Type => $1, TransactionData => $ARGSRef->{PeopleComment} || $ARGSRef->{GlobalComment} ); push @results, $msg; } # }}} # Add new wathchers elsif ( ( $key =~ /^AddWatcher-Type-(\w+)$/ ) ) { my $type = $1; foreach my $watcher ( ref( $ARGSRef->{$key} ) ? @{ $ARGSRef->{$key} } : ( $ARGSRef->{$key} ) ) { next unless ( $watcher =~ /^Principal-(\d+)$/ ); my $WatcherId = $1; my ( $code, $msg ) = $Asset->AddWatcher( PrincipalId => $WatcherId, Type => $type, TransactionData => $ARGSRef->{PeopleComment} || $ARGSRef->{GlobalComment} ); push @results, $msg; } } #Add requestors in the simple style demanded by the bulk manipulator elsif ( $key =~ /^Add(\w+)$/ ) { my ( $code, $msg ) = $Asset->AddWatcher( Type => $1, Email => $ARGSRef->{$key}, TransactionData => $ARGSRef->{PeopleComment} || $ARGSRef->{GlobalComment}, ); push @results, $msg; } # Add new watchers by owner elsif ( ( $ARGSRef->{$key} =~ /^(AdminCc|Cc|Requestor)$/ ) and ( $key =~ /^Asset-AddWatcher-Principal-(\d*)$/ ) ) { #They're in this order because otherwise $1 gets clobbered :/ my ( $code, $msg ) = $Asset->AddWatcher( Type => $ARGSRef->{$key}, PrincipalId => $1, TransactionData => $ARGSRef->{PeopleComment} || $ARGSRef->{GlobalComment} ); push @results, $msg; } } # }}} # Modify Asset watchers as given by Grid.html my $id = $Asset->id; for my $role ( RTx::AssetTracker::Type->RoleGroupTypes() ) { if ( $ARGSRef->{"Asset-$id-$role"} ) { $ARGSRef->{"Asset-$id-$role"} =~ s/\r\n/\n/g; my $watchers = [split('\n', $ARGSRef->{"Asset-$id-$role"})]; push @results, SetWatchers( Asset => $Asset, Watchers => $watchers, Type => $role, TransactionData => $ARGSRef->{PeopleComment} || $ARGSRef->{GlobalComment} ); } } return (@results); } sub SetWatchers { my %args = @_; my $Asset = $args{Asset}; my $Data = $args{TransactionData}; my @Watchers = @{$args{Watchers}}; my $Type = $args{Type}; my @results; my $TypeGroup = $Type."Group"; my $Group = $Asset->$TypeGroup; # Process user additions my $UserMembers = $Group->MembersObj; $UserMembers->LimitToUsers; foreach my $watcher (@Watchers) { next if $watcher =~ /^Group:/; my $user = RT::User->new( $session{CurrentUser} ); my ($rv, $msg); if ($watcher =~ /\@/) { ($rv, $msg) = $user->LoadByEmail($watcher); } else { ($rv, $msg) = $user->Load($watcher); } unless ($rv) { push @results, "User: $watcher could not be found"; next; } ($rv, $msg) = $Asset->AddWatcher(Type => $Type, PrincipalId => $user->PrincipalId); if ($rv) { push @results, "Added user: $watcher to $TypeGroup of " . $Asset->Name; } else { push @results, "Error adding user: $watcher to $TypeGroup: $msg" unless $msg =~ /principal is already/; } } # Process group additions my $GroupMembers = $Group->MembersObj; $GroupMembers->LimitToGroups; foreach my $watcher (@Watchers) { next unless $watcher =~ /^Group: *(.*)/; my $watcher_name = $1; my $group = RT::Group->new( $session{CurrentUser} ); my ($rv, $msg); ($rv, $msg) = $group->LoadUserDefinedGroup($watcher_name); unless ($rv) { push @results, "Group: '$watcher_name' $msg"; next; } ($rv, $msg) = $Asset->AddWatcher(Type => $Type, PrincipalId => $group->PrincipalId); if ($rv) { push @results, "Added group: '$watcher_name' to $TypeGroup of " . $Asset->Name; } else { push @results, "Error adding group: '$watcher_name' to $TypeGroup: $msg" unless $msg =~ /principal is already/; } } # Remove each watcher from the group if they aren't in @Watchers my $Members = $Group->MembersObj; while (my $member = $Members->Next) { my ($rv, $msg); my $principal = $member->MemberObj; if ($principal->IsGroup) { my $name = $principal->Object->Name; unless (grep {/Group\:\s*$name$/i} @Watchers) { ($rv, $msg) = $Asset->DeleteWatcher(Type => $Type, PrincipalId => $principal->Id); if ($rv) { push @results, "Deleted group: '$name' from $TypeGroup of " . $Asset->Name; } else { push @results, "Error deleting group: '$name' from $TypeGroup: $msg"; } } } else { my $name = $principal->Object->Name; my $email = $principal->Object->EmailAddress; unless ( (grep {/^$name$/i} @Watchers) || (grep {/^$email$/i} @Watchers)) { ($rv, $msg) = $Asset->DeleteWatcher(Type => $Type, PrincipalId => $principal->Id); if ($rv) { push @results, "Deleted user: '$name' from $TypeGroup of " . $Asset->Name; } else { push @results, "Error deleting user: '$name' from $TypeGroup: $msg"; } } } } return @results; } # }}} # {{{ sub CreateAsset =head2 CreateAsset ARGS Create a new asset, using Mason's %ARGS. returns @results. =cut sub CreateAsset { my %ARGS = (@_); my (@Actions); my $Asset = new RTx::AssetTracker::Asset( $session{'CurrentUser'} ); my $Type = new RTx::AssetTracker::Type( $session{'CurrentUser'} ); unless ( $Type->Load( $ARGS{'Type'} ) ) { Abort('Type not found'); } unless ( $Type->CurrentUserHasRight('CreateAsset') ) { Abort('You have no permission to create assets of that type.'); } my %create_args = ( Type => $ARGS{'Type'}, Name => $ARGS{'Name'}, Owner => [], Admin => [], Description => $ARGS{'Description'}, Status => $ARGS{'Status'}, TransactionData => $ARGS{'GlobalComment'}, ); foreach my $arg (keys %ARGS) { next if ($arg =~ /-Magic$/); #Object-RT::Ticket--CustomField-3-Values if ($arg =~ /^Object-RT::Transaction--CustomField-/) { $create_args{$arg} = $ARGS{$arg}; } elsif ($arg =~ /^Object-RTx::AssetTracker::Asset--CustomField-(\d+)(.*?)$/) { my $cfid = $1; my $cf = RT::CustomField->new( $session{'CurrentUser'}); $cf->Load($cfid); if ($cf->Type eq 'FreeformMultiple') { $ARGS{$arg} =~ s/\r\n/\n/g; $ARGS{$arg} = [split('\n', $ARGS{$arg})]; } if ( $arg =~ /-Upload$/ ) { $create_args{"CustomField-".$cfid} = _UploadedFile($arg); } else { $create_args{"CustomField-".$cfid} = $ARGS{"$arg"}; } } elsif ($arg =~ /^AddWatcher-Type-(\w+)$/) { my $role = $1; push(@{$create_args{$role}}, map { s/Principal-//; $_ } ref( $ARGS{$arg} ) ? @{ $ARGS{$arg} } : ( $ARGS{$arg} ) ); } } my ( $id, $Trans, $ErrMsg ) = $Asset->Create(%create_args); unless ( $id && $Trans ) { Abort($ErrMsg); } push ( @Actions, split("\n", $ErrMsg) ); unless ( $Asset->CurrentUserHasRight('ShowAsset') ) { Abort( "No permission to view newly created asset #" . $Asset->id . "." ); } return ( $Asset, @Actions ); } # }}} # {{{ Sub ProcessCustomFieldUpdates sub ProcessBulkCustomFieldUpdates { my %args = ( Object => undef, ARGSRef => undef, TransactionData => undef, @_ ); my $Object = $args{'Object'}; my $ARGSRef = $args{'ARGSRef'}; my @results; my @NoUpdate = grep { /^\d+-DoNotUpdate$/ } keys %$ARGSRef; foreach (@NoUpdate) { /^(\d+)-/; my $cfid = $1; if ($ARGSRef->{$_}) { #push @results, "Will not update $_"; foreach my $key ( keys %$ARGSRef ) { $key =~ /^$cfid-/ and delete $ARGSRef->{$key}; } } } # We should do this once and cache it my %CFsToProcess; foreach my $key ( keys %$ARGSRef ) { if ($key =~ /^(\d+)-Value/) { $CFsToProcess{$1} = 1; } } foreach my $cfid (keys %CFsToProcess) { my ($rv, $msg); if (exists $ARGSRef->{"$cfid-Value"} and $ARGSRef->{"$cfid-Value"}) { #Add the value or delete it if false ($rv, $msg) = $Object->AddCustomFieldValue( Field => $cfid, Value => $ARGSRef->{"$cfid-Value"}); push @results, $msg; } elsif (exists $ARGSRef->{"$cfid-Value-Magic"}) { #Delete the value ($rv, $msg) = $Object->DeleteCustomFieldValue( Field => $cfid, Value => $Object->FirstCustomFieldValue($cfid)); push @results, $msg; } elsif (exists $ARGSRef->{"$cfid-Values"} and $ARGSRef->{"$cfid-Values"}) { #Add the value or delete it if false ($rv, $msg) = $Object->AddCustomFieldValue( Field => $cfid, Value => $ARGSRef->{"$cfid-Values"}); push @results, $msg; } elsif (exists $ARGSRef->{"$cfid-Values-Magic"}) { #Delete the value ($rv, $msg) = $Object->DeleteCustomFieldValue( Field => $cfid, Value => $Object->FirstCustomFieldValue($cfid)); push @results, $msg; } elsif (exists $ARGSRef->{"$cfid-Values-AddValues-Magic"}) { # If the arg is one value with newlines then we need to split and add/delete each value if (exists $ARGSRef->{"$cfid-Values-AddValues"}) { my @values = ( ref $ARGSRef->{"$cfid-Values-AddValues"} eq 'ARRAY' ) ? @{$ARGSRef->{"$cfid-Values-AddValues"}} : ($ARGSRef->{"$cfid-Values-AddValues"}); if (@values == 1) { $values[0] =~ s/\r\n/\n/g; @values = split(/\n/, $values[0]); } foreach my $value (@values) { ($rv, $msg) = $Object->AddUniqueCustomFieldValue( Field => $cfid, Value => $value); push @results, $msg; } } else { } if (exists $ARGSRef->{"$cfid-Values-DeleteValues"}) { my @values = ( ref $ARGSRef->{"$cfid-Values-DeleteValues"} eq 'ARRAY' ) ? @{$ARGSRef->{"$cfid-Values-DeleteValues"}} : ($ARGSRef->{"$cfid-Values-DeleteValues"}); if (@values == 1) { $values[0] =~ s/\r\n/\n/g; @values = split(/\n/, $values[0]); } foreach my $value (@values) { ($rv, $msg) = $Object->DeleteCustomFieldValue( Field => $cfid, Value => $value); push @results, $msg; } } else { } } else { } } return (@results); } # }}} # {{{ sub ProcessAssetLinks =head2 ProcessAssetLinks ( AssetObj => $Asset, ARGSRef => \%ARGS ); Returns an array of results messages. =cut sub ProcessAssetLinks { my %args = ( AssetObj => undef, ARGSRef => undef, @_ ); my @results; my $AssetObj = $args{'AssetObj'}; my $ARGSRef = $args{'ARGSRef'}; my $LINKTYPEMAP = RTx::AssetTracker::Asset::LINKTYPEMAP(); my $all_links = join('|', keys %$LINKTYPEMAP); my $LINKDIRMAP = RTx::AssetTracker::Asset::LINKDIRMAP(); my $all_bases = join('|', keys %$LINKDIRMAP); foreach my $arg (keys %$ARGSRef) { if ($arg =~ /^AddLink-Asset-(.+)$/) { my $id = $1; next unless $ARGSRef->{$arg} =~ /($all_links)/; my $linktype = $1; if ($linktype =~ /^($all_bases)$/) { my ( $val, $msg ) = $AssetObj->AddLink( Target => $id, Type => $linktype, TransactionData => $ARGSRef->{LinkComment} || $ARGSRef->{GlobalComment} ); push @results, $msg; } else { $linktype = $LINKTYPEMAP->{$linktype}{Type} if $LINKTYPEMAP->{$linktype}{Mode} eq 'Base'; my ( $val, $msg ) = $AssetObj->AddLink( Base => $id, Type => $linktype, TransactionData => $ARGSRef->{LinkComment} || $ARGSRef->{GlobalComment} ); push @results, $msg; } } elsif ($arg =~ /^AddLink-Other$/) { next unless $ARGSRef->{$arg} =~ /($all_links)/; my $id = $ARGSRef->{'AddLink-Other-URI'}; next unless $id; my $linktype = $ARGSRef->{$arg}; if ($linktype =~ /^($all_bases)$/) { my ( $val, $msg ) = $AssetObj->AddLink( Target => $id, Type => $linktype, TransactionData => $ARGSRef->{LinkComment} || $ARGSRef->{GlobalComment} ); push @results, $msg; } else { $linktype = $LINKTYPEMAP->{$linktype}{Type} if $LINKTYPEMAP->{$linktype}{Mode} eq 'Base'; my ( $val, $msg ) = $AssetObj->AddLink( Base => $id, Type => $linktype, TransactionData => $ARGSRef->{LinkComment} || $ARGSRef->{GlobalComment} ); push @results, $msg; } } elsif ($arg =~ /^DeleteLink-(.*?)-($all_bases)-(.*)$/) { my $base = $1; my $type = $2; my $target = $3; my ( $val, $msg ) = $AssetObj->DeleteLink( Base => $base, Type => $type, Target => $target, TransactionData => $ARGSRef->{LinkComment} || $ARGSRef->{GlobalComment} ); if ($val) { push @results, "Deleted link: Base: $base Target: $target Type: $type"; } else { push @results, $msg; } } } foreach my $linktype ( keys %$LINKDIRMAP ) { if ( $ARGSRef->{ $AssetObj->Id . "-$linktype" } ) { $ARGSRef->{ $AssetObj->Id . "-$linktype" } = join( ' ', @{ $ARGSRef->{ $AssetObj->Id . "-$linktype" } } ) if ref( $ARGSRef->{ $AssetObj->Id . "-$linktype" } ); for my $luri ( split( / /, $ARGSRef->{ $AssetObj->Id . "-$linktype" } ) ) { next unless $luri; $luri =~ s/\s+$//; # Strip trailing whitespace my ( $val, $msg ) = $AssetObj->AddLink( Target => $luri, Type => $linktype ); push @results, $msg; } } if ( $ARGSRef->{ "$linktype-" . $AssetObj->Id } ) { $ARGSRef->{ "$linktype-" . $AssetObj->Id } = join( ' ', @{ $ARGSRef->{ "$linktype-" . $AssetObj->Id } } ) if ref( $ARGSRef->{ "$linktype-" . $AssetObj->Id } ); for my $luri ( split( / /, $ARGSRef->{ "$linktype-" . $AssetObj->Id } ) ) { next unless $luri; my ( $val, $msg ) = $AssetObj->AddLink( Base => $luri, Type => $linktype ); push @results, $msg; } } } return (@results); } # }}} # Just a copy of ProcessObjectCustomFieldUpdates sub ProcessATObjectCustomFieldUpdates { my %args = @_; my $ARGSRef = $args{'ARGSRef'}; my @results; # Build up a list of objects that we want to work with my %custom_fields_to_mod; foreach my $arg ( keys %$ARGSRef ) { # format: Object---CustomField-- next unless $arg =~ /^Object-([\w:]+)-(\d*)-CustomField-(\d+)-(.*)$/; # For each of those objects, find out what custom fields we want to work with. $custom_fields_to_mod{$1}{ $2 || 0 }{$3}{$4} = $ARGSRef->{$arg}; } # For each of those objects foreach my $class ( keys %custom_fields_to_mod ) { foreach my $id ( keys %{ $custom_fields_to_mod{$class} } ) { my $Object = $args{'Object'}; $Object = $class->new( $session{'CurrentUser'} ) unless $Object && ref $Object eq $class; $Object->Load($id) unless ( $Object->id || 0 ) == $id; unless ( $Object->id ) { $RT::Logger->warning("Couldn't load object $class #$id"); next; } foreach my $cf ( keys %{ $custom_fields_to_mod{$class}{$id} } ) { my $CustomFieldObj = RT::CustomField->new( $session{'CurrentUser'} ); $CustomFieldObj->LoadById($cf); unless ( $CustomFieldObj->id ) { $RT::Logger->warning("Couldn't load custom field #$cf"); next; } push @results, _ProcessATObjectCustomFieldUpdates( Prefix => "Object-$class-$id-CustomField-$cf-", Object => $Object, CustomField => $CustomFieldObj, ARGS => $custom_fields_to_mod{$class}{$id}{$cf}, ARGSRef => $ARGSRef ); } } } return @results; } # Just a copy of _ProcessObjectCustomFieldUpdates with the Data parameter passed # to various calls to Add/DeleteCustomFieldValue sub _ProcessATObjectCustomFieldUpdates { my %args = @_; my $cf = $args{'CustomField'}; my $cf_type = $cf->Type; my $ARGSRef = $args{'ARGSRef'}; # Normalise - remove blank Values since magic value will take care of this. This is # because sometimes, the browser gives you a blank value and also a magic value # which causes CFs to be processed twice by the code below. if ( defined $args{'ARGS'}->{'Values'} && !length $args{'ARGS'}->{'Values'} && $args{'ARGS'}->{'Values-Magic'} ) { delete $args{'ARGS'}->{'Values'}; } my @results; foreach my $arg ( keys %{ $args{'ARGS'} } ) { # skip category argument next if $arg eq 'Category'; # since http won't pass in a form element with a null value, we need # to fake it if ( $arg eq 'Values-Magic' ) { # We don't care about the magic, if there's really a values element; next if defined $args{'ARGS'}->{'Value'} && length $args{'ARGS'}->{'Value'}; next if defined $args{'ARGS'}->{'Values'} && length $args{'ARGS'}->{'Values'}; # "Empty" values does not mean anything for Image and Binary fields next if $cf_type =~ /^(?:Image|Binary)$/; $arg = 'Values'; $args{'ARGS'}->{'Values'} = undef; } my @values = (); if ( ref $args{'ARGS'}->{$arg} eq 'ARRAY' ) { @values = @{ $args{'ARGS'}->{$arg} }; } elsif ( $cf_type =~ /text/i ) { # Both Text and Wikitext @values = ( $args{'ARGS'}->{$arg} ); } else { @values = split /\r*\n/, $args{'ARGS'}->{$arg} if defined $args{'ARGS'}->{$arg}; } @values = grep length, map { s/\r+\n/\n/g; s/^\s+//; s/\s+$//; $_; } grep defined, @values; if ( $arg eq 'AddValue' || $arg eq 'Value' ) { foreach my $value (@values) { my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue( Field => $cf->id, Value => $value, Data => $ARGSRef->{'FieldComment'} || $ARGSRef->{'GlobalComment'}, ); push ( @results, $msg ); } } elsif ( $arg eq 'Upload' ) { my $value_hash = _UploadedFile( $args{'Prefix'} . $arg ) or next; my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue( %$value_hash, Field => $cf, Data => $ARGSRef->{'FieldComment'} || $ARGSRef->{'GlobalComment'},); push ( @results, $msg ); } elsif ( $arg eq 'DeleteValues' ) { foreach my $value (@values) { my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue( Field => $cf, Value => $value, Data => $ARGSRef->{'FieldComment'} || $ARGSRef->{'GlobalComment'}, ); push ( @results, $msg ); } } elsif ( $arg eq 'DeleteValueIds' ) { foreach my $value (@values) { my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue( Field => $cf, ValueId => $value, Data => $ARGSRef->{'FieldComment'} || $ARGSRef->{'GlobalComment'}, ); push ( @results, $msg ); } } elsif ( $arg eq 'Values' && !$cf->Repeated ) { my $cf_values = $args{'Object'}->CustomFieldValues( $cf->id ); my %values_hash; foreach my $value (@values) { if ( my $entry = $cf_values->HasEntry($value) ) { $values_hash{ $entry->id } = 1; next; } my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue( Field => $cf, Value => $value, Data => $ARGSRef->{'FieldComment'} || $ARGSRef->{'GlobalComment'}, ); push ( @results, $msg ); $values_hash{ $val } = 1 if $val; } # For Date Cfs, @values is empty when there is no changes (no datas in form input) return @results if ( $cf->Type =~ /^Date(?:Time)?$/ && ! @values ); $cf_values->RedoSearch; while ( my $cf_value = $cf_values->Next ) { next if $values_hash{ $cf_value->id }; my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue( Field => $cf, ValueId => $cf_value->id, Data => $ARGSRef->{'FieldComment'} || $ARGSRef->{'GlobalComment'}, ); push ( @results, $msg); } } elsif ( $arg eq 'Values' ) { my $cf_values = $args{'Object'}->CustomFieldValues( $cf->id ); # keep everything up to the point of difference, delete the rest my $delete_flag; foreach my $old_cf ( @{ $cf_values->ItemsArrayRef } ) { if ( !$delete_flag and @values and $old_cf->Content eq $values[0] ) { shift @values; next; } $delete_flag ||= 1; $old_cf->Delete; } # now add/replace extra things, if any foreach my $value (@values) { my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue( Field => $cf, Value => $value, Data => $ARGSRef->{'FieldComment'} || $ARGSRef->{'GlobalComment'}, ); push ( @results, $msg ); } } else { push( @results, loc("User asked for an unknown update type for custom field [_1] for [_2] object #[_3]", $cf->Name, ref $args{'Object'}, $args{'Object'}->id ) ); } } return @results; } =head2 LimitToRolesForQueue QUEUE_ID Limits the set of groups found to role groups for queue QUEUE_ID =cut sub RT::Groups::LimitToRolesForAssetType { my $self = shift; my $type = shift; $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RTx::AssetTracker::Type-Role'); $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $type); } =head2 GetPrincipalsMap OBJECT, CATEGORIES Gets the Role principals for Asset Types, or falls back on the original version. =cut my $Orig_GetPrincipalsMap = __PACKAGE__->can('GetPrincipalsMap') or die "API change? Can't find method 'GetPrincipalsMap'"; *GetPrincipalsMap = sub { my $object = shift; my @map; for (@_) { if (/Roles/ && $object->isa('RTx::AssetTracker::Type')) { my $roles = RT::Groups->new($session{'CurrentUser'}); $roles->LimitToRolesForAssetType($object->Id); $roles->OrderBy( FIELD => 'Type', ORDER => 'ASC' ); push @map, [ 'Roles' => $roles, # loc_left_pair 'Type' => 1 ]; } else { push @map, $Orig_GetPrincipalsMap->( $object, $_ ); } } return @map; }; 1; rt-extension-assettracker-3.0.0/lib/RT/Report/000077500000000000000000000000001222742774700212265ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/Report/Assets.pm000066400000000000000000000243111222742774700230270ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} package RT::Report::Assets; use base qw/RTx::AssetTracker::Assets/; use RT::Report::Assets::Entry; use strict; use warnings; sub Groupings { my $self = shift; my %args = (@_); my @fields = map { $self->CurrentUser->loc($_), $_ } qw( Status Type ); # loc_qw foreach my $type ( qw(Creator LastUpdatedBy Owner Admin Watcher) ) { # loc_qw for my $field ( qw( Name EmailAddress RealName NickName Organization Lang City Country Timezone ) # loc_qw ) { push @fields, $self->CurrentUser->loc($type) . ' ' . $self->CurrentUser->loc($field), $type . '.' . $field; } } for my $field (qw(Created LastUpdated)) { # loc_qw for my $frequency (qw(Hourly Daily Monthly Annually)) { # loc_qw push @fields, $self->CurrentUser->loc($field) . $self->CurrentUser->loc($frequency), $field . $frequency; } } my $types = $args{'Types'}; if ( !$types && $args{'Query'} ) { require RT::Interface::Web::QueryBuilder::Tree; my $tree = RT::Interface::Web::QueryBuilder::Tree->new('AND'); $tree->ParseSQL( Query => $args{'Query'}, CurrentUser => $self->CurrentUser ); $types = $tree->GetReferencedTypes; } if ( $types ) { my $CustomFields = RT::CustomFields->new( $self->CurrentUser ); foreach my $id (keys %$types) { my $type = RTx::AssetTracker::Type->new( $self->CurrentUser ); $type->Load($id); $CustomFields->LimitToAssetType($type->Id) if $type->Id; } $CustomFields->LimitToGlobal; while ( my $CustomField = $CustomFields->Next ) { push @fields, $self->CurrentUser->loc( "Custom field '[_1]'", $CustomField->Name ), "CF.{" . $CustomField->id . "}"; } } return @fields; } sub Label { my $self = shift; my $field = shift; if ( $field =~ /^(?:CF|CustomField)\.{(.*)}$/ ) { my $cf = $1; return $self->CurrentUser->loc( "Custom field '[_1]'", $cf ) if $cf =~ /\D/; my $obj = RT::CustomField->new( $self->CurrentUser ); $obj->Load( $cf ); return $self->CurrentUser->loc( "Custom field '[_1]'", $obj->Name ); } return $self->CurrentUser->loc($field); } sub SetupGroupings { my $self = shift; my %args = (Query => undef, GroupBy => undef, @_); $self->FromSQL( $args{'Query'} ); my @group_by = ref( $args{'GroupBy'} )? @{ $args{'GroupBy'} } : ($args{'GroupBy'}); $self->GroupBy( map { {FIELD => $_} } @group_by ); # UseSQLForACLChecks may add late joins my $joined = ($self->_isJoined || RT->Config->Get('UseSQLForACLChecks')) ? 1 : 0; my @res; push @res, $self->Column( FUNCTION => ($joined? 'DISTINCT COUNT' : 'COUNT'), FIELD => 'id' ); push @res, map $self->Column( FIELD => $_ ), @group_by; return @res; } sub GroupBy { my $self = shift; my @args = ref $_[0]? @_ : { @_ }; @{ $self->{'_group_by_field'} ||= [] } = map $_->{'FIELD'}, @args; $_ = { $self->_FieldToFunction( %$_ ) } foreach @args; $self->SUPER::GroupBy( @args ); } sub Column { my $self = shift; my %args = (@_); if ( $args{'FIELD'} && !$args{'FUNCTION'} ) { %args = $self->_FieldToFunction( %args ); } return $self->SUPER::Column( %args ); } =head2 _DoSearch Subclass _DoSearch from our parent so we can go through and add in empty columns if it makes sense =cut sub _DoSearch { my $self = shift; $self->SUPER::_DoSearch( @_ ); if ( $self->{'must_redo_search'} ) { $RT::Logger->crit( "_DoSearch is not so successful as it still needs redo search, won't call AddEmptyRows" ); } else { $self->AddEmptyRows; } } =head2 _FieldToFunction FIELD Returns a tuple of the field or a database function to allow grouping on that field. =cut sub _FieldToFunction { my $self = shift; my %args = (@_); my $field = $args{'FIELD'}; if ($field =~ /^(.*)(Hourly|Daily|Monthly|Annually)$/) { my ($field, $grouping) = ($1, $2); my $alias = $args{'ALIAS'} || 'main'; my $func = "$alias.$field"; my $db_type = RT->Config->Get('DatabaseType'); if ( RT->Config->Get('ChartsTimezonesInDB') ) { my $tz = $self->CurrentUser->UserObj->Timezone || RT->Config->Get('Timezone') || 'UTC'; if ( lc $tz eq 'utc' ) { # do nothing } elsif ( $db_type eq 'Pg' ) { $func = "timezone('UTC', $func)"; $func = "timezone(". $self->_Handle->dbh->quote($tz) .", $func)"; } elsif ( $db_type eq 'mysql' ) { $func = "CONVERT_TZ($func, 'UTC', " . $self->_Handle->dbh->quote($tz) .")"; } else { $RT::Logger->warning( "ChartsTimezonesInDB config option" ." is not supported on $db_type." ); } } # Pg 8.3 requires explicit casting $func .= '::text' if $db_type eq 'Pg'; if ( $grouping eq 'Hourly' ) { $func = "SUBSTR($func,1,13)"; } if ( $grouping eq 'Daily' ) { $func = "SUBSTR($func,1,10)"; } elsif ( $grouping eq 'Monthly' ) { $func = "SUBSTR($func,1,7)"; } elsif ( $grouping eq 'Annually' ) { $func = "SUBSTR($func,1,4)"; } $args{'FUNCTION'} = $func; } elsif ( $field =~ /^(?:CF|CustomField)\.{(.*)}$/ ) { #XXX: use CFDecipher method my $cf_name = $1; my $cf = RT::CustomField->new( $self->CurrentUser ); $cf->Load($cf_name); unless ( $cf->id ) { $RT::Logger->error("Couldn't load CustomField #$cf_name"); } else { my ($asset_cf_alias, $cf_alias) = $self->_CustomFieldJoin($cf->id, $cf->id, $cf_name); @args{qw(ALIAS FIELD)} = ($asset_cf_alias, 'Content'); } } elsif ( $field =~ /^(?:(Creator|LastUpdatedBy))(?:\.(.*))?$/ ) { my $type = $1 || ''; my $column = $2 || 'Name'; my $u_alias = $self->{"_sql_report_${type}_users_${column}"} ||= $self->Join( TYPE => 'LEFT', ALIAS1 => 'main', FIELD1 => $type, TABLE2 => 'Users', FIELD2 => 'id', ); @args{qw(ALIAS FIELD)} = ($u_alias, $column); } elsif ( $field =~ /^(?:Watcher|(Owner|Admin))(?:\.(.*))?$/ ) { my $type = $1 || ''; my $column = $2 || 'Name'; my $u_alias = $self->{"_sql_report_watcher_users_alias_$type"}; unless ( $u_alias ) { my ($g_alias, $gm_alias); ($g_alias, $gm_alias, $u_alias) = $self->_WatcherJoin( $type ); $self->{"_sql_report_watcher_users_alias_$type"} = $u_alias; } @args{qw(ALIAS FIELD)} = ($u_alias, $column); } return %args; } # Override the AddRecord from DBI::SearchBuilder::Unique. id isn't id here # wedon't want to disambiguate all the items with a count of 1. sub AddRecord { my $self = shift; my $record = shift; push @{$self->{'items'}}, $record; $self->{'rows'}++; } 1; # Gotta skip over RTx::AssetTracker::Assets->Next, since it does all sorts of crazy magic we # don't want. sub Next { my $self = shift; $self->RT::SearchBuilder::Next(@_); } sub NewItem { my $self = shift; return RT::Report::Assets::Entry->new(RT->SystemUser); # $self->CurrentUser); } =head2 AddEmptyRows If we're grouping on a criterion we know how to add zero-value rows for, do that. =cut sub AddEmptyRows { my $self = shift; if ( @{ $self->{'_group_by_field'} || [] } == 1 && $self->{'_group_by_field'}[0] eq 'Status' ) { my %has = map { $_->__Value('Status') => 1 } @{ $self->ItemsArrayRef || [] }; foreach my $status ( grep !$has{$_}, RTx::AssetTracker::Type->new($self->CurrentUser)->StatusArray ) { my $record = $self->NewItem; $record->LoadFromHash( { id => 0, status => $status } ); $self->AddRecord($record); } } } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RT/Report/Assets/000077500000000000000000000000001222742774700224705ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/Report/Assets/Entry.pm000066400000000000000000000060241222742774700241310ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} package RT::Report::Assets::Entry; use warnings; use strict; use base qw/RT::Record/; # XXX TODO: how the heck do we acl a report? sub CurrentUserHasRight {1} =head2 LabelValue If you're pulling a value out of this collection and using it as a label, you may want the "cleaned up" version. This includes scrubbing 1970 dates and ensuring that dates are in local not DB timezones. =cut sub LabelValue { my $self = shift; my $field = shift; my $value = $self->__Value( $field ); if ( $field =~ /(Daily|Monthly|Annually|Hourly)$/ ) { my $re; # it's not just 1970-01-01 00:00:00 because of timezone shifts # and conversion from UTC to user's TZ $re = qr{19(?:70-01-01|69-12-31) [0-9]{2}} if $field =~ /Hourly$/; $re = qr{19(?:70-01-01|69-12-31)} if $field =~ /Daily$/; $re = qr{19(?:70-01|69-12)} if $field =~ /Monthly$/; $re = qr{19(?:70|69)} if $field =~ /Annually$/; $value =~ s/^$re/Not Set/; } return $value; } sub ObjectType { return 'RTx::AssetTracker::Asset'; } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RT/Shredder/000077500000000000000000000000001222742774700215135ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/Shredder/Plugin/000077500000000000000000000000001222742774700227515ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/Shredder/Plugin/AssetObjects.pm000066400000000000000000000062341222742774700257050ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2010 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} package RT::Shredder::Plugin::AssetObjects; use strict; use warnings FATAL => 'all'; use base qw(RT::Shredder::Plugin::Base::Search); use RT::Shredder; =head1 NAME RT::Shredder::Plugin::AssetObjects - search plugin for wiping any selected object. =head1 ARGUMENTS This plugin searches an RT object you want, so you can use the object name as argument and id as value, for example if you want select ticket #123 then from CLI you write next command: rt-shredder --plugin 'Objects=Ticket,123' =cut sub SupportArgs { return $_[0]->SUPER::SupportArgs, qw(Asset IP Port); } sub TestArgs { my $self = shift; my %args = @_; my @strings; foreach my $name( qw(Asset IP Port) ) { next unless $args{$name}; my $list = $args{$name}; $list = [$list] unless UNIVERSAL::isa( $list, 'ARRAY' ); push @strings, map "RTx::AssetTracker::$name\-$_", @$list; } my @objs = RT::Shredder->CastObjectsToRecords( Objects => \@strings ); my @res = $self->SUPER::TestArgs( %args ); $self->{'opt'}->{'objects'} = \@objs; return (@res); } sub Run { my $self = shift; my %args = ( Shredder => undef, @_ ); return (1, @{$self->{'opt'}->{'objects'}}); } 1; rt-extension-assettracker-3.0.0/lib/RT/Shredder/Plugin/Assets.pm000066400000000000000000000114711222742774700245550ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2010 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} package RT::Shredder::Plugin::Assets; use strict; use warnings FATAL => 'all'; use base qw(RT::Shredder::Plugin::Base::Search); =head1 NAME RT::Shredder::Plugin::Assets - search plugin for wiping assets. =head1 ARGUMENTS =head2 query - query string Search tickets with query string. Examples: Queue = 'my queue' AND ( Status = 'deleted' OR Status = 'rejected' ) LastUpdated < '2003-12-31 23:59:59' B You can construct query with the query builder in RT's web interface and then open advanced page and copy query string. Arguments C, C and C have been dropped as you can easy make the same search with the C option. See examples above. =head2 with_linked - boolen Deletes all tickets that are linked to tickets that match L. =head2 apply_query_to_linked - boolean Delete linked tickets only if those too match L. See also L. =cut sub SupportArgs { return $_[0]->SUPER::SupportArgs, qw(query with_linked apply_query_to_linked) } sub TestArgs { my $self = shift; my %args = @_; my $queue; if( $args{'query'} ) { my $objs = RTx::AssetTracker::Assets->new( $RT::SystemUser ); $objs->{'allow_deleted_search'} = 1; my ($status, $msg) = $objs->FromSQL( $args{'query'} ); return( 0, "Bad query argument, error: $msg" ) unless $status; $self->{'opt'}{'objects'} = $objs; } #$args{'with_linked'} = 1 if $args{'apply_query_to_linked'}; return $self->SUPER::TestArgs( %args ); } sub Run { my $self = shift; my $objs = $self->{'opt'}{'objects'} or return (1, undef); $objs->OrderByCols( { FIELD => 'id', ORDER => 'ASC' } ); unless ( $self->{'opt'}{'with_linked'} ) { if( $self->{'opt'}{'limit'} ) { $objs->RowsPerPage( $self->{'opt'}{'limit'} ); } return (1, $objs); } my (@top, @linked, %seen); $self->FetchNext($objs, 1); while ( my $obj = $self->FetchNext( $objs ) ) { next if $seen{ $obj->id }++; push @linked, $self->GetLinked( Object => $obj, Seen => \%seen ); push @top, $obj; last if $self->{'opt'}{'limit'} && @top >= $self->{'opt'}{'limit'}; } return (1, @top, @linked); } sub GetLinked { my $self = shift; my %arg = @_; my @res = (); my $query = 'Linked = '. $arg{'Object'}->id; if ( $self->{'opt'}{'apply_query_to_linked'} ) { $query .= " AND ( ". $self->{'opt'}{'query'} ." )"; } my $objs = RTx::AssetTracker::Assets->new( $RT::SystemUser ); $objs->{'allow_deleted_search'} = 1; $objs->FromSQL( $query ); $self->FetchNext( $objs, 1 ); while ( my $linked_obj = $self->FetchNext( $objs ) ) { next if $arg{'Seen'}->{ $linked_obj->id }++; push @res, $self->GetLinked( %arg, Object => $linked_obj ); push @res, $linked_obj; } return @res; } 1; rt-extension-assettracker-3.0.0/lib/RT/Ticket_ATOverlay.pm000066400000000000000000000025161222742774700234660ustar00rootroot00000000000000 package RT::Ticket; use strict; no warnings qw(redefine); =head2 _AddLink Asset Tracker wraps this method to add a "TicketLink" transaction when an asset is referred to by a ticket =cut my $Orig_AddLink = __PACKAGE__->can('_AddLink') or die "API change? Can't find method '_AddLink'"; *_AddLink = sub { my $self = shift; my ($linkid, $msg) = $Orig_AddLink->($self, @_); return ($linkid, $msg) unless $linkid; my $linkObj = RT::Link->new( $self->CurrentUser ); my ($LinkId, $Msg) = $linkObj->Load($linkid); $LinkId or return ($linkid, $msg); my $TargetObj = $linkObj->TargetObj(); my $BaseObj = $linkObj->BaseObj(); return ($linkid, $msg) unless (ref $BaseObj eq 'RT::Ticket'); if (ref $TargetObj eq 'RTx::AssetTracker::Asset') { $TargetObj->_NewTransaction( Type => 'TicketLink', NewValue => $BaseObj->Id, ); } return ($linkid, $msg); }; =head2 LINKTYPEMAP Asset Tracker wraps this method to add the asset link types =cut my $Orig_LINKTYPEMAP = __PACKAGE__->can('LINKTYPEMAP') or die "API change? Can't find method 'LINKTYPEMAP'"; *LINKTYPEMAP = sub { my $self = shift; my $ticket_map = $Orig_LINKTYPEMAP->($self); my $asset_map = RTx::AssetTracker::Asset->LINKTYPEMAP(); return { %$asset_map, %$ticket_map }; }; 1; rt-extension-assettracker-3.0.0/lib/RT/Transaction_ATOverlay.pm000066400000000000000000000202221222742774700245220ustar00rootroot00000000000000package RT::Transaction; use strict; no warnings qw(redefine); =head2 Create Asset Tracker wraps this method to enable the execution of Asset Scrips =cut my $Orig_Create = __PACKAGE__->can('Create') or die "API change? Can't find method 'Create'"; *Create = sub { my $self = shift; my %args = ( ActivateScrips => 1, CommitScrips => 1, @_ ); my ($id, $msg) = $Orig_Create->($self, @_); #Provide a way to turn off scrips if we need to $RT::Logger->debug('About to think about scrips for transaction #' .$self->Id); if ( $args{'ActivateScrips'} and $self->ObjectType eq 'RTx::AssetTracker::Asset' ) { $self->{'scrips'} = RTx::AssetTracker::Scrips->new($RT::SystemUser); $RT::Logger->debug('About to prepare scrips for transaction #' .$self->Id); $self->{'scrips'}->Prepare( Stage => 'TransactionCreate', Type => $self->Type, Asset => $self->ObjectId, Transaction => $self->id, ); if ($args{'CommitScrips'} ) { $RT::Logger->debug('About to commit scrips for transaction #' .$self->Id); $self->{'scrips'}->Commit(); } } return ( $id, $self->loc("Transaction Created") ); }; =head2 FriendlyObjectType Asset Tracker wraps this method so history can just show eg "Asset" or "Type" instead of the full class name =cut my $Orig_FriendlyObjectType = __PACKAGE__->can('FriendlyObjectType') or die "API change? Can't find method 'FriendlyObjectType'"; *FriendlyObjectType = sub { my $self = shift; my $type = $self->ObjectType or return undef; if ($type =~ s/^RTx::AssetTracker:://) { return $self->loc($type); } else { return $Orig_FriendlyObjectType->($self); } }; =head2 BriefDescription Asset Tracker adds new transaction types, adds more link types to the AddLink and DeleteLink transaction types, and adds the asset Type field to the Set transaction type. =cut $_BriefDescriptions{Update} = sub { my $self = shift; return $self->loc( "Asset update" ); }; $_BriefDescriptions{AddIP} = sub { my $self = shift; return $self->loc( "IP address [_1] added", $self->NewValue); }; $_BriefDescriptions{DelIP} = sub { my $self = shift; return $self->loc( "IP address [_1] deleted", $self->OldValue); }; $_BriefDescriptions{AddPort} = sub { my $self = shift; return $self->loc( "Port [_1] added", $self->NewValue); }; $_BriefDescriptions{DelPort} = sub { my $self = shift; return $self->loc( "Port [_1] deleted", $self->OldValue); }; $_BriefDescriptions{TicketLink} = sub { my $self = shift; my $ticket = RT::Ticket->new( $self->CurrentUser ); $ticket->Load($self->NewValue) or return $self->loc("Ticket #[_1] linked", $self->NewValue); return $self->loc( "Ticket #[_1] : [_2] ([_3])", $self->NewValue, $ticket->Subject, $ticket->Status); }; my $Orig_BriefDescriptions_AddLink = $_BriefDescriptions{AddLink}; $_BriefDescriptions{AddLink} = sub { my $self = shift; return $Orig_BriefDescriptions_AddLink->($self) unless ( $self->Data && $Orig_BriefDescriptions_AddLink->($self) eq $self->Data ); my $value; if ( $self->NewValue ) { my $URI = RT::URI->new( $self->CurrentUser ); $URI->FromURI( $self->NewValue ); if ( $URI->Resolver ) { $value = $URI->Resolver->AsString; } else { $value = $self->NewValue; } if ( $self->Field eq 'DependsOn' ) { return $self->loc( "Dependency on [_1] added", $value ); } elsif ( $self->Field eq 'DependedOnBy' ) { return $self->loc( "Dependency by [_1] added", $value ); } elsif ( $self->Field eq 'RefersTo' ) { return $self->loc( "Reference to [_1] added", $value ); } elsif ( $self->Field eq 'ReferredToBy' ) { return $self->loc( "Reference by [_1] added", $value ); } elsif ( $self->Field eq 'ComponentOf' ) { return $self->loc( "Component of [_1] added", $value ); } elsif ( $self->Field eq 'HasComponent' ) { return $self->loc( "Component [_1] added", $value ); } elsif ( $self->Field eq 'IsRunning' ) { return $self->loc( "Is running [_1] added", $value ); } elsif ( $self->Field eq 'RunsOn' ) { return $self->loc( "Runs on [_1] added", $value ); } else { my $string = $self->Field; $string =~ s/([a-z])([A-Z])/$1 $2/g; return $self->loc( "$string [_1] added", $value ); } } else { return ( $self->Data ); } }; my $Orig_BriefDescriptions_DeleteLink = $_BriefDescriptions{DeleteLink}; $_BriefDescriptions{DeleteLink} = sub { my $self = shift; return $Orig_BriefDescriptions_DeleteLink->($self) unless ( $self->Data && $Orig_BriefDescriptions_DeleteLink->($self) eq $self->Data ); my $value; if ( $self->OldValue ) { my $URI = RT::URI->new( $self->CurrentUser ); $URI->FromURI( $self->OldValue ); if ( $URI->Resolver ) { $value = $URI->Resolver->AsString; } else { $value = $self->OldValue; } if ( $self->Field eq 'DependsOn' ) { return $self->loc( "Dependency on [_1] deleted", $value ); } elsif ( $self->Field eq 'DependedOnBy' ) { return $self->loc( "Dependency by [_1] deleted", $value ); } elsif ( $self->Field eq 'RefersTo' ) { return $self->loc( "Reference to [_1] deleted", $value ); } elsif ( $self->Field eq 'ReferredToBy' ) { return $self->loc( "Reference by [_1] deleted", $value ); } elsif ( $self->Field eq 'ComponentOf' ) { return $self->loc( "Component of [_1] deleted", $value ); } elsif ( $self->Field eq 'HasComponent' ) { return $self->loc( "Component [_1] deleted", $value ); } elsif ( $self->Field eq 'IsRunning' ) { return $self->loc( "Is running [_1] deleted", $value ); } elsif ( $self->Field eq 'RunsOn' ) { return $self->loc( "Runs on [_1] deleted", $value ); } else { my $string = $self->Field; $string =~ s/([a-z])([A-Z])/$1 $2/g; return $self->loc( "$string [_1] deleted", $value ); } } else { return ( $self->Data ); } }; my $Orig_BriefDescriptions_Set = $_BriefDescriptions{Set}; $_BriefDescriptions{Set} = sub { my $self = shift; if ( $self->ObjectType eq 'RTx::AssetTracker::Asset' && $self->Field eq 'Type' ) { my $t1 = new RTx::AssetTracker::Type( $self->CurrentUser ); $t1->Load( $self->OldValue ); my $t2 = new RTx::AssetTracker::Type( $self->CurrentUser ); $t2->Load( $self->NewValue ); return $self->loc("[_1] changed from [_2] to [_3]", $self->loc($self->Field) , $t1->Name , $t2->Name); } else { return $Orig_BriefDescriptions_Set->($self); } }; =head2 CustomFieldLookupType Returns the RT::Transaction lookup type, which can be passed to RT::CustomField->Create() via the 'LookupType' hash key. =cut my $Orig_CustomFieldLookupType = __PACKAGE__->can('CustomFieldLookupType') or die "API change? Can't find method 'CustomFieldLookupType'"; *CustomFieldLookupType = sub { my $self = shift; if ( ref $self && $self->{values}->{objecttype} eq 'RTx::AssetTracker::Asset' ) { return "RTx::AssetTracker::Type-RTx::AssetTracker::Asset-RT::Transaction"; } else { return $Orig_CustomFieldLookupType->($self); } }; 1; rt-extension-assettracker-3.0.0/lib/RT/URI/000077500000000000000000000000001222742774700204125ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RT/URI/at.pm000066400000000000000000000124541222742774700213620ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} use strict; use warnings; package RT::URI::at; use base 'RT::URI::base'; =head2 LocalURIPrefix Returns the prefix for a local URI. =cut sub LocalURIPrefix { my $self = shift; my $prefix = $self->Scheme. "://". RT->Config->Get('Organization'); return ($prefix); } =head2 ObjectType =cut sub ObjectType { my $self = shift; my $object = shift || $self->Object; my $type = 'asset'; if (ref($object) && (ref($object) ne 'RTx::AssetTracker::Asset')) { $type = ref($object); } return ($type); } =head2 URIForObject RT::Record Returns the RT URI for a local RT::Record object =cut sub URIForObject { my $self = shift; my $obj = shift; return ($self->LocalURIPrefix ."/". $self->ObjectType($obj) ."/". $obj->Id); } =head2 ParseURI URI When handed an at: URI, figures out things like whether its a local record and what its ID is =cut sub ParseURI { my $self = shift; my $uri = shift; if ( $uri =~ /^\d+$/ ) { use RTx::AssetTracker::Asset; my $asset = RTx::AssetTracker::Asset->new( $self->CurrentUser ); $asset->Load( $uri ); $self->{'uri'} = $asset->URI; $self->{'object'} = $asset; return ($asset->id); } else { $self->{'uri'} = $uri; } #If it's a local URI, load the asset object and return its URI if ( $self->IsLocal ) { my $local_uri_prefix = $self->LocalURIPrefix; if ( $self->{'uri'} =~ /^\Q$local_uri_prefix\E\/(.*?)\/(\d+)$/i ) { my $type = $1; my $id = $2; if ( $type eq 'asset' ) { $type = 'RTx::AssetTracker::Asset' } # We can instantiate any RT::Record subtype. but not anything else if ( UNIVERSAL::isa( $type, 'RTx::AssetTracker::Record' ) ) { my $record = $type->new( $self->CurrentUser ); $record->Load($id); if ( $record->Id ) { $self->{'object'} = $record; return ( $record->Id ); } } } } return undef; } =head2 IsLocal Returns true if this URI is for a local asset. Returns undef otherwise. =cut sub IsLocal { my $self = shift; my $local_uri_prefix = $self->LocalURIPrefix; if ( $self->{'uri'} =~ /^\Q$local_uri_prefix/i ) { return 1; } else { return undef; } } =head2 Object Returns the object for this URI, if it's local. Otherwise returns undef. =cut sub Object { my $self = shift; return ($self->{'object'}); } =head2 Scheme Return the URI scheme for RT records =cut sub Scheme { my $self = shift; return "at"; } =head2 HREF If this is a local asset, return an HTTP url to it. Otherwise, return its URI =cut sub HREF { my $self = shift; return $self->URI unless $self->IsLocal; my $obj = $self->Object; if ( $obj && $self->ObjectType eq 'asset' ) { return RT->Config->Get('WebURL') ."AssetTracker/Asset/Display.html?id=". $obj->id; } return $self->URI; } =head2 AsString Returns either a localized string 'asset #23' or the full URI if the object is not local =cut sub AsString { my $self = shift; if ($self->IsLocal && $self->Object) { return $self->loc("[_1] #[_2]", $self->ObjectType, $self->Object->Id); } else { return $self->URI; } } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/000077500000000000000000000000001222742774700201435ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker.pm000066400000000000000000000031761222742774700231030ustar00rootroot00000000000000# BEGIN LICENSE BLOCK # # Copyright (c) 2002-2003 Jesse Vincent # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License # as published by the Free Software Foundation. # # A copy of that license should have arrived with this # software, but in any event can be snarfed from www.gnu.org. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # END LICENSE BLOCK package RTx::AssetTracker; use strict; use warnings; use version; our $VERSION = version->declare("3.0.0"); use RT::System; RT::System::AddRights( BulkUpdate => "Perform bulk updates on assets", # loc_pair AssetImport => "Import assets", # loc_pair ); RT::System::AddRightCategories( BulkUpdate => 'Staff', AssetImport => 'Staff', ); RT->AddJavaScript('assetautocomplete.js'); # load overlays for RT classes my @Classes = qw( RT::CustomField RT::CustomFields RT::Interface::Web RT::Interface::Web::QueryBuilder::Tree RT::Ticket RT::Transaction ); for (@Classes) { s|::|/|g; require $_.'.pm'; require $_.'_ATOverlay.pm'; } use RTx::AssetTracker::Types; use RTx::AssetTracker::Assets; use RTx::AssetTracker::Templates; use RTx::AssetTracker::Scrips; use RTx::AssetTracker::ScripConditions; use RTx::AssetTracker::ScripActions; RTx::AssetTracker::Type->ConfigureRoles(); RTx::AssetTracker::Asset->ConfigureLinks(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/000077500000000000000000000000001222742774700225365ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Asset.pm000066400000000000000000002050761222742774700241650ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Asset - an AssetTracker Asset object =head1 SYNOPSIS use RTx::AssetTracker::Asset; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Asset; use base 'RTx::AssetTracker::Record'; sub Table {'AT_Assets'}; use RTx::AssetTracker::Type; use RTx::AssetTracker::Assets; use RT::Group; use RT::URI::at; use RTx::AssetTracker::IPs; use RTx::AssetTracker::Ports; use RT::URI; use RT::CustomField; RT::CustomField->_ForObjectType( 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset' => "Assets" ); # A helper table for links mapping to make it easier # to build and parse links between assets our %LINKMAP = (); our @LINKORDER = (); our %LINKTYPEMAP = (); our %LINKDIRMAP = (); sub RegisterLinkType { my $class = shift; my $base = shift; my $target = shift; #return if exists $LINKTYPEMAP{$base}; $LINKTYPEMAP{$base}{Type} = $base; $LINKTYPEMAP{$base}{Mode} = 'Target'; my $base_name = $base; $base_name =~ s/([a-z])([A-Z])/$1 $2/g; $LINKTYPEMAP{$base}{Name} = $base_name; $LINKTYPEMAP{$base}{Mate} = $target; $LINKTYPEMAP{$target}{Type} = $base; $LINKTYPEMAP{$target}{Mode} = 'Base'; my $target_name = $target; $target_name =~ s/([a-z])([A-Z])/$1 $2/g; $LINKTYPEMAP{$target}{Name} = $target_name; $LINKTYPEMAP{$target}{Mate} = $base; $LINKDIRMAP{$base} = { Base => $base, Target => $target }; push @LINKORDER, $base, $target; { no strict 'refs'; *$base = sub { my $self = shift; return ( $self->_Links( $LINKTYPEMAP{$target}{Mode}, $base ) ); } unless $class->can($base); *$target = sub { my $self = shift; return ( $self->_Links( $LINKTYPEMAP{$base}{Mode}, $base ) ); } unless $class->can($target); } # sets up the Limit methods for links #RTx::AssetTracker::Assets::RegisterLinkField($base, $target); $RTx::AssetTracker::Assets::FIELD_METADATA{$base} = [ 'LINK' => To => $base ]; $RTx::AssetTracker::Assets::FIELD_METADATA{$target} = [ 'LINK' => From => $base ]; $RTx::AssetTracker::Assets::LOWER_CASE_FIELDS{lc $base} = $base; $RTx::AssetTracker::Assets::LOWER_CASE_FIELDS{lc $target} = $target; { no strict 'refs'; package RTx::AssetTracker::Assets; my $limit_base = "Limit$base"; my $limit_target = "Limit$target"; *$limit_base = sub { my $self = shift; my $asset_uri = shift; $self->LimitLinkedTo ( TARGET => $asset_uri, TYPE => $base, ); } unless $class->can($limit_base); *$limit_target = sub { my $self = shift; my $asset_uri = shift; $self->LimitLinkedFrom ( BASE => $asset_uri, TYPE => $target, ); } unless $class->can($limit_target); } } sub LINKMAP { return \%LINKMAP } sub LINKTYPEMAP { return \%LINKTYPEMAP } sub LINKDIRMAP { return \%LINKDIRMAP } sub LINKORDER { return @LINKORDER } sub ConfigureLinks { my $class = shift; $RT::Logger->critical('Old config option @AssetLinkTypes found. Check the Asset Tracker README for upgrade instructions.') if (@RT::AssetLinkTypes); my $map = RT->Config->Get('AssetLinkTypes') or return; while ( my ($forward, $reverse) = each %$map ) { $class->RegisterLinkType( $forward, $reverse ); } } =head2 Load Takes a single argument. This can be an asset id or asset name. If the asset can't be loaded, returns undef. Otherwise, returns the asset id. =cut sub Load { my $self = shift; my $id = shift; $id = '' unless defined $id; #If we have an integer URI, load the asset if ( $id =~ /^\d+$/ ) { my ($assetid,$msg) = $self->LoadById($id); unless ($self->Id) { $RT::Logger->debug("$self tried to load a bogus asset: $id"); return (undef); } } elsif ( $id ) { my ($assetid,$msg) = $self->LoadByCol('Name', $id); unless ($self->Id) { $RT::Logger->debug("$self tried to load a bogus asset named: $id"); return (undef); } } else { $RT::Logger->debug("Tried to load a bogus asset id: '$id'"); return (undef); } #Ok. we're loaded. lets get outa here. return $self->Id; } =head2 Create (ARGS) Arguments: ARGS is a hash of named parameters. Valid parameters are: id Type - Either a Type object or a Type Name Name -- The unique name of the asset Description -- A string describing the asset Status -- any valid status (Defined in RTx::AssetTracker::Type) Owner -- A reference to a list of email addresses or Names Admin -- A reference to a list of email addresses or Names CustomField- -- a scalar or array of values for the customfield with the id IP Address -- A reference to a list of either bare IP addresses or hashes of the appropiate form. Returns: ASSETID, Transaction Object, Error Message =cut sub Create { my $self = shift; my %args = ( id => undef, Type => undef, Name => undef, Description => '', Status => undef, Owner => undef, Admin => undef, TransactionData => undef, _RecordTransaction => 1, _Commit => 1, @_ ); unless ($args{'_Commit'}) { $args{'_RecordTransaction'} = 0; } my ($ErrStr, @non_fatal_errors); my $TypeObj = RTx::AssetTracker::Type->new( RT->SystemUser ); if ( ref $args{'Type'} eq 'RTx::AssetTracker::Type' ) { $TypeObj->Load( $args{'Type'}->Id ); } elsif ( $args{'Type'} ) { $TypeObj->Load( $args{'Type'} ); } else { $RT::Logger->debug("'". ( $args{'Type'} ||'') . "' not a recognized type object." ); } #Can't create an asset without a type. unless ( $TypeObj->Id ) { $RT::Logger->debug("$self No type given for asset creation."); return ( 0, 0, $self->loc('Could not create asset. Type not set') ); } #Now that we have a type, Check the ACLS unless ( $self->CurrentUser->HasRight( Right => 'CreateAsset', Object => $TypeObj, ) ) { return ( 0, 0, $self->loc( "No permission to create assets of type '[_1]'", $TypeObj->Name)); } my $cycle = $TypeObj->Lifecycle; unless ( defined $args{'Status'} && length $args{'Status'} ) { $args{'Status'} = $cycle->DefaultOnCreate; } $args{'Status'} = lc $args{'Status'}; unless ( $cycle->IsValid( $args{'Status'} ) ) { return ( 0, 0, $self->loc("Status '[_1]' isn't a valid status for assets of this type.", $self->loc($args{'Status'})) ); } unless ( $cycle->IsTransition( '' => $args{'Status'} ) ) { return ( 0, 0, $self->loc("New assets of this type can not have status '[_1]'.", $self->loc($args{'Status'})) ); } $args{'Name'} =~ s/\n//g; $args{'Description'} =~ s/\n//g; # test name uniqueness my ($rv, $msg) = $self->SatisfiesUniqueness($args{Name}, $TypeObj->Id, $args{Status}); return ($rv, 0, $msg) unless $rv; $RT::Handle->BeginTransaction() if $args{'_Commit'}; my %params = ( Type => $TypeObj->Id, Name => $args{'Name'}, Description => $args{'Description'}, Status => $args{'Status'}, ); # Parameters passed in during an import that we probably don't want to touch, otherwise foreach my $attr (qw(id Creator Created LastUpdated LastUpdatedBy)) { $params{$attr} = $args{$attr} if $args{$attr}; } my ($id,$asset_message) = $self->SUPER::Create( %params ); unless ($id) { $RT::Logger->crit( "Couldn't create an asset: " . $asset_message ); $RT::Handle->Rollback(); return ( 0, 0, $self->loc("Asset could not be created due to an internal error") ); } my $create_groups_ret = $self->_CreateAssetGroups( _RecordTransaction => $args{_RecordTransaction}, _Commit => $args{_Commit} ); unless ($create_groups_ret) { $RT::Logger->crit( "Couldn't create asset groups for asset " . $self->Id . ". aborting Asset creation." ); $RT::Handle->Rollback(); return ( 0, 0, $self->loc("Asset could not be created due to an internal error") ); } # Deal with setting up watchers foreach my $type ( RTx::AssetTracker::Type->RoleGroupTypes() ) { next unless ( defined $args{$type} ); foreach my $watcher ( ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) ) { # we reason that all-digits number must be a principal id, not email # this is the only way to can add my $field = 'Email'; if ($watcher =~ /^\d+$/) { $field = 'PrincipalId'; } elsif (my ($group_name) = $watcher =~ /^@(.+)$/) { my $group = RT::Group->new($self->CurrentUser); $group->LoadUserDefinedGroup($group_name); if ($group->Id) { $field = 'PrincipalId'; $watcher = $group->PrincipalId; } } # Note that we're using AddWatcher, rather than _AddWatcher, as we # actually _want_ that ACL check. Otherwise, random asset creators # could make themselves admins and maybe get asset rights. that would # be poor my ( $val, $msg ) = $self->AddWatcher( Type => $type, $field => $watcher, Silent => 1, ); push @non_fatal_errors, $self->loc("Couldn't set [_1] watcher: [_2]", $type, $msg) unless $val; } } # }}} # Add all the custom fields foreach my $arg ( keys %args ) { next unless $arg =~ /^CustomField-(\d+)$/i; my $cfid = $1; foreach my $value ( UNIVERSAL::isa( $args{$arg} => 'ARRAY' ) ? @{ $args{$arg} } : ( $args{$arg} ) ) { next unless defined $value && length $value; # Allow passing in uploaded LargeContent etc by hash reference my ($status, $msg) = $self->_AddCustomFieldValue( (UNIVERSAL::isa( $value => 'HASH' ) ? %$value : (Value => $value) ), Field => $cfid, RecordTransaction => 0, ); push @non_fatal_errors, $msg unless $status; } } #Add links my ($addlink_rv, $addlink_errors) = $self->_AddLinksOnCreateOrUpdate(%args); push @non_fatal_errors, @$addlink_errors; #Add IP addresses my $ips = defined $args{'IP Address'} ? $args{'IP Address'} : []; for my $ip (@$ips) { my $ip_ref = {}; if (!ref $ip) { $ip = { IP => $ip }; } my ($rv, $msg) = $self->AddIP(%$ip, Silent => 1, SilentPorts => 1); return (0,0,$msg) unless ($rv); } # We override the URI lookup. the whole reason # we have a URI column is so that joins on the links table # aren't expensive and stupid my $uri = RT::URI::at->new( $self->CurrentUser ); $self->__Set( Field => 'URI', Value => $uri->URIForObject($self) ); if ( $args{'_RecordTransaction'} ) { # {{{ Add a transaction for the create my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction( Type => "Create", Data => $args{TransactionData}, CommitScrips => !$args{'DryRun'}, ); if ( $self->Id && $Trans ) { $TransObj->UpdateCustomFields(ARGSRef => \%args); $RT::Logger->info( "Asset " . $self->Id . " created of type '" . $TypeObj->Name . "' by " . $self->CurrentUser->Name ); $ErrStr = $self->loc( "Asset [_1] created of type '[_2]'", $self->Id, $TypeObj->Name ); $ErrStr = join( "\n", $ErrStr, @non_fatal_errors ); } else { $RT::Handle->Rollback(); $ErrStr = join( "\n", $ErrStr, @non_fatal_errors ); $RT::Logger->error("Asset couldn't be created: $ErrStr"); return ( 0, 0, $self->loc( "Asset could not be created due to an internal error")); } $RT::Handle->Commit(); return ( $self->Id, $TransObj->Id, $ErrStr ); # }}} } else { # Not going to record a transaction $RT::Handle->Commit() if $args{'_Commit'}; $ErrStr = $self->loc( "Asset [_1] created of type '[_2]'", $self->Id, $TypeObj->Name ); $ErrStr = join( "\n", $ErrStr, @non_fatal_errors ); return ( $self->Id, $0, $ErrStr ); } } sub _AddLinksOnCreateOrUpdate { my ($self, %args) = @_; my @errors; #Add links foreach my $type ( keys %LINKTYPEMAP ) { next unless ( defined $args{$type} ); foreach my $link ( ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) ) { my ( $val, $msg, $obj ) = $self->__GetAssetFromURI( URI => $link ); unless ($val) { push @errors, $msg; next; } # Check rights on the other end of the link if we must # then run _AddLink that doesn't check for ACLs if ( RT->Config->Get( 'StrictLinkACL' ) ) { if ( $obj && !$obj->CurrentUserHasRight('ModifyAsset') ) { push @errors, $self->loc('Linking. Permission denied'); next; } } if ( $obj && lc $obj->Status eq 'deleted' ) { push @errors, $self->loc("Linking. Can't link to a deleted asset"); next; } my ( $wval, $wmsg ) = $self->_AddLink( Type => $LINKTYPEMAP{$type}->{'Type'}, $LINKTYPEMAP{$type}->{'Mode'} => $link, Silent => !$args{'_RecordTransaction'}, 'Silent'. ( $LINKTYPEMAP{$type}->{'Mode'} eq 'Base'? 'Target': 'Base' ) => 1, ); push @errors, $wmsg unless ($wval); } } return (@errors ? 0 : 1), \@errors; } sub UpdateAsset { my $self = shift; my %args = ( id => undef, Type => undef, Name => undef, Description => undef, Status => undef, TransactionData => undef, _RecordTransaction => 1, _Commit => 1, _Detailed => 0, @_ ); unless ($args{_Commit}) { $args{_RecordTransaction} = 0; } return 0, 0, $self->loc('Permission denied') unless $self->CurrentUserHasRight('ModifyAsset'); my $changing_type = undef; my $changing_name = undef; my $changing_status = undef; my $asset_updated = undef; # If the Type is being updated make sure we can find the new Type and have CreateAsset on it my $TypeObj = RTx::AssetTracker::Type->new($RT::SystemUser); if ( defined( $args{Type} ) && $args{Type} ne $self->TypeObj->Name ) { $changing_type = 1; $TypeObj->Load( $args{Type} ); #Can't create an asset without a type. unless ( $TypeObj->Id ) { return ( 0, 0, $self->loc("Could not load asset type '[_1]'", $args{Type}) ); } #Now that we have a type, Check the ACLS unless ( $self->CurrentUser->HasRight( Right => 'CreateAsset', Object => $TypeObj, ) ) { return ( 0, 0, $self->loc( "No permission to create assets of this type '[_1]'", $TypeObj->Name)); } } if (defined( $args{Name} ) && $args{Name} ne $self->Name ) { $changing_name = 1; } if (defined( $args{Status} ) && $args{Status} ne $self->Status ) { $changing_status = 1; } my $name = defined $args{Name} ? $args{Name} : $self->Name; my $status = defined $args{Status} ? $args{Status} : $self->Status; # test name uniqueness if ($changing_type || $changing_name || $changing_status) { $asset_updated++; my ($rv, $msg) = $self->SatisfiesUniqueness($name, $TypeObj->Id, $status); unless ($rv) { warn ($rv, 0, $msg); return ($rv, 0, $msg); } } $RT::Handle->BeginTransaction() if $args{_Commit}; $self->SetType( Value => $TypeObj->Id, RecordTransaction => $args{_Detailed}) if $changing_type; $self->SetName( Value => $name, RecordTransaction => $args{_Detailed}) if $changing_name; if (defined $args{Description} && $args{Description} ne $self->Description) { $self->SetDescription( Value => $args{Description}, RecordTransaction => $args{_Detailed}); $asset_updated++; } if (defined($args{Status}) && $args{Status} ne $self->Status) { unless ( $TypeObj->IsValidStatus( $args{Status} ) ) { return ( 0, 0, $self->loc('Invalid value for status') ); } $self->SetStatus( Value => $args{Status}, RecordTransaction => $args{_Detailed}); $asset_updated++; } #watchers foreach my $type ( RTx::AssetTracker::Type->RoleGroupTypes() ) { next unless ( exists $args{$type} ); my $role_method = $type . 'RoleGroup'; my $members_current = $self->$role_method->MembersObj->ItemsArrayRef; my $role_current = [ map { $_->MemberObj->IsGroup ? '@'. $_->MemberObj->Object->Name() : $_->MemberObj->Object->EmailAddress } @$members_current ]; my $role_new = ref( $args{$type} ) ? $args{$type} : [ $args{$type} ]; my ($add_role, $delete_role) = $self->_set_compare($role_current, $role_new); $asset_updated++ if @$add_role || @$delete_role; for my $watcher (@$delete_role) { next unless $watcher; my $field = 'Email'; if ($watcher =~ /^\d+$/) { $field = 'PrincipalId'; } elsif (my ($group_name) = $watcher =~ /^@(.+)$/) { my $group = RT::Group->new($self->CurrentUser); $group->LoadUserDefinedGroup($group_name); if ($group->Id) { $field = 'PrincipalId'; $watcher = $group->PrincipalId; } } my ( $wval, $wmsg ) = $self->DeleteWatcher( Type => $type, $field => $watcher, Silent => !$args{_Detailed}, ); return $wval, 0, $wmsg unless ($wval); } foreach my $watcher ( @$add_role ) { next unless $watcher; my $field = 'Email'; if ($watcher =~ /^\d+$/) { $field = 'PrincipalId'; } elsif (my ($group_name) = $watcher =~ /^@(.+)$/) { my $group = RT::Group->new($self->CurrentUser); $group->LoadUserDefinedGroup($group_name); if ($group->Id) { $field = 'PrincipalId'; $watcher = $group->PrincipalId; } } my ( $wval, $wmsg ) = $self->_AddWatcher( Type => $type, $field => $watcher, Silent => !$args{_Detailed}, ); return $wval, 0, $wmsg unless ($wval); } } #custom fields foreach my $arg ( keys %args ) { next unless ( $arg =~ /^CustomField-(\d+)$/i ); my $cfid = $1; my $values_new = ref( $args{$arg} ) eq 'ARRAY' ? $args{$arg} : [ $args{$arg} ]; my $values_current = [ map { $_->Content } @{ $self->CustomFieldValues($cfid)->ItemsArrayRef }]; my ($add_values, $delete_values) = $self->_set_compare($values_current, $values_new); $asset_updated++ if @$add_values || @$delete_values; #delete old values for my $val (@$delete_values) { my $cf = RT::CustomField->new($self->CurrentUser); $cf->Load($cfid); my ( $rv, $msg ) = $cf->DeleteValueForObject( Object => $self, Content => $val, ); return $rv, 0, $msg unless $rv; if ( $args{_Detailed} ) { my ( $TransactionId, $Msg, $TransactionObj ) = $self->_NewTransaction( Type => 'CustomField', Field => $cf->Id, OldReference => $val, ReferenceType => 'RT::ObjectCustomFieldValue',); } } #add new values for my $value ( @$add_values ) { next unless ( defined($value) && length($value) ); # Allow passing in uploaded LargeContent etc by hash reference $self->_AddCustomFieldValue( (UNIVERSAL::isa( $value => 'HASH' ) ? %$value : (Value => $value) ), Field => $cfid, RecordTransaction => $args{_Detailed}, ); } } #TODO support link transactions my $base_links = RT::Links->new($self->CurrentUser); $base_links->Limit( FIELD => 'Base', VALUE => $self->URI ); $_->Delete for @{ $base_links->ItemsArrayRef }; my $target_links = RT::Links->new($self->CurrentUser); $target_links->Limit( FIELD => 'Target', VALUE => $self->URI ); $_->Delete for @{ $target_links->ItemsArrayRef }; my ($addlink_rv, $addlink_errors) = $self->_AddLinksOnCreateOrUpdate(%args); return $addlink_rv, 0, join("\n", @$addlink_errors) unless $addlink_rv; #TODO support IP transactions #Delete existing IPs $_->Delete for @{$self->IPs->ItemsArrayRef}; #Add IP addresses my $ips = defined $args{'IP Address'} ? $args{'IP Address'} : []; for my $ip (@$ips) { my $ip_ref = {}; if (!ref $ip) { $ip = { IP => $ip }; } my ($rv, $msg) = $self->AddIP(%$ip, Silent => 1, SilentPorts => 1); return (0,0,$msg) unless ($rv); } #unless ($asset_updated) { #return ($self->Id, 0, $self->loc('Asset not changed')); #} my $trans_id = 0; if ( $args{_RecordTransaction} ) { my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction( Type => "Update", Data => $self->loc('Asset updated on import'), ); $trans_id = $TransObj->Id; } if ($args{_Commit}) { $RT::Handle->Commit(); } return $self->Id, $trans_id, $self->loc('Asset [_1] updated', $self->Id); } =head2 _CreateAssetGroups Create the asset groups and links for this asset. This routine expects to be called from Asset->Create _inside of a transaction_ It will create two groups for this asset: Admin and Owner. It will return true on success and undef on failure. =cut sub _CreateAssetGroups { my $self = shift; my %args = @_; my @types = RTx::AssetTracker::Type->RoleGroupTypes; foreach my $type (@types) { my $type_obj = RT::Group->new($self->CurrentUser); my ($id, $msg) = $type_obj->_Create(Domain => 'RTx::AssetTracker::Asset-Role', Instance => $self->Id, Type => $type, InsideTransaction => 1, '_RecordTransaction' => $args{'_Commit'} && $args{'_RecordTransaction'} ); unless ($id) { $RT::Logger->error("Couldn't create an asset group of type '$type' for asset ". $self->Id.": ".$msg); return(undef); } } return(1); } =head2 AddWatcher AddWatcher takes a parameter hash. The keys are as follows: Type One of Asset->RoleGroupTypes PrinicpalId The RT::Principal id of the user or group that's being added as a watcher Email The email address of the new watcher. If a user with this email address can't be found, a new nonprivileged user will be created. If the watcher you're trying to set has an RT account, set the PrincipalId paremeter to their User Id. Otherwise, set the Email parameter to their Email address. =cut sub AddWatcher { my $self = shift; my %args = ( Type => undef, PrincipalId => undef, Email => undef, @_ ); # ModifyAsset works in any case return $self->_AddWatcher( %args ) if $self->CurrentUserHasRight('ModifyAsset'); if ( $args{'Email'} ) { my ($addr) = RT::EmailParser->ParseEmailAddress( $args{'Email'} ); return (0, $self->loc("Couldn't parse address from '[_1]' string", $args{'Email'} )) unless $addr; if ( lc $self->CurrentUser->EmailAddress eq lc RT::User->CanonicalizeEmailAddress( $addr->address ) ) { $args{'PrincipalId'} = $self->CurrentUser->id; delete $args{'Email'}; } } # If the watcher isn't the current user then the current user has no right # bail unless ( $args{'PrincipalId'} && $self->CurrentUser->id == $args{'PrincipalId'} ) { return ( 0, $self->loc("Permission Denied") ); } # If they don't have RoleRight for this role, bail if ( $args{'Type'} ) { unless ( $self->CurrentUserHasRight(RTx::AssetTracker::Type->RoleRight($args{'Type'})) ) { return ( 0, $self->loc('Permission Denied') ); } } else { $RT::Logger->warning( "AddWatcher got passed a bogus type"); return ( 0, $self->loc('Error in parameters to Asset->AddWatcher') ); } return $self->_AddWatcher( %args ); } #This contains the meat of AddWatcher. but can be called from a routine like # Create, which doesn't need the additional acl check sub _AddWatcher { my $self = shift; my %args = ( Type => undef, Silent => undef, PrincipalId => undef, Email => undef, Name => undef, TransactionData => undef, @_ ); my $principal = RT::Principal->new($self->CurrentUser); if ($args{'Email'}) { if ( RT::EmailParser->IsRTAddress( $args{'Email'} ) ) { return (0, $self->loc("[_1] is an address RT receives mail at. Adding it as a '[_2]' would create a mail loop", $args{'Email'}, $self->loc($args{'Type'}))); } my $user = RT::User->new(RT->SystemUser); $user->LoadByEmail($args{'Email'}); $args{'PrincipalId'} = $user->PrincipalId if $user->Id; } elsif ($args{'Name'}) { my $user = RT::User->new(RT->SystemUser); $user->Load($args{'Name'}); $args{'PrincipalId'} = $user->PrincipalId if $user->Id; } if ($args{'PrincipalId'}) { $principal->Load($args{'PrincipalId'}); if ( $principal->id and $principal->IsUser and my $email = $principal->Object->EmailAddress ) { return (0, $self->loc("[_1] is an address RT receives mail at. Adding it as a '[_2]' would create a mail loop", $email, $self->loc($args{'Type'}))) if RT::EmailParser->IsRTAddress( $email ); } } # If we can't find this watcher, we need to bail. unless ($principal->Id) { $RT::Logger->error("Could not load a user with the email address '".$args{'Email'}. "' to add as a watcher for asset ".$self->Id); return(0, $self->loc("Could not find that user")); } my $group = $self->LoadAssetRoleGroup(Type => $args{'Type'}); unless ($group->id) { return(0,$self->loc("Group not found")); } if ( $group->HasMember( $principal)) { return ( 0, $self->loc('[_1] is already a [_2] for this asset', $principal->Object->Name, $self->loc($args{'Type'})) ); } my ( $m_id, $m_msg ) = $group->_AddMember( PrincipalId => $principal->Id, InsideTransaction => 1 ); unless ($m_id) { $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id.": ".$m_msg); return ( 0, $self->loc('Could not make [_1] a [_2] for this asset', $principal->Object->Name, $self->loc($args{'Type'})) ); } unless ( $args{'Silent'} ) { $self->_NewTransaction( Type => 'AddWatcher', NewValue => $principal->Id, Field => $args{'Type'}, Data => $args{TransactionData}, ); } return ( 1, $self->loc('Added [_1] as a [_2] for this asset', $principal->Object->Name, $self->loc($args{'Type'})) ); } =head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS } Deletes an Asset watcher. Takes two arguments: Type (one of Asset->RoleGroupTypes) and one of PrincipalId (an RT::Principal Id of the watcher you want to remove) OR Email (the email address of an existing wathcer) =cut sub DeleteWatcher { my $self = shift; my %args = ( Type => undef, PrincipalId => undef, Email => undef, Name => undef, TransactionData => undef, @_ ); unless ( $args{'PrincipalId'} || $args{'Email'} ) { return ( 0, $self->loc("No principal specified") ); } my $principal = RT::Principal->new( $self->CurrentUser ); if ( $args{'PrincipalId'} ) { $principal->Load( $args{'PrincipalId'} ); } elsif ($args{'Email'}) { my $user = RT::User->new( $self->CurrentUser ); $user->LoadByEmail( $args{'Email'} ); $principal->Load( $user->Id ); } elsif ($args{'Name'}) { my $user = RT::User->new( $self->CurrentUser ); $user->Load( $args{'Name'} ); $principal->Load( $user->Id ); } # If we can't find this watcher, we need to bail. unless ( $principal->Id ) { return ( 0, $self->loc("Could not find that principal") ); } my $group = $self->LoadAssetRoleGroup( Type => $args{'Type'} ); unless ( $group->id ) { return ( 0, $self->loc("Group not found") ); } # Check ACLS #If the watcher we're trying to add is for the current user if ( $self->CurrentUser->PrincipalId == $principal->id ) { # If they don't have 'RoleRight' for this role # or 'ModifyAsset', bail if ( $args{'Type'} ) { unless ( $self->CurrentUserHasRight('ModifyAsset') or $self->CurrentUserHasRight(RTx::AssetTracker::Type->RoleRight($args{'Type'})) ) { return ( 0, $self->loc('Permission Denied') ); } } else { $RT::Logger->warning("$self -> DeleteWatcher got passed a bogus type"); return ( 0, $self->loc('Error in parameters to Asset->DeleteWatcher') ); } } # If the watcher isn't the current user # and the current user doesn't have 'ModifyAsset' bail else { unless ( $self->CurrentUserHasRight('ModifyAsset') ) { return ( 0, $self->loc("Permission Denied") ); } } # }}} # see if this user is already a watcher. unless ( $group->HasMember($principal) ) { return ( 0, $self->loc( '[_1] is not a [_2] for this asset', $principal->Object->Name, $args{'Type'} ) ); } my ( $m_id, $m_msg ) = $group->_DeleteMember( $principal->Id ); unless ($m_id) { $RT::Logger->error( "Failed to delete " . $principal->Id . " as a member of group " . $group->Id . ": " . $m_msg ); return (0, $self->loc( 'Could not remove [_1] as a [_2] for this asset', $principal->Object->Name, $args{'Type'} ) ); } unless ( $args{'Silent'} ) { $self->_NewTransaction( Type => 'DelWatcher', OldValue => $principal->Id, Field => $args{'Type'}, Data => $args{TransactionData} ); } return ( 1, $self->loc( "[_1] is no longer a [_2] for this asset.", $principal->Object->Name, $args{'Type'} ) ); } # a generic routine to be called by IsOwner and IsAdmin =head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL } Takes a param hash with the attributes Type and either PrincipalId or Email Type is one of Asset->RoleGroupTypes PrincipalId is an RT::Principal id, and Email is an email address. Returns true if the specified principal (or the one corresponding to the specified address) is a member of the group Type for this asset. XX TODO: This should be Memoized. =cut sub IsWatcher { my $self = shift; my %args = ( Type => 'Owner', PrincipalId => undef, Email => undef, @_ ); # Load the relevant group. my $group = $self->LoadAssetRoleGroup(Type => $args{'Type'}); # Find the relevant principal. if (!$args{PrincipalId} && $args{Email}) { # Look up the specified user. my $user = RT::User->new($self->CurrentUser); $user->LoadByEmail($args{Email}); if ($user->Id) { $args{PrincipalId} = $user->PrincipalId; } else { # A non-existent user can't be a group member. return 0; } } # Ask if it has the member in question return $group->HasMember( $args{'PrincipalId'} ); } =head2 IsOwner PRINCIPAL_ID Takes an RT::Principal id Returns true if the principal is a owner of the current asset. This method is auto-generated. =cut =head2 IsAdmin PRINCIPAL_ID Takes an RT::Principal id. Returns true if the principal is a requestor of the current asset. This method is auto-generated. =cut sub SetType { my $self = shift; my %args = ( @_ ); my $NewType = $args{'Value'}; #Redundant. ACL gets checked in _Set; unless ( $self->CurrentUserHasRight('ModifyAsset') ) { return ( 0, $self->loc("Permission Denied") ); } my $NewTypeObj = RTx::AssetTracker::Type->new( $self->CurrentUser ); $NewTypeObj->Load($NewType); unless ( $NewTypeObj->Id() ) { return ( 0, $self->loc("That type does not exist") ); } if ( $NewTypeObj->Id == $self->TypeObj->Id ) { return ( 0, $self->loc('That is the same value') ); } unless ( $self->CurrentUser->HasRight( Right => 'CreateAsset', Object => $NewTypeObj)) { return ( 0, $self->loc("You may not create assets of that type.") ); } my $new_status; my $old_lifecycle = $self->TypeObj->Lifecycle; my $new_lifecycle = $NewTypeObj->Lifecycle; if ( $old_lifecycle->Name ne $new_lifecycle->Name ) { unless ( $old_lifecycle->HasMoveMap( $new_lifecycle ) ) { return ( 0, $self->loc("There is no mapping for statuses between these types. Contact your system administrator.") ); } $new_status = $old_lifecycle->MoveMap( $new_lifecycle )->{ lc $self->Status }; return ( 0, $self->loc("Mapping between types' lifecycles is incomplete. Contact your system administrator.") ) unless $new_status; } if ( $new_status ) { my $clone = RTx::AssetTracker::Asset->new( RT->SystemUser ); $clone->Load( $self->Id ); unless ( $clone->Id ) { return ( 0, $self->loc("Couldn't load copy of asset #[_1].", $self->Id) ); } #Actually update the status my ($val, $msg)= $clone->_Set( Field => 'Status', Value => $new_status, RecordTransaction => 0, ); $RT::Logger->error( 'Status change failed on type change: '. $msg ) unless $val; } my ($status, $msg) = $self->_Set( %args, Field => 'Type', Value => $NewTypeObj->Id() ); if ( $status ) { # Clear the type object cache; $self->{_type_obj} = undef; } return ($status, $msg); } =head2 TypeObj Takes nothing. returns this asset's type object =cut sub TypeObj { my $self = shift; if(!$self->{_type_obj} || ! $self->{_type_obj}->id) { $self->{_type_obj} = RTx::AssetTracker::Type->new( $self->CurrentUser ); #We call __Value so that we can avoid the ACL decision and some deep recursion my ($result) = $self->{_type_obj}->Load( $self->__Value('Type') ); } return ($self->{_type_obj}); } sub AssetTypeObj { $_[0]->TypeObj() } sub SetName { my $self = shift; my %args = ( Value => undef, TransactionData => undef, @_, ); my ($rv, $msg) = $self->SatisfiesUniqueness($args{Value}, $self->Type, $self->Status); if ($rv) { return $self->_Set(%args, Field => 'Name');#, Value => $args{Value}, TransactionData => $args{TransactionData}); } else { return $rv, $msg; } } sub SatisfiesUniqueness { my $self = shift; my $name = shift; my $type = shift; my $stat = shift; my $Assets = RTx::AssetTracker::Assets->new( $RT::SystemUser ); my $Type = RTx::AssetTracker::Type->new( $RT::SystemUser ); $Type->Load($type); if (RT->Config->Get('GlobalUniqueAssetName')) { $Assets->Limit(FIELD => "Name", VALUE => $name); $Assets->Limit(FIELD => "id", OPERATOR => "!=", VALUE => $self->Id) if $self->Id; return (0, "Asset name $name isn't unique across the entire asset database") if $Assets->Count; } if (RT->Config->Get('TypeUniqueAssetName')) { $Assets->Limit(FIELD => "Type", VALUE => $type); $Assets->Limit(FIELD => "id", OPERATOR => "!=", VALUE => $self->Id) if $self->Id; return (0, "Asset name $name isn't unique among assets of type: " . $Type->Name) if $Assets->Count; } if (RT->Config->Get('TypeStatusUniqueAssetName')) { $Assets->Limit(FIELD => "Status", VALUE => $stat); $Assets->Limit(FIELD => "id", OPERATOR => "!=", VALUE => $self->Id) if $self->Id; return (0, "Asset name $name isn't unique among assets of type: " . $Type->Name . ", and status: $stat") if $Assets->Count; } return 1, "Asset name satifies uniqueness."; } sub SetDescription { my $self = shift; $self->_SetBasic(@_, Field => 'Description'); } sub _Links { my $self = shift; #TODO: Field isn't the right thing here. but I ahave no idea what mnemonic --- #tobias meant by $f my $field = shift; my $type = shift || ""; my $cache_key = "$field$type"; return $self->{ $cache_key } if $self->{ $cache_key }; my $links = $self->{ $cache_key } = RT::Links->new( $self->CurrentUser ); unless ( $self->CurrentUserHasRight('ShowAsset') ) { $links->Limit( FIELD => 'id', VALUE => 0, SUBCLAUSE => 'acl' ); return $links; } # at least to myself $links->Limit( FIELD => $field, VALUE => $self->URI ); $links->Limit( FIELD => 'Type', VALUE => $type, ) if $type; return $links; } =head2 DeleteLink Delete a link. takes a paramhash of Base, Target, Type, Silent, SilentBase and SilentTarget. Either Base or Target must be null. The null value will be replaced with this asset's id. If Silent is true then no transaction would be recorded, in other case you can control creation of transactions on both base and target with SilentBase and SilentTarget respectively. By default both transactions are created. =cut sub DeleteLink { my $self = shift; my %args = ( Base => undef, Target => undef, Type => undef, Silent => undef, SilentBase => undef, SilentTarget => undef, TransactionData => undef, @_ ); unless ( $args{'Target'} || $args{'Base'} ) { $RT::Logger->error("Base or Target must be specified"); return ( 0, $self->loc('Either base or target must be specified') ); } #check acls my $right = 0; $right++ if $self->CurrentUserHasRight('ModifyAsset'); if ( !$right && RT->Config->Get( 'StrictAssetLinkACL' ) ) { return ( 0, $self->loc("Permission Denied") ); } # If the other URI is an RTx::AssetTracker::Asset, we want to make sure the user # can modify it too... my ($status, $msg, $other_asset) = $self->__GetAssetFromURI( URI => $args{'Target'} || $args{'Base'} ); return (0, $msg) unless $status; if ( !$other_asset || $other_asset->CurrentUserHasRight('ModifyAsset') ) { $right++; } if ( ( !RT->Config->Get( 'StrictAssetLinkACL' ) && $right == 0 ) || ( RT->Config->Get( 'StrictAssetLinkACL' ) && $right < 2 ) ) { return ( 0, $self->loc("Permission Denied") ); } my ($val, $Msg) = $self->SUPER::_DeleteLink(%args); return ( 0, $Msg ) unless $val; return ( $val, $Msg ) if $args{'Silent'}; my ($direction, $remote_link); if ( $args{'Base'} ) { $remote_link = $args{'Base'}; $direction = 'Target'; } elsif ( $args{'Target'} ) { $remote_link = $args{'Target'}; $direction = 'Base'; } my $remote_uri = RT::URI->new( $self->CurrentUser ); $remote_uri->FromURI( $remote_link ); unless ( $args{ 'Silent'. $direction } ) { my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction( Type => 'DeleteLink', Field => $LINKDIRMAP{$args{'Type'}}->{$direction}, OldValue => $remote_uri->URI || $remote_link, TimeTaken => 0, Data => $args{TransactionData}, ); $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans; } if ( !$args{ 'Silent'. ( $direction eq 'Target'? 'Base': 'Target' ) } && $remote_uri->IsLocal ) { my $OtherObj = $remote_uri->Object; my ( $val, $Msg ) = $OtherObj->_NewTransaction( Type => 'DeleteLink', Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base} : $LINKDIRMAP{$args{'Type'}}->{Target}, OldValue => $self->URI, ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'), TimeTaken => 0, ); $RT::Logger->error("Couldn't create transaction: $Msg") unless $val; } return ( $val, $Msg ); } =head2 AddLink Takes a paramhash of Type and one of Base or Target. Adds that link to this asset. If Silent is true then no transaction would be recorded, in other case you can control creation of transactions on both base and target with SilentBase and SilentTarget respectively. By default both transactions are created. =cut sub AddLink { my $self = shift; my %args = ( Target => '', Base => '', Type => '', Silent => undef, SilentBase => undef, SilentTarget => undef, TransactionData => undef, @_ ); unless ( $args{'Target'} || $args{'Base'} ) { $RT::Logger->error("Base or Target must be specified"); return ( 0, $self->loc('Either base or target must be specified') ); } my $right = 0; $right++ if $self->CurrentUserHasRight('ModifyAsset'); if ( !$right && RT->Config->Get( 'StrictAssetLinkACL' ) ) { return ( 0, $self->loc("Permission Denied") ); } # If the other URI is an RTx::AssetTracker::Asset, we want to make sure the user # can modify it too... my ($status, $msg, $other_asset) = $self->__GetAssetFromURI( URI => $args{'Target'} || $args{'Base'} ); return (0, $msg) unless $status; if ( !$other_asset || $other_asset->CurrentUserHasRight('ModifyAsset') ) { $right++; } if ( ( !RT->Config->Get( 'StrictAssetLinkACL' ) && $right == 0 ) || ( RT->Config->Get( 'StrictAssetLinkACL' ) && $right < 2 ) ) { return ( 0, $self->loc("Permission Denied") ); } return ( 0, "Can't link to a deleted asset" ) if $other_asset && lc $other_asset->Status eq 'deleted'; return $self->_AddLink(%args); } sub __GetAssetFromURI { my $self = shift; my %args = ( URI => '', @_ ); # If the other URI is an RTx::AssetTracker::Asset, we want to make sure the user # can modify it too... my $uri_obj = RT::URI->new( $self->CurrentUser ); unless ($uri_obj->FromURI( $args{'URI'} )) { my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} ); $RT::Logger->warning( $msg ); return( 0, $msg ); } my $obj = $uri_obj->Resolver->Object; unless ( UNIVERSAL::isa($obj, 'RTx::AssetTracker::Asset') && $obj->id ) { return (1, 'Found not an asset', undef); } return (1, 'Found asset', $obj); } =head2 _AddLink Private non-acled variant of AddLink so that links can be added during create. =cut sub _AddLink { my $self = shift; my %args = ( Target => '', Base => '', Type => '', Silent => undef, SilentBase => undef, SilentTarget => undef, @_ ); my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args); return ($val, $msg) if !$val || $exist; return ($val, $msg) if $args{'Silent'}; my ($direction, $remote_link); if ( $args{'Target'} ) { $remote_link = $args{'Target'}; $direction = 'Base'; } elsif ( $args{'Base'} ) { $remote_link = $args{'Base'}; $direction = 'Target'; } my $remote_uri = RT::URI->new( $self->CurrentUser ); $remote_uri->FromURI( $remote_link ); unless ( $args{ 'Silent'. $direction } ) { my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction( Type => 'AddLink', Field => $LINKDIRMAP{$args{'Type'}}->{$direction}, NewValue => $remote_uri->URI || $remote_link, TimeTaken => 0, Data => $args{TransactionData}, ); $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans; } if ( !$args{ 'Silent'. ( $direction eq 'Target'? 'Base': 'Target' ) } && $remote_uri->IsLocal ) { my $OtherObj = $remote_uri->Object; my ( $val, $msg ) = $OtherObj->_NewTransaction( Type => 'AddLink', Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base} : $LINKDIRMAP{$args{'Type'}}->{Target}, NewValue => $self->URI, ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'), TimeTaken => 0, ); $RT::Logger->error("Couldn't create transaction: $msg") unless $val; } return ( $val, $msg ); } =head2 SetStatus STATUS Set this asset's status. Alternatively, you can pass in a list of named parameters (Status => STATUS). =cut sub SetStatus { my $self = shift; my %args; if (@_ == 1) { $args{Status} = shift; } else { %args = (@_); } $args{Status} = $args{Status} || $args{Value}; my $lifecycle = $self->TypeObj->Lifecycle; my $new = lc $args{'Status'}; unless ( $lifecycle->IsValid( $new ) ) { return (0, $self->loc("Status '[_1]' isn't a valid status for assets of this type.", $self->loc($new))); } my $old = $self->__Value('Status'); unless ( $lifecycle->IsTransition( $old => $new ) ) { return (0, $self->loc("You can't change status from '[_1]' to '[_2]'.", $self->loc($old), $self->loc($new))); } my $check_right = $lifecycle->CheckRight( $old => $new ); unless ( $self->CurrentUserHasRight( $check_right ) ) { return ( 0, $self->loc('Permission Denied') ); } if ( $lifecycle->IsInactive( $new ) ) { # We don't want inactive assets to have IP addresses my $ips = $self->IPs(); while (my $ip = $ips->Next) { my($a, $b) = $self->DeleteIP( IP => $ip->IP ); } } #Actually update the status my ($val, $msg)= $self->_Set( %args, Field => 'Status', Value => $new, TimeTaken => 0, CheckACL => 0, TransactionType => 'Status', ); return ($val, $msg); } =head2 Delete Takes no arguments. Marks this ticket for garbage collection =cut sub Delete { my $self = shift; unless ( $self->TypeObj->Lifecycle->IsValid('deleted') ) { return (0, $self->loc('Delete operation is disabled by lifecycle configuration') ); #loc } return ( $self->SetStatus('deleted') ); } sub _Set { my $self = shift; my %args = ( Field => undef, Value => undef, TimeTaken => 0, RecordTransaction => 1, UpdateAsset => 1, CheckACL => 1, TransactionType => 'Set', TransactionData => undef, @_ ); if ($args{'CheckACL'}) { unless ( $self->CurrentUserHasRight('ModifyAsset')) { return ( 0, $self->loc("Permission Denied")); } } unless ($args{'UpdateAsset'} || $args{'RecordTransaction'}) { $RT::Logger->error("Asset->_Set called without a mandate to record an update or update the asset"); return(0, $self->loc("Internal Error")); } #if the user is trying to modify the record #Take care of the old value we really don't want to get in an ACL loop. # so ask the super::_Value my $Old = $self->SUPER::_Value("$args{'Field'}"); my ($ret, $msg); if ( $args{'UpdateAsset'} ) { #Set the new value ( $ret, $msg ) = $self->SUPER::_Set( Field => $args{'Field'}, Value => $args{'Value'} ); #If we can't actually set the field to the value, don't record # a transaction. instead, get out of here. return ( 0, $msg ) unless $ret; } if ( $args{'RecordTransaction'} == 1 ) { my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction( Type => $args{'TransactionType'}, Field => $args{'Field'}, NewValue => $args{'Value'}, OldValue => $Old, TimeTaken => undef, Data => $args{TransactionData}, ); # Ensure that we can read the transaction, even if the change # just made the asset unreadable to us $TransObj->{ _object_is_readable } = 1; return ( $Trans, scalar $TransObj->BriefDescription ); } else { return ( $ret, $msg ); } } =head2 _Value Takes the name of a table column. Returns its value as a string, if the user passes an ACL check =cut sub _Value { my $self = shift; my $field = shift; #if the field is public, return it. if ( $self->_Accessible( $field, 'public' ) ) { #$RT::Logger->debug("Skipping ACL check for $field"); return ( $self->SUPER::_Value($field) ); } #If the current user doesn't have ACLs, don't let em at it. unless ( $self->CurrentUserHasRight('ShowAsset') ) { return (undef); } return ( $self->SUPER::_Value($field) ); } =head2 CurrentUserHasRight Takes the textual name of an Asset scoped right (from RT::ACE) and returns 1 if the user has that right. It returns 0 if the user doesn't have that right. =cut sub CurrentUserHasRight { my $self = shift; my $right = shift; return $self->CurrentUser->PrincipalObj->HasRight( Object => $self, Right => $right, ) } =head2 CurrentUserCanSee Returns true if the current user can see the asset, using ShowAsset =cut sub CurrentUserCanSee { my $self = shift; return $self->CurrentUserHasRight('ShowAsset'); } =head2 HasRight Takes a paramhash with the attributes 'Right' and 'Principal' 'Right' is an asset-scoped textual right from RT::ACE 'Principal' is an RT::User object Returns 1 if the principal has the right. Returns undef if not. =cut sub HasRight { my $self = shift; my %args = ( Right => undef, Principal => undef, @_ ); unless ( ( defined $args{'Principal'} ) and ( ref( $args{'Principal'} ) ) ) { Carp::cluck("Principal attrib undefined for Asset::HasRight"); $RT::Logger->crit("Principal attrib undefined for Asset::HasRight"); return(undef); } return ( $args{'Principal'}->HasRight( Object => $self, Right => $args{'Right'} ) ); } =head2 CustomFieldLookupType Returns the RTx::AssetTracker::Asset lookup type, which can be passed to RT::CustomField->Create() via the 'LookupType' hash key. =cut sub CustomFieldLookupType { "RTx::AssetTracker::Type-RTx::AssetTracker::Asset"; } =head2 ACLEquivalenceObjects This method returns a list of objects for which a user's rights also apply to this ticket. Generally, this is only the ticket's queue, but some RT extensions may make other objects available too. This method is called from L. =cut sub ACLEquivalenceObjects { my $self = shift; return $self->TypeObj; } =head2 CustomFieldValues # Do name => id mapping (if needed) before falling back to # RT::Record's CustomFieldValues See L =cut sub CustomFieldValues { my $self = shift; my $field = shift; if ( $field and $field !~ /^\d+$/ ) { my $cf = RT::CustomField->new( $self->CurrentUser ); $cf->SetContextObject( $self ); $cf->LoadByNameAndAssetType( Name => $field, Type => $self->TypeObj->Id ); unless ( $cf->id ) { $cf->LoadByNameAndAssetType( Name => $field, Type => '0' ); } $field = $cf->id; unless ($field) { return RT::CustomFieldValues->new( $self->CurrentUser ); } } return $self->SUPER::CustomFieldValues($field); } sub IPs { my $self = shift; my $ips = RTx::AssetTracker::IPs->new( $self->CurrentUser ); $ips->Limit( FIELD => 'Asset', VALUE => $self->Id ); return $ips; } sub DeleteIP { my $self = shift; my %args = ( IP => undef, TransactionData => undef, @_ ); unless ( $self->CurrentUserHasRight('ModifyAsset') || $self->CurrentUserHasRight('RetireAsset') || $self->CurrentUserHasRight('DeleteAsset') ) { return ( 0, $self->loc("Permission Denied") ); } my $ip = RTx::AssetTracker::IP->new( $self->CurrentUser ); $ip->Load( $args{IP} ); my $addr = $ip->IP; # If we can't find this IP, we need to bail. unless ( $ip->Id ) { return ( 0, $self->loc("Could not find that IP") ); } my ($rv, $msg) = $ip->DeleteAllPorts(); unless ($rv) { return($rv, "IP address could not be deleted: $msg"); } my $retval = $ip->Delete(); if ($retval) { unless ( $args{'Silent'} ) { $self->_NewTransaction( Type => 'DelIP', OldValue => $addr, Field => 'IP', Data => $args{TransactionData} ); } return ( 1, $self->loc( "$addr is no longer an IP for this asset." ) ); } else { return(0, $self->loc("IP address could not be deleted")); } } sub AddIP { my $self = shift; my %args = ( IP => undef, Interface => undef, MAC => undef, TCPPorts => [], UDPPorts => [], Silent => 0, SilentPorts => 0, TransactionData => undef, @_ ); unless ( $self->CurrentUserHasRight('ModifyAsset') ) { return ( 0, $self->loc("Permission Denied") ); } if ( lc $self->Status eq 'retired') { return ( 0, $self->loc("Retired assets cannot have IP addresses") ); } if ( lc $self->Status eq 'deleted') { return ( 0, $self->loc("Deleted assets cannot have IP addresses") ); } unless ($args{IP} or $args{Interface}) { return ( 0, $self->loc("IP address or interface must be specified") ); } my $ip = RTx::AssetTracker::IP->new( $self->CurrentUser ); my ($rc, $msg) = $ip->Create( IP => $args{IP}, Interface => $args{Interface}, MAC => $args{MAC}, Asset => $self->Id, TCPPorts => $args{TCPPorts}, UDPPorts => $args{UDPPorts}, Silent => $args{Silent}, SilentPorts => $args{SilentPorts} ); if ($ip->Id) { unless ( $args{'Silent'} ) { $self->_NewTransaction( Type => 'AddIP', NewValue => $args{IP}, Field => 'IP', Data => $args{TransactionData} ); } return ( $ip->Id, $self->loc( "$args{IP} is now an IP for this asset." ) ); } else { return( 0, $self->loc("IP address could not be created: $msg")); } } sub _export_formatted_IPs { my ($self) = @_; my $ips = $self->IPs->ItemsArrayRef; return join('|', map { $self->_format_IP($_) } @$ips); } sub _format_IP { my ($self, $ip) = @_; return join(':', $ip->Interface, $ip->IP, $ip->MAC, join(',', $ip->TCPPorts), join(',', $ip->UDPPorts)); } sub IPsAsList { my $self = shift; my $ips = $self->IPs; my @ips; while (my $ip = $ips->Next) { push @ips, $ip->IP; } return @ips; } sub IPsAsString { my $self = shift; return join(',', $self->IPsAsList); } =head2 LoadAssetRoleGroup { Type => TYPE } Loads an asset group from the database. Takes a param hash with 1 parameters: Type is the type of Group we're trying to load: Requestor, Cc, AdminCc, Owner =cut sub LoadAssetRoleGroup { my $self = shift; my %args = ( Type => undef, @_); my $group = RT::Group->new( $self->CurrentUser ); $group->LoadByCols( Domain => 'RTx::AssetTracker::Asset-Role', Instance =>$self->Id, Type => $args{'Type'} ); # if it doesn't exits ( like when we add a new role in the config file ) # create it unless ( $group->id ) { my ($id, $msg) = $group->_Create(Instance => $self->Id, Type => $args{Type}, Domain => 'RTx::AssetTracker::Asset-Role', InsideTransaction => 0); unless ($id) { $RT::Logger->error("Couldn't create an Asset role group of type '$args{Type}' for asset ". $self->Id.": ".$msg); } } return $group; } sub _SetBasic { my $self = shift; my $extra_value; if (@_ % 2 ) { # odd number of arguments left. first is the value. called using old-skool one arg way (no TransactionData) $extra_value = shift; $RT::Logger->crit("Deprecated call to Set*. Asset Tracker uses named parameters for Set methods"); } my %args = ( Field => undef, Value => undef, TransactionData => undef, @_, ); $args{Value} = $extra_value if defined $extra_value; if ( $self->_Accessible( $args{Field}, 'write' ) ) { return ( $self->_Set( Field => $args{Field}, Value => $args{Value}, TransactionData => $args{TransactionData} ) ); } elsif ( $self->_Accessible( $args{Field}, 'read' ) ) { return ( 0, 'Immutable field' ); } else { return ( 0, 'Nonexistant field?' ); } } ## Shredder methods ## use RT::Shredder::Constants; use RT::Shredder::Exceptions; use RT::Shredder::Dependencies; sub __DependsOn { my $self = shift; my %args = ( Shredder => undef, Dependencies => undef, @_, ); my $deps = $args{'Dependencies'}; my $list = []; # Asset role groups( Owner, Admin, etc. ) my $objs = RT::Groups->new( $self->CurrentUser ); $objs->Limit( FIELD => 'Domain', VALUE => 'RTx::AssetTracker::Asset-Role' ); $objs->Limit( FIELD => 'Instance', VALUE => $self->Id ); push( @$list, $objs ); # IP Addresses $objs = RTx::AssetTracker::IPs->new( $self->CurrentUser ); $objs->Limit( FIELD => 'Asset', VALUE => $self->Id ); push( @$list, $objs ); #TODO: Users, Types if we wish export tool $deps->_PushDependencies( BaseObject => $self, Flags => DEPENDS_ON, TargetObjects => $list, Shredder => $args{'Shredder'} ); return $self->SUPER::__DependsOn( %args ); } sub Export { my ($self) = @_; return $self; } use Set::Scalar; sub _set_compare { my ($self, $current, $new) = @_; my $current_set = Set::Scalar->new(@$current); my $new_set = Set::Scalar->new(@$new); my $add = $new_set - $current_set; my $delete = $current_set - $new_set; return $add, $delete; } =head2 id Returns the current value of id. (In the database, id is stored as int(11).) =cut =head2 Type Returns the current value of Type. (In the database, Type is stored as int(11).) =head2 SetType VALUE Set Type to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Type will be stored as a int(11).) =cut =head2 Name Returns the current value of Name. (In the database, Name is stored as varchar(200).) =head2 SetName VALUE Set Name to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Name will be stored as a varchar(200).) =cut =head2 Description Returns the current value of Description. (In the database, Description is stored as varchar(255).) =head2 SetDescription VALUE Set Description to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Description will be stored as a varchar(255).) =cut =head2 Status Returns the current value of Status. (In the database, Status is stored as varchar(64).) =head2 SetStatus VALUE Set Status to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Status will be stored as a varchar(64).) =cut =head2 LastUpdatedBy Returns the current value of LastUpdatedBy. (In the database, LastUpdatedBy is stored as int(11).) =cut =head2 LastUpdated Returns the current value of LastUpdated. (In the database, LastUpdated is stored as datetime.) =cut =head2 Creator Returns the current value of Creator. (In the database, Creator is stored as int(11).) =cut =head2 Created Returns the current value of Created. (In the database, Created is stored as datetime.) =cut =head2 URI Returns the current value of URI. (In the database, URI is stored as varchar(255).) =head2 SetURI VALUE Set URI to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, URI will be stored as a varchar(255).) =cut sub _CoreAccessible { { id => {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, Type => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Name => {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''}, Description => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''}, Status => {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''}, LastUpdatedBy => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, LastUpdated => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, Creator => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Created => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, URI => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''}, } }; RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Assets.pm000066400000000000000000002762421222742774700243530ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Assets - a collection of AssetTracker Assets objects =head1 SYNOPSIS use RTx::AssetTracker::Assets; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Assets; use base 'RT::SearchBuilder'; use RTx::AssetTracker::Asset; sub Table {'AT_Assets'}; # most logic "borrowed" from RT::Tickets rev 2814 use vars qw( %FIELDS ); use RT::CustomFields; use File::Temp 'tempdir'; use HTML::Mason; use XML::Parser; # Configuration Tables: # FIELD_METADATA is a mapping of searchable Field name, to Type, and other # metadata. our %FIELD_METADATA = ( Name => [ 'STRING', ], Description => [ 'STRING', ], Status => [ 'ENUM' ], Type => [ 'ENUM' => 'Type', ], Creator => [ 'ENUM' => 'RT::User', ], LastUpdatedBy => [ 'ENUM' => 'RT::User', ], id => [ 'INT', ], URI => [ 'STRING', ], Linked => [ 'LINK' ], #loc_left_pair LinkedTo => [ 'LINK' => 'To' ], #loc_left_pair LinkedFrom => [ 'LINK' => 'From' ], #loc_left_pair LastUpdated => [ 'DATE' => 'LastUpdated', ], Created => [ 'DATE' => 'Created', ], TransactionData => [ 'TRANSFIELD' => 'Data', ], IP => [ 'IPFIELD', ], MAC => [ 'IPFIELD', ], Interface => [ 'IPFIELD', ], Port => [ 'PORTFIELD', ], Transport => [ 'PORTFIELD', ], TransactionDate => [ 'TRANSDATE', ], Updated => [ 'TRANSDATE', ], Watcher => [ 'WATCHERFIELD' ], TypeWatcher => [ 'WATCHERFIELD' => undef => 'Type', ], CustomFieldValue => [ 'CUSTOMFIELD' => 'Asset' ], #loc_left_pair CustomField => [ 'CUSTOMFIELD' => 'Asset' ], #loc_left_pair CF => [ 'CUSTOMFIELD' => 'Asset' ], #loc_left_pair WatcherGroup => [ 'MEMBERSHIPFIELD', ], ); # Lower Case version of FIELDS, for case insensitivity our %LOWER_CASE_FIELDS = map { ( lc($_) => $_ ) } (keys %FIELD_METADATA); #sub RegisterLinkField { # # my $base = shift; # my $target = shift; # # $FIELD_METADATA{$base} = [ 'LINK' => To => $base ]; # $FIELD_METADATA{$target} = [ 'LINK' => From => $base ]; # # { # no strict 'refs'; # # my $limit_base = "Limit$base"; # my $limit_target = "Limit$target"; # # *$limit_base = sub { # my $self = shift; # my $asset_uri = shift; # $self->LimitLinkedTo ( TARGET => $asset_uri, # TYPE => $base, ); # # }; # *$limit_target = sub { # my $self = shift; # my $asset_uri = shift; # $self->LimitLinkedFrom ( BASE => $asset_uri, # TYPE => $base, ); # # }; # # } # #} our %SEARCHABLE_SUBFIELDS = ( User => [qw( EmailAddress Name RealName Nickname Organization Address1 Address2 WorkPhone HomePhone MobilePhone PagerPhone id )], ); # Mapping of Field Type to Function our %dispatch = ( ENUM => \&_EnumLimit, INT => \&_IntLimit, LINK => \&_LinkLimit, DATE => \&_DateLimit, STRING => \&_StringLimit, TRANSFIELD => \&_TransLimit, TRANSDATE => \&_TransDateLimit, WATCHERFIELD => \&_WatcherLimit, MEMBERSHIPFIELD => \&_WatcherMembershipLimit, CUSTOMFIELD => \&_CustomFieldLimit, IPFIELD => \&_IPLimit, PORTFIELD => \&_PortLimit, ); our %can_bundle = ();# WATCHERFIELD => "yes", ); # Default EntryAggregator per type # if you specify OP, you must specify all valid OPs my %DefaultEA = ( INT => 'AND', ENUM => { '=' => 'OR', '!=' => 'AND' }, DATE => { '=' => 'OR', '>=' => 'AND', '<=' => 'AND', '>' => 'AND', '<' => 'AND' }, STRING => { '=' => 'OR', '!=' => 'AND', 'LIKE' => 'AND', 'NOT LIKE' => 'AND' }, TRANSFIELD => 'AND', TRANSDATE => 'AND', LINK => 'OR', LINKFIELD => 'AND', TARGET => 'AND', BASE => 'AND', WATCHERFIELD => { '=' => 'OR', '!=' => 'AND', 'LIKE' => 'OR', 'NOT LIKE' => 'AND' }, CUSTOMFIELD => 'OR', IPFIELD => 'OR', ); # Helper functions for passing the above lexically scoped tables above # into Assets_SQL. sub FIELDS { return \%FIELD_METADATA } sub dispatch { return \%dispatch } sub can_bundle { return \%can_bundle } # Bring in the clowns. require RTx::AssetTracker::Assets_SQL; our @SORTFIELDS = qw(id Status Type Name Description Created LastUpdated); =head2 SortFields Returns the list of fields that lists of assets can easily be sorted by =cut sub SortFields { my $self = shift; return (@SORTFIELDS); } # BEGIN SQL STUFF ********************************* sub CleanSlate { my $self = shift; $self->SUPER::CleanSlate( @_ ); delete $self->{$_} foreach qw( _sql_cf_alias _sql_group_members_aliases _sql_object_cfv_alias _sql_role_group_aliases _sql_u_watchers_alias_for_sort _sql_u_watchers_aliases _sql_current_user_can_see_applied _sql_ipalias _sql_portalias ); } =head1 Limit Helper Routines These routines are the targets of a dispatch table depending on the type of field. They all share the same signature: my ($self,$field,$op,$value,@rest) = @_; The values in @rest should be suitable for passing directly to DBIx::SearchBuilder::Limit. Essentially they are an expanded/broken out (and much simplified) version of what ProcessRestrictions used to do. They're also much more clearly delineated by the TYPE of field being processed. =head2 _EnumLimit Handle Fields which are limited to certain values, and potentially need to be looked up from another class. This subroutine actually handles two different kinds of fields. For some the user is responsible for limiting the values. (i.e. Status, Type). For others, the value specified by the user will be looked by via specified class. Meta Data: name of class to lookup in (Optional) =cut sub _EnumLimit { my ( $sb, $field, $op, $value, @rest ) = @_; # SQL::Statement changes != to <>. (Can we remove this now?) $op = "!=" if $op eq "<>"; die "Invalid Operation: $op for $field" unless $op eq "=" or $op eq "!="; my $meta = $FIELD_METADATA{$field}; if ( defined $meta->[1] && defined $value && $value !~ /^\d+$/ ) { my $class = ( $meta->[1] =~ /::/ ? '' : "RTx::AssetTracker::" ) . $meta->[1]; my $o = $class->new( $sb->CurrentUser ); $o->Load($value); $value = $o->Id || 0; } elsif ($field eq "Status") { $value = lc $value; } $sb->_SQLLimit( FIELD => $field, VALUE => $value, OPERATOR => $op, @rest, ); } =head2 _IntLimit Handle fields where the values are limited to integers. (For example, Priority, TimeWorked.) Meta Data: None =cut sub _IntLimit { my ( $sb, $field, $op, $value, @rest ) = @_; die "Invalid Operator $op for $field" unless $op =~ /^(=|!=|>|<|>=|<=)$/; $sb->_SQLLimit( FIELD => $field, VALUE => $value, OPERATOR => $op, @rest, ); } =head2 _LinkLimit Handle fields which deal with links between assets. Meta Data: 1: Direction (From, To) 2: Link Type (one of 'AssetLinkTypes') =cut sub _LinkLimit { my ( $sb, $field, $op, $value, @rest ) = @_; my $meta = $FIELD_METADATA{$field}; die "Invalid Operator $op for $field" unless $op =~ /^(=|!=|IS|IS NOT)$/io; my $is_negative = 0; if ( $op eq '!=' || $op =~ /\bNOT\b/i ) { $is_negative = 1; } my $is_null = 0; $is_null = 1 if !$value || $value =~ /^null$/io; unless ($is_null) { $value = RT::URI->new( $sb->CurrentUser )->CanonicalizeURI( $value ); } my $direction = $meta->[1] || ''; my ($matchfield, $linkfield) = ('', ''); if ( $direction eq 'To' ) { ($matchfield, $linkfield) = ("Target", "Base"); } elsif ( $direction eq 'From' ) { ($matchfield, $linkfield) = ("Base", "Target"); } elsif ( $direction ) { die "Invalid link direction '$direction' for $field\n"; } else { $sb->_OpenParen; $sb->_LinkLimit( 'LinkedTo', $op, $value, @rest ); $sb->_LinkLimit( 'LinkedFrom', $op, $value, @rest, ENTRYAGGREGATOR => (($is_negative && $is_null) || (!$is_null && !$is_negative))? 'OR': 'AND', ); $sb->_CloseParen; return; } my $is_local = 1; if ( $is_null ) { $op = ($op =~ /^(=|IS)$/i)? 'IS': 'IS NOT'; } elsif ( $value =~ /\D/ ) { $is_local = 0; } $matchfield = "Local$matchfield" if $is_local; #For doing a left join to find "unlinked assets" we want to generate a query that looks like this # SELECT main.* FROM AT_Assets main # LEFT JOIN Links Links_1 ON ( (Links_1.Type = 'MemberOf') # AND(main.URI = Links_1.Target)) # WHERE Links_1.Base IS NULL; if ( $is_null ) { my $linkalias = $sb->Join( TYPE => 'LEFT', ALIAS1 => 'main', FIELD1 => 'URI', TABLE2 => 'Links', FIELD2 => $linkfield ); $sb->SUPER::Limit( LEFTJOIN => $linkalias, FIELD => 'Type', OPERATOR => '=', VALUE => $meta->[2], ) if $meta->[2]; $sb->_SQLLimit( @rest, ALIAS => $linkalias, FIELD => $matchfield, OPERATOR => $op, VALUE => 'NULL', QUOTEVALUE => 0, ); } else { my $linkalias = $sb->Join( TYPE => 'LEFT', ALIAS1 => 'main', FIELD1 => 'URI', TABLE2 => 'Links', FIELD2 => $linkfield ); $sb->SUPER::Limit( LEFTJOIN => $linkalias, FIELD => 'Type', OPERATOR => '=', VALUE => $meta->[2], ) if $meta->[2]; $sb->SUPER::Limit( LEFTJOIN => $linkalias, FIELD => $matchfield, OPERATOR => '=', VALUE => $value, ); $sb->_SQLLimit( @rest, ALIAS => $linkalias, FIELD => $matchfield, OPERATOR => $is_negative? 'IS': 'IS NOT', VALUE => 'NULL', QUOTEVALUE => 0, ); } } =head2 _DateLimit Handle date fields. (Created, LastTold..) Meta Data: 1: type of link. (Probably not necessary.) =cut sub _DateLimit { my ( $sb, $field, $op, $value, @rest ) = @_; die "Invalid Date Op: $op" unless $op =~ /^(=|>|<|>=|<=)$/; my $meta = $FIELD_METADATA{$field}; die "Incorrect Meta Data for $field" unless ( defined $meta->[1] ); my $date = RT::Date->new( $sb->CurrentUser ); $date->Set( Format => 'unknown', Value => $value ); if ( $op eq "=" ) { # if we're specifying =, that means we want everything on a # particular single day. in the database, we need to check for > # and < the edges of that day. $date->SetToMidnight( Timezone => 'server' ); my $daystart = $date->ISO; $date->AddDay; my $dayend = $date->ISO; $sb->_OpenParen; $sb->_SQLLimit( FIELD => $meta->[1], OPERATOR => ">=", VALUE => $daystart, @rest, ); $sb->_SQLLimit( FIELD => $meta->[1], OPERATOR => "<", VALUE => $dayend, @rest, ENTRYAGGREGATOR => 'AND', ); $sb->_CloseParen; } else { $sb->_SQLLimit( FIELD => $meta->[1], OPERATOR => $op, VALUE => $date->ISO, @rest, ); } } =head2 _StringLimit Handle simple fields which are just strings. (Subject,Type) Meta Data: None =cut sub _StringLimit { my ( $sb, $field, $op, $value, @rest ) = @_; # FIXME: # Valid Operators: # =, !=, LIKE, NOT LIKE if ( RT->Config->Get('DatabaseType') eq 'Oracle' && (!defined $value || !length $value) && lc($op) ne 'is' && lc($op) ne 'is not' ) { if ($op eq '!=' || $op =~ /^NOT\s/i) { $op = 'IS NOT'; } else { $op = 'IS'; } $value = 'NULL'; } $sb->_SQLLimit( FIELD => $field, OPERATOR => $op, VALUE => $value, CASESENSITIVE => 0, @rest, ); } =head2 _IPLimit Handle fields limiting based on IP Meta Data: None =cut sub _IPLimit { my ( $sb, $field, $op, $value, @rest ) = @_; # See the comments for TransLimit, they apply here too $sb->{_sql_ipalias} = $sb->NewAlias('AT_IPs') unless defined $sb->{_sql_ipalias}; #$sb->_OpenParen; $sb->_SQLLimit( ALIAS => $sb->{_sql_ipalias}, FIELD => $field, OPERATOR => $op, VALUE => $value, @rest ); # Join IPs to Assets $sb->_SQLJoin( ALIAS1 => 'main', FIELD1 => $sb->{'primary_key'}, # UGH! ALIAS2 => $sb->{_sql_ipalias}, FIELD2 => 'Asset' ); #$sb->_CloseParen; } =head2 _PortLimit Handle fields limiting based on Port Meta Data: None =cut sub _PortLimit { my ( $self, $field, $op, $value, @rest ) = @_; # See the comments for TransLimit, they apply here too $self->{_sql_ipalias} = $self->NewAlias('AT_IPs') unless defined $self->{_sql_ipalias}; $self->{_sql_portalias} = $self->NewAlias('AT_Ports') unless defined $self->{_sql_portalias}; $self->_OpenParen; #Search for the right field $self->_SQLLimit( ALIAS => $self->{_sql_portalias}, FIELD => $field, OPERATOR => $op, VALUE => $value, CASESENSITIVE => 0, @rest ); # Join Ports to IPs $self->_SQLJoin( ALIAS1 => $self->{_sql_portalias}, FIELD1 => 'IP', ALIAS2 => $self->{_sql_ipalias}, FIELD2 => 'id', ); # Join IPs to Assets $self->_SQLJoin( ALIAS1 => 'main', FIELD1 => 'id', ALIAS2 => $self->{_sql_ipalias}, FIELD2 => 'Asset' ); $self->_CloseParen; } =head2 _TransDateLimit Handle fields limiting based on Transaction Date. The inpupt value must be in a format parseable by Time::ParseDate Meta Data: None =cut # This routine should really be factored into translimit. sub _TransDateLimit { my ( $sb, $field, $op, $value, @rest ) = @_; # See the comments for TransLimit, they apply here too my $txn_alias = $sb->JoinTransactions; my $date = RT::Date->new( $sb->CurrentUser ); $date->Set( Format => 'unknown', Value => $value ); $sb->_OpenParen; if ( $op eq "=" ) { # if we're specifying =, that means we want everything on a # particular single day. in the database, we need to check for > # and < the edges of that day. $date->SetToMidnight( Timezone => 'server' ); my $daystart = $date->ISO; $date->AddDay; my $dayend = $date->ISO; $sb->_SQLLimit( ALIAS => $txn_alias, FIELD => 'Created', OPERATOR => ">=", VALUE => $daystart, @rest ); $sb->_SQLLimit( ALIAS => $txn_alias, FIELD => 'Created', OPERATOR => "<=", VALUE => $dayend, @rest, ENTRYAGGREGATOR => 'AND', ); } # not searching for a single day else { #Search for the right field $sb->_SQLLimit( ALIAS => $txn_alias, FIELD => 'Created', OPERATOR => $op, VALUE => $date->ISO, @rest ); } $sb->_CloseParen; } =head2 _TransLimit Limit based on transaction. (Data, i.e. change comments) Meta Data: 1: Field to query on =cut sub _TransLimit { my ( $self, $field, $op, $value, %rest ) = @_; my $meta = $FIELD_METADATA{$field}; die "Incorrect Meta Data for $field" unless ( defined $meta->[1] ); my $txn_alias = $self->JoinTransactions; $self->_SQLLimit( %rest, ALIAS => $txn_alias, FIELD => $meta->[1], OPERATOR => $op, VALUE => $value, CASESENSITIVE => 0, ); } =head2 _WatcherLimit Handle watcher limits. (Requestor, CC, etc..) Meta Data: 1: Field to query on =cut sub _WatcherLimit { my $self = shift; my $field = shift; my $op = shift; my $value = shift; my %rest = (@_); my $meta = $FIELD_METADATA{ $field }; my $type = $meta->[1] || ''; my $class = $meta->[2] || 'Asset'; # Bail if the subfield is not allowed if ( $rest{SUBKEY} and not grep { $_ eq $rest{SUBKEY} } @{$SEARCHABLE_SUBFIELDS{'User'}}) { die "Invalid watcher subfield: '$rest{SUBKEY}'"; } # if it's equality op and search by Email or Name then we can preload user # we do it to help some DBs better estimate number of rows and get better plans if ( $op =~ /^!?=$/ && (!$rest{'SUBKEY'} || $rest{'SUBKEY'} eq 'Name' || $rest{'SUBKEY'} eq 'EmailAddress') ) { my $o = RT::User->new( $self->CurrentUser ); my $method = !$rest{'SUBKEY'} ? $field eq 'Owner'? 'Load' : 'LoadByEmail' : $rest{'SUBKEY'} eq 'EmailAddress' ? 'LoadByEmail': 'Load'; $o->$method( $value ); $rest{'SUBKEY'} = 'id'; $value = $o->id || 0; } $rest{SUBKEY} ||= 'EmailAddress'; my ($groups, $group_members, $users); if ( $rest{'BUNDLE'} ) { ($groups, $group_members, $users) = @{ $rest{'BUNDLE'} }; } else { $groups = $self->_RoleGroupsJoin( Type => $type, Class => $class, New => !$type ); } $self->_OpenParen; if ( $op =~ /^IS(?: NOT)?$/i ) { # is [not] empty case $group_members ||= $self->_GroupMembersJoin( GroupsAlias => $groups ); # to avoid joining the table Users into the query, we just join GM # and make sure we don't match records where group is member of itself $self->SUPER::Limit( LEFTJOIN => $group_members, FIELD => 'GroupId', OPERATOR => '!=', VALUE => "$group_members.MemberId", QUOTEVALUE => 0, ); $self->_SQLLimit( ALIAS => $group_members, FIELD => 'GroupId', OPERATOR => $op, VALUE => $value, %rest, ); } elsif ( $op =~ /^!=$|^NOT\s+/i ) { # negative condition case # reverse op $op =~ s/!|NOT\s+//i; # XXX: we have no way to build correct "Watcher.X != 'Y'" when condition # "X = 'Y'" matches more then one user so we try to fetch two records and # do the right thing when there is only one exist and semi-working solution # otherwise. my $users_obj = RT::Users->new( $self->CurrentUser ); $users_obj->Limit( FIELD => $rest{SUBKEY}, OPERATOR => $op, VALUE => $value, ); $users_obj->OrderBy; $users_obj->RowsPerPage(2); my @users = @{ $users_obj->ItemsArrayRef }; $group_members ||= $self->_GroupMembersJoin( GroupsAlias => $groups ); if ( @users <= 1 ) { my $uid = 0; $uid = $users[0]->id if @users; $self->SUPER::Limit( LEFTJOIN => $group_members, ALIAS => $group_members, FIELD => 'MemberId', VALUE => $uid, ); $self->_SQLLimit( %rest, ALIAS => $group_members, FIELD => 'id', OPERATOR => 'IS', VALUE => 'NULL', ); } else { $self->SUPER::Limit( LEFTJOIN => $group_members, FIELD => 'GroupId', OPERATOR => '!=', VALUE => "$group_members.MemberId", QUOTEVALUE => 0, ); $users ||= $self->Join( TYPE => 'LEFT', ALIAS1 => $group_members, FIELD1 => 'MemberId', TABLE2 => 'Users', FIELD2 => 'id', ); $self->SUPER::Limit( LEFTJOIN => $users, ALIAS => $users, FIELD => $rest{SUBKEY}, OPERATOR => $op, VALUE => $value, CASESENSITIVE => 0, ); $self->_SQLLimit( %rest, ALIAS => $users, FIELD => 'id', OPERATOR => 'IS', VALUE => 'NULL', ); } } else { # positive condition case $group_members ||= $self->_GroupMembersJoin( GroupsAlias => $groups, New => 1, Left => 0 ); $users ||= $self->Join( TYPE => 'LEFT', ALIAS1 => $group_members, FIELD1 => 'MemberId', TABLE2 => 'Users', FIELD2 => 'id', ); $self->_SQLLimit( %rest, ALIAS => $users, FIELD => $rest{'SUBKEY'}, VALUE => $value, OPERATOR => $op, CASESENSITIVE => 0, ); } $self->_CloseParen; return ($groups, $group_members, $users); } sub _RoleGroupsJoin { my $self = shift; my %args = (New => 0, Class => 'Asset', Type => '', @_); return $self->{'_sql_role_group_aliases'}{ $args{'Class'} .'-'. $args{'Type'} } if $self->{'_sql_role_group_aliases'}{ $args{'Class'} .'-'. $args{'Type'} } && !$args{'New'}; # we always have watcher groups for asset, so we use INNER join my $groups = $self->Join( ALIAS1 => 'main', FIELD1 => $args{'Class'} eq 'Type'? 'Type': 'id', TABLE2 => 'Groups', FIELD2 => 'Instance', ENTRYAGGREGATOR => 'AND', ); $self->SUPER::Limit( LEFTJOIN => $groups, ALIAS => $groups, FIELD => 'Domain', VALUE => 'RTx::AssetTracker::'. $args{'Class'} .'-Role', ); $self->SUPER::Limit( LEFTJOIN => $groups, ALIAS => $groups, FIELD => 'Type', VALUE => $args{'Type'}, ) if $args{'Type'}; $self->{'_sql_role_group_aliases'}{ $args{'Class'} .'-'. $args{'Type'} } = $groups unless $args{'New'}; return $groups; } sub _GroupMembersJoin { my $self = shift; my %args = (New => 1, GroupsAlias => undef, Left => 1, @_); return $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} } if $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} } && !$args{'New'}; my $alias = $self->Join( $args{'Left'} ? (TYPE => 'LEFT') : (), ALIAS1 => $args{'GroupsAlias'}, FIELD1 => 'id', TABLE2 => 'CachedGroupMembers', FIELD2 => 'GroupId', ENTRYAGGREGATOR => 'AND', ); $self->SUPER::Limit( $args{'Left'} ? (LEFTJOIN => $alias) : (), ALIAS => $alias, FIELD => 'Disabled', VALUE => 0, ); $self->{'_sql_group_members_aliases'}{ $args{'GroupsAlias'} } = $alias unless $args{'New'}; return $alias; } =head2 _WatcherJoin Helper function which provides joins to a watchers table both for limits and for ordering. =cut sub _WatcherJoin { my $self = shift; my $type = shift || ''; my $groups = $self->_RoleGroupsJoin( Type => $type ); my $group_members = $self->_GroupMembersJoin( GroupsAlias => $groups ); # XXX: work around, we must hide groups that # are members of the role group we search in, # otherwise them result in wrong NULLs in Users # table and break ordering. Now, we know that # RT doesn't allow to add groups as members of the # ticket roles, so we just hide entries in CGM table # with MemberId == GroupId from results $self->SUPER::Limit( LEFTJOIN => $group_members, FIELD => 'GroupId', OPERATOR => '!=', VALUE => "$group_members.MemberId", QUOTEVALUE => 0, ); my $users = $self->Join( TYPE => 'LEFT', ALIAS1 => $group_members, FIELD1 => 'MemberId', TABLE2 => 'Users', FIELD2 => 'id', ); return ($groups, $group_members, $users); } =head2 _WatcherMembershipLimit Handle watcher membership limits, i.e. whether the watcher belongs to a specific group or not. Meta Data: 1: Field to query on SELECT DISTINCT main.* FROM Tickets main, Groups Groups_1, CachedGroupMembers CachedGroupMembers_2, Users Users_3 WHERE ( (main.EffectiveId = main.id) ) AND ( (main.Status != 'deleted') ) AND ( (main.Type = 'ticket') ) AND ( ( (Users_3.EmailAddress = '22') AND (Groups_1.Domain = 'RT::Ticket-Role') AND (Groups_1.Type = 'RequestorGroup') ) ) AND Groups_1.Instance = main.id AND Groups_1.id = CachedGroupMembers_2.GroupId AND CachedGroupMembers_2.MemberId = Users_3.id ORDER BY main.id ASC LIMIT 25 =cut sub _WatcherMembershipLimit { my ( $self, $field, $op, $value, @rest ) = @_; my %rest = @rest; $self->_OpenParen; my $groups = $self->NewAlias('Groups'); my $groupmembers = $self->NewAlias('CachedGroupMembers'); my $users = $self->NewAlias('Users'); my $memberships = $self->NewAlias('CachedGroupMembers'); if ( ref $field ) { # gross hack my @bundle = @$field; $self->_OpenParen; for my $chunk (@bundle) { ( $field, $op, $value, @rest ) = @$chunk; $self->_SQLLimit( ALIAS => $memberships, FIELD => 'GroupId', VALUE => $value, OPERATOR => $op, @rest, ); } $self->_CloseParen; } else { $self->_SQLLimit( ALIAS => $memberships, FIELD => 'GroupId', VALUE => $value, OPERATOR => $op, @rest, ); } # Tie to groups for tickets we care about $self->_SQLLimit( ALIAS => $groups, FIELD => 'Domain', VALUE => 'RTx::AssetTracker::Asset-Role', ENTRYAGGREGATOR => 'AND' ); $self->Join( ALIAS1 => $groups, FIELD1 => 'Instance', ALIAS2 => 'main', FIELD2 => 'id' ); # }}} # If we care about which sort of watcher my $meta = $FIELD_METADATA{$field}; my $type = ( defined $meta->[1] ? $meta->[1] : undef ); if ($type) { $self->_SQLLimit( ALIAS => $groups, FIELD => 'Type', VALUE => $type, ENTRYAGGREGATOR => 'AND' ); } $self->Join( ALIAS1 => $groups, FIELD1 => 'id', ALIAS2 => $groupmembers, FIELD2 => 'GroupId' ); $self->Join( ALIAS1 => $groupmembers, FIELD1 => 'MemberId', ALIAS2 => $users, FIELD2 => 'id' ); $self->Limit( ALIAS => $groupmembers, FIELD => 'Disabled', VALUE => 0, ); $self->Join( ALIAS1 => $memberships, FIELD1 => 'MemberId', ALIAS2 => $users, FIELD2 => 'id' ); $self->Limit( ALIAS => $memberships, FIELD => 'Disabled', VALUE => 0, ); $self->_CloseParen; } =head2 _CustomFieldDecipher Try and turn a CF descriptor into (cfid, cfname) object pair. Takes an optional second parameter of the CF LookupType, defaults to Asset CFs. =cut sub _CustomFieldDecipher { my ($self, $string, $lookuptype) = @_; $lookuptype ||= $self->_SingularClass->CustomFieldLookupType; my ($object, $field, $column) = ($string =~ /^(?:(.+?)\.)?{(.+)}(?:\.(Content|LargeContent))?$/); $field ||= ($string =~ /^{(.*?)}$/)[0] || $string; my ($cf, $applied_to); if ( $object ) { my $record_class = RT::CustomField->RecordClassFromLookupType($lookuptype); $applied_to = $record_class->new( $self->CurrentUser ); $applied_to->Load( $object ); if ( $applied_to->id ) { RT->Logger->debug("Limiting to CFs identified by '$field' applied to $record_class #@{[$applied_to->id]} (loaded via '$object')"); } else { RT->Logger->warning("$record_class '$object' doesn't exist, parsed from '$string'"); $object = 0; undef $applied_to; } } if ( $field =~ /\D/ ) { $object ||= ''; my $cfs = RT::CustomFields->new( $self->CurrentUser ); $cfs->Limit( FIELD => 'Name', VALUE => $field, ($applied_to ? (CASESENSITIVE => 0) : ()) ); $cfs->LimitToLookupType($lookuptype); if ($applied_to) { $cfs->SetContextObject($applied_to); $cfs->LimitToObjectId($applied_to->id); } # if there is more then one field the current user can # see with the same name then we shouldn't return cf object # as we don't know which one to use $cf = $cfs->First; if ( $cf ) { $cf = undef if $cfs->Next; } } else { $cf = RT::CustomField->new( $self->CurrentUser ); $cf->Load( $field ); $cf->SetContextObject($applied_to) if $cf->id and $applied_to; } return ($object, $field, $cf, $column); } =head2 _CustomFieldJoin Factor out the Join of custom fields so we can use it for sorting too =cut our %JOIN_ALIAS_FOR_LOOKUP_TYPE = ( RTx::AssetTracker::Asset->CustomFieldLookupType => sub { "main" }, ); sub _CustomFieldJoin { my ($self, $cfkey, $cfid, $field, $type) = @_; $type ||= RTx::AssetTracker::Asset->CustomFieldLookupType; # Perform one Join per CustomField if ( $self->{_sql_object_cfv_alias}{$cfkey} || $self->{_sql_cf_alias}{$cfkey} ) { return ( $self->{_sql_object_cfv_alias}{$cfkey}, $self->{_sql_cf_alias}{$cfkey} ); } my $ObjectAlias = $JOIN_ALIAS_FOR_LOOKUP_TYPE{$type} ? $JOIN_ALIAS_FOR_LOOKUP_TYPE{$type}->($self) : die "We don't know how to join on $type"; my ($ObjectCFs, $CFs); if ( $cfid ) { $ObjectCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join( TYPE => 'LEFT', ALIAS1 => $ObjectAlias, FIELD1 => 'id', TABLE2 => 'ObjectCustomFieldValues', FIELD2 => 'ObjectId', ); $self->SUPER::Limit( LEFTJOIN => $ObjectCFs, FIELD => 'CustomField', VALUE => $cfid, ENTRYAGGREGATOR => 'AND' ); } else { my $ocfalias = $self->Join( TYPE => 'LEFT', FIELD1 => 'Type', TABLE2 => 'ObjectCustomFields', FIELD2 => 'ObjectId', ); $self->SUPER::Limit( LEFTJOIN => $ocfalias, ENTRYAGGREGATOR => 'OR', FIELD => 'ObjectId', VALUE => '0', ); $CFs = $self->{_sql_cf_alias}{$cfkey} = $self->Join( TYPE => 'LEFT', ALIAS1 => $ocfalias, FIELD1 => 'CustomField', TABLE2 => 'CustomFields', FIELD2 => 'id', ); $self->SUPER::Limit( LEFTJOIN => $CFs, ENTRYAGGREGATOR => 'AND', FIELD => 'LookupType', VALUE => $type, ); $self->SUPER::Limit( LEFTJOIN => $CFs, ENTRYAGGREGATOR => 'AND', FIELD => 'Name', VALUE => $field, ); $ObjectCFs = $self->{_sql_object_cfv_alias}{$cfkey} = $self->Join( TYPE => 'LEFT', ALIAS1 => $CFs, FIELD1 => 'id', TABLE2 => 'ObjectCustomFieldValues', FIELD2 => 'CustomField', ); $self->SUPER::Limit( LEFTJOIN => $ObjectCFs, FIELD => 'ObjectId', VALUE => "$ObjectAlias.id", QUOTEVALUE => 0, ENTRYAGGREGATOR => 'AND', ); } $self->SUPER::Limit( LEFTJOIN => $ObjectCFs, FIELD => 'ObjectType', VALUE => RT::CustomField->ObjectTypeFromLookupType($type), ENTRYAGGREGATOR => 'AND' ); $self->SUPER::Limit( LEFTJOIN => $ObjectCFs, FIELD => 'Disabled', OPERATOR => '=', VALUE => '0', ENTRYAGGREGATOR => 'AND' ); return ($ObjectCFs, $CFs); } =head2 _CustomFieldLimit Limit based on CustomFields Meta Data: none =cut use Regexp::Common qw(RE_net_IPv4); use Regexp::Common::net::CIDR; sub _CustomFieldLimit { my ( $self, $_field, $op, $value, %rest ) = @_; my $meta = $FIELD_METADATA{ $_field }; my $class = $meta->[1] || 'Asset'; my $type = "RTx::AssetTracker::$class"->CustomFieldLookupType; my $field = $rest{'SUBKEY'} || die "No field specified"; # For our sanity, we can only limit on one asset type at a time my ($object, $cfid, $cf, $column); ($object, $field, $cf, $column) = $self->_CustomFieldDecipher( $field, $type ); $cfid = $cf ? $cf->id : 0 ; # If we're trying to find custom fields that don't match something, we # want assets where the custom field has no value at all. Note that # we explicitly don't include the "IS NULL" case, since we would # otherwise end up with a redundant clause. my ($negative_op, $null_op, $inv_op, $range_op) = $self->ClassifySQLOperation( $op ); my $fix_op = sub { return @_ unless RT->Config->Get('DatabaseType') eq 'Oracle'; my %args = @_; return %args unless $args{'FIELD'} eq 'LargeContent'; my $op = $args{'OPERATOR'}; if ( $op eq '=' ) { $args{'OPERATOR'} = 'MATCHES'; } elsif ( $op eq '!=' ) { $args{'OPERATOR'} = 'NOT MATCHES'; } elsif ( $op =~ /^[<>]=?$/ ) { $args{'FUNCTION'} = "TO_CHAR( $args{'ALIAS'}.LargeContent )"; } return %args; }; if ( $cf && $cf->Type eq 'IPAddress' ) { my $parsed = RT::ObjectCustomFieldValue->ParseIP($value); if ($parsed) { $value = $parsed; } else { $RT::Logger->warn("$value is not a valid IPAddress"); } } if ( $cf && $cf->Type eq 'IPAddressRange' ) { if ( $value =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/o ) { # convert incomplete 192.168/24 to 192.168.0.0/24 format $value = join( '.', map $_ || 0, ( split /\./, $1 )[ 0 .. 3 ] ) . "/$2" || $value; } my ( $start_ip, $end_ip ) = RT::ObjectCustomFieldValue->ParseIPRange($value); if ( $start_ip && $end_ip ) { if ( $op =~ /^([<>])=?$/ ) { my $is_less = $1 eq '<' ? 1 : 0; if ( $is_less ) { $value = $start_ip; } else { $value = $end_ip; } } else { $value = join '-', $start_ip, $end_ip; } } else { $RT::Logger->warn("$value is not a valid IPAddressRange"); } } if ( $cf && $cf->Type =~ /^Date(?:Time)?$/ ) { my $date = RT::Date->new( $self->CurrentUser ); $date->Set( Format => 'unknown', Value => $value ); if ( $date->Unix ) { if ( $cf->Type eq 'Date' || $value =~ /^\s*(?:today|tomorrow|yesterday)\s*$/i || ( $value !~ /midnight|\d+:\d+:\d+/i && $date->Time( Timezone => 'user' ) eq '00:00:00' ) ) { $value = $date->Date( Timezone => 'user' ); } else { $value = $date->DateTime; } } else { $RT::Logger->warn("$value is not a valid date string"); } } my $single_value = !$cf || !$cfid || $cf->SingleValue; my $cfkey = $cfid ? $cfid : "$type-$object.$field"; if ( $null_op && !$column ) { # IS[ NOT] NULL without column is the same as has[ no] any CF value, # we can reuse our default joins for this operation # with column specified we have different situation my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type ); $self->_OpenParen; $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => 'id', OPERATOR => $op, VALUE => $value, %rest ); $self->_SQLLimit( ALIAS => $CFs, FIELD => 'Name', OPERATOR => 'IS NOT', VALUE => 'NULL', QUOTEVALUE => 0, ENTRYAGGREGATOR => 'AND', ) if $CFs; $self->_CloseParen; } elsif ( $op !~ /^[<>]=?$/ && ( $cf && $cf->Type eq 'IPAddressRange')) { my ($start_ip, $end_ip) = split /-/, $value; $self->_OpenParen; if ( $op !~ /NOT|!=|<>/i ) { # positive equation $self->_CustomFieldLimit( $_field, '<=', $end_ip, %rest, SUBKEY => $rest{'SUBKEY'}. '.Content', ); $self->_CustomFieldLimit( $_field, '>=', $start_ip, %rest, SUBKEY => $rest{'SUBKEY'}. '.LargeContent', ENTRYAGGREGATOR => 'AND', ); # as well limit borders so DB optimizers can use better # estimations and scan less rows # have to disable this tweak because of ipv6 # $self->_CustomFieldLimit( # $_field, '>=', '000.000.000.000', %rest, # SUBKEY => $rest{'SUBKEY'}. '.Content', # ENTRYAGGREGATOR => 'AND', # ); # $self->_CustomFieldLimit( # $_field, '<=', '255.255.255.255', %rest, # SUBKEY => $rest{'SUBKEY'}. '.LargeContent', # ENTRYAGGREGATOR => 'AND', # ); } else { # negative equation $self->_CustomFieldLimit($_field, '>', $end_ip, %rest); $self->_CustomFieldLimit( $_field, '<', $start_ip, %rest, SUBKEY => $rest{'SUBKEY'}. '.LargeContent', ENTRYAGGREGATOR => 'OR', ); # TODO: as well limit borders so DB optimizers can use better # estimations and scan less rows, but it's harder to do # as we have OR aggregator } $self->_CloseParen; } elsif ( !$negative_op || $single_value ) { $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if !$single_value && !$range_op; my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type ); $self->_OpenParen; $self->_OpenParen; $self->_OpenParen; # if column is defined then deal only with it # otherwise search in Content and in LargeContent if ( $column ) { $self->_SQLLimit( $fix_op->( ALIAS => $ObjectCFs, FIELD => $column, OPERATOR => $op, VALUE => $value, CASESENSITIVE => 0, %rest ) ); $self->_CloseParen; $self->_CloseParen; $self->_CloseParen; } else { # need special treatment for Date if ( $cf and $cf->Type eq 'DateTime' and $op eq '=' && $value !~ /:/ ) { # no time specified, that means we want everything on a # particular day. in the database, we need to check for > # and < the edges of that day. my $date = RT::Date->new( $self->CurrentUser ); $date->Set( Format => 'unknown', Value => $value ); my $daystart = $date->ISO; $date->AddDay; my $dayend = $date->ISO; $self->_OpenParen; $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => ">=", VALUE => $daystart, %rest, ); $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => "<", VALUE => $dayend, %rest, ENTRYAGGREGATOR => 'AND', ); $self->_CloseParen; } elsif ( $op eq '=' || $op eq '!=' || $op eq '<>' ) { if ( length( Encode::encode_utf8($value) ) < 256 ) { $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => $op, VALUE => $value, CASESENSITIVE => 0, %rest ); } else { $self->_OpenParen; $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => '=', VALUE => '', ENTRYAGGREGATOR => 'OR' ); $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => 'IS', VALUE => 'NULL', ENTRYAGGREGATOR => 'OR' ); $self->_CloseParen; $self->_SQLLimit( $fix_op->( ALIAS => $ObjectCFs, FIELD => 'LargeContent', OPERATOR => $op, VALUE => $value, ENTRYAGGREGATOR => 'AND', CASESENSITIVE => 0, ) ); } } else { $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => $op, VALUE => $value, CASESENSITIVE => 0, %rest ); $self->_OpenParen; $self->_OpenParen; $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => '=', VALUE => '', ENTRYAGGREGATOR => 'OR' ); $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => 'IS', VALUE => 'NULL', ENTRYAGGREGATOR => 'OR' ); $self->_CloseParen; $self->_SQLLimit( $fix_op->( ALIAS => $ObjectCFs, FIELD => 'LargeContent', OPERATOR => $op, VALUE => $value, ENTRYAGGREGATOR => 'AND', CASESENSITIVE => 0, ) ); $self->_CloseParen; } $self->_CloseParen; # XXX: if we join via CustomFields table then # because of order of left joins we get NULLs in # CF table and then get nulls for those records # in OCFVs table what result in wrong results # as decifer method now tries to load a CF then # we fall into this situation only when there # are more than one CF with the name in the DB. # the same thing applies to order by call. # TODO: reorder joins T <- OCFVs <- CFs <- OCFs if # we want treat IS NULL as (not applies or has # no value) $self->_SQLLimit( ALIAS => $CFs, FIELD => 'Name', OPERATOR => 'IS NOT', VALUE => 'NULL', QUOTEVALUE => 0, ENTRYAGGREGATOR => 'AND', ) if $CFs; $self->_CloseParen; if ($negative_op) { $self->_SQLLimit( ALIAS => $ObjectCFs, FIELD => $column || 'Content', OPERATOR => 'IS', VALUE => 'NULL', QUOTEVALUE => 0, ENTRYAGGREGATOR => 'OR', ); } $self->_CloseParen; } } else { $cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++; my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field, $type ); # reverse operation $op =~ s/!|NOT\s+//i; # if column is defined then deal only with it # otherwise search in Content and in LargeContent if ( $column ) { $self->SUPER::Limit( $fix_op->( LEFTJOIN => $ObjectCFs, ALIAS => $ObjectCFs, FIELD => $column, OPERATOR => $op, VALUE => $value, CASESENSITIVE => 0, ) ); } else { $self->SUPER::Limit( LEFTJOIN => $ObjectCFs, ALIAS => $ObjectCFs, FIELD => 'Content', OPERATOR => $op, VALUE => $value, CASESENSITIVE => 0, ); } $self->_SQLLimit( %rest, ALIAS => $ObjectCFs, FIELD => 'id', OPERATOR => 'IS', VALUE => 'NULL', QUOTEVALUE => 0, ); } } # End Helper Functions # End of SQL Stuff ------------------------------------------------- =head2 OrderByCols ARRAY A modified version of the OrderBy method which automatically joins where C is set to the name of a watcher type. =cut sub OrderByCols { my $self = shift; my @args = @_; my $clause; my @res = (); my $order = 0; foreach my $row (@args) { if ( $row->{ALIAS} ) { push @res, $row; next; } if ( $row->{FIELD} !~ /\./ ) { my $meta = $self->FIELDS->{ $row->{FIELD} }; unless ( $meta ) { push @res, $row; next; } if ( $meta->[0] eq 'ENUM' && ($meta->[1]||'') eq 'Type' ) { my $alias = $self->Join( TYPE => 'LEFT', ALIAS1 => 'main', FIELD1 => $row->{'FIELD'}, TABLE2 => 'AT_Types', FIELD2 => 'id', ); push @res, { %$row, ALIAS => $alias, FIELD => "Name" }; } elsif ( ( $meta->[0] eq 'ENUM' && ($meta->[1]||'') eq 'User' ) || ( $meta->[0] eq 'WATCHERFIELD' && ($meta->[1]||'') eq 'Owner' ) ) { my $alias = $self->Join( TYPE => 'LEFT', ALIAS1 => 'main', FIELD1 => $row->{'FIELD'}, TABLE2 => 'Users', FIELD2 => 'id', ); push @res, { %$row, ALIAS => $alias, FIELD => "Name" }; } else { push @res, $row; } next; } my ( $field, $subkey ) = split /\./, $row->{FIELD}, 2; my $meta = $self->FIELDS->{$field}; if ( defined $meta->[0] && $meta->[0] eq 'WATCHERFIELD' ) { # cache alias as we want to use one alias per watcher type for sorting my $users = $self->{_sql_u_watchers_alias_for_sort}{ $meta->[1] }; unless ( $users ) { $self->{_sql_u_watchers_alias_for_sort}{ $meta->[1] } = $users = ( $self->_WatcherJoin( $meta->[1] ) )[2]; } push @res, { %$row, ALIAS => $users, FIELD => $subkey }; } elsif ( defined $meta->[0] && $meta->[0] eq 'CUSTOMFIELD' ) { my ($object, $field, $cf_obj, $column) = $self->_CustomFieldDecipher( $subkey ); my $cfkey = $cf_obj ? $cf_obj->id : "$object.$field"; $cfkey .= ".ordering" if !$cf_obj || ($cf_obj->MaxValues||0) != 1; my ($ObjectCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, ($cf_obj ?$cf_obj->id :0) , $field ); # this is described in _CustomFieldLimit $self->_SQLLimit( ALIAS => $CFs, FIELD => 'Name', OPERATOR => 'IS NOT', VALUE => 'NULL', QUOTEVALUE => 1, ENTRYAGGREGATOR => 'AND', ) if $CFs; unless ($cf_obj) { # For those cases where we are doing a join against the # CF name, and don't have a CFid, use Unique to make sure # we don't show duplicate assets. NOTE: I'm pretty sure # this will stay mixed in for the life of the # class/package, and not just for the life of the object. # Potential performance issue. require DBIx::SearchBuilder::Unique; DBIx::SearchBuilder::Unique->import; } my $CFvs = $self->Join( TYPE => 'LEFT', ALIAS1 => $ObjectCFs, FIELD1 => 'CustomField', TABLE2 => 'CustomFieldValues', FIELD2 => 'CustomField', ); $self->SUPER::Limit( LEFTJOIN => $CFvs, FIELD => 'Name', QUOTEVALUE => 0, VALUE => $ObjectCFs . ".Content", ENTRYAGGREGATOR => 'AND' ); push @res, { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' }; push @res, { %$row, ALIAS => $ObjectCFs, FIELD => 'Content' }; } else { push @res, $row; } } return $self->SUPER::OrderByCols(@res); } =head2 Limit Takes a paramhash with the fields FIELD, OPERATOR, VALUE and DESCRIPTION Generally best called from LimitFoo methods =cut sub Limit { my $self = shift; my %args = ( FIELD => undef, OPERATOR => '=', VALUE => undef, DESCRIPTION => undef, @_ ); $args{'DESCRIPTION'} = $self->loc( "[_1] [_2] [_3]", $args{'FIELD'}, $args{'OPERATOR'}, $args{'VALUE'} ) if ( !defined $args{'DESCRIPTION'} ); my $index = $self->_NextIndex; # make the AssetRestrictions hash the equivalent of whatever we just passed in; %{ $self->{'AssetRestrictions'}{$index} } = %args; $self->{'RecalcAssetLimits'} = 1; return ($index); } =head2 LimitType LimitType takes a paramhash with the fields OPERATOR and VALUE. OPERATOR is one of = or !=. (It defaults to =). VALUE is an asset type id or Name. =cut sub LimitType { my $self = shift; my %args = ( VALUE => undef, OPERATOR => '=', @_ ); #TODO VALUE should also take queue objects if ( defined $args{'VALUE'} && $args{'VALUE'} !~ /^\d+$/ ) { my $type = RTx::AssetTracker::Type->new( $self->CurrentUser ); $type->Load( $args{'VALUE'} ); $args{'VALUE'} = $type->Id; } # What if they pass in an Id? Check for isNum() and convert to # string. #TODO check for a valid queue here $self->Limit( FIELD => 'Type', VALUE => $args{VALUE}, OPERATOR => $args{'OPERATOR'}, DESCRIPTION => join( ' ', $self->loc('Type'), $args{'OPERATOR'}, $args{'VALUE'}, ), ); } =head2 LimitStatus Takes a paramhash with the fields OPERATOR and VALUE. OPERATOR is one of = or !=. VALUE is a status. RT adds Status != 'deleted' until object has allow_deleted_search internal property set. $assets->{'allow_deleted_search'} = 1; $assets->LimitStatus( VALUE => 'deleted' ); =cut sub LimitStatus { my $self = shift; my %args = ( OPERATOR => '=', @_ ); $self->Limit( FIELD => 'Status', VALUE => $args{'VALUE'}, OPERATOR => $args{'OPERATOR'}, DESCRIPTION => join( ' ', $self->loc('Status'), $args{'OPERATOR'}, $self->loc( $args{'VALUE'} ) ), ); } =head2 IgnoreType Stub. AT doesn't use this flag because assets don't have an equivalent to "ticket types") =cut sub IgnoreType { my $self = shift; # Instead of faking a Limit that later gets ignored, fake up the # fact that we're already looking at type, so that the check in # Tickets_SQL/FromSQL goes down the right branch # $self->LimitType(VALUE => '__any'); #$self->{looking_at_type} = 1; } =head2 LimitDescription Takes a paramhash with the fields OPERATOR and VALUE. OPERATOR is one of = or !=. VALUE is a string to search for in the description of the asset. =cut sub LimitDescription { my $self = shift; my %args = (@_); $self->Limit( FIELD => 'Description', VALUE => $args{'VALUE'}, OPERATOR => $args{'OPERATOR'}, DESCRIPTION => join( ' ', $self->loc('Description'), $args{'OPERATOR'}, $args{'VALUE'}, ), ); } =head2 LimitId Takes a paramhash with the fields OPERATOR and VALUE. OPERATOR is one of =, >, < or !=. VALUE is an asset Id to search for =cut sub LimitId { my $self = shift; my %args = ( OPERATOR => '=', @_ ); $self->Limit( FIELD => 'id', VALUE => $args{'VALUE'}, OPERATOR => $args{'OPERATOR'}, DESCRIPTION => join( ' ', $self->loc('Id'), $args{'OPERATOR'}, $args{'VALUE'}, ), ); } =head2 LimitURI Takes a paramhash with the fields OPERATOR and VALUE. OPERATOR is one of =, >, < or !=. VALUE is an asset URI to search for =cut sub LimitURI { my $self = shift; my %args = ( OPERATOR => '=', @_ ); $self->Limit( FIELD => 'URI', VALUE => $args{'VALUE'}, OPERATOR => $args{'OPERATOR'}, DESCRIPTION => join( ' ', $self->loc('Id'), $args{'OPERATOR'}, $args{'VALUE'}, ), ); } =head2 LimitWatcher Takes a paramhash with the fields OPERATOR, TYPE and VALUE. OPERATOR is one of =, LIKE, NOT LIKE or !=. VALUE is a value to match the ticket\'s watcher email addresses against TYPE is the sort of watchers you want to match against. Leave it undef if you want to search all of them =cut sub LimitWatcher { my $self = shift; my %args = ( OPERATOR => '=', VALUE => undef, TYPE => undef, @_ ); #build us up a description my ( $watcher_type, $desc ); if ( $args{'TYPE'} ) { $watcher_type = $args{'TYPE'}; } else { $watcher_type = "Watcher"; } $self->Limit( FIELD => $watcher_type, VALUE => $args{'VALUE'}, OPERATOR => $args{'OPERATOR'}, TYPE => $args{'TYPE'}, DESCRIPTION => join( ' ', $self->loc($watcher_type), $args{'OPERATOR'}, $args{'VALUE'}, ), ); } =head2 LimitLinkedTo LimitLinkedTo takes a paramhash with two fields: TYPE and TARGET TYPE limits the sort of link we want to search on TYPE = one of Asset->RoleGroupTypes TARGET is the id or URI of the TARGET of the link =cut sub LimitLinkedTo { my $self = shift; my %args = ( TARGET => undef, TYPE => undef, OPERATOR => '=', @_ ); $self->Limit( FIELD => 'LinkedTo', BASE => undef, TARGET => $args{'TARGET'}, TYPE => $args{'TYPE'}, DESCRIPTION => $self->loc( "Assets [_1] by [_2]", $self->loc( $args{'TYPE'} ), $args{'TARGET'} ), OPERATOR => $args{'OPERATOR'}, ); } =head2 LimitLinkedFrom LimitLinkedFrom takes a paramhash with two fields: TYPE and BASE TYPE limits the sort of link we want to search on BASE is the id or URI of the BASE of the link =cut sub LimitLinkedFrom { my $self = shift; my %args = ( BASE => undef, TYPE => undef, OPERATOR => '=', @_ ); $self->Limit( FIELD => 'LinkedTo', TARGET => undef, BASE => $args{'BASE'}, TYPE => $args{'TYPE'}, DESCRIPTION => $self->loc( "Assets [_1] [_2]", $self->loc( $args{'TYPE'} ), $args{'BASE'} ), OPERATOR => $args{'OPERATOR'}, ); } =head2 LimitDate (FIELD => 'DateField', OPERATOR => $oper, VALUE => $ISODate) Takes a paramhash with the fields FIELD OPERATOR and VALUE. OPERATOR is one of > or < VALUE is a date and time in ISO format in GMT FIELD is one of Created, LastUpdated There are also helper functions of the form LimitFIELD that eliminate the need to pass in a FIELD argument. =cut sub LimitDate { my $self = shift; my %args = ( FIELD => undef, VALUE => undef, OPERATOR => undef, @_ ); #Set the description if we didn't get handed it above unless ( $args{'DESCRIPTION'} ) { $args{'DESCRIPTION'} = $args{'FIELD'} . " " . $args{'OPERATOR'} . " " . $args{'VALUE'} . " GMT"; } $self->Limit(%args); } sub LimitCreated { my $self = shift; $self->LimitDate( FIELD => 'Created', @_ ); } sub LimitLastUpdated { my $self = shift; $self->LimitDate( FIELD => 'LastUpdated', @_ ); } # =head2 LimitTransactionDate (OPERATOR => $oper, VALUE => $ISODate) Takes a paramhash with the fields FIELD OPERATOR and VALUE. OPERATOR is one of > or < VALUE is a date and time in ISO format in GMT =cut sub LimitTransactionDate { my $self = shift; my %args = ( FIELD => 'TransactionDate', VALUE => undef, OPERATOR => undef, @_ ); # <20021217042756.GK28744@pallas.fsck.com> # "Kill It" - Jesse. #Set the description if we didn't get handed it above unless ( $args{'DESCRIPTION'} ) { $args{'DESCRIPTION'} = $args{'FIELD'} . " " . $args{'OPERATOR'} . " " . $args{'VALUE'} . " GMT"; } $self->Limit(%args); } =head2 LimitCustomField Takes a paramhash of key/value pairs with the following keys: =over 4 =item CUSTOMFIELD - CustomField name or id. If a name is passed, an additional parameter ASSETTYPE may also be passed to distinguish the custom field. =item OPERATOR - The usual Limit operators =item VALUE - The value to compare against =back =cut sub LimitCustomField { my $self = shift; my %args = ( VALUE => undef, CUSTOMFIELD => undef, OPERATOR => '=', DESCRIPTION => undef, FIELD => 'CustomFieldValue', QUOTEVALUE => 1, @_ ); my $CF = RT::CustomField->new( $self->CurrentUser ); if ( $args{CUSTOMFIELD} =~ /^\d+$/ ) { $CF->Load( $args{CUSTOMFIELD} ); } else { $CF->LoadByNameAndAssetType( Name => $args{CUSTOMFIELD}, AssetType => $args{ASSETTYPE} ); $args{CUSTOMFIELD} = $CF->Id; } #If we are looking to compare with a null value. if ( $args{'OPERATOR'} =~ /^is$/i ) { $args{'DESCRIPTION'} ||= $self->loc( "Custom field [_1] has no value.", $CF->Name ); } elsif ( $args{'OPERATOR'} =~ /^is not$/i ) { $args{'DESCRIPTION'} ||= $self->loc( "Custom field [_1] has a value.", $CF->Name ); } # if we're not looking to compare with a null value else { $args{'DESCRIPTION'} ||= $self->loc( "Custom field [_1] [_2] [_3]", $CF->Name, $args{OPERATOR}, $args{VALUE} ); } if ( defined $args{'ASSETTYPE'} && $args{'ASSETTYPE'} =~ /\D/ ) { my $TypeObj = RTx::AssetTracker::Type->new( $self->CurrentUser ); $TypeObj->Load( $args{'ASSETTYPE'} ); $args{'ASSETTYPE'} = $TypeObj->Id; } delete $args{'ASSETTYPE'} unless defined $args{'ASSETTYPE'} && length $args{'ASSETTYPE'}; my @rest; @rest = ( ENTRYAGGREGATOR => 'AND' ) if ( $CF->Type eq 'SelectMultiple' ); $self->Limit( VALUE => $args{VALUE}, FIELD => "CF" .(defined $args{'ASSETTYPE'}? ".$args{'ASSETTYPE'}" : '' ) .".{" . $CF->Name . "}", OPERATOR => $args{OPERATOR}, CUSTOMFIELD => 1, @rest, ); $self->{'RecalcAssetLimits'} = 1; } =head2 _NextIndex Keep track of the counter for the array of restrictions =cut sub _NextIndex { my $self = shift; return ( $self->{'restriction_index'}++ ); } sub _Init { my $self = shift; $self->{'table'} = "AT_Assets"; $self->{'RecalcAssetLimits'} = 1; $self->{'restriction_index'} = 1; $self->{'primary_key'} = "id"; delete $self->{'items_array'}; delete $self->{'item_map'}; delete $self->{'columns_to_display'}; $self->SUPER::_Init(@_); $self->_InitSQL; } sub Count { my $self = shift; $self->_ProcessRestrictions() if ( $self->{'RecalcAssetLimits'} == 1 ); return ( $self->SUPER::Count() ); } sub CountAll { my $self = shift; $self->_ProcessRestrictions() if ( $self->{'RecalcAssetLimits'} == 1 ); return ( $self->SUPER::CountAll() ); } =head2 ItemsArrayRef Returns a reference to the set of all items found in this search =cut sub ItemsArrayRef { my $self = shift; return $self->{'items_array'} if $self->{'items_array'}; my $placeholder = $self->_ItemsCounter; $self->GotoFirstItem(); while ( my $item = $self->Next ) { push( @{ $self->{'items_array'} }, $item ); } $self->GotoItem($placeholder); $self->{'items_array'} = $self->ItemsOrderBy( $self->{'items_array'} ); return $self->{'items_array'}; } sub ItemsArrayRefWindow { my $self = shift; my $window = shift; my @old = ($self->_ItemsCounter, $self->RowsPerPage, $self->FirstRow+1); $self->RowsPerPage( $window ); $self->FirstRow(1); $self->GotoFirstItem; my @res; while ( my $item = $self->Next ) { push @res, $item; } $self->RowsPerPage( $old[1] ); $self->FirstRow( $old[2] ); $self->GotoItem( $old[0] ); return \@res; } sub Next { my $self = shift; $self->_ProcessRestrictions() if ( $self->{'RecalcAssetLimits'} == 1 ); my $Asset = $self->SUPER::Next; return $Asset unless $Asset; if ( $Asset->__Value('Status') eq 'deleted' && !$self->{'allow_deleted_search'} ) { return $self->Next; } elsif ( RT->Config->Get('UseSQLForACLChecks') ) { # if we found an asset with this option enabled then # all assets we found are ACLed, cache this fact my $key = join ";:;", $self->CurrentUser->id, 'ShowAsset', 'RTx::AssetTracker::Asset-'. $Asset->id; $RT::Principal::_ACL_CACHE->set( $key => 1 ); return $Asset; } elsif ( $Asset->CurrentUserHasRight('ShowAsset') ) { # has rights return $Asset; } else { # If the user doesn't have the right to show this ticket return $self->Next; } } sub _DoSearch { my $self = shift; $self->CurrentUserCanSee if RT->Config->Get('UseSQLForACLChecks'); return $self->SUPER::_DoSearch( @_ ); } sub _DoCount { my $self = shift; $self->CurrentUserCanSee if RT->Config->Get('UseSQLForACLChecks'); return $self->SUPER::_DoCount( @_ ); } sub _RolesCanSee { my $self = shift; my $cache_key = 'RolesHasRight;:;ShowAsset'; if ( my $cached = $RT::Principal::_ACL_CACHE->fetch( $cache_key ) ) { return %$cached; } my $ACL = RT::ACL->new( RT->SystemUser ); $ACL->Limit( FIELD => 'RightName', VALUE => 'ShowAsset' ); $ACL->Limit( FIELD => 'PrincipalType', OPERATOR => '!=', VALUE => 'Group' ); my $principal_alias = $ACL->Join( ALIAS1 => 'main', FIELD1 => 'PrincipalId', TABLE2 => 'Principals', FIELD2 => 'id', ); $ACL->Limit( ALIAS => $principal_alias, FIELD => 'Disabled', VALUE => 0 ); my %res = (); foreach my $ACE ( @{ $ACL->ItemsArrayRef } ) { my $role = $ACE->__Value('PrincipalType'); my $type = $ACE->__Value('ObjectType'); if ( $type eq 'RT::System' ) { $res{ $role } = 1; } elsif ( $type eq 'RTx::AssetTracker::Type' ) { next if $res{ $role } && !ref $res{ $role }; push @{ $res{ $role } ||= [] }, $ACE->__Value('ObjectId'); } else { $RT::Logger->error('ShowAsset right is granted on unsupported object'); } } $RT::Principal::_ACL_CACHE->set( $cache_key => \%res ); return %res; } sub _DirectlyCanSeeIn { my $self = shift; my $id = $self->CurrentUser->id; my $cache_key = 'User-'. $id .';:;ShowAsset;:;DirectlyCanSeeIn'; if ( my $cached = $RT::Principal::_ACL_CACHE->fetch( $cache_key ) ) { return @$cached; } my $ACL = RT::ACL->new( RT->SystemUser ); $ACL->Limit( FIELD => 'RightName', VALUE => 'ShowAsset' ); my $principal_alias = $ACL->Join( ALIAS1 => 'main', FIELD1 => 'PrincipalId', TABLE2 => 'Principals', FIELD2 => 'id', ); $ACL->Limit( ALIAS => $principal_alias, FIELD => 'Disabled', VALUE => 0 ); my $cgm_alias = $ACL->Join( ALIAS1 => 'main', FIELD1 => 'PrincipalId', TABLE2 => 'CachedGroupMembers', FIELD2 => 'GroupId', ); $ACL->Limit( ALIAS => $cgm_alias, FIELD => 'MemberId', VALUE => $id ); $ACL->Limit( ALIAS => $cgm_alias, FIELD => 'Disabled', VALUE => 0 ); my @res = (); foreach my $ACE ( @{ $ACL->ItemsArrayRef } ) { my $type = $ACE->__Value('ObjectType'); if ( $type eq 'RT::System' ) { # If user is direct member of a group that has the right # on the system then he can see any ticket $RT::Principal::_ACL_CACHE->set( $cache_key => [-1] ); return (-1); } elsif ( $type eq 'RTx::AssetTracker::Type' ) { push @res, $ACE->__Value('ObjectId'); } else { $RT::Logger->error('ShowAsset right is granted on unsupported object'); } } $RT::Principal::_ACL_CACHE->set( $cache_key => \@res ); return @res; } sub CurrentUserCanSee { my $self = shift; return if $self->{'_sql_current_user_can_see_applied'}; return $self->{'_sql_current_user_can_see_applied'} = 1 if $self->CurrentUser->UserObj->HasRight( Right => 'SuperUser', Object => $RT::System ); my $id = $self->CurrentUser->id; # directly can see in all asset types then we have nothing to do my @direct_types = $self->_DirectlyCanSeeIn; return $self->{'_sql_current_user_can_see_applied'} = 1 if @direct_types && $direct_types[0] == -1; my %roles = $self->_RolesCanSee; { my %skip = map { $_ => 1 } @direct_types; foreach my $role ( keys %roles ) { next unless ref $roles{ $role }; my @types = grep !$skip{$_}, @{ $roles{ $role } }; if ( @types ) { $roles{ $role } = \@types; } else { delete $roles{ $role }; } } } # there is no global watchers, only asset types and assets, if at # some point we will add global roles then it's gonna blow # the idea here is that if the right is set globally for a role # and user plays this role for a type directly not an asset # then we have to check in advance if ( my @tmp = grep !ref $roles{ $_ }, keys %roles ) { my $groups = RT::Groups->new( RT->SystemUser ); $groups->Limit( FIELD => 'Domain', VALUE => 'RTx::AssetTracker::Type-Role' ); foreach ( @tmp ) { $groups->Limit( FIELD => 'Type', VALUE => $_ ); } my $principal_alias = $groups->Join( ALIAS1 => 'main', FIELD1 => 'id', TABLE2 => 'Principals', FIELD2 => 'id', ); $groups->Limit( ALIAS => $principal_alias, FIELD => 'Disabled', VALUE => 0 ); my $cgm_alias = $groups->Join( ALIAS1 => 'main', FIELD1 => 'id', TABLE2 => 'CachedGroupMembers', FIELD2 => 'GroupId', ); $groups->Limit( ALIAS => $cgm_alias, FIELD => 'MemberId', VALUE => $id ); $groups->Limit( ALIAS => $cgm_alias, FIELD => 'Disabled', VALUE => 0 ); while ( my $group = $groups->Next ) { push @direct_types, $group->Instance; } } unless ( @direct_types || keys %roles ) { $self->SUPER::Limit( SUBCLAUSE => 'ACL', ALIAS => 'main', FIELD => 'id', VALUE => 0, ENTRYAGGREGATOR => 'AND', ); return $self->{'_sql_current_user_can_see_applied'} = 1; } { my $join_roles = keys %roles; my ($role_group_alias, $cgm_alias); if ( $join_roles ) { $role_group_alias = $self->_RoleGroupsJoin( New => 1 ); $cgm_alias = $self->_GroupMembersJoin( GroupsAlias => $role_group_alias ); $self->SUPER::Limit( LEFTJOIN => $cgm_alias, FIELD => 'MemberId', OPERATOR => '=', VALUE => $id, ); } my $limit_types = sub { my $ea = shift; my @types = @_; return unless @types; if ( @types == 1 ) { $self->SUPER::Limit( SUBCLAUSE => 'ACL', ALIAS => 'main', FIELD => 'Type', VALUE => $_[0], ENTRYAGGREGATOR => $ea, ); } else { $self->SUPER::_OpenParen('ACL'); foreach my $q ( @types ) { $self->SUPER::Limit( SUBCLAUSE => 'ACL', ALIAS => 'main', FIELD => 'Type', VALUE => $q, ENTRYAGGREGATOR => $ea, ); $ea = 'OR'; } $self->SUPER::_CloseParen('ACL'); } return 1; }; $self->SUPER::_OpenParen('ACL'); my $ea = 'AND'; $ea = 'OR' if $limit_types->( $ea, @direct_types ); while ( my ($role, $types) = each %roles ) { $self->SUPER::_OpenParen('ACL'); $self->SUPER::Limit( SUBCLAUSE => 'ACL', ALIAS => $cgm_alias, FIELD => 'MemberId', OPERATOR => 'IS NOT', VALUE => 'NULL', QUOTEVALUE => 0, ENTRYAGGREGATOR => $ea, ); $self->SUPER::Limit( SUBCLAUSE => 'ACL', ALIAS => $role_group_alias, FIELD => 'Type', VALUE => $role, ENTRYAGGREGATOR => 'AND', ); $limit_types->( 'AND', @$types ) if ref $types; $ea = 'OR' if $ea eq 'AND'; $self->SUPER::_CloseParen('ACL'); } $self->SUPER::_CloseParen('ACL'); } return $self->{'_sql_current_user_can_see_applied'} = 1; } =head2 LoadRestrictions LoadRestrictions takes a string which can fully populate the AssetRestrictons hash. TODO It is not yet implemented =cut =head2 DescribeRestrictions takes nothing. Returns a hash keyed by restriction id. Each element of the hash is currently a one element hash that contains DESCRIPTION which is a description of the purpose of that AssetRestriction =cut sub DescribeRestrictions { my $self = shift; my %listing; foreach my $row ( keys %{ $self->{'AssetRestrictions'} } ) { $listing{$row} = $self->{'AssetRestrictions'}{$row}{'DESCRIPTION'}; } return (%listing); } =head2 RestrictionValues FIELD Takes a restriction field and returns a list of values this field is restricted to. =cut sub RestrictionValues { my $self = shift; my $field = shift; map $self->{'AssetRestrictions' }{$_}{'VALUE'}, grep { $self->{'AssetRestrictions'}{$_}{'FIELD'} eq $field && $self->{'AssetRestrictions'}{$_}{'OPERATOR'} eq "=" } keys %{ $self->{'AssetRestrictions'} }; } =head2 ClearRestrictions Removes all restrictions irretrievably =cut sub ClearRestrictions { my $self = shift; delete $self->{'AssetRestrictions'}; $self->{'RecalcAssetLimits'} = 1; } =head2 DeleteRestriction Takes the row Id of a restriction (From DescribeRestrictions' output, for example. Removes that restriction from the session's limits. =cut sub DeleteRestriction { my $self = shift; my $row = shift; delete $self->{'AssetRestrictions'}{$row}; $self->{'RecalcAssetLimits'} = 1; #make the underlying easysearch object forget all its preconceptions } # Convert a set of oldstyle SB Restrictions to Clauses for RQL sub _RestrictionsToClauses { my $self = shift; my %clause; foreach my $row ( keys %{ $self->{'AssetRestrictions'} } ) { my $restriction = $self->{'AssetRestrictions'}{$row}; # We need to reimplement the subclause aggregation that SearchBuilder does. # Default Subclause is ALIAS.FIELD, and default ALIAS is 'main', # Then SB AND's the different Subclauses together. # So, we want to group things into Subclauses, convert them to # SQL, and then join them with the appropriate DefaultEA. # Then join each subclause group with AND. my $field = $restriction->{'FIELD'}; my $realfield = $field; # CustomFields fake up a fieldname, so # we need to figure that out # One special case # Rewrite LinkedTo meta field to the real field if ( $field =~ /LinkedTo/ ) { $realfield = $field = $restriction->{'TYPE'}; } # Two special case # Handle subkey fields with a different real field if ( $field =~ /^(\w+)\./ ) { $realfield = $1; } die "I don't know about $field yet" unless ( exists $FIELD_METADATA{$realfield} or $restriction->{CUSTOMFIELD} ); my $type = $FIELD_METADATA{$realfield}->[0]; my $op = $restriction->{'OPERATOR'}; my $value = ( grep {defined} map { $restriction->{$_} } qw(VALUE ASSET BASE TARGET) )[0]; # this performs the moral equivalent of defined or/dor/C, # without the short circuiting.You need to use a 'defined or' # type thing instead of just checking for truth values, because # VALUE could be 0.(i.e. "false") # You could also use this, but I find it less aesthetic: # (although it does short circuit) #( defined $restriction->{'VALUE'}? $restriction->{VALUE} : # defined $restriction->{'ASSET'} ? # $restriction->{ASSET} : # defined $restriction->{'BASE'} ? # $restriction->{BASE} : # defined $restriction->{'TARGET'} ? # $restriction->{TARGET} ) my $ea = $restriction->{ENTRYAGGREGATOR} || $DefaultEA{$type} || "AND"; if ( ref $ea ) { die "Invalid operator $op for $field ($type)" unless exists $ea->{$op}; $ea = $ea->{$op}; } # Each CustomField should be put into a different Clause so they # are ANDed together. if ( $restriction->{CUSTOMFIELD} ) { $realfield = $field; } exists $clause{$realfield} or $clause{$realfield} = []; # Escape Quotes $field =~ s!(['\\])!\\$1!g; $value =~ s!(['\\])!\\$1!g; my $data = [ $ea, $type, $field, $op, $value ]; # here is where we store extra data, say if it's a keyword or # something. (I.e. "TYPE SPECIFIC STUFF") if (lc $ea eq 'none') { $clause{$realfield} = [ $data ]; } else { push @{ $clause{$realfield} }, $data; } } return \%clause; } =head2 _ProcessRestrictions PARAMHASH # The new _ProcessRestrictions is somewhat dependent on the SQL stuff, # but isn't quite generic enough to move into Assets_SQL. =cut sub _ProcessRestrictions { my $self = shift; #Blow away asset aliases since we'll need to regenerate them for #a new search delete $self->{'AssetAliases'}; delete $self->{'items_array'}; delete $self->{'item_map'}; delete $self->{'raw_rows'}; delete $self->{'rows'}; delete $self->{'count_all'}; my $sql = $self->Query; # Violating the _SQL namespace if ( !$sql || $self->{'RecalcAssetLimits'} ) { # "Restrictions to Clauses Branch\n"; my $clauseRef = eval { $self->_RestrictionsToClauses; }; if ($@) { $RT::Logger->error( "RestrictionsToClauses: " . $@ ); $self->FromSQL(""); } else { $sql = $self->ClausesToSQL($clauseRef); $self->FromSQL($sql) if $sql; } } $self->{'RecalcAssetLimits'} = 0; } =head2 _BuildItemMap Build up a L of first/last/next/prev items, so that we can display search nav quickly. =cut sub _BuildItemMap { my $self = shift; my $window = RT->Config->Get('AssetsItemMapSize'); $self->{'item_map'} = {}; my $items = $self->ItemsArrayRefWindow( $window ); return unless $items && @$items; my $prev = 0; $self->{'item_map'}{'first'} = $items->[0]->Id; for ( my $i = 0; $i < @$items; $i++ ) { my $item = $items->[$i]; my $id = $item->Id; $self->{'item_map'}{$id}{'defined'} = 1; $self->{'item_map'}{$id}{'prev'} = $prev; $self->{'item_map'}{$id}{'next'} = $items->[$i+1]->Id if $items->[$i+1]; $prev = $id; } $self->{'item_map'}{'last'} = $prev if !$window || @$items < $window; } =head2 ItemMap Returns an a map of all items found by this search. The map is a hash of the form: { first => , last => , => { prev => , next => , }, => { prev => ..., next => ..., }, } =cut sub ItemMap { my $self = shift; $self->_BuildItemMap unless $self->{'item_map'}; return $self->{'item_map'}; } =head2 PrepForSerialization You don't want to serialize a big assets object, as the {items} hash will be instantly invalid _and_ eat lots of space =cut sub PrepForSerialization { my $self = shift; delete $self->{'items'}; delete $self->{'items_array'}; $self->RedoSearch(); } sub Export { my ($self, $format) = @_; } sub ExportExcel { my ($self, $format) = @_; my $class = 'RTx__AssetTracker__Asset'; local $HTML::Mason::Commands::r = FakeRequest->new; my $row_data = ''; my $row_count = $self->Count()+1; my $export_format = [ { attribute => 'id' }, grep { ($_->{title}||'') ne 'NEWLINE' && $_->{attribute} ne 'id' } @$format ]; my $column_count = @$export_format; my $i = 1; my $header = ''; for my $f (@$export_format) { my $attr = $f->{attribute}; $row_data .= qq{\n}; my $value = run_component("/Elements/ColumnMap", Class => $class, Name => $attr, Attr => "title") || $attr; my $out = _xml_escape_value( HTML::Mason::Commands::ProcessColumnMapValue( $value, Arguments => [ $attr ], Escape => 0 ) ); $header .= qq{$out\n}; } $header .= ''; $row_data .= $header; while (my $asset = $self->Next) { my $row = "\n"; my $record = $asset->Export; for my $f (@$export_format) { my $attr = $f->{attribute}; my $style = run_component("/Elements/ColumnMap", Class => $class, Name => $attr, Attr => "export_style") || 'Default'; my $type = run_component("/Elements/ColumnMap", Class => $class, Name => $attr, Attr => "export_type") || 'String'; my $value = run_component("/Elements/ColumnMap", Class => $class, Name => $attr, Attr => "export_value") || run_component("/Elements/ColumnMap", Class => $class, Name => $attr, Attr => "value"); my $out = _xml_escape_value( HTML::Mason::Commands::ProcessColumnMapValue( $value, Arguments => [ $asset ], Escape => 0 ) ) || ''; $row .= qq{$out\n}; } $row .= ''; $row_data .= $row; } return <<"EOF" 12.0 $row_data
    EOF } sub ImportXML { my ($self, $xml, $runscrips, $detailed) = @_; return ['permission denied'] unless $self->CurrentUser->HasRight( Object => $RT::System, Right => 'AssetImport'); $runscrips = 1 unless defined $runscrips; my $dom = create_dom_from_xml($xml); my ($first_worksheet) = grep { ref($_) eq 'RTx::AssetTracker::Assets::Worksheet' } @{ $dom->[0]{Kids} }; my ($first_table) = grep { ref($_) eq 'RTx::AssetTracker::Assets::Table' } @{ $first_worksheet->{Kids} }; my @rows = grep { ref($_) eq 'RTx::AssetTracker::Assets::Row' } @{ $first_table->{Kids} }; my $headers = $self->_import_headers(shift @rows); @rows = map { $self->_import_row($_, scalar(@$headers)) } @rows; my ($rv, $msgs) = $self->Import($headers, \@rows, $runscrips, $detailed); return $rv, $msgs; } sub Import { my ($self, $headers, $rows, $runscrips, $detailed) = @_; return 0, ['permission denied'] unless $self->CurrentUser->HasRight( Object => $RT::System, Right => 'AssetImport'); $runscrips = 1 unless defined $runscrips; my $rv = 0; my $msgs = []; my $ids = []; unless ($headers->[0] eq 'id') { push @$msgs, "First column of import must be 'id'"; return $rv, $msgs; } $RT::Handle->BeginTransaction(); my @new = (); my @update = (); my $error = 0; my $n = 1; for my $row (@$rows) { $n++; next unless @$row; my ($aid, $msg) = $self->_import($headers, $row, $runscrips, $detailed); #warn $row->[0], $msg; if (!$aid) { push @$msgs, $self->loc("Row [_1]: [_2]", $n, $msg); $error++; } elsif ($row->[0] =~ /^(\d+)(\.0)?$/) { push @update, $1; push @$ids, $1; } else { push @new, $aid; push @$ids, $aid; } } if ($error) { $RT::Logger->info( "Asset import error. Rolling back DB transactions." ); $RT::Handle->Rollback(); return 0, $msgs; } else { $RT::Handle->Commit(); #load each asset and do a Create/Update transaction with $runscrips set for my $id (@new) { my $asset = RTx::AssetTracker::Asset->new($self->CurrentUser); $asset->Load($id); $asset->_NewTransaction(Type => "Create"); push @$msgs, $self->loc("Asset #[_1] created", $id); } for my $id (@update) { my $asset = RTx::AssetTracker::Asset->new($self->CurrentUser); $asset->Load($id); $asset->_NewTransaction(Type => "Update"); push @$msgs, $self->loc("Asset [_1] possibly updated", $id); } } return $ids, $msgs; } sub _import { my ($self, $header, $asset_row, $runscrips, $detailed) = @_; require Storable; my $row = Storable::dclone($asset_row); my %asset = (); for (@$header) { $asset{$_} = shift @$row; } %asset = $self->_fixup_import(%asset); my $id = delete $asset{id} or return 0, "id not defined"; my $asset = RTx::AssetTracker::Asset->new($self->CurrentUser); if ($id eq 'new') { my ($aid, undef, $err) = $asset->Create( %asset, _Commit => 0, _RecordTransaction => 0 ); return $aid, $err; } elsif ($id =~ /(\d+)/) { $asset->Load($1); return 0, "Nonexistent id: $id" unless $asset->Id; my ($aid, undef, $err) = $asset->UpdateAsset( %asset, _Commit => 0, _RecordTransaction => 0, _Detailed => $detailed ); return $aid, $self->loc("Asset #[_1] not updated: [_2]", $asset->Id, $err); } else { return 0, "Unrecognized id: $id"; } } sub _fixup_import { my ($self, %asset) = @_; my %fixed; map { $fixed{$_} = delete $asset{$_} if defined $asset{$_} } qw(id Name Type Status Description); #roles foreach my $type ( RTx::AssetTracker::Type->RoleGroupTypes() ) { next unless $asset{$type}; $fixed{$type} = [ split(/,\s*/, delete $asset{$type}) ]; } #links my $LINKTYPEMAP = RTx::AssetTracker::Asset::LINKTYPEMAP(); for my $type (keys %$LINKTYPEMAP) { next unless exists $asset{$type} && defined $asset{$type}; my @URIs = split(/,/, delete $asset{$type}); $fixed{$type} = \@URIs; } #ip addresses my @ips = (exists $asset{'IP Address'} && defined $asset{'IP Address'}) ? split(/\|/, delete $asset{'IP Address'}) : (); my $import_ips = []; for my $ip (@ips) { my ($interface, $address, $mac, $tcp, $udp) = split(/:/, $ip); my $tcp_ports = [ split(/,/, $tcp) ]; my $udp_ports = [ split(/,/, $udp) ]; push @$import_ips, { IP => $address, Interface => $interface, MAC => $mac, TCPPorts => $tcp_ports, UDPPorts => $udp_ports, }; } if (@$import_ips) { $fixed{'IP Address'} = $import_ips; } unless ( $fixed{'Type'} ) { my $a = RTx::AssetTracker::Asset->new($self->CurrentUser); $a->Load($fixed{'id'}); $fixed{'Type'} = $a->Type if defined($a->Type); } #custom fields are last for my $possible_cf (keys %asset) { my $cf = RT::CustomField->new($self->CurrentUser); $cf->LoadByNameAndAssetType( Name => $possible_cf, Type => $fixed{'Type'} ); unless ( $cf->id ) { $cf->LoadByNameAndAssetType( Name => $possible_cf, Type => '0' ); } if ($cf->id) { my $type = $cf->Type; if ($cf->MaxValues && $type =~ /^(Wikitext|Text|Freeform|Autocomplete)$/) { $asset{$possible_cf} =~ s/\r\n?/\n/g; $fixed{"CustomField-".$cf->id} = delete $asset{$possible_cf}; } elsif ( !$cf->MaxValues && $type =~ /^(Freeform|Autocomplete)$/) { $asset{$possible_cf} =~ s/\r\n?/\n/g; my @values = split('\n', $asset{$possible_cf}); $asset{$possible_cf} = \@values; $fixed{"CustomField-".$cf->id} = delete $asset{$possible_cf}; } elsif ( $cf->MaxValues && $type eq 'Select') { #make sure the value is valid??? $fixed{"CustomField-".$cf->id} = delete $asset{$possible_cf}; } elsif (!$cf->MaxValues && $type eq 'Select') { #make sure the values are valid??? $fixed{"CustomField-".$cf->id} = delete $asset{$possible_cf}; } else { #???? } } } #should we error on anything that is left? return %fixed; } sub _import_headers { my ($self, $row) = @_; my @headers; ROW: for my $cell (@{$row->{Kids}}) { next unless ref($cell) eq 'RTx::AssetTracker::Assets::Cell'; for my $data (@{$cell->{Kids}}) { next unless ref($data) eq 'RTx::AssetTracker::Assets::Data'; for my $characters (@{$data->{Kids}}) { next unless ref($characters) eq 'RTx::AssetTracker::Assets::Characters'; my $text = $characters->{Text}; last ROW unless defined $text; push @headers, $text; } } } return \@headers; } sub _import_row { my ($self, $row, $column_count) = @_; my @row; my $count = 0; ROW: for my $cell (@{$row->{Kids}}) { next unless ref($cell) eq 'RTx::AssetTracker::Assets::Cell'; for my $data (@{$cell->{Kids}}) { if ( ref($data->{Kids}) && @{$data->{Kids}} ) { for my $characters (@{$data->{Kids}}) { my $text = $characters->{Text}; push @row, $text; } } else { push @row, undef; } } last if ++$count == $column_count; } return \@row; } sub create_dom_from_xml { my ($xml) = @_; my $xp = XML::Parser->new(Style => "Objects", Namespaces => 1); my $dom = eval { $xp->parse($xml); }; unless ($dom && !$@) { die "XML document not properly formed: $@\n" } return $dom; } sub _xml_escape_value { my ($str) = @_; return unless $str; $str =~ s/&/&/g; $str =~ s//>/g; $str =~ s/"/"/g; $str =~ s/\n/ /g; return $str; } { my $mason; my $outbuf = ''; my $data_dir = ''; use HTML::Mason::FakeApache; sub mason { unless ($mason) { # user may not have permissions on the data directory, so create a # new one $data_dir = tempdir(CLEANUP => 1); $mason = HTML::Mason::Interp->new( #RT::Interface::Web::Handler->DefaultHandlerArgs, comp_root => [ [ local => $RT::MasonLocalComponentRoot ], (map {[ "plugin-".$_->Name => $_->ComponentRoot ]} @{RT->Plugins}), [ standard => $RT::MasonComponentRoot ] ], request_class => 'RT::Interface::Web::Request', cgi_request => new HTML::Mason::FakeApache, auto_send_headers => 0, out_method => \$outbuf, autohandler_name => '', # disable forced login and more data_dir => $data_dir, ); } return $mason; } sub run_component { my $x = mason->exec(@_); my $ret = $outbuf; $outbuf = ''; #return $ret; return $x; } } package FakeRequest; sub new { bless {}, shift } sub header_out { shift } sub headers_out { shift } sub content_type { my $self = shift; $self->{content_type} = shift if @_; return $self->{content_type}; } package RTx::AssetTracker::Assets; =head1 FLAGS RTx::AssetTracker::Assets supports several flags which alter search behavior: allow_deleted_search (Otherwise never show deleted assets in search results) These flags are set by calling $assets->{'flagname'} = 1; BUG: There should be an API for this =cut =head2 NewItem Returns an empty new RTx::AssetTracker::Asset item =cut sub NewItem { my $self = shift; return(RTx::AssetTracker::Asset->new($self->CurrentUser)); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Assets_SQL.pm000066400000000000000000000334351222742774700250650ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} package RTx::AssetTracker::Assets; use strict; use warnings; use RT::SQL; # Import configuration data from the lexcial scope of __PACKAGE__ (or # at least where those two Subroutines are defined.) our (%FIELD_METADATA, %LOWER_CASE_FIELDS, %dispatch, %can_bundle); sub _InitSQL { my $self = shift; # Private Member Variables (which should get cleaned) $self->{'_sql_transalias'} = undef; $self->{'_sql_cf_alias'} = undef; $self->{'_sql_object_cfv_alias'} = undef; $self->{'_sql_watcher_join_users_alias'} = undef; $self->{'_sql_query'} = ''; $self->{'_sql_looking_at'} = {}; } sub _SQLLimit { my $self = shift; my %args = (@_); # All SQL stuff goes into one SB subclause so we can deal with all # the aggregation $self->SUPER::Limit(%args, SUBCLAUSE => 'assetsql'); } sub _SQLJoin { # All SQL stuff goes into one SB subclause so we can deal with all # the aggregation my $this = shift; $this->SUPER::Join(@_, SUBCLAUSE => 'assetsql'); } # Helpers sub _OpenParen { $_[0]->SUPER::_OpenParen( 'assetsql' ); } sub _CloseParen { $_[0]->SUPER::_CloseParen( 'assetsql' ); } =head1 SQL Functions =cut =head2 Robert's Simple SQL Parser Documentation In Progress The Parser/Tokenizer is a relatively simple state machine that scans through a SQL WHERE clause type string extracting a token at a time (where a token is: VALUE -> quoted string or number AGGREGator -> AND or OR KEYWORD -> quoted string or single word OPerator -> =,!=,LIKE,etc.. PARENthesis -> open or close. And that stream of tokens is passed through the "machine" in order to build up a structure that looks like: KEY OP VALUE AND KEY OP VALUE OR KEY OP VALUE That also deals with parenthesis for nesting. (The parentheses are just handed off the SearchBuilder) =cut sub _close_bundle { my ($self, @bundle) = @_; return unless @bundle; if ( @bundle == 1 ) { $bundle[0]->{'dispatch'}->( $self, $bundle[0]->{'key'}, $bundle[0]->{'op'}, $bundle[0]->{'val'}, SUBCLAUSE => '', ENTRYAGGREGATOR => $bundle[0]->{ea}, SUBKEY => $bundle[0]->{subkey}, ); } else { my @args; foreach my $chunk (@bundle) { push @args, [ $chunk->{key}, $chunk->{op}, $chunk->{val}, SUBCLAUSE => '', ENTRYAGGREGATOR => $chunk->{ea}, SUBKEY => $chunk->{subkey}, ]; } $bundle[0]->{dispatch}->( $self, \@args ); } } sub _parser { my ($self,$string) = @_; my @bundle; my $ea = ''; # Bundling of joins is implemented by dynamically tracking a parallel query # tree in %sub_tree as the AssetSQL is parsed. Don't be fooled by # _close_bundle(), @bundle, and %can_bundle; they are completely unused for # quite a long time and removed in RT 4.2. For now they stay, a useless # relic. # # Only positive, OR'd watcher conditions are bundled currently. Each key # in %sub_tree is a watcher type (Owner, Admin) or the generic # "Watcher" for any watcher type. AND'd # conditions are not bundled since a record may have multiple watchers # which independently match the conditions, thus necessitating two joins. # # The values of %sub_tree are arrayrefs made up of: # # * Open parentheses "(" pushed on by the OpenParen callback # * Arrayrefs of bundled join aliases pushed on by the Condition callback # * Entry aggregators (AND/OR) pushed on by the EntryAggregator callback # # The CloseParen callback takes care of backing off the query trees until # outside of the just-closed parenthetical, thus restoring the tree state # an equivalent of before the parenthetical was entered. # # The Condition callback handles starting a new subtree or extending an # existing one, determining if bundling the current condition with any # subtree is possible, and pruning any dangling entry aggregators from # trees. # my %sub_tree; my $depth = 0; my %callback; $callback{'OpenParen'} = sub { $self->_close_bundle(@bundle); @bundle = (); $self->_OpenParen; $depth++; push @$_, '(' foreach values %sub_tree; }; $callback{'CloseParen'} = sub { $self->_close_bundle(@bundle); @bundle = (); $self->_CloseParen; $depth--; foreach my $list ( values %sub_tree ) { if ( $list->[-1] eq '(' ) { pop @$list; pop @$list if $list->[-1] =~ /^(?:AND|OR)$/i; } else { pop @$list while $list->[-2] ne '('; $list->[-1] = pop @$list; } } }; $callback{'EntryAggregator'} = sub { $ea = $_[0] || ''; push @$_, $ea foreach grep @$_ && $_->[-1] ne '(', values %sub_tree; }; $callback{'Condition'} = sub { my ($key, $op, $value) = @_; my ($negative_op, $null_op, $inv_op, $range_op) = $self->ClassifySQLOperation( $op ); # key has dot then it's compound variant and we have subkey my $subkey = ''; ($key, $subkey) = ($1, $2) if $key =~ /^([^\.]+)\.(.+)$/; # normalize key and get class (type) my $class; if (exists $LOWER_CASE_FIELDS{lc $key}) { $key = $LOWER_CASE_FIELDS{lc $key}; $class = $FIELD_METADATA{$key}->[0]; } die "Unknown field '$key' in '$string'" unless $class; # replace __CurrentUser__ with id $value = $self->CurrentUser->id if $value eq '__CurrentUser__'; unless( $dispatch{ $class } ) { die "No dispatch method for class '$class'" } my $sub = $dispatch{ $class }; if ( $can_bundle{ $class } && ( !@bundle || ( $bundle[-1]->{dispatch} == $sub && $bundle[-1]->{key} eq $key && $bundle[-1]->{subkey} eq $subkey ) ) ) { push @bundle, { dispatch => $sub, key => $key, op => $op, val => $value, ea => $ea, subkey => $subkey, }; } else { $self->_close_bundle(@bundle); @bundle = (); my @res; my $bundle_with; if ( $class eq 'WATCHERFIELD' && $key ne 'Owner' && !$negative_op && (!$null_op || $subkey) ) { if ( !$sub_tree{$key} ) { $sub_tree{$key} = [ ('(')x$depth, \@res ]; } else { $bundle_with = $self->_check_bundling_possibility( $string, @{ $sub_tree{$key} } ); if ( $sub_tree{$key}[-1] eq '(' ) { push @{ $sub_tree{$key} }, \@res; } } } # Remove our aggregator from subtrees where our condition didn't get added pop @$_ foreach grep @$_ && $_->[-1] =~ /^(?:AND|OR)$/i, values %sub_tree; # A reference to @res may be pushed onto $sub_tree{$key} from # above, and we fill it here. @res = $sub->( $self, $key, $op, $value, SUBCLAUSE => '', # don't need anymore ENTRYAGGREGATOR => $ea, SUBKEY => $subkey, BUNDLE => $bundle_with, ); } $self->{_sql_looking_at}{lc $key} = 1; $ea = ''; }; RT::SQL::Parse($string, \%callback); $self->_close_bundle(@bundle); @bundle = (); } sub _check_bundling_possibility { my $self = shift; my $string = shift; my @list = reverse @_; while (my $e = shift @list) { next if $e eq '('; if ( lc($e) eq 'and' ) { return undef; } elsif ( lc($e) eq 'or' ) { return shift @list; } else { # should not happen $RT::Logger->error( "Joins optimization failed when parsing '$string'. It's bug in RT, contact Best Practical" ); die "Internal error. Contact your system administrator."; } } return undef; } =head2 ClausesToSQL =cut sub ClausesToSQL { my $self = shift; my $clauses = shift; my @sql; for my $f (keys %{$clauses}) { my $sql; my $first = 1; # Build SQL from the data hash for my $data ( @{ $clauses->{$f} } ) { $sql .= $data->[0] unless $first; $first=0; # ENTRYAGGREGATOR $sql .= " '". $data->[2] . "' "; # FIELD $sql .= $data->[3] . " "; # OPERATOR $sql .= "'". $data->[4] . "' "; # VALUE } push @sql, " ( " . $sql . " ) "; } return join("AND",@sql); } =head2 FromSQL Convert a RT-SQL string into a set of SearchBuilder restrictions. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. =cut sub FromSQL { my ($self,$query) = @_; { # preserve first_row and show_rows across the CleanSlate local ($self->{'first_row'}, $self->{'show_rows'}); $self->CleanSlate; } $self->_InitSQL(); return (1, $self->loc("No Query")) unless $query; $self->{_sql_query} = $query; eval { $self->_parser( $query ); }; if ( $@ ) { my $error = "$@"; $RT::Logger->error("Couldn't parse query: $error"); return (0, $error); } # We don't want deleted assets unless 'allow_deleted_search' is set unless( $self->{'allow_deleted_search'} ) { $self->SUPER::Limit( FIELD => 'Status', OPERATOR => '!=', VALUE => 'deleted', ); } # set SB's dirty flag $self->{'must_redo_search'} = 1; $self->{'RecalcAssetLimits'} = 0; return (1, $self->loc("Valid Query")); } =head2 Query Returns the query that this object was initialized with =cut sub Query { return ($_[0]->{_sql_query}); } { my %inv = ( '=' => '!=', '!=' => '=', '<>' => '=', '>' => '<=', '<' => '>=', '>=' => '<', '<=' => '>', 'is' => 'IS NOT', 'is not' => 'IS', 'like' => 'NOT LIKE', 'not like' => 'LIKE', 'matches' => 'NOT MATCHES', 'not matches' => 'MATCHES', 'startswith' => 'NOT STARTSWITH', 'not startswith' => 'STARTSWITH', 'endswith' => 'NOT ENDSWITH', 'not endswith' => 'ENDSWITH', ); my %range = map { $_ => 1 } qw(> >= < <=); sub ClassifySQLOperation { my $self = shift; my $op = shift; my $is_negative = 0; if ( $op eq '!=' || $op =~ /\bNOT\b/i ) { $is_negative = 1; } my $is_null = 0; if ( 'is not' eq lc($op) || 'is' eq lc($op) ) { $is_null = 1; } return ($is_negative, $is_null, $inv{lc $op}, $range{lc $op}); } } 1; =pod =head2 Exceptions Most of the RT code does not use Exceptions (die/eval) but it is used in the AssetSQL code for simplicity and historical reasons. Lest you be worried that the dies will trigger user visible errors, all are trapped via evals. 99% of the dies fall in subroutines called via FromSQL and then parse. (This includes all of the _FooLimit routines in Assets.pm.) The other 1% or so are via _ProcessRestrictions. All dies are trapped by eval {}s, and will be logged at the 'error' log level. The general failure mode is to not display any assets. =head2 General Flow Legacy Layer: Legacy LimitFoo routines build up a RestrictionsHash _ProcessRestrictions converts the Restrictions to Clauses ([key,op,val,rest]). Clauses are converted to RT-SQL (AssetSQL) New RT-SQL Layer: FromSQL calls the parser The parser calls the _FooLimit routines to do DBIx::SearchBuilder limits. And then the normal SearchBuilder/Asset routines are used for display/navigation. =cut rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/IP.pm000066400000000000000000000311051222742774700234040ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::IP - an AssetTracker IP object =head1 SYNOPSIS use RTx::AssetTracker::IP; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::IP; use base 'RTx::AssetTracker::Record'; sub Table {'AT_IPs'}; use RTx::AssetTracker::Asset; use RTx::AssetTracker::Port; use RTx::AssetTracker::IP; # {{{ sub Create =head2 Create Create a new transaction. This routine should _never_ be called anything other Than RTx::AssetTracker::Asset. It should not be called from client code. Ever. Not ever. If you do this, we will hunt you down. and break your kneecaps. Then the unpleasant stuff will start. TODO: Document what gets passed to this =cut sub Create { my $self = shift; my %args = ( Asset => undef, IP => undef, Interface => undef, MAC => undef, TCPPorts => [], UDPPorts => [], Silent => 0, SilentPorts => 0, @_ ); my $exists = RTx::AssetTracker::IP->new( $RT::SystemUser ); $exists->Load( $args{IP} ); return (0, "IP address already in use by asset " . $exists->AssetObj->Name) if $exists->Id; my %ports = ( TCPPorts => $args{TCPPorts}, UDPPorts => $args{UDPPorts} ); delete @args{ qw( TCPPorts UDPPorts ) }; my ($rv, $msg) = $self->SUPER::Create(Asset => $args{Asset}, IP => $args{IP}, Interface => $args{Interface}, MAC => $args{MAC}); return ($rv, "$msg ".$exists->AssetObj->Name) unless ($rv); my $ip = RTx::AssetTracker::IP->new( $self->CurrentUser ); $ip->Load( $rv ) || return 0; #if success then add each port #do all this in a transaction???? foreach my $tcp (@{$ports{TCPPorts}}) { $ip->AddPort( Transport => 'TCP', Port => $tcp, Silent => $args{Silent} || $args{SilentPorts} ) } foreach my $udp (@{$ports{UDPPorts}}) { $ip->AddPort( Transport => 'UDP', Port => $udp, Silent => $args{Silent} || $args{SilentPorts} ) } return ($rv, $msg); } # {{{ sub Load =head2 Load Takes either a numerical id or a textual Name and loads the specified IP. =cut sub Load { my $self = shift; my $identifier = shift; if ( !$identifier ) { return (undef); } if ( $identifier =~ /^(\d+)$/ ) { $self->SUPER::LoadById($identifier); } else { $self->LoadByCols( IP => $identifier ); } return ( $self->Id ); } # }}} sub AssetObj { my $self = shift; my $asset = RTx::AssetTracker::Asset->new( $self->CurrentUser ); $asset->Load($self->Asset); return $asset; } sub AddPort { my $self = shift; my %args = ( Transport => '', Port => 0, TransactionData => undef, @_ ); unless ( $self->AssetObj->CurrentUserHasRight('ModifyAsset') || $self->AssetObj->CurrentUserHasRight('CreateAsset') ) { return ( 0, $self->loc("Permission Denied") ); } my $port = RTx::AssetTracker::Port->new( $self->CurrentUser ); my ($rv, $msg) = $port->Create(IP => $self->Id, %args); if ($rv) { unless ( $args{'Silent'} ) { $self->AssetObj->_NewTransaction( Type => 'AddPort', NewValue => $self->IP . " $args{Transport} $args{Port}", Field => 'Port', Data => $args{TransactionData} ); } return ( 1, $self->loc( "$args{Transport} $args{Port} has been added for this IP." ) ); } else { return(0, $self->loc("Port could not be added: $msg")); } } sub AddTCPPort { my $self = shift; my $port = shift; return $self->AddPort( Transport => 'TCP', Port => $port, @_ ); } sub AddUDPPort { my $self = shift; my $port = shift; return $self->AddPort( Transport => 'UDP', Port => $port, @_ ); } sub DeletePort { my $self = shift; my %args = ( Transport => undef, Port => undef, TransactionData => undef, @_ ); unless ( $self->AssetObj->CurrentUserHasRight('ModifyAsset') || $self->AssetObj->CurrentUserHasRight('CreateAsset') ) { return ( 0, $self->loc("Permission Denied") ); } my $port = RTx::AssetTracker::Port->new( $self->CurrentUser ); $port->LoadPort( IP => $self->Id, %args ); my $portnum = $port->Port; my $transport = $port->Transport; # If we can't find this IP, we need to bail. unless ( $port->Id ) { return ( 0, $self->loc("Could not find that $args{Transport} Port") ); } my $retval = $port->Delete(); if ($retval) { unless ( $args{'Silent'} ) { $self->AssetObj->_NewTransaction( Type => 'DelPort', OldValue => $self->IP . " $transport $portnum", Field => 'Port', Data => $args{TransactionData} ); } return ( 1, $self->loc( "$portnum $transport is no longer an port for this IP." ) ); } else { return(0, $self->loc("Port could not be deleted")); } } sub DeleteTCPPort { my $self = shift; my $port = shift; return $self->DeletePort( Transport => 'TCP', Port => $port, @_ ); } sub DeleteUDPPort { my $self = shift; my $port = shift; return $self->DeletePort( Transport => 'UDP', Port => $port, @_ ); } sub TCPPorts { my $self = shift; my $Ports = RTx::AssetTracker::Ports->new( $self->CurrentUser ); $Ports->LimitToIP($self->Id); $Ports->LimitToTCP; my @ports; while (my $port = $Ports->Next) { push @ports, $port->Port; } return @ports; } sub UDPPorts { my $self = shift; my $Ports = RTx::AssetTracker::Ports->new( $self->CurrentUser ); $Ports->LimitToIP($self->Id); $Ports->LimitToUDP; my @ports; while (my $port = $Ports->Next) { push @ports, $port->Port; } return @ports; } sub DeleteAllPorts { my $self = shift; my $Ports = RTx::AssetTracker::Ports->new( $self->CurrentUser ); $Ports->LimitToIP($self->Id); my $count = $Ports->Count; my $success = 0; while (my $port = $Ports->Next) { my $retval = $port->Delete(RecordTransaction => 0); $success++ if $retval; } if ($success == $count) { return( 1, "All ports deleted"); } return( 0, "$success of $count ports deleted"); } sub Delete { my $self = shift; my ($rv, $msg) = $self->DeleteAllPorts; if ($rv) { return $self->SUPER::Delete(); } else { return $rv, $msg; } } ## Shredder methods ## use RT::Shredder::Constants; use RT::Shredder::Exceptions; use RT::Shredder::Dependencies; sub __DependsOn { my $self = shift; my %args = ( Shredder => undef, Dependencies => undef, @_, ); my $deps = $args{'Dependencies'}; my $list = []; # IP Ports my $objs = RTx::AssetTracker::Ports->new( $self->CurrentUser ); $objs->Limit( FIELD => 'IP', VALUE => $self->Id ); push( @$list, $objs ); #IP Transactions $objs = RT::Transactions->new( $self->CurrentUser ); $objs->Limit( FIELD => 'Type', VALUE => 'AddIP' ); $objs->Limit( FIELD => 'ObjectType', VALUE => 'RTx::AssetTracker::Asset' ); $objs->Limit( FIELD => 'ObjectId', VALUE => $self->Asset ); $objs->Limit( FIELD => 'NewValue', VALUE => $self->IP ); push( @$list, $objs ); #TODO: Users, Types if we wish export tool $deps->_PushDependencies( BaseObject => $self, Flags => DEPENDS_ON, TargetObjects => $list, Shredder => $args{'Shredder'} ); return $self->SUPER::__DependsOn( %args ); } =head2 id Returns the current value of id. (In the database, id is stored as int(11).) =cut =head2 IP Returns the current value of IP. (In the database, IP is stored as varchar(15).) =head2 SetIP VALUE Set IP to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, IP will be stored as a varchar(15).) =cut =head2 MAC Returns the current value of MAC. (In the database, MAC is stored as varchar(12).) =head2 SetMAC VALUE Set MAC to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, MAC will be stored as a varchar(12).) =cut =head2 Interface Returns the current value of Interface. (In the database, Interface is stored as varchar(25).) =head2 SetInterface VALUE Set Interface to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Interface will be stored as a varchar(25).) =cut =head2 Asset Returns the current value of Asset. (In the database, Asset is stored as int(11).) =head2 SetAsset VALUE Set Asset to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Asset will be stored as a int(11).) =cut =head2 Creator Returns the current value of Creator. (In the database, Creator is stored as int(11).) =cut =head2 Created Returns the current value of Created. (In the database, Created is stored as datetime.) =cut =head2 LastUpdatedBy Returns the current value of LastUpdatedBy. (In the database, LastUpdatedBy is stored as int(11).) =cut =head2 LastUpdated Returns the current value of LastUpdated. (In the database, LastUpdated is stored as datetime.) =cut sub _CoreAccessible { { id => {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, IP => {read => 1, write => 1, sql_type => 12, length => 15, is_blob => 0, is_numeric => 0, type => 'varchar(15)', default => ''}, MAC => {read => 1, write => 1, sql_type => 12, length => 12, is_blob => 0, is_numeric => 0, type => 'varchar(12)', default => ''}, Interface => {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => '0'}, Asset => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Creator => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Created => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, LastUpdatedBy => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, LastUpdated => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, } }; RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/IPs.pm000066400000000000000000000051071222742774700235720ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::IPs -- Class Description =head1 SYNOPSIS use RTx::AssetTracker::IPs =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::IPs; use base 'RT::SearchBuilder'; use RTx::AssetTracker::IP; sub Table {'AT_IPs'}; sub _Init { my $self = shift; $self->{'table'} = 'AT_IPs'; $self->{'primary_key'} = 'id'; return ( $self->SUPER::_Init(@_) ); } =head2 NewItem Returns an empty new RTx::AssetTracker::IP item =cut sub NewItem { my $self = shift; return(RTx::AssetTracker::IP->new($self->CurrentUser)); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Port.pm000066400000000000000000000147441222742774700240320ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Port - an AssetTracker Port object =head1 SYNOPSIS use RTx::AssetTracker::Port; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Port; use base 'RTx::AssetTracker::Record'; sub Table {'AT_Ports'}; use RTx::AssetTracker::Asset; use RTx::AssetTracker::IP; sub IPObj { my $self = shift; my $ip = RTx::AssetTracker::IP->new( $self->CurrentUser ); $ip->Load($self->IP); return $ip; } ## Shredder methods ## use RT::Shredder::Constants; use RT::Shredder::Exceptions; use RT::Shredder::Dependencies; sub __DependsOn { my $self = shift; my %args = ( Shredder => undef, Dependencies => undef, @_, ); my $deps = $args{'Dependencies'}; my $list = []; # Port Transactions my $objs = RT::Transactions->new( $self->CurrentUser ); $objs->Limit( FIELD => 'Type', VALUE => 'AddPort' ); $objs->Limit( FIELD => 'ObjectType', VALUE => 'RTx::AssetTracker::Asset' ); $objs->Limit( FIELD => 'ObjectId', VALUE => $self->IPObj->Asset ); $objs->Limit( FIELD => 'NewValue', VALUE => $self->IPObj->IP . ' ' . $self->Transport . ' ' . $self->Port ); push( @$list, $objs ); $deps->_PushDependencies( BaseObject => $self, Flags => DEPENDS_ON, TargetObjects => $list, Shredder => $args{'Shredder'} ); return $self->SUPER::__DependsOn( %args ); } =head2 Create PARAMHASH Create takes a hash of values and creates a row in the database: char(15) 'Transport'. char(12) 'Port'. int(11) 'IP'. =cut sub Create { my $self = shift; my %args = ( Transport => '', Port => '', IP => '0', @_); $self->SUPER::Create( Transport => $args{'Transport'}, Port => $args{'Port'}, IP => $args{'IP'}, ); } =head2 id Returns the current value of id. (In the database, id is stored as int(11).) =cut =head2 Transport Returns the current value of Transport. (In the database, Transport is stored as char(15).) =head2 SetTransport VALUE Set Transport to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Transport will be stored as a char(15).) =cut =head2 Port Returns the current value of Port. (In the database, Port is stored as char(12).) =head2 SetPort VALUE Set Port to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Port will be stored as a char(12).) =cut =head2 IP Returns the current value of IP. (In the database, IP is stored as int(11).) =head2 SetIP VALUE Set IP to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, IP will be stored as a int(11).) =cut =head2 Creator Returns the current value of Creator. (In the database, Creator is stored as int(11).) =cut =head2 Created Returns the current value of Created. (In the database, Created is stored as datetime.) =cut =head2 LastUpdatedBy Returns the current value of LastUpdatedBy. (In the database, LastUpdatedBy is stored as int(11).) =cut =head2 LastUpdated Returns the current value of LastUpdated. (In the database, LastUpdated is stored as datetime.) =cut sub _CoreAccessible { { id => {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, Transport => {read => 1, write => 1, sql_type => 1, length => 15, is_blob => 0, is_numeric => 0, type => 'char(15)', default => ''}, Port => {read => 1, write => 1, sql_type => 1, length => 12, is_blob => 0, is_numeric => 0, type => 'char(12)', default => ''}, IP => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Creator => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Created => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, LastUpdatedBy => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, LastUpdated => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, } }; RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Ports.pm000066400000000000000000000064541222742774700242140ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Ports - a collection of AssetTracker Port objects =head1 SYNOPSIS use RTx::AssetTracker::Ports; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Ports; use base 'RT::SearchBuilder'; use RTx::AssetTracker::Port; sub Table {'AT_Ports'}; sub _Init { my $self = shift; $self->{'table'} = 'AT_Ports'; $self->{'primary_key'} = 'id'; return ( $self->SUPER::_Init(@_) ); } sub LimitToIP { my $self = shift; my $IPid = shift; my %args = ( Operator => '=', @_ ); $self->Limit( FIELD => 'IP', VALUE => $IPid, %args ); } sub LimitToTransport { my $self = shift; my $transport = shift; my %args = ( Operator => '=', @_ ); $self->Limit( FIELD => 'Transport', VALUE => $transport, %args ); } sub LimitToTCP { my $self = shift; my %args = ( Operator => '=', @_ ); $self->LimitToTransport('TCP', %args); } sub LimitToUDP { my $self = shift; my %args = ( Operator => '=', @_ ); $self->LimitToTransport('UDP', %args); } =head2 NewItem Returns an empty new RTx::AssetTracker::Port item =cut sub NewItem { my $self = shift; return(RTx::AssetTracker::Port->new($self->CurrentUser)); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Record.pm000066400000000000000000000443211222742774700243160ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Record - Base class for AT record objects =head1 SYNOPSIS =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Record; use base 'RT::Record'; =head2 URI Returns this record's URI =cut # Not sure why this is implemented, but need to override - Todd sub URI { my $self = shift; my $uri = RT::URI::at->new($self->CurrentUser); return($uri->URIForObject($self)); } =head2 Update ARGSHASH Updates fields on an object for you using the proper Set methods, skipping unchanged values. ARGSRef => a hashref of attributes => value for the update AttributesRef => an arrayref of keys in ARGSRef that should be updated AttributePrefix => a prefix that should be added to the attributes in AttributesRef when looking up values in ARGSRef Bare attributes are tried before prefixed attributes Returns a list of localized results of the update =cut sub Update { my $self = shift; my %args = ( ARGSRef => undef, AttributesRef => undef, AttributePrefix => undef, @_ ); my $attributes = $args{'AttributesRef'}; my $ARGSRef = $args{'ARGSRef'}; my %new_values; # gather all new values foreach my $attribute (@$attributes) { my $value; if ( defined $ARGSRef->{$attribute} ) { $value = $ARGSRef->{$attribute}; } elsif ( defined( $args{'AttributePrefix'} ) && defined( $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute } ) ) { $value = $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute }; } else { next; } $value =~ s/\r\n/\n/gs; my $truncated_value = $self->TruncateValue($attribute, $value); # If Queue is 'General', we want to resolve the queue name for # the object. # This is in an eval block because $object might not exist. # and might not have a Name method. But "can" won't find autoloaded # items. If it fails, we don't care do { no warnings "uninitialized"; local $@; eval { my $object = $attribute . "Obj"; my $name = $self->$object->Name; next if $name eq $value || $name eq ($value || 0); }; my $current = $self->$attribute(); # RT::Queue->Lifecycle returns a Lifecycle object instead of name $current = eval { $current->Name } if ref $current; next if $truncated_value eq $current; next if ( $truncated_value || 0 ) eq $current; }; $new_values{$attribute} = $value; } return $self->_UpdateAttributes( Attributes => $attributes, NewValues => \%new_values, TransactionData => $ARGSRef->{BasicComment} || $ARGSRef->{GlobalComment}, ); } sub _UpdateAttributes { my $self = shift; my %args = ( Attributes => [], NewValues => {}, TransactionData => undef, @_, ); my @results; foreach my $attribute (@{ $args{Attributes} }) { next if !exists($args{NewValues}{$attribute}); my $value = $args{NewValues}{$attribute}; my $method = "Set$attribute"; my ( $code, $msg ); if (ref $self eq 'RTx::AssetTracker::Asset') { ( $code, $msg ) = $self->$method(Value => $value, TransactionData => $args{'TransactionData'}); } else { ( $code, $msg ) = $self->$method($value); } my ($prefix) = ref($self) =~ /RT(?:.*)::(\w+)/; # Default to $id, but use name if we can get it. my $label = $self->id; $label = $self->Name if (UNIVERSAL::can($self,'Name')); # this requires model names to be loc'ed. =for loc "Asset" # loc "Asset type" # loc =cut push @results, $self->loc( $prefix ) . " $label: ". $msg; =for loc "[_1] could not be set to [_2].", # loc "That is already the current value", # loc "No value sent to _Set!", # loc "Illegal value for [_1]", # loc "The new value has been set.", # loc "No column specified", # loc "Immutable field", # loc "Nonexistant field?", # loc "Invalid data", # loc "Couldn't find row", # loc "Missing a primary key?: [_1]", # loc "Found Object", # loc =cut } return @results; } # TODO: This _only_ works for RT::Foo classes. it doesn't work, for # example, for RT::IR::Foo classes. sub CustomFieldLookupId { my $self = shift; my $lookup = shift || $self->CustomFieldLookupType; my @classes = ($lookup =~ /RTx::AssetTracker::(\w+)-/g); # Work on "RT::Queue", for instance return $self->Id unless @classes; my $object = $self; # Save a ->Load call by not calling ->FooObj->Id, just ->Foo my $final = shift @classes; foreach my $class (reverse @classes) { my $method = "${class}Obj"; $object = $object->$method; } my $id = $object->$final; unless (defined $id) { my $method = "${final}Obj"; $id = $object->$method->Id; } return $id; } =item AddCustomFieldValue { Field => FIELD, Value => VALUE } VALUE should be a string. FIELD can be a CustomField object OR a CustomField ID. Adds VALUE as a value of CustomField FIELD. If this is a single-value custom field, deletes the old value. If VALUE is not a valid value for the custom field, returns (0, 'Error message' ) otherwise, returns (1, 'Success Message') =cut # I guess I added this? -Todd sub AddUniqueCustomFieldValue { my $self = shift; my %args = ( Field => undef, Value => undef, @_ ); my $values = $self->CustomFieldValues($args{Field}); while (my $value = $values->Next) { return(1, "$args{Value} is already a value.") if $args{Value} eq $value->Content; } $self->AddCustomFieldValue(@_); } # Can get rid of this after RT supports Data option. -Todd sub _AddCustomFieldValue { my $self = shift; my %args = ( Field => undef, Value => undef, LargeContent => undef, ContentType => undef, RecordTransaction => 1, Data => undef, @_ ); my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'}); unless ( $cf->Id ) { return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) ); } my $OCFs = $self->CustomFields; $OCFs->Limit( FIELD => 'id', VALUE => $cf->Id ); unless ( $OCFs->Count ) { return ( 0, $self->loc( "Custom field [_1] does not apply to this object", ref $args{'Field'} ? $args{'Field'}->id : $args{'Field'} ) ); } # empty string is not correct value of any CF, so undef it foreach ( qw(Value LargeContent) ) { $args{ $_ } = undef if defined $args{ $_ } && !length $args{ $_ }; } unless ( $cf->ValidateValue( $args{'Value'} ) ) { return ( 0, $self->loc("Invalid value for custom field") ); } # If the custom field only accepts a certain # of values, delete the existing # value and record a "changed from foo to bar" transaction unless ( $cf->UnlimitedValues ) { # Load up a ObjectCustomFieldValues object for this custom field and this ticket my $values = $cf->ValuesForObject($self); # We need to whack any old values here. In most cases, the custom field should # only have one value to delete. In the pathalogical case, this custom field # used to be a multiple and we have many values to whack.... my $cf_values = $values->Count; if ( $cf_values > $cf->MaxValues ) { my $i = 0; #We want to delete all but the max we can currently have , so we can then # execute the same code to "change" the value from old to new while ( my $value = $values->Next ) { $i++; if ( $i < $cf_values ) { my ( $val, $msg ) = $cf->DeleteValueForObject( Object => $self, Content => $value->Content ); unless ($val) { return ( 0, $msg ); } my ( $TransactionId, $Msg, $TransactionObj ) = $self->_NewTransaction( Type => 'CustomField', Field => $cf->Id, OldReference => $value, Data => $args{'Data'}, ); } } $values->RedoSearch if $i; # redo search if have deleted at least one value } my ( $old_value, $old_content ); if ( $old_value = $values->First ) { $old_content = $old_value->Content; $old_content = undef if defined $old_content && !length $old_content; my $is_the_same = 1; if ( defined $args{'Value'} ) { $is_the_same = 0 unless defined $old_content && lc $old_content eq lc $args{'Value'}; } else { $is_the_same = 0 if defined $old_content; } if ( $is_the_same ) { my $old_content = $old_value->LargeContent; if ( defined $args{'LargeContent'} ) { $is_the_same = 0 unless defined $old_content && $old_content eq $args{'LargeContent'}; } else { $is_the_same = 0 if defined $old_content; } } return $old_value->id if $is_the_same; } my ( $new_value_id, $value_msg ) = $cf->AddValueForObject( Object => $self, Content => $args{'Value'}, LargeContent => $args{'LargeContent'}, ContentType => $args{'ContentType'}, ); unless ( $new_value_id ) { return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg ) ); } my $new_value = RT::ObjectCustomFieldValue->new( $self->CurrentUser ); $new_value->Load( $new_value_id ); # now that adding the new value was successful, delete the old one if ( $old_value ) { my ( $val, $msg ) = $old_value->Delete(); return ( 0, $msg ) unless $val; } if ( $args{'RecordTransaction'} ) { my ( $TransactionId, $Msg, $TransactionObj ) = $self->_NewTransaction( Type => 'CustomField', Field => $cf->Id, OldReference => $old_value, NewReference => $new_value, Data => $args{'Data'}, ); } my $new_content = $new_value->Content; # For datetime, we need to display them in "human" format in result message #XXX TODO how about date without time? if ($cf->Type eq 'DateTime') { my $DateObj = RT::Date->new( $self->CurrentUser ); $DateObj->Set( Format => 'ISO', Value => $new_content, ); $new_content = $DateObj->AsString; if ( defined $old_content && length $old_content ) { $DateObj->Set( Format => 'ISO', Value => $old_content, ); $old_content = $DateObj->AsString; } } unless ( defined $old_content && length $old_content ) { return ( $new_value_id, $self->loc( "[_1] [_2] added", $cf->Name, $new_content )); } elsif ( !defined $new_content || !length $new_content ) { return ( $new_value_id, $self->loc( "[_1] [_2] deleted", $cf->Name, $old_content ) ); } else { return ( $new_value_id, $self->loc( "[_1] [_2] changed to [_3]", $cf->Name, $old_content, $new_content)); } } # otherwise, just add a new value and record "new value added" else { my ($new_value_id, $msg) = $cf->AddValueForObject( Object => $self, Content => $args{'Value'}, LargeContent => $args{'LargeContent'}, ContentType => $args{'ContentType'}, ); unless ( $new_value_id ) { return ( 0, $self->loc( "Could not add new custom field value: [_1]", $msg ) ); } if ( $args{'RecordTransaction'} ) { my ( $tid, $msg ) = $self->_NewTransaction( Type => 'CustomField', Field => $cf->Id, NewReference => $new_value_id, ReferenceType => 'RT::ObjectCustomFieldValue', Data => $args{'Data'}, ); unless ( $tid ) { return ( 0, $self->loc( "Couldn't create a transaction: [_1]", $msg ) ); } } return ( $new_value_id, $self->loc( "[_1] added as a value for [_2]", $args{'Value'}, $cf->Name ) ); } } =head2 DeleteCustomFieldValue { Field => FIELD, Value => VALUE } Deletes VALUE as a value of CustomField FIELD. VALUE can be a string, a CustomFieldValue or a ObjectCustomFieldValue. If VALUE is not a valid value for the custom field, returns (0, 'Error message' ) otherwise, returns (1, 'Success Message') =cut # Need to submit patch to RT for Data option. -Todd sub DeleteCustomFieldValue { my $self = shift; my %args = ( Field => undef, Value => undef, ValueId => undef, Data => undef, @_ ); my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'}); unless ( $cf->Id ) { return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) ); } my ( $val, $msg ) = $cf->DeleteValueForObject( Object => $self, Id => $args{'ValueId'}, Content => $args{'Value'}, ); unless ($val) { return ( 0, $msg ); } my ( $TransactionId, $Msg, $TransactionObj ) = $self->_NewTransaction( Type => 'CustomField', Field => $cf->Id, OldReference => $val, ReferenceType => 'RT::ObjectCustomFieldValue', Data => $args{'Data'}, ); unless ($TransactionId) { return ( 0, $self->loc( "Couldn't create a transaction: [_1]", $Msg ) ); } my $old_value = $TransactionObj->OldValue; # For datetime, we need to display them in "human" format in result message if ( $cf->Type eq 'DateTime' ) { my $DateObj = RT::Date->new( $self->CurrentUser ); $DateObj->Set( Format => 'ISO', Value => $old_value, ); $old_value = $DateObj->AsString; } return ( $TransactionId, $self->loc( "[_1] is no longer a value for custom field [_2]", $old_value, $cf->Name ) ); } # Needed for asset selection when linking sub BasicColumns { ( [ Name => 'Name' ], [ Description => 'Description' ], [ Type => 'Type' ], [ Status => 'Status' ], ); } =head2 UnresolvedDependencies Returns an RT::Tickets object of tickets which this ticket depends on and which have a status of new, open or stalled. (That list comes from RT::Queue->ActiveStatusArray =cut # review -Todd sub UnresolvedDependencies { my $self = shift; my $deps = RTx::AssetTracker::Assets->new($self->CurrentUser); my @live_statuses = RTx::AssetTracker::Type->ActiveStatusArray(); foreach my $status (@live_statuses) { $deps->LimitStatus(VALUE => $status); } $deps->LimitDependedOnBy($self->Id); return($deps); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Scrip.pm000066400000000000000000000643211222742774700241620ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Scrip - an RT Scrip object =head1 SYNOPSIS use RTx::AssetTracker::Scrip; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Scrip; use base 'RTx::AssetTracker::Record'; sub Table {'AT_Scrips'}; use RTx::AssetTracker::Type; use RTx::AssetTracker::Template; use RTx::AssetTracker::ScripCondition; use RTx::AssetTracker::ScripAction; # {{{ sub Create =head2 Create Creates a new entry in the Scrips table. Takes a paramhash with: AssetType => 0, Description => undef, Template => undef, ScripAction => undef, ScripCondition => undef, CustomPrepareCode => undef, CustomCommitCode => undef, CustomIsApplicableCode => undef, Returns (retval, msg); retval is 0 for failure or scrip id. msg is a textual description of what happened. =cut sub Create { my $self = shift; my %args = ( AssetType => 0, Template => 0, # name or id ScripAction => 0, # name or id ScripCondition => 0, # name or id Stage => 'TransactionCreate', Description => undef, CustomPrepareCode => undef, CustomCommitCode => undef, CustomIsApplicableCode => undef, @_ ); if ($args{CustomPrepareCode} || $args{CustomCommitCode} || $args{CustomIsApplicableCode}) { unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'ExecuteCode' ) ) { return ( 0, $self->loc('Permission Denied') ); } } unless ( $args{'AssetType'} ) { unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'ModifyScrips' ) ) { return ( 0, $self->loc('Permission Denied') ); } $args{'AssetType'} = 0; # avoid undef sneaking in } else { my $AssetTypeObj = RTx::AssetTracker::Type->new( $self->CurrentUser ); $AssetTypeObj->Load( $args{'AssetType'} ); unless ( $AssetTypeObj->id ) { return ( 0, $self->loc('Invalid asset type') ); } unless ( $AssetTypeObj->CurrentUserHasRight('ModifyScrips') ) { return ( 0, $self->loc('Permission Denied') ); } $args{'AssetType'} = $AssetTypeObj->id; } #TODO +++ validate input require RTx::AssetTracker::ScripAction; return ( 0, $self->loc("Action is mandatory argument") ) unless $args{'ScripAction'}; my $action = RTx::AssetTracker::ScripAction->new( $self->CurrentUser ); $action->Load( $args{'ScripAction'} ); return ( 0, $self->loc( "Action '[_1]' not found", $args{'ScripAction'} ) ) unless $action->Id; require RTx::AssetTracker::Template; return ( 0, $self->loc("Template is mandatory argument") ) unless $args{'Template'}; my $template = RTx::AssetTracker::Template->new( $self->CurrentUser ); $template->Load( $args{'Template'} ); return ( 0, $self->loc( "Template '[_1]' not found", $args{'Template'} ) ) unless $template->Id; require RTx::AssetTracker::ScripCondition; return ( 0, $self->loc("Condition is mandatory argument") ) unless $args{'ScripCondition'}; my $condition = RTx::AssetTracker::ScripCondition->new( $self->CurrentUser ); $condition->Load( $args{'ScripCondition'} ); return ( 0, $self->loc( "Condition '[_1]' not found", $args{'ScripCondition'} ) ) unless $condition->Id; my ( $id, $msg ) = $self->SUPER::Create( AssetType => $args{'AssetType'}, Template => $template->Id, ScripCondition => $condition->id, Stage => $args{'Stage'}, ScripAction => $action->Id, Description => $args{'Description'}, CustomPrepareCode => $args{'CustomPrepareCode'}, CustomCommitCode => $args{'CustomCommitCode'}, CustomIsApplicableCode => $args{'CustomIsApplicableCode'}, ); if ( $id ) { return ( $id, $self->loc('Scrip Created') ); } else { return ( $id, $msg ); } } =head2 Delete Delete this object =cut sub Delete { my $self = shift; unless ( $self->CurrentUserHasRight('ModifyScrips') ) { return ( 0, $self->loc('Permission Denied') ); } return ( $self->SUPER::Delete(@_) ); } =head2 AssetTypeObj Retuns an RTx::AssetTracker::Type object with this Scrip's asset type =cut sub AssetTypeObj { my $self = shift; if ( !$self->{'AssetTypeObj'} ) { require RTx::AssetTracker::Type; $self->{'AssetTypeObj'} = RTx::AssetTracker::Type->new( $self->CurrentUser ); $self->{'AssetTypeObj'}->Load( $self->__Value('AssetType') ); } return ( $self->{'AssetTypeObj'} ); } =head2 ActionObj Retuns an RT::Action object with this Scrip's Action =cut sub ActionObj { my $self = shift; unless ( defined $self->{'ScripActionObj'} ) { require RTx::AssetTracker::ScripAction; $self->{'ScripActionObj'} = RTx::AssetTracker::ScripAction->new( $self->CurrentUser ); #TODO: why are we loading Actions with templates like this. # two separate methods might make more sense $self->{'ScripActionObj'}->Load( $self->ScripAction, $self->Template ); } return ( $self->{'ScripActionObj'} ); } =head2 ConditionObj Retuns an L object with this Scrip's IsApplicable =cut sub ConditionObj { my $self = shift; my $res = RTx::AssetTracker::ScripCondition->new( $self->CurrentUser ); $res->Load( $self->ScripCondition ); return $res; } =head2 LoadModules Loads scrip's condition and action modules. =cut sub LoadModules { my $self = shift; $self->ConditionObj->LoadCondition; $self->ActionObj->LoadAction; } =head2 TemplateObj Retuns an RTx::AssetTracker::Template object with this Scrip's Template =cut sub TemplateObj { my $self = shift; unless ( defined $self->{'TemplateObj'} ) { require RTx::AssetTracker::Template; $self->{'TemplateObj'} = RTx::AssetTracker::Template->new( $self->CurrentUser ); $self->{'TemplateObj'}->Load( $self->Template ); } return ( $self->{'TemplateObj'} ); } =head2 Apply { AssetObj => undef, TransactionObj => undef} This method instantiates the ScripCondition and ScripAction objects for a single execution of this scrip. it then calls the IsApplicable method of the ScripCondition. If that succeeds, it calls the Prepare method of the ScripAction. If that succeeds, it calls the Commit method of the ScripAction. Usually, the asset and transaction objects passed to this method should be loaded by the SuperUser role =cut # XXX TODO : This code appears to be obsoleted in favor of similar code in Scrips->Apply. # Why is this here? Is it still called? sub Apply { my $self = shift; my %args = ( AssetObj => undef, TransactionObj => undef, @_ ); $RT::Logger->debug("Now applying scrip ".$self->Id . " for transaction ".$args{'TransactionObj'}->id); my $ApplicableTransactionObj = $self->IsApplicable( AssetObj => $args{'AssetObj'}, TransactionObj => $args{'TransactionObj'} ); unless ( $ApplicableTransactionObj ) { return undef; } if ( $ApplicableTransactionObj->id != $args{'TransactionObj'}->id ) { $RT::Logger->debug("Found an applicable transaction ".$ApplicableTransactionObj->Id . " in the same batch with transaction ".$args{'TransactionObj'}->id); } #If it's applicable, prepare and commit it $RT::Logger->debug("Now preparing scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id); unless ( $self->Prepare( AssetObj => $args{'AssetObj'}, TransactionObj => $ApplicableTransactionObj ) ) { return undef; } $RT::Logger->debug("Now commiting scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id); unless ( $self->Commit( AssetObj => $args{'AssetObj'}, TransactionObj => $ApplicableTransactionObj) ) { return undef; } $RT::Logger->debug("We actually finished scrip ".$self->Id . " for transaction ".$ApplicableTransactionObj->id); return (1); } =head2 IsApplicable Calls the Condition object's IsApplicable method Upon success, returns the applicable Transaction object. Otherwise, undef is returned. If the Scrip is in the TransactionCreate Stage (the usual case), only test the associated Transaction object to see if it is applicable. For Scrips in the TransactionBatch Stage, test all Transaction objects created during the Asset object's lifetime, and returns the first one that is applicable. =cut sub IsApplicable { my $self = shift; my %args = ( AssetObj => undef, TransactionObj => undef, @_ ); my $return; eval { my @Transactions; if ( $self->Stage eq 'TransactionCreate') { # Only look at our current Transaction @Transactions = ( $args{'TransactionObj'} ); } elsif ( $self->Stage eq 'TransactionBatch') { # Look at all Transactions in this Batch @Transactions = @{ $args{'AssetObj'}->TransactionBatch || [] }; } else { $RT::Logger->error( "Unknown Scrip stage:" . $self->Stage ); return (undef); } my $ConditionObj = $self->ConditionObj; foreach my $TransactionObj ( @Transactions ) { # in TxnBatch stage we can select scrips that are not applicable to all txns my $txn_type = $TransactionObj->Type; next unless( $ConditionObj->ApplicableTransTypes =~ /(?:^|,)(?:Any|\Q$txn_type\E)(?:,|$)/i ); # Load the scrip's Condition object $ConditionObj->LoadCondition( ScripObj => $self, AssetObj => $args{'AssetObj'}, TransactionObj => $TransactionObj, ); if ( $ConditionObj->IsApplicable() ) { # We found an application Transaction -- return it $return = $TransactionObj; last; } } }; if ($@) { $RT::Logger->error( "Scrip IsApplicable " . $self->Id . " died. - " . $@ ); return (undef); } return ($return); } =head2 Prepare Calls the action object's prepare method =cut sub Prepare { my $self = shift; my %args = ( AssetObj => undef, TransactionObj => undef, @_ ); my $return; eval { $self->ActionObj->LoadAction( ScripObj => $self, AssetObj => $args{'AssetObj'}, TransactionObj => $args{'TransactionObj'}, ); $return = $self->ActionObj->Prepare(); }; if ($@) { $RT::Logger->error( "Scrip Prepare " . $self->Id . " died. - " . $@ ); return (undef); } unless ($return) { } return ($return); } =head2 Commit Calls the action object's commit method =cut sub Commit { my $self = shift; my %args = ( AssetObj => undef, TransactionObj => undef, @_ ); my $return; eval { $return = $self->ActionObj->Commit(); }; #Searchbuilder caching isn't perfectly coherent. got to reload the asset object, since it # may have changed $args{'AssetObj'}->Load( $args{'AssetObj'}->Id ); if ($@) { $RT::Logger->error( "Scrip Commit " . $self->Id . " died. - " . $@ ); return (undef); } # Not destroying or weakening hte Action and Condition here could cause a # leak return ($return); } # does an acl check and then passes off the call sub _Set { my $self = shift; my %args = ( Field => undef, Value => undef, @_, ); unless ( $self->CurrentUserHasRight('ModifyScrips') ) { $RT::Logger->debug( "CurrentUser can't modify Scrips for " . $self->AssetType . "\n" ); return ( 0, $self->loc('Permission Denied') ); } if (exists $args{Value}) { if ($args{Field} eq 'CustomIsApplicableCode' || $args{Field} eq 'CustomPrepareCode' || $args{Field} eq 'CustomCommitCode') { unless ( $self->CurrentUser->HasRight( Object => $RT::System, Right => 'ExecuteCode' ) ) { return ( 0, $self->loc('Permission Denied') ); } } elsif ($args{Field} eq 'AssetType') { if ($args{Value}) { # moving to another asset type my $assettype = RTx::AssetTracker::Type->new( $self->CurrentUser ); $assettype->Load($args{Value}); unless ($assettype->Id and $assettype->CurrentUserHasRight('ModifyScrips')) { return ( 0, $self->loc('Permission Denied') ); } } else { # moving to global unless ($self->CurrentUser->HasRight( Object => RT->System, Right => 'ModifyScrips' )) { return ( 0, $self->loc('Permission Denied') ); } } } elsif ($args{Field} eq 'Template') { my $template = RT::Template->new( $self->CurrentUser ); $template->Load($args{Value}); unless ($template->Id and $template->CurrentUserCanRead) { return ( 0, $self->loc('Permission Denied') ); } } } return $self->SUPER::_Set(@_); } # does an acl check and then passes off the call sub _Value { my $self = shift; unless ( $self->CurrentUserHasRight('ShowScrips') ) { $RT::Logger->debug( "CurrentUser can't modify Scrips for " . $self->__Value('AssetType') . "\n" ); return (undef); } return $self->__Value(@_); } =head2 CurrentUserHasRight Helper menthod for HasRight. Presets Principal to CurrentUser then calls HasRight. =cut sub CurrentUserHasRight { my $self = shift; my $right = shift; return ( $self->HasRight( Principal => $self->CurrentUser->UserObj, Right => $right ) ); } =head2 HasRight Takes a param-hash consisting of "Right" and "Principal" Principal is an RT::User object or an RT::CurrentUser object. "Right" is a textual Right string that applies to Scrips. =cut sub HasRight { my $self = shift; my %args = ( Right => undef, Principal => undef, @_ ); if ( $self->SUPER::_Value('AssetType') ) { return $args{'Principal'}->HasRight( Right => $args{'Right'}, Object => $self->AssetTypeObj ); } else { return $args{'Principal'}->HasRight( Object => $RT::System, Right => $args{'Right'}, ); } } =head2 CompileCheck This routine compile-checks the custom prepare, commit, and is-applicable code to see if they are syntactically valid Perl. We eval them in a codeblock to avoid actually executing the code. If one of the fields has a compile error, only the first is reported. Returns an (ok, message) pair. =cut sub CompileCheck { my $self = shift; for my $method (qw/CustomPrepareCode CustomCommitCode CustomIsApplicableCode/) { my $code = $self->$method; next if !defined($code); do { no strict 'vars'; eval "sub { $code \n }"; }; next if !$@; my $error = $@; return (0, $self->loc("Couldn't compile [_1] codeblock '[_2]': [_3]", $method, $code, $error)); } } =head2 SetScripAction =cut sub SetScripAction { my $self = shift; my $value = shift; return ( 0, $self->loc("Action is mandatory argument") ) unless $value; require RTx::AssetTracker::ScripAction; my $action = RTx::AssetTracker::ScripAction->new( $self->CurrentUser ); $action->Load($value); return ( 0, $self->loc( "Action '[_1]' not found", $value ) ) unless $action->Id; return $self->_Set( Field => 'ScripAction', Value => $action->Id ); } =head2 SetScripCondition =cut sub SetScripCondition { my $self = shift; my $value = shift; return ( 0, $self->loc("Condition is mandatory argument") ) unless $value; require RTx::AssetTracker::ScripCondition; my $condition = RTx::AssetTracker::ScripCondition->new( $self->CurrentUser ); $condition->Load($value); return ( 0, $self->loc( "Condition '[_1]' not found", $value ) ) unless $condition->Id; return $self->_Set( Field => 'ScripCondition', Value => $condition->Id ); } =head2 SetTemplate =cut sub SetTemplate { my $self = shift; my $value = shift; return ( 0, $self->loc("Template is mandatory argument") ) unless $value; require RTx::AssetTracker::Template; my $template = RTx::AssetTracker::Template->new( $self->CurrentUser ); $template->Load($value); return ( 0, $self->loc( "Template '[_1]' not found", $value ) ) unless $template->Id; return $self->_Set( Field => 'Template', Value => $template->Id ); } =head2 id Returns the current value of id. (In the database, id is stored as int(11).) =cut =head2 Description Returns the current value of Description. (In the database, Description is stored as varchar(255).) =head2 SetDescription VALUE Set Description to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Description will be stored as a varchar(255).) =cut =head2 ScripCondition Returns the current value of ScripCondition. (In the database, ScripCondition is stored as int(11).) =head2 SetScripCondition VALUE Set ScripCondition to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, ScripCondition will be stored as a int(11).) =cut =head2 ScripConditionObj Returns the ScripCondition Object which has the id returned by ScripCondition =cut sub ScripConditionObj { my $self = shift; my $ScripCondition = RTx::AssetTracker::ScripCondition->new($self->CurrentUser); $ScripCondition->Load($self->__Value('ScripCondition')); return($ScripCondition); } =head2 ScripAction Returns the current value of ScripAction. (In the database, ScripAction is stored as int(11).) =head2 SetScripAction VALUE Set ScripAction to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, ScripAction will be stored as a int(11).) =cut =head2 ScripActionObj Returns the ScripAction Object which has the id returned by ScripAction =cut sub ScripActionObj { my $self = shift; my $ScripAction = RTx::AssetTracker::ScripAction->new($self->CurrentUser); $ScripAction->Load($self->__Value('ScripAction')); return($ScripAction); } =head2 ConditionRules Returns the current value of ConditionRules. (In the database, ConditionRules is stored as text.) =head2 SetConditionRules VALUE Set ConditionRules to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, ConditionRules will be stored as a text.) =cut =head2 ActionRules Returns the current value of ActionRules. (In the database, ActionRules is stored as text.) =head2 SetActionRules VALUE Set ActionRules to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, ActionRules will be stored as a text.) =cut =head2 CustomIsApplicableCode Returns the current value of CustomIsApplicableCode. (In the database, CustomIsApplicableCode is stored as text.) =head2 SetCustomIsApplicableCode VALUE Set CustomIsApplicableCode to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, CustomIsApplicableCode will be stored as a text.) =cut =head2 CustomPrepareCode Returns the current value of CustomPrepareCode. (In the database, CustomPrepareCode is stored as text.) =head2 SetCustomPrepareCode VALUE Set CustomPrepareCode to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, CustomPrepareCode will be stored as a text.) =cut =head2 CustomCommitCode Returns the current value of CustomCommitCode. (In the database, CustomCommitCode is stored as text.) =head2 SetCustomCommitCode VALUE Set CustomCommitCode to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, CustomCommitCode will be stored as a text.) =cut =head2 Stage Returns the current value of Stage. (In the database, Stage is stored as varchar(32).) =head2 SetStage VALUE Set Stage to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Stage will be stored as a varchar(32).) =cut =head2 AssetType Returns the current value of AssetType. (In the database, AssetType is stored as int(11).) =head2 SetAssetType VALUE Set AssetType to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, AssetType will be stored as a int(11).) =cut =head2 Template Returns the current value of Template. (In the database, Template is stored as int(11).) =head2 SetTemplate VALUE Set Template to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Template will be stored as a int(11).) =cut =head2 Creator Returns the current value of Creator. (In the database, Creator is stored as int(11).) =cut =head2 Created Returns the current value of Created. (In the database, Created is stored as datetime.) =cut =head2 LastUpdatedBy Returns the current value of LastUpdatedBy. (In the database, LastUpdatedBy is stored as int(11).) =cut =head2 LastUpdated Returns the current value of LastUpdated. (In the database, LastUpdated is stored as datetime.) =cut sub _CoreAccessible { { id => {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, Description => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''}, ScripCondition => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, ScripAction => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, ConditionRules => {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''}, ActionRules => {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''}, CustomIsApplicableCode => {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''}, CustomPrepareCode => {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''}, CustomCommitCode => {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''}, Stage => {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => ''}, AssetType => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Template => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Creator => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Created => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, LastUpdatedBy => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, LastUpdated => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, } }; RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/ScripAction.pm000066400000000000000000000244551222742774700253240ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::ScripAction - RT Action object =head1 SYNOPSIS use RTx::AssetTracker::ScripAction; =head1 DESCRIPTION This module should never be called directly by client code. it's an internal module which should only be accessed through exported APIs in other modules. =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::ScripAction; use base 'RTx::AssetTracker::Record'; sub Table {'AT_ScripActions'}; use RTx::AssetTracker::Template; sub _Accessible { my $self = shift; my %Cols = ( Name => 'read', Description => 'read', ExecModule => 'read', Argument => 'read', Creator => 'read/auto', Created => 'read/auto', LastUpdatedBy => 'read/auto', LastUpdated => 'read/auto' ); return($self->SUPER::_Accessible(@_, %Cols)); } =head2 Create Takes a hash. Creates a new Action entry. should be better documented. =cut sub Create { my $self = shift; #TODO check these args and do smart things. return($self->SUPER::Create(@_)); } sub Delete { my $self = shift; return (0, "ScripAction->Delete not implemented"); } =head2 Load IDENTIFIER Loads an action by its Name. Returns: Id, Error Message =cut sub Load { my $self = shift; my $identifier = shift; if (!$identifier) { return (0, $self->loc('Input error')); } my ($ok, $msg); if ($identifier !~ /\D/) { ($ok, $msg) = $self->SUPER::Load($identifier); } else { ($ok, $msg) = $self->LoadByCol('Name', $identifier); } if (@_) { # Set the template Id to the passed in template my $template = shift; $self->{'Template'} = $template; } return ($ok, $msg); } =head2 LoadAction HASH Takes a hash consisting of AssetObj and TransactionObj. Loads an RT::Action:: module. =cut sub LoadAction { my $self = shift; my %args = ( TransactionObj => undef, AssetObj => undef, @_ ); $self->{_AssetObj} = $args{AssetObj}; #TODO: Put this in an eval $self->ExecModule =~ /^(\w+)$/; my $module = $1; my $type = "RT::Action::". $module; eval "require $type" || die "Require of $type failed.\n$@\n"; $self->{'Action'} = $type->new ( Argument => $self->Argument, CurrentUser => $self->CurrentUser, ScripActionObj => $self, ScripObj => $args{'ScripObj'}, TemplateObj => $self->TemplateObj, TicketObj => $args{'AssetObj'}, TransactionObj => $args{'TransactionObj'}, ); } =head2 TemplateObj Return this action's template object TODO: Why are we not using the Scrip's template object? =cut sub TemplateObj { my $self = shift; return undef unless $self->{Template}; if ( !$self->{'TemplateObj'} ) { $self->{'TemplateObj'} = RTx::AssetTracker::Template->new( $self->CurrentUser ); $self->{'TemplateObj'}->LoadById( $self->{'Template'} ); if ( ( $self->{'TemplateObj'}->__Value('AssetType') == 0 ) && $self->{'_AssetObj'} ) { my $tmptemplate = RTx::AssetTracker::Template->new( $self->CurrentUser ); my ( $ok, $err ) = $tmptemplate->LoadAssetTypeTemplate( AssetType => $self->{'_AssetObj'}->AssetTypeObj->id, Name => $self->{'TemplateObj'}->Name); if ( $tmptemplate->id ) { # found the queue-specific template with the same name $self->{'TemplateObj'} = $tmptemplate; } } } return ( $self->{'TemplateObj'} ); } # The following methods call the action object sub Prepare { my $self = shift; $self->{_Message_ID} = 0; return ($self->Action->Prepare()); } sub Commit { my $self = shift; return($self->Action->Commit()); } sub Describe { my $self = shift; return ($self->Action->Describe()); } =head2 Action Return the actual RT::Action object for this scrip. =cut sub Action { my $self = shift; return ($self->{'Action'}); } sub DESTROY { my $self=shift; $self->{'_AssetObj'} = undef; $self->{'Action'} = undef; $self->{'TemplateObj'} = undef; } =head2 TODO Between this, RTx::AssetTracker::Scrip and RT::Action::*, we need to be able to get rid of a class. This just reeks of too much complexity -- jesse =cut package RT::Action; sub AssetObj { $_[0]->{TicketObj} } package RTx::AssetTracker::ScripAction; =head2 id Returns the current value of id. (In the database, id is stored as int(11).) =cut =head2 Name Returns the current value of Name. (In the database, Name is stored as varchar(200).) =head2 SetName VALUE Set Name to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Name will be stored as a varchar(200).) =cut =head2 Description Returns the current value of Description. (In the database, Description is stored as varchar(255).) =head2 SetDescription VALUE Set Description to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Description will be stored as a varchar(255).) =cut =head2 ExecModule Returns the current value of ExecModule. (In the database, ExecModule is stored as varchar(60).) =head2 SetExecModule VALUE Set ExecModule to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, ExecModule will be stored as a varchar(60).) =cut =head2 Argument Returns the current value of Argument. (In the database, Argument is stored as varbinary(255).) =head2 SetArgument VALUE Set Argument to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Argument will be stored as a varbinary(255).) =cut =head2 Creator Returns the current value of Creator. (In the database, Creator is stored as int(11).) =cut =head2 Created Returns the current value of Created. (In the database, Created is stored as datetime.) =cut =head2 LastUpdatedBy Returns the current value of LastUpdatedBy. (In the database, LastUpdatedBy is stored as int(11).) =cut =head2 LastUpdated Returns the current value of LastUpdated. (In the database, LastUpdated is stored as datetime.) =cut sub _CoreAccessible { { id => {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, Name => {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''}, Description => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''}, ExecModule => {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''}, Argument => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varbinary(255)', default => ''}, Creator => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Created => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, LastUpdatedBy => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, LastUpdated => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, } }; ### Shredder methods ### use RT::Shredder::Constants; use RT::Shredder::Exceptions; use RT::Shredder::Dependencies; sub __DependsOn { my $self = shift; my %args = ( Shredder => undef, Dependencies => undef, @_, ); my $deps = $args{'Dependencies'}; my $list = []; # Scrips my $objs = RTx::AssetTracker::Scrips->new( $self->CurrentUser ); $objs->Limit( FIELD => 'ScripAction', VALUE => $self->Id ); $deps->_PushDependencies( BaseObject => $self, Flags => DEPENDS_ON, TargetObjects => $objs, Shredder => $args{'Shredder'} ); return $self->SUPER::__DependsOn( %args ); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/ScripActions.pm000066400000000000000000000057711222742774700255070ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::ScripActions - Collection of Action objects =head1 SYNOPSIS use RTx::AssetTracker::ScripActions; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::ScripActions; use base 'RT::SearchBuilder'; use RTx::AssetTracker::ScripAction; sub Table {'AT_ScripActions'}; sub _Init { my $self = shift; $self->{'table'} = "AT_ScripActions"; $self->{'primary_key'} = "id"; return ( $self->SUPER::_Init(@_)); } sub LimitToType { my $self = shift; my $type = shift; $self->Limit (ENTRYAGGREGATOR => 'OR', FIELD => 'Type', VALUE => "$type") if defined $type; $self->Limit (ENTRYAGGREGATOR => 'OR', FIELD => 'Type', VALUE => "Correspond") if $type eq "Create"; $self->Limit (ENTRYAGGREGATOR => 'OR', FIELD => 'Type', VALUE => 'any'); } =head2 NewItem Returns an empty new RTx::AssetTracker::ScripAction item =cut sub NewItem { my $self = shift; return(RTx::AssetTracker::ScripAction->new($self->CurrentUser)); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/ScripCondition.pm000066400000000000000000000225241222742774700260300ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::ScripCondition - RT scrip conditional =head1 SYNOPSIS use RTx::AssetTracker::ScripCondition; =head1 DESCRIPTION This module should never be called directly by client code. it's an internal module which should only be accessed through exported APIs in other modules. =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::ScripCondition; use base 'RTx::AssetTracker::Record'; sub Table {'AT_ScripConditions'}; sub _Init { my $self = shift; $self->{'table'} = "AT_ScripConditions"; return ($self->SUPER::_Init(@_)); } sub _Accessible { my $self = shift; my %Cols = ( Name => 'read', Description => 'read', ApplicableTransTypes => 'read', ExecModule => 'read', Argument => 'read', Creator => 'read/auto', Created => 'read/auto', LastUpdatedBy => 'read/auto', LastUpdated => 'read/auto' ); return($self->SUPER::_Accessible(@_, %Cols)); } =head2 Create Takes a hash. Creates a new Condition entry. should be better documented. =cut sub Create { my $self = shift; return($self->SUPER::Create(@_)); } =head2 Delete No API available for deleting things just yet. =cut sub Delete { my $self = shift; return(0, $self->loc('Unimplemented')); } =head2 Load IDENTIFIER Loads a condition takes a name or ScripCondition id. =cut sub Load { my $self = shift; my $identifier = shift; unless (defined $identifier) { return (undef); } if ($identifier !~ /\D/) { return ($self->SUPER::LoadById($identifier)); } else { return ($self->LoadByCol('Name', $identifier)); } } =head2 LoadCondition HASH takes a hash which has the following elements: TransactionObj and AssetObj. Loads the Condition module in question. =cut sub LoadCondition { my $self = shift; my %args = ( TransactionObj => undef, AssetObj => undef, @_ ); #TODO: Put this in an eval $self->ExecModule =~ /^(\w+)$/; my $module = $1; my $type = "RT::Condition::". $module; eval "require $type" || die "Require of $type failed.\n$@\n"; $self->{'Condition'} = $type->new ( 'ScripConditionObj' => $self, 'TicketObj' => $args{'AssetObj'}, 'ScripObj' => $args{'ScripObj'}, 'TransactionObj' => $args{'TransactionObj'}, 'Argument' => $self->Argument, 'ApplicableTransTypes' => $self->ApplicableTransTypes, CurrentUser => $self->CurrentUser ); } =head2 Describe Helper method to call the condition module's Describe method. =cut sub Describe { my $self = shift; return ($self->{'Condition'}->Describe()); } =head2 IsApplicable Helper method to call the condition module's IsApplicable method. =cut sub IsApplicable { my $self = shift; return ($self->{'Condition'}->IsApplicable()); } sub DESTROY { my $self=shift; $self->{'Condition'} = undef; } package RT::Condition; sub AssetObj { $_[0]->{TicketObj} } package RTx::AssetTracker::ScripCondition; =head2 id Returns the current value of id. (In the database, id is stored as int(11).) =cut =head2 Name Returns the current value of Name. (In the database, Name is stored as varchar(200).) =head2 SetName VALUE Set Name to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Name will be stored as a varchar(200).) =cut =head2 Description Returns the current value of Description. (In the database, Description is stored as varchar(255).) =head2 SetDescription VALUE Set Description to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Description will be stored as a varchar(255).) =cut =head2 ExecModule Returns the current value of ExecModule. (In the database, ExecModule is stored as varchar(60).) =head2 SetExecModule VALUE Set ExecModule to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, ExecModule will be stored as a varchar(60).) =cut =head2 Argument Returns the current value of Argument. (In the database, Argument is stored as varbinary(255).) =head2 SetArgument VALUE Set Argument to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Argument will be stored as a varbinary(255).) =cut =head2 ApplicableTransTypes Returns the current value of ApplicableTransTypes. (In the database, ApplicableTransTypes is stored as varchar(60).) =head2 SetApplicableTransTypes VALUE Set ApplicableTransTypes to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, ApplicableTransTypes will be stored as a varchar(60).) =cut =head2 Creator Returns the current value of Creator. (In the database, Creator is stored as int(11).) =cut =head2 Created Returns the current value of Created. (In the database, Created is stored as datetime.) =cut =head2 LastUpdatedBy Returns the current value of LastUpdatedBy. (In the database, LastUpdatedBy is stored as int(11).) =cut =head2 LastUpdated Returns the current value of LastUpdated. (In the database, LastUpdated is stored as datetime.) =cut sub _CoreAccessible { { id => {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, Name => {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''}, Description => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''}, ExecModule => {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''}, Argument => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varbinary(255)', default => ''}, ApplicableTransTypes => {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''}, Creator => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Created => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, LastUpdatedBy => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, LastUpdated => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, } }; ### Shredder methods ### use RT::Shredder::Constants; use RT::Shredder::Exceptions; use RT::Shredder::Dependencies; sub __DependsOn { my $self = shift; my %args = ( Shredder => undef, Dependencies => undef, @_, ); my $deps = $args{'Dependencies'}; my $list = []; # Scrips my $objs = RTx::AssetTracker::Scrips->new( $self->CurrentUser ); $objs->Limit( FIELD => 'ScripCondition', VALUE => $self->Id ); $deps->_PushDependencies( BaseObject => $self, Flags => DEPENDS_ON, TargetObjects => $objs, Shredder => $args{'Shredder'} ); return $self->SUPER::__DependsOn( %args ); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/ScripConditions.pm000066400000000000000000000060241222742774700262100ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::ScripConditions - Collection of Action objects =head1 SYNOPSIS use RTx::AssetTracker::ScripConditions; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::ScripConditions; use base 'RT::SearchBuilder'; use RTx::AssetTracker::ScripCondition; sub Table {'AT_ScripConditions'}; sub _Init { my $self = shift; $self->{'table'} = "AT_ScripConditions"; $self->{'primary_key'} = "id"; return ( $self->SUPER::_Init(@_)); } sub LimitToType { my $self = shift; my $type = shift; $self->Limit (ENTRYAGGREGATOR => 'OR', FIELD => 'Type', VALUE => "$type") if defined $type; $self->Limit (ENTRYAGGREGATOR => 'OR', FIELD => 'Type', VALUE => "Correspond") if $type eq "Create"; $self->Limit (ENTRYAGGREGATOR => 'OR', FIELD => 'Type', VALUE => 'any'); } =head2 NewItem Returns an empty new RTx::AssetTracker::ScripCondition item =cut sub NewItem { my $self = shift; return(RTx::AssetTracker::ScripCondition->new($self->CurrentUser)); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Scrips.pm000066400000000000000000000252761222742774700243530ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Scrips - a collection of RT Scrip objects =head1 SYNOPSIS use RTx::AssetTracker::Scrips; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Scrips; use base 'RT::SearchBuilder'; use RTx::AssetTracker::Scrip; sub Table {'AT_Scrips'}; =head2 LimitToAssetType Takes an asset type id (numerical) as its only argument. Makes sure that Scopes it pulls out apply to this asset type (or another that you've selected with another call to this method =cut sub LimitToAssetType { my $self = shift; my $assettype = shift; $self->Limit(ENTRYAGGREGATOR => 'OR', FIELD => 'AssetType', VALUE => "$assettype") if defined $assettype; } =head2 LimitToGlobal Makes sure that Scopes it pulls out apply to all asset types (or another that you've selected with another call to this method or LimitToAssetType =cut sub LimitToGlobal { my $self = shift; $self->Limit (ENTRYAGGREGATOR => 'OR', FIELD => 'AssetType', VALUE => 0); } # {{{ sub Next =head2 Next Returns the next scrip that this user can see. =cut sub Next { my $self = shift; my $Scrip = $self->SUPER::Next(); if ((defined($Scrip)) and (ref($Scrip))) { if ($Scrip->CurrentUserHasRight('ShowScrips')) { return($Scrip); } #If the user doesn't have the right to show this scrip else { return($self->Next()); } } #if there never was any scrip else { return(undef); } } =head2 Apply Run through the relevant scrips. Scrips will run in order based on description. (Most common use case is to prepend a number to the description, forcing the scrips to run in ascending alphanumerical order.) =cut sub Apply { my $self = shift; my %args = ( AssetObj => undef, Asset => undef, Transaction => undef, TransactionObj => undef, Stage => undef, Type => undef, @_ ); $self->Prepare(%args); $self->Commit(); } =head2 Commit Commit all of this object's prepared scrips =cut sub Commit { my $self = shift; foreach my $scrip (@{$self->Prepared}) { $RT::Logger->debug( "Committing scrip #". $scrip->id ." on txn #". $self->{'TransactionObj'}->id ." of asset #". $self->{'AssetObj'}->id ); $scrip->Commit( AssetObj => $self->{'AssetObj'}, TransactionObj => $self->{'TransactionObj'} ); } } =head2 Prepare Only prepare the scrips, returning an array of the scrips we're interested in in order of preparation, not execution =cut sub Prepare { my $self = shift; my %args = ( AssetObj => undef, Asset => undef, Transaction => undef, TransactionObj => undef, Stage => undef, Type => undef, @_ ); #We're really going to need a non-acled asset for the scrips to work $self->_SetupSourceObjects( AssetObj => $args{'AssetObj'}, Asset => $args{'Asset'}, TransactionObj => $args{'TransactionObj'}, Transaction => $args{'Transaction'} ); $self->_FindScrips( Stage => $args{'Stage'}, Type => $args{'Type'} ); #Iterate through each script and check it's applicability. while ( my $scrip = $self->Next() ) { unless ( $scrip->IsApplicable( AssetObj => $self->{'AssetObj'}, TransactionObj => $self->{'TransactionObj'} ) ) { $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it isn't applicable"); next; } #If it's applicable, prepare and commit it unless ( $scrip->Prepare( AssetObj => $self->{'AssetObj'}, TransactionObj => $self->{'TransactionObj'} ) ) { $RT::Logger->debug("Skipping Scrip #".$scrip->Id." because it didn't Prepare"); next; } push @{$self->{'prepared_scrips'}}, $scrip; } return (@{$self->Prepared}); }; =head2 Prepared Returns an arrayref of the scrips this object has prepared =cut sub Prepared { my $self = shift; return ($self->{'prepared_scrips'} || []); } =head2 _SetupSourceObjects { AssetObj , Asset, Transaction, TransactionObj } Setup an asset and transaction for this Scrip collection to work with as it runs through the relevant scrips. (Also to figure out which scrips apply) Returns: nothing =cut sub _SetupSourceObjects { my $self = shift; my %args = ( AssetObj => undef, Asset => undef, Transaction => undef, TransactionObj => undef, @_ ); if ( $args{'AssetObj'} ) { # This loads a clean copy of the Asset object to ensure that we # don't accidentally escalate the privileges of the passed in # asset (this function can be invoked from the UI). # We copy the TransactionBatch transactions so that Scrips # running against the new Asset will have access to them. We # use RanTransactionBatch to guard against running # TransactionBatch Scrips more than once. $self->{'AssetObj'} = RTx::AssetTracker::Asset->new( $self->CurrentUser ); $self->{'AssetObj'}->Load( $args{'AssetObj'}->Id ); if ( $args{'AssetObj'}->TransactionBatch ) { # try to ensure that we won't infinite loop if something dies, triggering DESTROY while # we have the _TransactionBatch objects; $self->{'AssetObj'}->RanTransactionBatch(1); $self->{'AssetObj'}->{'_TransactionBatch'} = $args{'AssetObj'}->{'_TransactionBatch'}; } } else { $self->{'AssetObj'} = RTx::AssetTracker::Asset->new( $self->CurrentUser ); $self->{'AssetObj'}->Load( $args{'Asset'} ) || $RT::Logger->err("$self couldn't load asset $args{'Asset'}"); } if ( ( $self->{'TransactionObj'} = $args{'TransactionObj'} ) ) { $self->{'TransactionObj'}->CurrentUser( $self->CurrentUser ); } else { $self->{'TransactionObj'} = RT::Transaction->new( $self->CurrentUser ); $self->{'TransactionObj'}->Load( $args{'Transaction'} ) || $RT::Logger->err( "$self couldn't load transaction $args{'Transaction'}"); } } =head2 _FindScrips Find only the apropriate scrips for whatever we're doing now. Order them by their description. (Most common use case is to prepend a number to the description, forcing the scrips to display and run in ascending alphanumerical order.) =cut sub _FindScrips { my $self = shift; my %args = ( Stage => undef, Type => undef, @_ ); $self->LimitToAssetType( $self->{'AssetObj'}->AssetTypeObj->Id ); #Limit it to $Asset->AssetTypeObj->Id $self->LimitToGlobal(); # or to "global" $self->Limit( FIELD => "Stage", VALUE => $args{'Stage'} ); my $ConditionsAlias = $self->NewAlias('AT_ScripConditions'); $self->Join( ALIAS1 => 'main', FIELD1 => 'ScripCondition', ALIAS2 => $ConditionsAlias, FIELD2 => 'id' ); #We only want things where the scrip applies to this sort of transaction # TransactionBatch stage can define list of transaction foreach( split /\s*,\s*/, ($args{'Type'} || '') ) { $self->Limit( ALIAS => $ConditionsAlias, FIELD => 'ApplicableTransTypes', OPERATOR => 'LIKE', VALUE => $_, ENTRYAGGREGATOR => 'OR', ) } # Or where the scrip applies to any transaction $self->Limit( ALIAS => $ConditionsAlias, FIELD => 'ApplicableTransTypes', OPERATOR => 'LIKE', VALUE => "Any", ENTRYAGGREGATOR => 'OR', ); # Promise some kind of ordering $self->OrderBy( FIELD => 'Description' ); # we call Count below, but later we always do search # so just do search and get count from results $self->_DoSearch if $self->{'must_redo_search'}; $RT::Logger->debug( "Found ". $self->Count ." scrips for $args{'Stage'} stage" ." with applicable type(s) $args{'Type'}" ." for txn #".$self->{TransactionObj}->Id ." on asset #".$self->{AssetObj}->Id ); } =head2 NewItem Returns an empty new RTx::AssetTracker::Scrip item =cut sub NewItem { my $self = shift; return(RTx::AssetTracker::Scrip->new($self->CurrentUser)); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Template.pm000066400000000000000000000636651222742774700246670ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} # Portions Copyright 2000 Tobias Brox =head1 NAME RTx::AssetTracker::Template - RT's template object =head1 SYNOPSIS use RTx::AssetTracker::Template; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Template; use base 'RT::Record'; sub Table {'AT_Templates'}; use RTx::AssetTracker::Asset; use Text::Template; use MIME::Entity; use MIME::Parser; sub _Accessible { my $self = shift; my %Cols = ( id => 'read', Name => 'read/write', Description => 'read/write', Type => 'read/write', #Type is one of Perl or Simple Content => 'read/write', AssetType => 'read/write', Creator => 'read/auto', Created => 'read/auto', LastUpdatedBy => 'read/auto', LastUpdated => 'read/auto' ); return $self->SUPER::_Accessible( @_, %Cols ); } sub _Set { my $self = shift; my %args = ( Field => undef, Value => undef, @_, ); unless ( $self->CurrentUserHasAssetTypeRight('ModifyTemplate') ) { return ( 0, $self->loc('Permission Denied') ); } if (exists $args{Value}) { if ($args{Field} eq 'AssetType') { if ($args{Value}) { # moving to another asset type my $assettype = RTx::AssetTracker::Type->new( $self->CurrentUser ); $assettype->Load($args{Value}); unless ($assettype->Id and $assettype->CurrentUserHasRight('ModifyTemplate')) { return ( 0, $self->loc('Permission Denied') ); } } else { # moving to global unless ($self->CurrentUser->HasRight( Object => RT->System, Right => 'ModifyTemplate' )) { return ( 0, $self->loc('Permission Denied') ); } } } } return $self->SUPER::_Set( @_ ); } =head2 _Value Takes the name of a table column. Returns its value as a string, if the user passes an ACL check, otherwise returns undef. =cut sub _Value { my $self = shift; unless ( $self->CurrentUserCanRead() ) { return undef; } return $self->__Value( @_ ); } =head2 Load Load a template, either by number or by name. Note that loading templates by name using this method B. Several asset types may have template with the same name and as well global template with the same name may exist. Use L and/or L to get precise result. =cut sub Load { my $self = shift; my $identifier = shift; return undef unless $identifier; if ( $identifier =~ /\D/ ) { return $self->LoadByCol( 'Name', $identifier ); } return $self->LoadById( $identifier ); } =head2 LoadGlobalTemplate NAME Load the global template with the name NAME =cut sub LoadGlobalTemplate { my $self = shift; my $name = shift; return ( $self->LoadAssetTypeTemplate( AssetType => 0, Name => $name ) ); } =head2 LoadAssetTypeTemplate (AssetType => TYPEID, Name => NAME) Loads the AssetType template named NAME for Asset Type TYPE. Note that this method doesn't load a global template with the same name if template in the asset type doesn't exist. The following code can be used: $template->LoadAssetTypeTemplate( AssetType => $type_id, Name => $template_name ); unless ( $template->id ) { $template->LoadGlobalTemplate( $template_name ); unless ( $template->id ) { # no template ... } } # ok, template either asset type's or global ... =cut sub LoadAssetTypeTemplate { my $self = shift; my %args = ( AssetType => undef, Name => undef, @_ ); return ( $self->LoadByCols( Name => $args{'Name'}, AssetType => $args{'AssetType'} ) ); } =head2 Create Takes a paramhash of Content, AssetType, Name and Description. Name should be a unique string identifying this Template. Description and Content should be the template's title and content. AssetType should be 0 for a global template and the asset type # for an asset types-specific template. Returns the Template's id # if the create was successful. Returns undef for unknown database failure. =cut sub Create { my $self = shift; my %args = ( Content => undef, AssetType => 0, Description => '[no description]', Type => 'Perl', Name => undef, @_ ); if ( $args{Type} eq 'Perl' && !$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System) ) { return ( undef, $self->loc('Permission Denied') ); } unless ( $args{'AssetType'} ) { unless ( $self->CurrentUser->HasRight(Right =>'ModifyTemplate', Object => $RT::System) ) { return ( undef, $self->loc('Permission Denied') ); } $args{'AssetType'} = 0; } else { my $AssetTypeObj = RTx::AssetTracker::Type->new( $self->CurrentUser ); $AssetTypeObj->Load( $args{'AssetType'} ) || return ( undef, $self->loc('Invalid asset type') ); unless ( $AssetTypeObj->CurrentUserHasRight('ModifyTemplate') ) { return ( undef, $self->loc('Permission Denied') ); } $args{'AssetType'} = $AssetTypeObj->Id; } my $result = $self->SUPER::Create( Content => $args{'Content'}, AssetType => $args{'AssetType'}, Description => $args{'Description'}, Name => $args{'Name'}, Type => $args{'Type'}, ); return ($result); } =head2 Delete Delete this template. =cut sub Delete { my $self = shift; unless ( $self->CurrentUserHasAssetTypeRight('ModifyTemplate') ) { return ( 0, $self->loc('Permission Denied') ); } return ( $self->SUPER::Delete(@_) ); } =head2 IsEmpty Returns true value if content of the template is empty, otherwise returns false. =cut sub IsEmpty { my $self = shift; my $content = $self->Content; return 0 if defined $content && length $content; return 1; } =head2 MIMEObj Returns L object parsed using L method. Returns undef if last call to L failed or never be called. Note that content of the template is UTF-8, but L is not good at handling it and all data of the entity should be treated as octets and converted to perl strings using Encode::decode_utf8 or something else. =cut sub MIMEObj { my $self = shift; return ( $self->{'MIMEObj'} ); } =head2 Parse This routine performs L parsing on the template and then imports the results into a L so we can really use it. Use L method to get the L object. Takes a hash containing Argument, AssetObj, and TransactionObj and other arguments that will be available in the template's code. AssetObj and TransactionObj are not mandatory, but highly recommended. It returns a tuple of (val, message). If val is false, the message contains an error message. =cut sub Parse { my $self = shift; my ($rv, $msg); if (not $self->IsEmpty and $self->Content =~ m{^Content-Type:\s+text/html\b}im) { local $RT::Transaction::PreferredContentType = 'text/html'; ($rv, $msg) = $self->_Parse(@_); } else { ($rv, $msg) = $self->_Parse(@_); } return ($rv, $msg) unless $rv; my $mime_type = $self->MIMEObj->mime_type; if (defined $mime_type and $mime_type eq 'text/html') { $self->_DowngradeFromHTML(@_); } return ($rv, $msg); } sub _Parse { my $self = shift; # clear prev MIME object $self->{'MIMEObj'} = undef; #We're passing in whatever we were passed. it's destined for _ParseContent my ($content, $msg) = $self->_ParseContent(@_); return ( 0, $msg ) unless defined $content && length $content; if ( $content =~ /^\S/s && $content !~ /^\S+:/ ) { $RT::Logger->error( "Template #". $self->id ." has leading line that doesn't" ." look like header field, if you don't want to override" ." any headers and don't want to see this error message" ." then leave first line of the template empty" ); $content = "\n".$content; } my $parser = MIME::Parser->new(); $parser->output_to_core(1); $parser->tmp_to_core(1); $parser->use_inner_files(1); ### Should we forgive normally-fatal errors? $parser->ignore_errors(1); # MIME::Parser doesn't play well with perl strings utf8::encode($content); $self->{'MIMEObj'} = eval { $parser->parse_data( \$content ) }; if ( my $error = $@ || $parser->last_error ) { $RT::Logger->error( "$error" ); return ( 0, $error ); } # Unfold all headers $self->{'MIMEObj'}->head->unfold; $self->{'MIMEObj'}->head->modify(1); return ( 1, $self->loc("Template parsed") ); } # Perform Template substitutions on the template sub _ParseContent { my $self = shift; my %args = ( Argument => undef, AssetObj => undef, TransactionObj => undef, @_ ); unless ( $self->CurrentUserCanRead() ) { return (undef, $self->loc("Permission Denied")); } if ( $self->IsEmpty ) { return ( undef, $self->loc("Template is empty") ); } my $content = $self->SUPER::_Value('Content'); # We need to untaint the content of the template, since we'll be working # with it $content =~ s/^(.*)$/$1/; $args{'Asset'} = delete $args{'AssetObj'} if $args{'AssetObj'}; $args{'Transaction'} = delete $args{'TransactionObj'} if $args{'TransactionObj'}; #$args{'Requestor'} = eval { $args{'Ticket'}->Requestors->UserMembersObj->First->Name } #if $args{'Ticket'}; $args{'rtname'} = RT->Config->Get('rtname'); if ( $args{'Asset'} ) { my $t = $args{'Asset'}; # avoid memory leak $args{'loc'} = sub { $t->loc(@_) }; } else { $args{'loc'} = sub { $self->loc(@_) }; } if ($self->Type eq 'Perl') { return $self->_ParseContentPerl( Content => $content, TemplateArgs => \%args, ); } else { return $self->_ParseContentSimple( Content => $content, TemplateArgs => \%args, ); } } # uses Text::Template for Perl templates sub _ParseContentPerl { my $self = shift; my %args = ( Content => undef, TemplateArgs => {}, @_, ); foreach my $key ( keys %{ $args{TemplateArgs} } ) { my $val = $args{TemplateArgs}{ $key }; next unless ref $args{ $key }; next if ref $args{ $key } =~ /^(ARRAY|HASH|SCALAR|CODE)$/; $args{TemplateArgs}{ $key } = \$val; } my $template = Text::Template->new( TYPE => 'STRING', SOURCE => $args{Content}, ); my $is_broken = 0; my $retval = $template->fill_in( HASH => $args{TemplateArgs}, BROKEN => sub { my (%args) = @_; $RT::Logger->error("Template parsing error: $args{error}") unless $args{error} =~ /^Died at /; # ignore intentional die() $is_broken++; return undef; }, ); return ( undef, $self->loc('Template parsing error') ) if $is_broken; return ($retval); } sub _ParseContentSimple { my $self = shift; my %args = ( Content => undef, TemplateArgs => {}, @_, ); $self->_MassageSimpleTemplateArgs(%args); my $template = Text::Template->new( TYPE => 'STRING', SOURCE => $args{Content}, ); my ($ok) = $template->compile; return ( undef, $self->loc('Template parsing error: [_1]', $Text::Template::ERROR) ) if !$ok; # copied from Text::Template::fill_in and refactored to be simple variable # interpolation my $fi_r = ''; foreach my $fi_item (@{$template->{SOURCE}}) { my ($fi_type, $fi_text, $fi_lineno) = @$fi_item; if ($fi_type eq 'TEXT') { $fi_r .= $fi_text; } elsif ($fi_type eq 'PROG') { my $fi_res; my $original_fi_text = $fi_text; # strip surrounding whitespace for simpler regexes $fi_text =~ s/^\s+//; $fi_text =~ s/\s+$//; # if the codeblock is a simple $Variable lookup, use the value from # the TemplateArgs hash... if (my ($var) = $fi_text =~ /^\$(\w+)$/) { if (exists $args{TemplateArgs}{$var}) { $fi_res = $args{TemplateArgs}{$var}; } } # if there was no substitution then just reinsert the codeblock if (!defined $fi_res) { $fi_res = "{$original_fi_text}"; } # If the value of the filled-in text really was undef, # change it to an explicit empty string to avoid undefined # value warnings later. $fi_res = '' unless defined $fi_res; $fi_r .= $fi_res; } } return $fi_r; } sub _MassageSimpleTemplateArgs { my $self = shift; my %args = ( TemplateArgs => {}, @_, ); my $template_args = $args{TemplateArgs}; if (my $asset = $template_args->{Asset}) { for my $column (qw/Id Name Description Status/) { $template_args->{"Asset".$column} = $asset->$column; } for my $role ( RTx::AssetTracker::Type->RoleGroupTypes ) { my $roleobj = $role.'Obj'; $template_args->{"Asset".$role} = $asset->$roleobj->MemberEmailAddressesAsString; } $template_args->{"AssetTypeId"} = $asset->Type; $template_args->{"AssetTypeName"} = $asset->TypeObj->Name; my $cfs = $asset->CustomFields; while (my $cf = $cfs->Next) { $template_args->{"AssetCF" . $cf->Name} = $asset->CustomFieldValuesAsString($cf->Name); } } if (my $txn = $template_args->{Transaction}) { for my $column (qw/Id TimeTaken Type Field OldValue NewValue Data Content Subject Description BriefDescription/) { $template_args->{"Transaction".$column} = $txn->$column; } my $cfs = $txn->CustomFields; while (my $cf = $cfs->Next) { $template_args->{"TransactionCF" . $cf->Name} = $txn->CustomFieldValuesAsString($cf->Name); } } } sub _DowngradeFromHTML { my $self = shift; my $orig_entity = $self->MIMEObj; my $new_entity = $orig_entity->dup; # this will fail badly if we go away from InCore parsing $new_entity->head->mime_attr( "Content-Type" => 'text/plain' ); $new_entity->head->mime_attr( "Content-Type.charset" => 'utf-8' ); $orig_entity->head->mime_attr( "Content-Type" => 'text/html' ); $orig_entity->head->mime_attr( "Content-Type.charset" => 'utf-8' ); $orig_entity->make_multipart('alternative', Force => 1); require HTML::FormatText; require HTML::TreeBuilder; require Encode; # need to decode_utf8, see the doc of MIMEObj method my $tree = HTML::TreeBuilder->new_from_content( Encode::decode_utf8($new_entity->bodyhandle->as_string) ); $new_entity->bodyhandle(MIME::Body::InCore->new( \(scalar HTML::FormatText->new( leftmargin => 0, rightmargin => 78, )->format( $tree )) )); $tree->delete; $orig_entity->add_part($new_entity, 0); # plain comes before html $self->{MIMEObj} = $orig_entity; return; } =head2 CurrentUserHasAssetTypeRight Helper function to call the template's asset type's CurrentUserHasAssetTypeRight with the passed in args. =cut sub CurrentUserHasAssetTypeRight { my $self = shift; return ( $self->AssetTypeObj->CurrentUserHasRight(@_) ); } =head2 SetType If setting Type to Perl, require the ExecuteCode right. =cut sub SetType { my $self = shift; my $NewType = shift; if ($NewType eq 'Perl' && !$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System)) { return ( undef, $self->loc('Permission Denied') ); } return $self->_Set( Field => 'Type', Value => $NewType ); } =head2 SetContent If changing content and the type is Perl, require the ExecuteCode right. =cut sub SetContent { my $self = shift; my $NewContent = shift; if ($self->Type eq 'Perl' && !$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System)) { return ( undef, $self->loc('Permission Denied') ); } return $self->_Set( Field => 'Content', Value => $NewContent ); } sub _UpdateAttributes { my $self = shift; my %args = ( NewValues => {}, @_, ); my $type = $args{NewValues}{Type} || $self->Type; # forbid updating content when the (possibly new) value of Type is Perl if ($type eq 'Perl' && exists $args{NewValues}{Content}) { if (!$self->CurrentUser->HasRight(Right => 'ExecuteCode', Object => $RT::System)) { return $self->loc('Permission Denied'); } } return $self->SUPER::_UpdateAttributes(%args); } =head2 CompileCheck If the template's Type is Perl, then compile check all the codeblocks to see if they are syntactically valid. We eval them in a codeblock to avoid actually executing the code. Returns an (ok, message) pair. =cut sub CompileCheck { my $self = shift; return (1, $self->loc("Template does not include Perl code")) unless $self->Type eq 'Perl'; my $content = $self->Content; $content = '' if !defined($content); my $template = Text::Template->new( TYPE => 'STRING', SOURCE => $content, ); my ($ok) = $template->compile; return ( undef, $self->loc('Template parsing error: [_1]', $Text::Template::ERROR) ) if !$ok; # copied from Text::Template::fill_in and refactored to be compile checks foreach my $fi_item (@{$template->{SOURCE}}) { my ($fi_type, $fi_text, $fi_lineno) = @$fi_item; next unless $fi_type eq 'PROG'; do { no strict 'vars'; eval "sub { $fi_text }"; }; next if !$@; my $error = $@; # provide a (hopefully) useful line number for the error, but clean up # all the other extraneous garbage $error =~ s/\(eval \d+\) line (\d+).*/"template line " . ($1+$fi_lineno-1)/es; return (0, $self->loc("Couldn't compile template codeblock '[_1]': [_2]", $fi_text, $error)); } return (1, $self->loc("Template compiles")); } =head2 CurrentUserCanRead =cut sub CurrentUserCanRead { my $self =shift; return 1 if $self->CurrentUserHasAssetTypeRight('ShowTemplate'); return $self->CurrentUser->HasRight( Right =>'ShowGlobalTemplates', Object => $RT::System ) if !$self->AssetTypeObj->Id; return; } =head2 id Returns the current value of id. (In the database, id is stored as int(11).) =cut =head2 AssetType Returns the current value of AssetType. (In the database, AssetType is stored as int(11).) =head2 SetAssetType VALUE Set AssetType to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, AssetType will be stored as a int(11).) =cut =head2 AssetTypeObj Returns the AssetType Object which has the id returned by AssetType =cut sub AssetTypeObj { my $self = shift; my $Type = RTx::AssetTracker::Type->new($self->CurrentUser); $Type->Load($self->__Value('AssetType')); return($Type); } =head2 Name Returns the current value of Name. (In the database, Name is stored as varchar(200).) =head2 SetName VALUE Set Name to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Name will be stored as a varchar(200).) =cut =head2 Description Returns the current value of Description. (In the database, Description is stored as varchar(255).) =head2 SetDescription VALUE Set Description to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Description will be stored as a varchar(255).) =cut =head2 Type Returns the current value of Type. (In the database, Type is stored as varchar(16).) =head2 SetType VALUE Set Type to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Type will be stored as a varchar(16).) =cut =head2 Language Returns the current value of Language. (In the database, Language is stored as varchar(16).) =head2 SetLanguage VALUE Set Language to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Language will be stored as a varchar(16).) =cut =head2 TranslationOf Returns the current value of TranslationOf. (In the database, TranslationOf is stored as int(11).) =head2 SetTranslationOf VALUE Set TranslationOf to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, TranslationOf will be stored as a int(11).) =cut =head2 Content Returns the current value of Content. (In the database, Content is stored as text.) =head2 SetContent VALUE Set Content to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Content will be stored as a text.) =cut =head2 LastUpdated Returns the current value of LastUpdated. (In the database, LastUpdated is stored as datetime.) =cut =head2 LastUpdatedBy Returns the current value of LastUpdatedBy. (In the database, LastUpdatedBy is stored as int(11).) =cut =head2 Creator Returns the current value of Creator. (In the database, Creator is stored as int(11).) =cut =head2 Created Returns the current value of Created. (In the database, Created is stored as datetime.) =cut sub _CoreAccessible { { id => {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, AssetType => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Name => {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''}, Description => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''}, Type => {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''}, Language => {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''}, TranslationOf => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Content => {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''}, LastUpdated => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, LastUpdatedBy => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Creator => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Created => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, } }; ### Shredder methods ### use RT::Shredder::Constants; use RT::Shredder::Exceptions; use RT::Shredder::Dependencies; sub __DependsOn { my $self = shift; my %args = ( Shredder => undef, Dependencies => undef, @_, ); my $deps = $args{'Dependencies'}; my $list = []; # Scrips my $objs = RTx::AssetTracker::Scrips->new( $self->CurrentUser ); $objs->Limit( FIELD => 'Template', VALUE => $self->Id ); push( @$list, $objs ); $deps->_PushDependencies( BaseObject => $self, Flags => DEPENDS_ON, TargetObjects => $list, Shredder => $args{'Shredder'}, ); return $self->SUPER::__DependsOn( %args ); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Templates.pm000066400000000000000000000111151222742774700250310ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Templates - a collection of RT Template objects =head1 SYNOPSIS use RTx::AssetTracker::Templates; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Templates; use base 'RT::SearchBuilder'; use RTx::AssetTracker::Template; sub Table {'AT_Templates'}; =head2 _Init Returns RTx::AssetTracker::Templates specific init info like table and primary key names =cut sub _Init { my $self = shift; $self->{'table'} = "AT_Templates"; $self->{'primary_key'} = "id"; return ($self->SUPER::_Init(@_)); } =head2 LimitToNotInAssetType Takes an asset type id # and limits the returned set of templates to those which aren't thatn asset type's templates. =cut sub LimitToNotInAssetType { my $self = shift; my $assettype_id = shift; $self->Limit(FIELD => 'AssetType', VALUE => "$assettype_id", OPERATOR => '!=' ); } =head2 LimitToGlobal Takes no arguments. Limits the returned set to "Global" templates which can be used with any asset type. =cut sub LimitToGlobal { my $self = shift; $self->Limit(FIELD => 'AssetType', VALUE => "0", OPERATOR => '=' ); } =head2 LimitToAssetType Takes an asset type id # and limits the returned set of templates to that asset type's templates =cut sub LimitToAssetType { my $self = shift; my $assettype_id = shift; $self->Limit(FIELD => 'AssetType', VALUE => "$assettype_id", OPERATOR => '=' ); } =head2 Next Returns the next template that this user can see. =cut sub Next { my $self = shift; my $templ = $self->SUPER::Next(); if ((defined($templ)) and (ref($templ))) { # If it's part of an asset type, and the user can read templates in # that asset type, or the user can globally read templates, show it if ($templ->AssetType && $templ->CurrentUserHasAssetTypeRight('ShowTemplate') or $templ->CurrentUser->HasRight(Object => $RT::System, Right => 'ShowTemplate') or $templ->CurrentUser->HasRight(Object => $RT::System, Right => 'ShowGlobalTemplates')) { return($templ); } #If the user doesn't have the right to show this template else { return($self->Next()); } } #if there never was any template else { return(undef); } } =head2 NewItem Returns an empty new RTx::AssetTracker::Template item =cut sub NewItem { my $self = shift; return(RTx::AssetTracker::Template->new($self->CurrentUser)); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Test.pm.in000066400000000000000000000143201222742774700244200ustar00rootroot00000000000000use strict; use warnings; ### after: use lib qw(@RT_LIB_PATH@); use lib qw(/opt/rt3/local/lib /opt/rt3/lib); package RTx::AssetTracker::Test; our @ISA; BEGIN { local $@; eval { require RT::Test; 1 } or do { require Test::More; Test::More::BAIL_OUT( "requires 3.8 to run tests. Error:\n$@\n" ."You may need to set PERL5LIB=/path/to/rt/lib" ); }; push @ISA, 'RT::Test'; } sub import { my $class = shift; my %args = @_; $args{'requires'} ||= []; if ( $args{'testing'} ) { unshift @{ $args{'requires'} }, 'RTx::AssetTracker'; } else { $args{'testing'} = 'RTx::AssetTracker'; } $class->SUPER::import( %args ); RT->Config->LoadConfig( File => 'AT_Config.pm' ); require RTx::AssetTracker; } =head2 load_or_create_type =cut sub load_or_create_type { my $self = shift; my %args = ( Disabled => 0, @_ ); my $obj = RTx::AssetTracker::Type->new( RT->SystemUser ); if ( $args{'Name'} ) { $obj->LoadByCols( Name => $args{'Name'} ); } else { die "Name is required"; } unless ( $obj->id ) { my ($val, $msg) = $obj->Create( %args ); die "$msg" unless $val; } return $obj; } =head2 load_or_create_asset_custom_field =cut sub load_or_create_asset_custom_field { my $self = shift; my %args = ( Disabled => 0, LookupType => 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset', @_ ); my $obj = RT::CustomField->new( $RT::SystemUser ); if ( $args{'Name'} ) { $obj->LoadByName( Name => $args{'Name'}, Type => $args{'AssetType'} ); } else { die "Name is required"; } unless ( $obj->id ) { my ($val, $msg) = $obj->Create( %args ); die "$msg" unless $val; my $typeobj = RTx::AssetTracker::Type->new( $RT::SystemUser ); $typeobj->Load( $args{'AssetType'} ); ($val, $msg) = $obj->AddToObject( $typeobj ); die "$msg" unless $val; } return $obj; } sub delete_type_watchers { my $self = shift; my @types = @_; foreach my $q ( @types ) { foreach my $t ( RTx::AssetTracker::Type->RoleGroupTypes ) { $q->DeleteWatcher( Type => $t, PrincipalId => $_->MemberId ) foreach @{ $q->$t()->MembersObj->ItemsArrayRef }; } } } sub create_assets { local $Test::Builder::Level = $Test::Builder::Level + 1; my $self = shift; my $defaults = shift; my @data = @_; @data = sort { rand(100) <=> rand(100) } @data if delete $defaults->{'RandomOrder'}; $defaults->{'Type'} ||= 'Servers'; my @res = (); my $i = 0; while ( @data ) { my %args = %{ shift @data }; $args{$_} = $res[ $args{$_} ]->id foreach grep $args{ $_ }, keys %RTx::AssetTracker::Asset::LINKTYPEMAP; $args{'Name'} ||= $defaults->{'Type'} . " asset #" . ++$i; push @res, $self->create_asset( %$defaults, %args ); } return @res; } sub create_asset { local $Test::Builder::Level = $Test::Builder::Level + 1; my $self = shift; my %args = @_; unless ( $args{'Name'} ) { die "Name is required"; } if ($args{Type} && $args{Type} =~ /\D/) { my $type = RTx::AssetTracker::Type->new(RT->SystemUser); if (my $id = $type->Load($args{Type}) ) { $args{Type} = $id; } else { die ("Error: Invalid type $args{Type}"); } } my $asset = RTx::AssetTracker::Asset->new( RT->SystemUser ); my ( $id, undef, $msg ) = $asset->Create( %args ); Test::More::ok( $id, "asset created" ) or Test::More::diag("error: $msg"); # hackish, but simpler if ( $args{'LastUpdatedBy'} ) { $asset->__Set( Field => 'LastUpdatedBy', Value => $args{'LastUpdatedBy'} ); } for my $field ( keys %args ) { #TODO check links and watchers if ( $field =~ /CustomField-(\d+)/ ) { my $cf = $1; my $got = join ',', sort map $_->Content, @{ $asset->CustomFieldValues($cf)->ItemsArrayRef }; my $expected = ref $args{$field} ? join( ',', sort @{ $args{$field} } ) : $args{$field}; Test::More::is( $got, $expected, 'correct CF values' ); } else { next if ref $args{$field}; next unless $asset->can($field) or $asset->_Accessible($field,"read"); next if ref $asset->$field(); Test::More::is( $asset->$field(), $args{$field}, "$field is correct" ); } } return $asset; } sub delete_assets { my $self = shift; my $query = shift; my $assets = RTx::AssetTracker::Assets->new( RT->SystemUser ); if ( $query ) { $assets->FromSQL( $query ); } else { $assets->UnLimit; } while ( my $asset = $assets->Next ) { $asset->Delete; } } sub add_rights { my $self = shift; my @list = ref $_[0]? @_: @_? { @_ }: (); require RT::ACL; foreach my $e (@list) { my $principal = delete $e->{'Principal'}; unless ( ref $principal ) { if ( $principal =~ /^(everyone|(?:un)?privileged)$/i ) { $principal = RT::Group->new( RT->SystemUser ); $principal->LoadSystemInternalGroup($1); } elsif ( my @princ = grep { $_ eq $principal } RTx::AssetTracker::Type->RoleGroupTypes ) { $principal = RT::Group->new( RT->SystemUser ); $principal->LoadByCols( Domain => (ref($e->{'Object'})||'RT::System').'-Role', Type => $princ[0], ref($e->{'Object'})? (Instance => $e->{'Object'}->id): (), ); } else { die "principal is not an object, but also is not name of a system group"; } } unless ( $principal->isa('RT::Principal') ) { if ( $principal->can('PrincipalObj') ) { $principal = $principal->PrincipalObj; } } my @rights = ref $e->{'Right'}? @{ $e->{'Right'} }: ($e->{'Right'}); foreach my $right ( @rights ) { my ($status, $msg) = $principal->GrantRight( %$e, Right => $right ); $RT::Logger->debug($msg); } } return 1; } 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Type.pm000066400000000000000000001147461222742774700240320ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Type - an AssetTracker Type object =head1 SYNOPSIS use RTx::AssetTracker::Type; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Type; use base 'RTx::AssetTracker::Record'; sub Table {'AT_Types'}; use RT::CustomField; use RT::CustomFields; use RT::Group; our @DEFAULT_ACTIVE_STATUS = qw(production development qa pilot dr test); our @DEFAULT_INACTIVE_STATUS = qw(retired deleted); our $RIGHTS = { SeeType => 'Can this principal see this asset type', # loc_pair AdminType => 'Create, delete and modify asset types', # loc_pair AssignCustomFields => 'Assign and remove custom fields', # loc_pair ModifyTypeAdmins => 'Modify administrators for asset type', # loc_pair ModifyTypeWatchers => 'Modify watchers for asset type', # loc_pair ShowAsset => 'See asset details', # loc_pair CreateAsset => 'Create assets of this asset type', # loc_pair ModifyAsset => 'Modify assets of this asset type', # loc_pair RetireAsset => 'Retire assets of this asset type', # loc_pair DeleteAsset => 'Delete assets', # loc_pair }; our $RIGHT_CATEGORIES = { SeeType => 'General', ShowAsset => 'General', ModifyAsset => 'General', CreateAsset => 'General', AdminType => 'Admin', AssignCustomFields => 'Staff', ModifyTypeAdmins => 'Staff', ModifyTypeWatchers => 'Staff', RetireAsset => 'Staff', DeleteAsset => 'Staff', }; # Tell RT::ACE that this sort of object can get acls granted $RT::ACE::OBJECT_TYPES{'RTx::AssetTracker::Type'} = 1; # TODO: This should be refactored out into an RT::ACLedObject or something # stuff the rights into a hash of rights that can exist. __PACKAGE__->AddRights(%$RIGHTS); __PACKAGE__->AddRightCategories(%$RIGHT_CATEGORIES); RT::System::AddRights(%$RIGHTS); RT::System::AddRightCategories(%$RIGHT_CATEGORIES); require RT::Lifecycle; =head2 AddRights C, C [, ...] Adds the given rights to the list of possible rights. This method should be called during server startup, not at runtime. =cut sub AddRights { my $self = shift; my %new = @_; $RIGHTS = { %$RIGHTS, %new }; %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES, map { lc($_) => $_ } keys %new); } =head2 AddRightCategories C, C [, ...] Adds the given right and category pairs to the list of right categories. This method should be called during server startup, not at runtime. =cut sub AddRightCategories { my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__; my %new = @_; $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new }; } # Custom field support RT::CustomField->_ForObjectType( 'RTx::AssetTracker::Type' => "Asset Types" ); # {{{ Setup Roles/Watchers our %DEFAULT_ROLES = ( Admin => { Role => 'Admin', Label => 'Administrators', Right => 'WatchAsAdmin', Description => 'Right to administer asset of this type', }, Owner => { Role => 'Owner', Label => 'Owners', Right => 'OwnAsset', Description => 'Right to own asset of this type', }, ); sub RoleLabel { my $self = shift; my $role = shift; if (exists $DEFAULT_ROLES{$role}) { return $DEFAULT_ROLES{$role}{Label}; } return $role; } sub RoleRight { my $self = shift; my $role = shift; if (exists $DEFAULT_ROLES{$role}) { return $DEFAULT_ROLES{$role}{Right}; } return $role.'Role'; } sub RoleDescription { my $self = shift; my $role = shift; if (exists $DEFAULT_ROLES{$role}) { return $DEFAULT_ROLES{$role}{Description}; } return "Right to have role '$role' for this asset type"; } sub ConfigureRoles { my $self = shift; foreach my $role ( $self->RoleGroupTypes ) { $self->ConfigureRole( $role ); } } sub ConfigureRole { my $self = shift; my $role = shift; # if the system role group doesn't exist, create it my $group = RT::Group->new( $RT::SystemUser ); $group->LoadByCols( Domain => 'RT::System-Role', Type => $role ); unless ( $group->id ) { $group->_Create( Domain => 'RT::System-Role', Instance => 0, Type => $role, Description => 'SystemRolegroup for internal use', # loc InsideTransaction => 0 ); $group->id or $RT::Logger->error("Couldn't create group for system role '$role'"); } $RTx::AssetTracker::Assets::FIELD_METADATA{$role} = [ 'WATCHERFIELD' => $role ]; $RTx::AssetTracker::Assets::FIELD_METADATA{'Type'.$role} = [ 'WATCHERFIELD' => $role => 'Type' ]; $RTx::AssetTracker::Assets::FIELD_METADATA{$role.'Group'} = [ 'MEMBERSHIPFIELD' => $role ]; $RTx::AssetTracker::Assets::LOWER_CASE_FIELDS{lc $role} = $role; $RTx::AssetTracker::Assets::LOWER_CASE_FIELDS{lc 'Type'.$role} = 'Type'.$role; $RTx::AssetTracker::Assets::LOWER_CASE_FIELDS{lc $role.'Group'} = $role.'Group'; my $right = $self->RoleRight($role); my $desc = $self->RoleDescription($role); $self->AddRights( $right => $desc ); $self->AddRightCategories( $right => 'General' ); RT::System->AddRights( $right => $desc ); RT::System->AddRightCategories( $right => 'General' ); $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right; my $group_role_method = $role . "RoleGroup"; my $is_role_method = 'Is' . $role; my $export_role_method = $role . "RoleGroupExportString"; { no strict 'refs'; *$group_role_method = sub { my $self = shift; my $group; if ( $self->CurrentUserHasRight('SeeType') ) { $group = $self->LoadTypeRoleGroup(Type => $role); } return ($group); }; *$is_role_method = sub { my $self = shift; my $owner = shift; return ( $self->IsWatcher( Type => $role, PrincipalId => $owner ) ); }; } { no strict 'refs'; package RTx::AssetTracker::Asset; *$group_role_method = sub { my $self = shift; my $group; if ( $self->CurrentUserHasRight('ShowAsset') ) { $group = $self->LoadAssetRoleGroup(Type => $role); } return ($group); }; *$is_role_method = sub { my $self = shift; my $owner = shift; return ( $self->IsWatcher( Type => $role, PrincipalId => $owner ) ); }; *$export_role_method = sub { my $self = shift; my $export_string = undef; if ($group = $self->LoadAssetRoleGroup(Type => $role)) { my $members = $group->MembersObj->ItemsArrayRef; $export_string = join(",", map { $_->MemberObj->IsGroup ? '@'. $_->MemberObj->Object->Name() : $_->MemberObj->Object->EmailAddress } @$members); } return $export_string; }; } } # }}} =head2 AvailableRights Returns a hash of available rights for this object. The keys are the right names and the values are a description of what the rights do =cut sub AvailableRights { my $self = shift; return($RIGHTS); } =head2 RightCategories Returns a hashref where the keys are rights for this type of object and the values are the category (General, Staff, Admin) the right falls into. =cut sub RightCategories { return $RIGHT_CATEGORIES; } sub Lifecycle { my $self = shift; unless (ref $self && $self->id) { return RT::Lifecycle->Load('') } my $name = $self->_Value( Lifecycle => @_ ); $name ||= 'at_default'; my $res = RT::Lifecycle->Load( $name ); unless ( $res ) { $RT::Logger->error("Lifecycle '$name' for asset type '".$self->Name."' doesn't exist"); return RT::Lifecycle->Load('at_default'); } return $res; } sub SetLifecycle { my $self = shift; my $value = shift || 'at_default'; return ( 0, $self->loc( '[_1] is not a valid lifecycle', $value ) ) unless $self->ValidateLifecycle($value); return $self->_Set( Field => 'Lifecycle', Value => $value, @_ ); } =head2 ValidateLifecycle NAME Takes a lifecycle name. Returns true if it's an ok name and such lifecycle is configured. Returns undef otherwise. =cut sub ValidateLifecycle { my $self = shift; my $value = shift; return undef unless RT::Lifecycle->Load( $value ); return 1; } =head2 ActiveStatusArray Returns an array of all ActiveStatuses for this asset type =cut sub ActiveStatusArray { my $self = shift; return $self->Lifecycle->Valid('initial', 'active'); } =head2 InactiveStatusArray Returns an array of all InactiveStatuses for this asset type =cut sub InactiveStatusArray { my $self = shift; return $self->Lifecycle->Inactive; } =head2 StatusArray Returns an array of all statuses for this asset type =cut sub StatusArray { my $self = shift; return $self->Lifecycle->Valid( @_ ); } =head2 IsValidStatus value Returns true if value is a valid status. Otherwise, returns 0. =cut sub IsValidStatus { my $self = shift; return $self->Lifecycle->IsValid( shift ); } =head2 IsActiveStatus value Returns true if value is a Active status. Otherwise, returns 0 =cut sub IsActiveStatus { my $self = shift; return $self->Lifecycle->IsValid( shift, 'initial', 'active'); } =head2 IsInactiveStatus value Returns true if value is a Inactive status. Otherwise, returns 0 =cut sub IsInactiveStatus { my $self = shift; return $self->Lifecycle->IsInactive( shift ); } =head2 Create Create takes the name of the new type If you pass the ACL check, it creates the type and returns its type id. =cut sub Create { my $self = shift; my %args = ( Name => undef, Description => '', Lifecycle => 'at_default', _RecordTransaction => 1, @_ ); unless ( $self->CurrentUser->HasRight(Right => 'AdminType', Object => $RT::System) ) { #Check them ACLs return ( 0, $self->loc("No permission to create asset types") ); } { my ($val, $msg) = $self->_ValidateName( $args{'Name'} ); return ($val, $msg) unless $val; } $args{'Lifecycle'} ||= 'at_default'; return ( 0, $self->loc('[_1] is not a valid lifecycle', $args{'Lifecycle'} ) ) unless $self->ValidateLifecycle( $args{'Lifecycle'} ); my %attrs = map {$_ => 1} $self->ReadableAttributes; #TODO better input validation $RT::Handle->BeginTransaction(); my $id = $self->SUPER::Create( map { $_ => $args{$_} } grep exists $args{$_}, keys %attrs ); unless ($id) { $RT::Handle->Rollback(); return ( 0, $self->loc('Asset type could not be created') ); } my $create_ret = $self->_CreateTypeGroups(); unless ($create_ret) { $RT::Handle->Rollback(); return ( 0, $self->loc('Asset type could not be created') ); } if ( $args{'_RecordTransaction'} ) { $self->_NewTransaction( Type => "Create" ); } $RT::Handle->Commit; #RT->System->AssetTypeCacheNeedsUpdate(1); return ( $id, $self->loc("Asset type created") ); } sub Delete { my $self = shift; return ( 0, $self->loc('Deleting this object would break referential integrity') ); } =head2 SetDisabled Takes a boolean. 1 will cause this asset type to no longer be available for assets. 0 will re-enable this asset type. =cut sub SetDisabled { my $self = shift; my $val = shift; $RT::Handle->BeginTransaction(); my $set_err = $self->_Set( Field =>'Disabled', Value => $val); unless ($set_err) { $RT::Handle->Rollback(); $RT::Logger->warning("Couldn't ".($val == 1) ? "disable" : "enable"." asset type ".$self->PrincipalObj->Id); return (undef); } $self->_NewTransaction( Type => ($val == 1) ? "Disabled" : "Enabled" ); $RT::Handle->Commit(); #RT->System->AssetTypeCacheNeedsUpdate(1); if ( $val == 1 ) { return (1, $self->loc("Asset Type disabled")); } else { return (1, $self->loc("Asset Type enabled")); } } =head2 Load Takes either a numerical id or a textual Name and loads the specified asset type. =cut sub Load { my $self = shift; my $identifier = shift; if ( !$identifier ) { return (undef); } if ( $identifier =~ /^(\d+)$/ ) { $self->SUPER::LoadById($identifier); } else { $self->LoadByCols( Name => $identifier ); } return ( $self->Id ); } =head2 ValidateName NAME Takes an asset type name. Returns true if it's an ok name for a new asset type. Returns undef if there's already an asset type by that name. =cut sub ValidateName { my $self = shift; my $name = shift; my ($ok, $msg) = $self->_ValidateName($name); return $ok ? 1 : 0; } sub _ValidateName { my $self = shift; my $name = shift; return (undef, "Asset type name is required") unless length $name; # Validate via the superclass first # Case: short circuit if it's an integer so we don't have # fale negatives when loading a temp asset type unless ( my $q = $self->SUPER::ValidateName($name) ) { return ($q, $self->loc("'[_1]' is not a valid name.", $name)); } my $temptype = RTx::AssetTracker::Type->new(RT->SystemUser); $temptype->Load($name); #If this asset type exists, return undef if ( $temptype->Name() && $temptype->id != $self->id) { return (undef, $self->loc("Asset type already exists") ); } return (1); } =head2 Templates Returns an RTx::AssetTracker::Templates object of all of this asset type's templates. =cut sub Templates { my $self = shift; my $templates = RTx::AssetTracker::Templates->new( $self->CurrentUser ); if ( $self->CurrentUserHasRight('ShowTemplate') ) { $templates->LimitToAssetType( $self->id ); } return ($templates); } =head2 CustomField NAME Load the asset type-specific custom field named NAME =cut sub CustomField { my $self = shift; my $name = shift; my $cf = RT::CustomField->new($self->CurrentUser); $cf->LoadByNameAndAssetType(Name => $name, Type => $self->Id); return ($cf); } =head2 AssetCustomFields Returns an L object containing all global and type-specific B custom fields. =cut sub AssetCustomFields { my $self = shift; my $cfs = RT::CustomFields->new( $self->CurrentUser ); if ( $self->CurrentUserHasRight('SeeType') ) { $cfs->SetContextObject( $self ); $cfs->LimitToGlobalOrObjectId( $self->Id ); $cfs->LimitToLookupType( 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset' ); $cfs->ApplySortOrder; } return ($cfs); } =head2 AssetTransactionCustomFields Returns an L object containing all global and type-specific B custom fields. =cut sub AssetTransactionCustomFields { my $self = shift; my $cfs = RT::CustomFields->new( $self->CurrentUser ); if ( $self->CurrentUserHasRight('SeeType') ) { $cfs->SetContextObject( $self ); $cfs->LimitToGlobalOrObjectId( $self->Id ); $cfs->LimitToLookupType( 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset-RT::Transaction' ); $cfs->ApplySortOrder; } return ($cfs); } =head2 RoleGroupTypes Returns a list of the names of the various role group types that this asset type has. =cut sub RoleGroupTypes { my $self = shift; if (@RT::AssetRoles) { return (@RT::AssetRoles) } else { return (sort keys %DEFAULT_ROLES); } } =head2 IsRoleGroupType Returns whether the passed-in type is a role group type. =cut sub IsRoleGroupType { my $self = shift; my $type = shift; for my $valid_type ($self->RoleGroupTypes) { return 1 if $type eq $valid_type; } return 0; } =head2 _CreateTypeGroups Create the asset groups and links for this asset. This routine expects to be called from Asset->Create _inside of a transaction_ It will create two groups for this asset: Admin and Owner ( Or, whatever roles were configured in AT_SiteConfig. It will return true on success and undef on failure. =cut sub _CreateTypeGroups { my $self = shift; my @types = $self->RoleGroupTypes(); foreach my $type (@types) { my $ok = $self->_CreateTypeRoleGroup($type); return undef if !$ok; } return 1; } # Wrap RT::Group::CreateRoleGroup so that it can deal with groups for # the RTx::AssetTracker domain. { package RT::Group; no warnings qw(redefine); my $Orig_CreateRoleGroup = __PACKAGE__->can('CreateRoleGroup') or die "API change? Can't find method 'CreateRoleGroup'"; *CreateRoleGroup = sub { my $self = shift; my %args = ( Instance => undef, Type => undef, Domain => undef, @_ ); return $Orig_CreateRoleGroup->($self, %args) unless $args{'Domain'} =~ /^RTx::AssetTracker/; unless (RTx::AssetTracker::Type->IsRoleGroupType($args{Type})) { return ( 0, $self->loc("Invalid Group Type") ); } return ( $self->_Create( Domain => $args{'Domain'}, Instance => $args{'Instance'}, Type => $args{'Type'}, InsideTransaction => 1 ) ); } } sub _CreateTypeRoleGroup { my $self = shift; my $type = shift; my $type_obj = RT::Group->new($self->CurrentUser); my ($id, $msg) = $type_obj->CreateRoleGroup(Instance => $self->Id, Type => $type, Domain => 'RTx::AssetTracker::Type-Role'); unless ($id) { $RT::Logger->error("Couldn't create an Asset Type group of type '$type' for type ". $self->Id.": ".$msg); return(undef); } return $id; } # _HasModifyWatcherRight {{{ sub _HasModifyWatcherRight { my $self = shift; my %args = ( Type => undef, PrincipalId => undef, Email => undef, @_ ); return 1 if $self->CurrentUserHasRight('ModifyTypeWatchers'); #If the watcher we're trying to add is for the current user if ( defined $args{'PrincipalId'} && $self->CurrentUser->PrincipalId eq $args{'PrincipalId'}) { if ( $self->IsRoleGroupType( $args{'Type'} ) ) { return 1 if $self->CurrentUserHasRight( $self->RoleRight($args{'Type'}) ); } else { $RT::Logger->warning( "$self -> _HasModifyWatcher got passed a bogus type $args{Type}"); return ( 0, $self->loc('Invalid asset type role group type [_1]', $args{Type}) ); } } return ( 0, $self->loc("Permission Denied") ); } =head2 AddWatcher AddWatcher takes a parameter hash. The keys are as follows: Type One of Owner, Admin ( Or any roles names configured in AT_SiteConfig) PrinicpalId The RT::Principal id of the user or group that's being added as a watcher Email The email address of the new watcher. If a user with this email address can't be found, a new nonprivileged user will be created. If the watcher you're trying to set has an RT account, set the Owner paremeter to their User Id. Otherwise, set the Email parameter to their Email address. Returns a tuple of (status/id, message). =cut sub AddWatcher { my $self = shift; my %args = ( Type => undef, PrincipalId => undef, Email => undef, @_ ); return ( 0, "No principal specified" ) unless $args{'Email'} or $args{'PrincipalId'}; if ( !$args{'PrincipalId'} && $args{'Email'} ) { my $user = RT::User->new( $self->CurrentUser ); $user->LoadByEmail( $args{'Email'} ); $args{'PrincipalId'} = $user->PrincipalId if $user->id; } return ( 0, "Unknown watcher type [_1]", $args{Type} ) unless $self->IsRoleGroupType($args{Type}); my ($ok, $msg) = $self->_HasModifyWatcherRight(%args); return ($ok, $msg) if !$ok; return $self->_AddWatcher(%args); } #This contains the meat of AddWatcher. but can be called from a routine like # Create, which doesn't need the additional acl check sub _AddWatcher { my $self = shift; my %args = ( Type => undef, Silent => undef, PrincipalId => undef, Email => undef, @_ ); my $principal = RT::Principal->new( $self->CurrentUser ); if ( $args{'PrincipalId'} ) { $principal->Load( $args{'PrincipalId'} ); if ( $principal->id and $principal->IsUser and my $email = $principal->Object->EmailAddress ) { return (0, $self->loc("[_1] is an address RT receives mail at. Adding it as a '[_2]' would create a mail loop", $email, $self->loc($args{'Type'}))) if RT::EmailParser->IsRTAddress( $email ); } } elsif ( $args{'Email'} ) { if ( RT::EmailParser->IsRTAddress( $args{'Email'} ) ) { return (0, $self->loc("[_1] is an address RT receives mail at. Adding it as a '[_2]' would create a mail loop", $args{'Email'}, $self->loc($args{'Type'}))); } my $user = RT::User->new($self->CurrentUser); $user->LoadByEmail( $args{'Email'} ); $user->Load( $args{'Email'} ) unless $user->id; if ( $user->Id ) { # If the user exists $principal->Load( $user->PrincipalId ); } else { # if the user doesn't exist, we need to create a new user my $new_user = RT::User->new(RT->SystemUser); my ( $Address, $Name ) = RT::Interface::Email::ParseAddressFromHeader($args{'Email'}); my ( $Val, $Message ) = $new_user->Create( Name => $Address, EmailAddress => $Address, RealName => $Name, Privileged => 0, Comments => 'Autocreated when added as a watcher' ); unless ($Val) { $RT::Logger->error("Failed to create user ".$args{'Email'} .": " .$Message); # Deal with the race condition of two account creations at once $new_user->LoadByEmail( $args{'Email'} ); } $principal->Load( $new_user->PrincipalId ); } } # If we can't find this watcher, we need to bail. unless ( $principal->Id ) { return(0, $self->loc("Could not find or create that user")); } my $group = $self->LoadTypeRoleGroup(Type => $args{'Type'}); unless ($group->id) { return(0,$self->loc("Group not found")); } if ( $group->HasMember( $principal)) { return ( 0, $self->loc('[_1] is already a [_2] for this asset type', $principal->Object->Name, $args{'Type'}) ); } my ($m_id, $m_msg) = $group->_AddMember(PrincipalId => $principal->Id); unless ($m_id) { $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id.": ".$m_msg); return ( 0, $self->loc('Could not make [_1] a [_2] for this asset type', $principal->Object->Name, $args{'Type'}) ); } return ( 1, $self->loc("Added [_1] to members of [_2] for this asset type.", $principal->Object->Name, $args{'Type'} )); } =head2 DeleteWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID, Email => EMAIL_ADDRESS } Deletes an asset type watcher. Takes two arguments: Type (one of Owner,Admin or any role configured in AT_SiteConfig) and one of PrincipalId (an RT::Principal Id of the watcher you want to remove) OR Email (the email address of an existing wathcer) =cut sub DeleteWatcher { my $self = shift; my %args = ( Type => undef, PrincipalId => undef, Email => undef, @_ ); unless ( $args{'PrincipalId'} || $args{'Email'} ) { return ( 0, $self->loc("No principal specified") ); } if ( !$args{PrincipalId} and $args{Email} ) { my $user = RT::User->new( $self->CurrentUser ); my ($rv, $msg) = $user->LoadByEmail( $args{Email} ); $args{PrincipalId} = $user->PrincipalId if $rv; } my $principal = RT::Principal->new( $self->CurrentUser ); if ( $args{'PrincipalId'} ) { $principal->Load( $args{'PrincipalId'} ); } else { my $user = RT::User->new( $self->CurrentUser ); $user->LoadByEmail( $args{'Email'} ); $principal->Load( $user->Id ); } # If we can't find this watcher, we need to bail. unless ( $principal->Id ) { return ( 0, $self->loc("Could not find that principal") ); } my $group = $self->LoadTypeRoleGroup(Type => $args{'Type'}); unless ($group->id) { return(0,$self->loc("Group not found")); } return ( 0, $self->loc('Unknown watcher type [_1]', $args{Type}) ) unless $self->IsRoleGroupType($args{Type}); my ($ok, $msg) = $self->_HasModifyWatcherRight(%args); return ($ok, $msg) if !$ok; # see if this user is already a watcher. unless ( $group->HasMember($principal)) { return ( 0, $self->loc('[_1] is not a [_2] for this asset type', $principal->Object->Name, $args{'Type'}) ); } my ($m_id, $m_msg) = $group->_DeleteMember($principal->Id); unless ($m_id) { $RT::Logger->error("Failed to delete ".$principal->Id. " as a member of group ".$group->Id.": ".$m_msg); return ( 0, $self->loc('Could not remove [_1] as a [_2] for this asset type', $principal->Object->Name, $args{'Type'}) ); } return ( 1, $self->loc("Removed [_1] from members of [_2] for this asset type.", $principal->Object->Name, $args{'Type'} )); } =head2 Admin Takes nothing. Returns an RT::Group object which contains this Type's Admins. If the user doesn't have "ShowType" permission, returns an empty group This method is here for backwards compatability. All role based methods are autogenerated based on the AT_SiteConfig file or system defauls. =cut sub Admin { my $self = shift; return $self->AdminRoleGroup(@_); } =head2 Owner Takes nothing. Returns an RT::Group object which contains this Type's Owner. If the user doesn't have "ShowType" permission, returns an empty group This method is here for backwards compatability. All role based methods are autogenerated based on the AT_SiteConfig file or system defauls. =cut sub Owner { my $self = shift; return $self->OwnerRoleGroup(@_); } # a generic routine to be called by IsOwner and IsAdmin =head2 IsWatcher { Type => TYPE, PrincipalId => PRINCIPAL_ID } Takes a param hash with the attributes Type and PrincipalId Type is one of Admin and Owner PrincipalId is an RT::Principal id Returns true if that principal is a member of the group Type for this asset type =cut sub IsWatcher { my $self = shift; my %args = ( Type => 'Owner', PrincipalId => undef, @_ ); # Load the relevant group. my $group = $self->LoadTypeRoleGroup(Type => $args{'Type'}); # Ask if it has the member in question my $principal = RT::Principal->new($self->CurrentUser); $principal->Load($args{'PrincipalId'}); unless ($principal->Id) { return (undef); } return ($group->HasMemberRecursively($principal)); } =head2 IsOwner PRINCIPAL_ID Takes an RT::Principal id. Returns true if the principal is an owner of the current asset type. This method is autogenerated for each AT role. Since they can be configured in AT_SiteConfig this method might not make sense in a particular installation. =cut =head2 IsAdmin PRINCIPAL_ID Takes an RT::Principal id. Returns true if the principal is an owner of the current asset type. This method is autogenerated for each AT role. Since they can be configured in AT_SiteConfig this method might not make sense in a particular installation. =cut =head2 LoadTypeRoleGroup { Type => TYPE } Loads a Type group from the database. Takes a param hash with 1 parameters: Type is the type of Group we're trying to load: Admin, Owner, or any role in AT_SiteConfig =cut sub LoadTypeRoleGroup { my $self = shift; my %args = ( Type => undef, @_); my $group = RT::Group->new( $self->CurrentUser ); $group->LoadByCols( Domain => 'RTx::AssetTracker::Type-Role', Instance =>$self->Id, Type => $args{'Type'} ); # if it doesn't exits ( like when we add a new role in the config file ) # create it unless ( $group->id ) { my ($id, $msg) = $group->_Create(Instance => $self->Id, Type => $args{Type}, Domain => 'RTx::AssetTracker::Type-Role', InsideTransaction => 0); unless ($id) { $RT::Logger->error("Couldn't create a Type role group of type '$args{Type}' for asset type ". $self->Id.": ".$msg); } } return $group; } =item RolesForType TYPE_ID Limits the set of groups found to role groups for this type =cut sub RolesForType { my $self = shift; my $type = shift; my $groups = RT::Groups->new( $self->CurrentUser ); $groups->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RTx::AssetTracker::Type-Role'); $groups->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $self->Id); return $groups; } ### Shredder methods ### use RT::Shredder::Constants; use RT::Shredder::Exceptions; use RT::Shredder::Dependencies; sub __DependsOn { my $self = shift; my %args = ( Shredder => undef, Dependencies => undef, @_, ); my $deps = $args{'Dependencies'}; my $list = []; # Tickets my $objs = RTx::AssetTracker::Assets->new( $self->CurrentUser ); $objs->{'allow_deleted_search'} = 1; $objs->Limit( FIELD => 'Type', VALUE => $self->Id ); push( @$list, $objs ); # Type role groups( Owner, Admin ) $objs = RT::Groups->new( $self->CurrentUser ); $objs->Limit( FIELD => 'Domain', VALUE => 'RTx::AssetTracker::Type-Role' ); $objs->Limit( FIELD => 'Instance', VALUE => $self->Id ); push( @$list, $objs ); # Scrips $objs = RTx::AssetTracker::Scrips->new( $self->CurrentUser ); $objs->LimitToAssetType( $self->id ); push( @$list, $objs ); # Templates $objs = $self->Templates; push( @$list, $objs ); # Custom Fields $objs = RT::CustomFields->new( $self->CurrentUser ); $objs->SetContextObject( $self ); $objs->LimitToAssetType( $self->id ); push( @$list, $objs ); $deps->_PushDependencies( BaseObject => $self, Flags => DEPENDS_ON, TargetObjects => $list, Shredder => $args{'Shredder'} ); return $self->SUPER::__DependsOn( %args ); } sub _Set { my $self = shift; unless ( $self->CurrentUserHasRight('AdminType') ) { return ( 0, $self->loc('Permission Denied') ); } #RT->System->AssetTypeCacheNeedsUpdate(1); return ( $self->SUPER::_Set(@_) ); } sub _Value { my $self = shift; unless ( $self->CurrentUserHasRight('SeeType') ) { return (undef); } return ( $self->__Value(@_) ); } =head2 CurrentUserHasRight Takes one argument. A textual string with the name of the right we want to check. Returns true if the current user has that right for this type. Returns undef otherwise. =cut sub CurrentUserHasRight { my $self = shift; my $right = shift; return ( $self->HasRight( Principal => $self->CurrentUser, Right => "$right" ) ); } =head2 CurrentUserCanSee Returns true if the current user can see the type, using SeeType =cut sub CurrentUserCanSee { my $self = shift; return $self->CurrentUserHasRight('SeeType'); } =head2 HasRight Takes a param hash with the fields 'Right' and 'Principal'. Principal defaults to the current user. Returns true if the principal has that right for this type. Returns undef otherwise. =cut # TAKES: Right and optional "Principal" which defaults to the current user sub HasRight { my $self = shift; my %args = ( Right => undef, Principal => $self->CurrentUser, @_ ); my $principal = delete $args{'Principal'}; unless ( $principal ) { $RT::Logger->error("Principal undefined in Type::HasRight"); return undef; } return $principal->HasRight( %args, Object => ($self->Id ? $self : $RT::System), ); } =head2 id Returns the current value of id. (In the database, id is stored as int(11).) =cut =head2 Name Returns the current value of Name. (In the database, Name is stored as varchar(200).) =head2 SetName VALUE Set Name to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Name will be stored as a varchar(200).) =cut =head2 Description Returns the current value of Description. (In the database, Description is stored as varchar(255).) =head2 SetDescription VALUE Set Description to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Description will be stored as a varchar(255).) =cut =head2 DefaultAdmin Returns the current value of DefaultAdmin. (In the database, DefaultAdmin is stored as int(11).) =head2 SetDefaultAdmin VALUE Set DefaultAdmin to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, DefaultAdmin will be stored as a int(11).) =cut =head2 Lifecycle Returns the current value of Lifecycle. (In the database, Lifecycle is stored as varchar(32).) =head2 SetLifecycle VALUE Set Lifecycle to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Lifecycle will be stored as a varchar(32).) =cut =head2 Creator Returns the current value of Creator. (In the database, Creator is stored as int(11).) =cut =head2 Created Returns the current value of Created. (In the database, Created is stored as datetime.) =cut =head2 LastUpdatedBy Returns the current value of LastUpdatedBy. (In the database, LastUpdatedBy is stored as int(11).) =cut =head2 LastUpdated Returns the current value of LastUpdated. (In the database, LastUpdated is stored as datetime.) =cut =head2 Disabled Returns the current value of Disabled. (In the database, Disabled is stored as smallint(6).) =head2 SetDisabled VALUE Set Disabled to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, Disabled will be stored as a smallint(6).) =cut sub _CoreAccessible { { id => {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, Name => {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''}, Description => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''}, DefaultAdmin => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Lifecycle => {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => 'at_default'}, Creator => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, Created => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, LastUpdatedBy => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, LastUpdated => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, Disabled => {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'}, } }; RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/lib/RTx/AssetTracker/Types.pm000066400000000000000000000063461222742774700242110ustar00rootroot00000000000000# BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} =head1 NAME RTx::AssetTracker::Types - a collection of AssetTracker Types objects =head1 SYNOPSIS use RTx::AssetTracker::Types; =head1 DESCRIPTION =head1 METHODS =cut use strict; use warnings; package RTx::AssetTracker::Types; use base 'RT::SearchBuilder'; use RTx::AssetTracker::Type; sub Table {'AT_Types'}; sub _Init { my $self = shift; $self->{'table'} = "AT_Types"; $self->{'primary_key'} = "id"; $self->{'with_disabled_column'} = 1; # By default, order by name $self->OrderBy( ALIAS => 'main', FIELD => 'Name', ORDER => 'ASC'); return ($self->SUPER::_Init(@_)); } sub Limit { my $self = shift; my %args = ( ENTRYAGGREGATOR => 'AND', @_); $self->SUPER::Limit(%args); } =head2 AddRecord Adds a record object to this collection if this user can see. This is used for filtering objects for both Next and ItemsArrayRef. =cut sub AddRecord { my $self = shift; my $Type = shift; return unless $Type->CurrentUserHasRight('SeeType'); push @{$self->{'items'}}, $Type; $self->{'rows'}++; } =head2 NewItem Returns an empty new RTx::AssetTracker::Type item =cut sub NewItem { my $self = shift; return(RTx::AssetTracker::Type->new($self->CurrentUser)); } RT::Base->_ImportOverlays(); 1; rt-extension-assettracker-3.0.0/po/000077500000000000000000000000001222742774700172765ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/po/RTx-AssetTracker/000077500000000000000000000000001222742774700224045ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/po/RTx-AssetTracker/ru.po000066400000000000000000002730441222742774700234040ustar00rootroot00000000000000# translation of ru.po to Russian # FIXME # Andrew Kornilov , 2005. msgid "" msgstr "" "Last-Translator: Andrew Kornilov \n" "PO-Revision-Date: 2005-04-01 15:02+0300\n" "Project-Id-Version: ru\n" "Language-Team: Russian \n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 1.9.1\n" "MIME-Version: 1.0\n" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1163 lib/RTx/AssetTracker/Asset_Overlay.pm:1163 msgid "$addr is no longer an IP for this asset." msgstr "$addr больше не является адресом IP для этого ресурса." #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1203 lib/RTx/AssetTracker/Asset_Overlay.pm:1203 msgid "$args{IP} is now an IP for this asset." msgstr "$args{IP} теперь является адресом IP для этого ресурса." #: lib/RTx/AssetTracker/.svn/text-base/IP_Overlay.pm.svn-base:146 lib/RTx/AssetTracker/IP_Overlay.pm:146 msgid "$args{Transport} $args{Port} has been added for this IP." msgstr "$args{Transport} $args{Port} был добавлен для этого адреса IP." #: lib/RTx/AssetTracker/.svn/text-base/IP_Overlay.pm.svn-base:197 lib/RTx/AssetTracker/IP_Overlay.pm:197 msgid "$portnum $transport is no longer an port for this IP." msgstr "$portnum $transport больше не является портом для этого адреса IP." #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:851 lib/RTx/AssetTracker/Record.pm:851 #. ($self->id) msgid "$prefix %1" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:845 lib/RTx/AssetTracker/Record.pm:845 #. ( . $self->Name, $self->id) msgid "$prefix %1 " msgstr "" #: lib/RT/URI/.svn/text-base/at.pm.svn-base:260 lib/RT/URI/at.pm:260 #. ($self->ObjectType, $self->Object->Id) msgid "%1 #%2" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1190 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:900 lib/RTx/AssetTracker/Record.pm:1190 lib/RTx/AssetTracker/Transaction_Overlay.pm:900 #. ($cf->Name, $new_value->Content) #. ($field, $self->NewValue) msgid "%1 %2 added" msgstr "%1 %2 добавлено" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1197 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:907 lib/RTx/AssetTracker/Record.pm:1197 lib/RTx/AssetTracker/Transaction_Overlay.pm:907 #. ($cf->Name, $old_content, $new_value->Content) #. ($field, $self->OldValue, $self->NewValue) msgid "%1 %2 changed to %3" msgstr "%1 %2 изменено на %3" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1194 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:903 lib/RTx/AssetTracker/Record.pm:1194 lib/RTx/AssetTracker/Transaction_Overlay.pm:903 #. ($cf->Name, $old_value->Content) #. ($field, $self->OldValue) msgid "%1 %2 deleted" msgstr "%1 %2 удалено" #: html/AssetTracker/Elements/.svn/text-base/RecentAssets.svn-base:47 html/AssetTracker/Elements/RecentAssets:47 #. ($rows) msgid "%1 Most recently updated assets" msgstr "%1 Наиболее часто обновляемых ресурсов" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1227 lib/RTx/AssetTracker/Record.pm:1227 #. ($args{'Value'}, $cf->Name) msgid "%1 added as a value for %2" msgstr "%1 добавлено как значение для %2" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:520 lib/RTx/AssetTracker/Transaction_Overlay.pm:520 #. ($self->BriefDescription , $self->CreatorObj->Name) msgid "%1 by %2" msgstr "%1 пользователем %2" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:576 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:920 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:929 lib/RTx/AssetTracker/Transaction_Overlay.pm:576 lib/RTx/AssetTracker/Transaction_Overlay.pm:920 lib/RTx/AssetTracker/Transaction_Overlay.pm:929 #. ($self->Field , ( $self->OldValue || $no_value ) , $self->NewValue) #. ($self->Field , $t1->Name , $t2->Name) msgid "%1 changed from %2 to %3" msgstr "%1 изменено с %2 на %3" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:192 html/AssetTracker/Search/Build.html:192 #. ($Description) msgid "%1 copy" msgstr "%1 копия" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:857 lib/RTx/AssetTracker/Record.pm:857 msgid "%1 could not be set to %2." msgstr "%1 невозможно быть установлено в %2." #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:561 lib/RTx/AssetTracker/Transaction_Overlay.pm:561 #. ($obj_type) msgid "%1 created" msgstr "%1 создано" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:566 lib/RTx/AssetTracker/Transaction_Overlay.pm:566 #. ($obj_type) msgid "%1 deleted" msgstr "%1 удалено" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:894 lib/RTx/AssetTracker/Type_Overlay.pm:894 #. ($principal->Object->Name, $args{'Type'}) msgid "%1 is no longer a %2 for this type." msgstr "%1 больше не явялется %2 для этой категории." #: html/AssetTracker/Search/Elements/.svn/text-base/SelectSearchesForObjects.svn-base:56 html/AssetTracker/Search/Elements/SelectSearchesForObjects:56 #. ($object->Name) msgid "%1's saved searches" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:470 lib/RTx/AssetTracker/Transaction_Overlay.pm:470 #. ($self) msgid "%1: no attachment specified" msgstr "%1: вложения не указаны" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowTransactionAttachments.svn-base:78 html/AssetTracker/Asset/Elements/ShowTransactionAttachments:78 #. ($size) msgid "%1b" msgstr "%1б" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowTransactionAttachments.svn-base:75 html/AssetTracker/Asset/Elements/ShowTransactionAttachments:75 #. (int( $size / 102.4 ) / 10) msgid "%1k" msgstr "%1к" #: html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:92 html/AssetTracker/Asset/Create.html:92 msgid "(Administrators)" msgstr "(Администраторы)" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditTypeWatchers.abk.svn-base:50 html/AssetTracker/Admin/Elements/.svn/text-base/EditTypeWatchers.svn-base:50 html/AssetTracker/Admin/Elements/EditTypeWatchers.abk:50 html/AssetTracker/Admin/Elements/EditTypeWatchers:50 html/AssetTracker/Asset/Elements/.svn/text-base/EditIPs.svn-base:24 html/AssetTracker/Asset/Elements/EditIPs:24 msgid "(Check box to delete)" msgstr "(Выделите пункты для удаления)" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomFields.svn-base:74 html/AssetTracker/Admin/Elements/EditCustomFields:74 msgid "(No custom fields)" msgstr "(Нет дополнительных полей)" #: html/AssetTracker/Asset/Elements/.svn/text-base/EditCustomField.svn-base:88 html/AssetTracker/Asset/Elements/.svn/text-base/EditCustomFields.svn-base:88 html/AssetTracker/Asset/Elements/EditCustomField:88 html/AssetTracker/Asset/Elements/EditCustomFields:88 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:575 lib/RTx/AssetTracker/Transaction_Overlay.pm:575 msgid "(no value)" msgstr "(нет значения)" #: html/AssetTracker/Elements/RTx__Asset/.svn/text-base/ColumnMap.svn-base:166 html/AssetTracker/Elements/RTx__Asset/ColumnMap:166 msgid "(pending approval)" msgstr "(в ожидании подтверждения)" #: html/AssetTracker/Elements/RTx__Asset/.svn/text-base/ColumnMap.svn-base:169 html/AssetTracker/Elements/RTx__Asset/ColumnMap:169 msgid "(pending other Collection)" msgstr "(в ожидании решения связанных заявок)" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowTransactionAttachments.svn-base:82 html/AssetTracker/Asset/Elements/ShowTransactionAttachments:82 msgid "(untitled)" msgstr "(без названия)" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:57 html/AssetTracker/Asset/Elements/ShowBasics:57 msgid "<% $Asset->Description%>" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:65 html/AssetTracker/Asset/Elements/ShowBasics:65 msgid "<% $Asset->Location%>" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:61 html/AssetTracker/Asset/Elements/ShowBasics:61 msgid "<% $Asset->Model%>" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:53 html/AssetTracker/Asset/Elements/ShowBasics:53 msgid "<% $Asset->Name%>" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:69 html/AssetTracker/Asset/Elements/ShowBasics:69 msgid "<% $Asset->Status%>" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Quicksearch.orig.svn-base:52 html/AssetTracker/Elements/.svn/text-base/Quicksearch.svn-base:52 html/AssetTracker/Elements/Quicksearch.orig:52 html/AssetTracker/Elements/Quicksearch:52 html/AssetTracker/Search/Elements/.svn/text-base/SelectLinks.svn-base:48 html/AssetTracker/Search/Elements/SelectLinks:48 msgid "<%$_%>" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/CreateAsset.svn-base:47 html/AssetTracker/Elements/CreateAsset:47 #. ($m->scomp('/AssetTracker/Elements/SelectNewAssetType')) msgid " %1" msgstr " %1" #: html/AssetTracker/Elements/.svn/text-base/ShowLinks.svn-base:67 html/AssetTracker/Elements/ShowLinks.andy:67 html/AssetTracker/Elements/ShowLinks.orig:67 html/AssetTracker/Elements/ShowLinks:67 #. ($m->scomp('/Elements/SelectNewTicketQueue' )) msgid " %1" msgstr " %1" #: html/Callbacks/AssetTracker/Admin/Elements/SystemTabs/.svn/text-base/Default.svn-base:2 html/Callbacks/AssetTracker/Admin/Elements/SystemTabs/Default:2 html/Callbacks/AssetTracker/Admin/Global/index.html/.svn/text-base/Default.svn-base:2 html/Callbacks/AssetTracker/Admin/Global/index.html/Default:2 msgid "AT Group Rights" msgstr "Права группы в AT" #: html/Callbacks/AssetTracker/Admin/Elements/SystemTabs/.svn/text-base/Default.svn-base:5 html/Callbacks/AssetTracker/Admin/Elements/SystemTabs/Default:5 html/Callbacks/AssetTracker/Admin/Global/index.html/.svn/text-base/Default.svn-base:6 html/Callbacks/AssetTracker/Admin/Global/index.html/Default:6 msgid "AT User Rights" msgstr "Права пользователя в AT" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:65 html/AssetTracker/Search/Build.html:65 msgid "Add" msgstr "Добавить" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:111 html/AssetTracker/Search/Bulk.html:111 msgid "Add Admin" msgstr "Добавить Администратора" #: html/AssetTracker/Search/Elements/.svn/text-base/EditFormat.svn-base:49 html/AssetTracker/Search/Elements/EditFormat:49 msgid "Add Columns" msgstr "Добавить колонки" #: html/AssetTracker/Search/Elements/.svn/text-base/PickCriteria.svn-base:46 html/AssetTracker/Search/Elements/PickCriteria:46 msgid "Add Criteria" msgstr "Добавить критерий" #: html/AssetTracker/Asset/Elements/.svn/text-base/EditIPs.svn-base:4 html/AssetTracker/Asset/Elements/EditIPs:4 msgid "Add IPs" msgstr "Добавить адреса IP" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:107 html/AssetTracker/Search/Bulk.html:107 msgid "Add Owner" msgstr "Добавить Ответственного" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:65 html/AssetTracker/Search/Build.html:65 msgid "Add additional criteria" msgstr "Добавить дополнительный критерий" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:137 html/AssetTracker/Search/Bulk.html:137 msgid "Add comments or replies to selected assets" msgstr "Добавить комментарии или ответы на выбранные ресурсы" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:87 html/AssetTracker/Admin/Types/People.html:87 msgid "Add new watchers" msgstr "Добавить новых наблюдателей" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:641 lib/RTx/AssetTracker/Asset_Overlay.pm:641 #. ($self->loc($args{'Type'})) msgid "Added principal as a %1 for this asset" msgstr "Принципал добавлен как %1 для этого ресурса" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:795 lib/RTx/AssetTracker/Type_Overlay.pm:795 #. ($args{'Type'}) msgid "Added principal as a %1 for this type" msgstr "Принципал добавлен как %1 для этой категории" #: etc/AssetTracker/.svn/text-base/initialdata.svn-base:11 etc/AssetTracker/initialdata:11 msgid "Admin" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/index.html.svn-base:46 html/AssetTracker/Admin/Types/.svn/text-base/index.html.svn-base:49 html/AssetTracker/Admin/Types/index.html:46 html/AssetTracker/Admin/Types/index.html:49 msgid "Admin types" msgstr "Управление категориями" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:39 lib/RTx/AssetTracker/Type_Overlay.pm:39 msgid "AdminType" msgstr "УправлятьКатегориями" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:69 html/AssetTracker/Admin/Types/People.html:69 html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:87 html/AssetTracker/Asset/Create.html:87 msgid "Administrators" msgstr "Администраторы" #: html/AssetTracker/Asset/Elements/.svn/text-base/EditPeople.svn-base:56 html/AssetTracker/Asset/Elements/.svn/text-base/ShowPeople.svn-base:52 html/AssetTracker/Asset/Elements/EditPeople:56 html/AssetTracker/Asset/Elements/ShowPeople:52 msgid "Admins" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:170 html/AssetTracker/Elements/Tabs:170 msgid "Advanced" msgstr "Дополнительно" #: html/AssetTracker/Search/Elements/.svn/text-base/PickCriteria.svn-base:52 html/AssetTracker/Search/Elements/PickCriteria:52 msgid "Aggregator" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/index.html.svn-base:74 html/AssetTracker/Admin/Types/index.html:74 msgid "All Types" msgstr "Все категории" #: lib/RTx/AssetTracker/.svn/text-base/System.pm.svn-base:53 lib/RTx/AssetTracker/System.pm:53 msgid "Allow educated users to perform bulk updates" msgstr "" #: html/Callbacks/AssetTracker/Admin/Elements/CustomFieldTabs/.svn/text-base/Default.svn-base:8 html/Callbacks/AssetTracker/Admin/Elements/CustomFieldTabs/Default:8 msgid "Applies to" msgstr "Применяется к" #: html/AssetTracker/Search/.svn/text-base/Edit.html.svn-base:64 html/AssetTracker/Search/Edit.html:64 msgid "Apply" msgstr "Применить" #: html/AssetTracker/Search/.svn/text-base/Edit.html.svn-base:64 html/AssetTracker/Search/Edit.html:64 msgid "Apply your changes" msgstr "Применить ваши изменения" #: html/AssetTracker/Search/Elements/.svn/text-base/DisplayOptions.svn-base:73 html/AssetTracker/Search/Elements/DisplayOptions:73 msgid "Ascending" msgstr "По возрастанию" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:322 html/AssetTracker/Search/.svn/text-base/Grid.html.svn-base:267 html/AssetTracker/Search/Bulk.html:322 html/AssetTracker/Search/Grid.html:267 #. ($Asset->Id,$Asset->Name,$_) msgid "Asset #%1 %2: %3" msgstr "Ресурс #%1 %2: %3" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:391 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:411 lib/RTx/AssetTracker/Asset_Overlay.pm:391 lib/RTx/AssetTracker/Asset_Overlay.pm:411 #. ($self->Id, $TypeObj->Name) msgid "Asset %1 created of type '%2'" msgstr "Ресурс %1 создан в категории '%2'" #: html/AssetTracker/Asset/.svn/text-base/ModifyAll.html.svn-base:46 html/AssetTracker/Asset/.svn/text-base/ModifyAll.html.svn-base:50 html/AssetTracker/Asset/ModifyAll.html:46 html/AssetTracker/Asset/ModifyAll.html:50 #. ($Action, $Name) msgid "Asset %1: %2" msgstr "Ресурс %1: %2" #: html/AssetTracker/Admin/Elements/.svn/text-base/TypeTabs.svn-base:67 html/AssetTracker/Admin/Elements/TypeTabs:67 msgid "Asset Custom Fields" msgstr "Дополнительные поля ресурса" #: html/AssetTracker/Asset/.svn/text-base/History.html.svn-base:46 html/AssetTracker/Asset/.svn/text-base/History.html.svn-base:49 html/AssetTracker/Asset/History.html:46 html/AssetTracker/Asset/History.html:49 #. ($Asset->Name) msgid "Asset History # %1" msgstr "История ресурса # %1" #: html/AssetTracker/.svn/text-base/index.html.svn-base:6 html/AssetTracker/index.html:6 msgid "Asset Tracker" msgstr "Ресурсы" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:72 html/AssetTracker/Asset/Elements/ShowBasics:72 html/AssetTracker/Elements/.svn/text-base/Quicksearch.svn-base:50 html/AssetTracker/Elements/Quicksearch:50 msgid "Asset Type" msgstr "Категория ресурса" #: html/Callbacks/AssetTracker/Admin/Elements/Tabs/.svn/text-base/Default.svn-base:2 html/Callbacks/AssetTracker/Admin/Elements/Tabs/Default:2 html/Callbacks/AssetTracker/Admin/index.html/.svn/text-base/Default.svn-base:2 html/Callbacks/AssetTracker/Admin/index.html/Default:2 msgid "Asset Types" msgstr "Категории ресурсов" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:287 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:298 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:399 lib/RTx/AssetTracker/Asset_Overlay.pm:287 lib/RTx/AssetTracker/Asset_Overlay.pm:298 lib/RTx/AssetTracker/Asset_Overlay.pm:399 msgid "Asset could not be created due to an internal error" msgstr "Невозможно создать ресурс из-за внутренней ошибки" #: html/AssetTracker/Asset/.svn/text-base/Display.html.svn-base:55 html/AssetTracker/Asset/Display.html:55 msgid "Asset metadata" msgstr "Метаданные ресурса" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:244 lib/RTx/AssetTracker/Type_Overlay.pm:244 msgid "Asset type already exists" msgstr "Данная категория ресурсов уже существует" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:253 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:259 lib/RTx/AssetTracker/Type_Overlay.pm:253 lib/RTx/AssetTracker/Type_Overlay.pm:259 msgid "Asset type could not be created" msgstr "Невозможно создать категорию ресурсов" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:264 lib/RTx/AssetTracker/Type_Overlay.pm:264 msgid "Asset type created" msgstr "Создана категория ресурсов" #: html/Callbacks/AssetTracker/Admin/Elements/GlobalCustomFieldTabs/.svn/text-base/Default.svn-base:2 html/Callbacks/AssetTracker/Admin/Elements/GlobalCustomFieldTabs/Default:2 html/Callbacks/AssetTracker/Admin/Global/CustomFields/index.html/.svn/text-base/Default.svn-base:2 html/Callbacks/AssetTracker/Admin/Global/CustomFields/index.html/Default:2 html/Callbacks/AssetTracker/Elements/EditLinks/.svn/text-base/DeleteLinks.svn-base:2 html/Callbacks/AssetTracker/Elements/EditLinks/.svn/text-base/NewLinks.svn-base:2 html/Callbacks/AssetTracker/Elements/EditLinks/DeleteLinks:2 html/Callbacks/AssetTracker/Elements/EditLinks/NewLinks:2 html/Callbacks/AssetTracker/Elements/Tabs/.svn/text-base/Default.svn-base:2 html/Callbacks/AssetTracker/Elements/Tabs/Default:2 msgid "Assets" msgstr "Ресурсы" #: html/AssetTracker/.svn/text-base/index.html.svn-base:2 html/AssetTracker/index.html:2 msgid "Assets at a glance" msgstr "Обзор ресурсов" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:40 lib/RTx/AssetTracker/Type_Overlay.pm:40 msgid "Assign and remove custom fields" msgstr "Назначить или удалить дополнительные поля" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:40 lib/RTx/AssetTracker/Type_Overlay.pm:40 msgid "AssignCustomFields" msgstr "НазначатьДополнительныеПоля" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:155 html/AssetTracker/Search/Bulk.html:155 msgid "Attach" msgstr "Вложить" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:478 lib/RTx/AssetTracker/Transaction_Overlay.pm:478 msgid "Attachment created" msgstr "Вложение создано" #: html/AssetTracker/Admin/Elements/.svn/text-base/TypeTabs.svn-base:60 html/AssetTracker/Admin/Elements/TypeTabs:60 msgid "Basics" msgstr "Основное" #: html/AssetTracker/Search/.svn/text-base/Results.html.svn-base:80 html/AssetTracker/Search/Results.html:80 msgid "Batch Update multiple assets" msgstr "Пакетное изменение множества ресурсов" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:91 html/AssetTracker/Admin/CustomFields/.svn/text-base/UserRights.html.svn-base:74 html/AssetTracker/Admin/CustomFields/GroupRights.html:91 html/AssetTracker/Admin/CustomFields/UserRights.html:74 msgid "Be sure to save your changes" msgstr "После любых изменений не забудьте сохранить их" #: html/AssetTracker/Search/Elements/.svn/text-base/EditFormat.svn-base:88 html/AssetTracker/Search/Elements/EditFormat:88 msgid "Bold" msgstr "Жирный" #: html/AssetTracker/Search/.svn/text-base/Results.html.svn-base:82 html/AssetTracker/Search/Results.html:82 msgid "Bookmarkable link" msgstr "Ссылка на этот запрос для добавления в избранное" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowHistory.svn-base:60 html/AssetTracker/Asset/Elements/.svn/text-base/ShowHistory.svn-base:66 html/AssetTracker/Asset/Elements/ShowHistory:60 html/AssetTracker/Asset/Elements/ShowHistory:66 msgid "Brief headers" msgstr "Краткие заголовки" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:46 html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:47 html/AssetTracker/Search/Bulk.html:46 html/AssetTracker/Search/Bulk.html:47 msgid "Bulk asset update" msgstr "Изменить выбранные ресурсы" #: lib/RTx/AssetTracker/.svn/text-base/System.pm.svn-base:53 lib/RTx/AssetTracker/System.pm:53 msgid "BulkUpdate" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:38 lib/RTx/AssetTracker/Type_Overlay.pm:38 msgid "Can this principal see this asset type" msgstr "Может ли данный принципал видеть данную категорию ресурсов" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:819 html/AssetTracker/Search/Build.html:819 msgid "Can't find a saved search to work with" msgstr "Невозможно найти сохраненный запрос для работы с ним" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:824 html/AssetTracker/Search/Build.html:824 msgid "Can't save this search" msgstr "Невозможно сохранить данный запрос" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1560 lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1638 lib/RTx/AssetTracker/Record.pm:1560 lib/RTx/AssetTracker/Record.pm:1638 msgid "Can't specifiy both base and target" msgstr "Невозможно указывать одновременно и источник и адрес назначения" #: NOT FOUND IN SOURCE msgid "Check box to revoke right" msgstr "Выделите права, которые хотите отозвать" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowTransaction.svn-base:193 html/AssetTracker/Asset/Elements/ShowTransaction:193 msgid "Comment" msgstr "Комментировать" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:141 html/AssetTracker/Search/Bulk.html:141 msgid "Comments (not sent to requestors)" msgstr "Комментарии (не отправляются авторам заявок)" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowSummary.svn-base:76 html/AssetTracker/Asset/Elements/ShowSummary:76 msgid "Components" msgstr "Компоненты" #: html/AssetTracker/Elements/.svn/text-base/SelectAttachmentField.svn-base:49 html/AssetTracker/Elements/SelectAttachmentField:49 msgid "Content" msgstr "Содержание" #: html/AssetTracker/Elements/.svn/text-base/SelectAttachmentField.svn-base:50 html/AssetTracker/Elements/SelectAttachmentField:50 msgid "Content-Type" msgstr "Тип содержания" #: html/AssetTracker/Search/Elements/.svn/text-base/EditSearches.svn-base:64 html/AssetTracker/Search/Elements/EditSearches:64 msgid "Copy" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1212 lib/RTx/AssetTracker/Record.pm:1212 msgid "Could not add new custom field value. " msgstr "Невозможно добавить новое значение дополнительного поля. " #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1165 lib/RTx/AssetTracker/Record.pm:1165 #. (, $value_msg) msgid "Could not add new custom field value. %1 " msgstr "Невозможно добавить новое значение дополнительного поля. %1 " #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:113 html/AssetTracker/Admin/Elements/EditCustomField:113 #. ($msg) msgid "Could not create CustomField: %1" msgstr "Невозможно создать дополнительное поле: %1" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:182 lib/RTx/AssetTracker/Asset_Overlay.pm:182 msgid "Could not create asset. Type not set" msgstr "Невозможно создать ресурс. Не указана категория" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:774 lib/RTx/AssetTracker/Type_Overlay.pm:774 msgid "Could not find or create that user" msgstr "Невозможно найти или создать данного пользователя" #: lib/RTx/AssetTracker/.svn/text-base/IP_Overlay.pm.svn-base:186 lib/RTx/AssetTracker/IP_Overlay.pm:186 msgid "Could not find that $args{Transport} Port" msgstr "Невозможно найти данный $args{Transport} Port" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1147 lib/RTx/AssetTracker/Asset_Overlay.pm:1147 msgid "Could not find that IP" msgstr "Невозможно найти данный адрес IP" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:689 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:834 lib/RTx/AssetTracker/Asset_Overlay.pm:689 lib/RTx/AssetTracker/Type_Overlay.pm:834 msgid "Could not find that principal" msgstr "Невозможно найти данного принципала" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:610 lib/RTx/AssetTracker/Asset_Overlay.pm:610 msgid "Could not find that user" msgstr "Невозможно найти данного пользователя" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/Objects.html.svn-base:69 html/AssetTracker/Admin/CustomFields/Objects.html:69 msgid "Could not load CustomField %1" msgstr "Невозможно загрузить дополнительное поле %1" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:630 lib/RTx/AssetTracker/Asset_Overlay.pm:630 #. ($self->loc($args{'Type'})) msgid "Could not make that principal a %1 for this asset" msgstr "Невозможно установить данного принципала как %1 для данного ресурса" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:793 lib/RTx/AssetTracker/Type_Overlay.pm:793 #. ($args{'Type'}) msgid "Could not make that principal a %1 for this type" msgstr "Невозможно установить данного принципала как %1 для данной категории" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:891 lib/RTx/AssetTracker/Type_Overlay.pm:891 #. ($args{'Type'}) msgid "Could not remove that principal as a %1 for this type" msgstr "Невозможно удалить данного принципала как %1 для данной категории" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1224 lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1283 lib/RTx/AssetTracker/Record.pm:1224 lib/RTx/AssetTracker/Record.pm:1283 #. ($Msg) msgid "Couldn't create a transaction: %1" msgstr "Невозможно создать транзакцию: %1" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:866 lib/RTx/AssetTracker/Record.pm:866 msgid "Couldn't find row" msgstr "Невозможно найти строку" #: NOT FOUND IN SOURCE msgid "Couldn't load %1 from the users database.\\n" msgstr "Невозможно загрузить %1 из базы данных пользователей. \\n" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/UserRights.html.svn-base:149 html/AssetTracker/Admin/CustomFields/UserRights.html:149 #. ($id) msgid "Couldn't load Class %1" msgstr "Невозможно загрузить категорию %1" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:107 html/AssetTracker/Admin/CustomFields/GroupRights.html:107 #. ($id) msgid "Couldn't load CustomField %1" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/ObjectCustomFields.svn-base:90 html/AssetTracker/Admin/Elements/ObjectCustomFields:90 #. ($id) msgid "Couldn't load object %1" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:142 html/AssetTracker/Admin/Types/People.html:142 #. ($id) msgid "Couldn't load type" msgstr "Невомзожно загрузить категорию" #: html/AssetTracker/Admin/Types/.svn/text-base/GroupRights.html.svn-base:121 html/AssetTracker/Admin/Types/.svn/text-base/UserRights.html.svn-base:93 html/AssetTracker/Admin/Types/GroupRights.html:121 html/AssetTracker/Admin/Types/UserRights.html:93 #. ($id) msgid "Couldn't load type %1" msgstr "Невозможно загрузить категорию %1" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:84 html/AssetTracker/Admin/Elements/EditCustomField:84 html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:130 html/AssetTracker/Asset/Create.html:130 msgid "Create" msgstr "Создать" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:96 html/AssetTracker/Admin/Elements/EditCustomField:96 msgid "Create a CustomField" msgstr "Новое дополнительное поле" #: html/AssetTracker/Admin/Types/.svn/text-base/CustomField.html.svn-base:69 html/AssetTracker/Admin/Types/CustomField.html:69 #. ($TypeObj->Name()) msgid "Create a CustomField for type %1" msgstr "Создание дополнительного поля для категории %1" #: html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:46 html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:49 html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:55 html/AssetTracker/Asset/Create.html:46 html/AssetTracker/Asset/Create.html:49 html/AssetTracker/Asset/Create.html:55 msgid "Create a new asset" msgstr "Создание нового ресурса" #: html/AssetTracker/Admin/Types/.svn/text-base/Modify.html.svn-base:94 html/AssetTracker/Admin/Types/Modify.html:94 msgid "Create a type" msgstr "Создание категории" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:44 lib/RTx/AssetTracker/Type_Overlay.pm:44 msgid "Create assets of this type" msgstr "Создать ресурсы в данной категории" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:39 lib/RTx/AssetTracker/Type_Overlay.pm:39 msgid "Create, delete and modify asset types" msgstr "Создать, удалить или изменить категории ресурсов" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:44 lib/RTx/AssetTracker/Type_Overlay.pm:44 msgid "CreateAsset" msgstr "СоздаватьРесурс" #: html/AssetTracker/Elements/.svn/text-base/SelectDateType.svn-base:47 html/AssetTracker/Elements/SelectDateType:47 msgid "Created" msgstr "Создано" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:117 html/AssetTracker/Admin/Elements/EditCustomField:117 #. ($CustomFieldObj->Name()) msgid "Created CustomField %1" msgstr "Создано дополнительное поле %1" #: html/AssetTracker/Asset/Elements/.svn/text-base/EditIPs.svn-base:23 html/AssetTracker/Asset/Elements/EditIPs:23 msgid "Current IPs" msgstr "Текущие адреса IP" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:62 html/AssetTracker/Admin/Types/People.html:62 html/AssetTracker/Asset/Elements/.svn/text-base/EditPeople.svn-base:50 html/AssetTracker/Asset/Elements/EditPeople:50 msgid "Current watchers" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowSummary.svn-base:99 html/AssetTracker/Asset/Elements/ShowSummary:99 msgid "Custom Fields" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1097 lib/RTx/AssetTracker/Record.pm:1097 #. ($args{'Field'}) msgid "Custom field %1 not found" msgstr "" #: lib/RT/.svn/text-base/CustomField_Vendor.pm.svn-base:70 lib/RT/CustomField_Vendor.pm:70 lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1265 lib/RTx/AssetTracker/Record.pm:1265 msgid "Custom field not found" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:891 lib/RTx/AssetTracker/Transaction_Overlay.pm:891 msgid "CustomField" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowSummary.svn-base:109 html/AssetTracker/Asset/Elements/ShowSummary:109 html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:119 html/AssetTracker/Elements/Tabs:119 msgid "Dates" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:583 lib/RTx/AssetTracker/Transaction_Overlay.pm:583 #. ($type, $self->Field, $self->OldValue, $self->NewValue) msgid "Default: %1/%2 changed from %3 to %4" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/EditFormat.svn-base:111 html/AssetTracker/Search/Elements/.svn/text-base/EditSearches.svn-base:63 html/AssetTracker/Search/Elements/EditFormat:111 html/AssetTracker/Search/Elements/EditSearches:63 msgid "Delete" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:333 lib/RTx/AssetTracker/Type_Overlay.pm:333 msgid "Deleting this object would break referential integrity" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/DisplayOptions.svn-base:78 html/AssetTracker/Search/Elements/DisplayOptions:78 msgid "Descending" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:60 html/AssetTracker/Admin/Elements/EditCustomField:60 html/AssetTracker/Admin/Types/.svn/text-base/Modify.html.svn-base:69 html/AssetTracker/Admin/Types/Modify.html:69 html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:97 html/AssetTracker/Asset/Create.html:97 html/AssetTracker/Asset/Elements/.svn/text-base/EditBasics.svn-base:76 html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:56 html/AssetTracker/Asset/Elements/EditBasics:76 html/AssetTracker/Asset/Elements/ShowBasics:56 html/AssetTracker/Elements/.svn/text-base/SelectAttachmentField.svn-base:48 html/AssetTracker/Elements/SelectAttachmentField:48 html/AssetTracker/Search/Elements/.svn/text-base/EditSearches.svn-base:56 html/AssetTracker/Search/Elements/EditSearches:56 msgid "Description" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:109 html/AssetTracker/Elements/Tabs:109 html/AssetTracker/Search/Elements/.svn/text-base/EditFormat.svn-base:76 html/AssetTracker/Search/Elements/EditFormat:76 msgid "Display" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/DisplayOptions.svn-base:46 html/AssetTracker/Search/Elements/DisplayOptions:46 msgid "Display Columns" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowHistory.svn-base:56 html/AssetTracker/Asset/Elements/ShowHistory:56 msgid "Display mode" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/System.pm.svn-base:52 lib/RTx/AssetTracker/System.pm:52 msgid "Do anything with assets" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:92 html/AssetTracker/Search/Build.html:92 msgid "Do the Search" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:183 html/AssetTracker/Search/Bulk.html:183 msgid "Don't update <% $cf->Name %>" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowTransactionAttachments.svn-base:82 html/AssetTracker/Asset/Elements/ShowTransactionAttachments:82 msgid "Download" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:125 html/AssetTracker/Elements/Tabs:125 msgid "Edit All" msgstr "Все данные" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:112 html/AssetTracker/Elements/Tabs:112 msgid "Edit Basics" msgstr "Основное" #: html/AssetTracker/Admin/Elements/.svn/text-base/ObjectCustomFields.svn-base:99 html/AssetTracker/Admin/Elements/ObjectCustomFields:99 #. ($Object->Name) msgid "Edit Custom Fields for %1" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:114 html/AssetTracker/Elements/Tabs:114 html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:169 html/AssetTracker/Search/Bulk.html:169 msgid "Edit Fields" msgstr "Дополнительные поля" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:116 html/AssetTracker/Elements/Tabs:116 msgid "Edit IPs" msgstr "Адреса IP" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:164 html/AssetTracker/Search/Bulk.html:164 msgid "Edit Links" msgstr "Связи" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:122 html/AssetTracker/Elements/Tabs:122 msgid "Edit People" msgstr "Пользователи" #: html/AssetTracker/Search/.svn/text-base/Edit.html.svn-base:68 html/AssetTracker/Search/Edit.html:68 msgid "Edit Query" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/Modify.html.svn-base:109 html/AssetTracker/Admin/Types/Modify.html:109 #. ($TypeObj->Name) msgid "Editing Configuration for type %1" msgstr "Редактирование конфигурации категории %1" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:120 html/AssetTracker/Admin/Elements/EditCustomField:120 #. ($CustomFieldObj->Name()) msgid "Editing CustomField %1" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1575 lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1652 lib/RTx/AssetTracker/Record.pm:1575 lib/RTx/AssetTracker/Record.pm:1652 msgid "Either base or target must be specified" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:72 html/AssetTracker/Admin/Elements/EditCustomField:72 msgid "Enabled (Unchecking this box disables this custom field)" msgstr "Используется (снятие отметки отключает использование этого дополнительного поля)" #: html/AssetTracker/Admin/Types/.svn/text-base/Modify.html.svn-base:74 html/AssetTracker/Admin/Types/Modify.html:74 msgid "Enabled (Unchecking this box disables this type)" msgstr "Используется (снятие отметки отключает использование этой категории)" #: html/AssetTracker/Admin/Types/.svn/text-base/index.html.svn-base:77 html/AssetTracker/Admin/Types/index.html:77 msgid "Enabled Types" msgstr "Используемые категории" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:136 html/AssetTracker/Admin/Elements/EditCustomField:136 #. (loc_fuzzy($msg)) msgid "Enabled status %1" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/Modify.html.svn-base:130 html/AssetTracker/Admin/Types/Modify.html:130 #. (loc_fuzzy($msg)) msgid "Enabled status: %1" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:165 html/AssetTracker/Search/Bulk.html:165 msgid "Enter assets or URIs to link assets to. Separate multiple entries with spaces." msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:563 lib/RTx/AssetTracker/Asset_Overlay.pm:563 msgid "Error in parameters to Asset->AddWatcher" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:713 lib/RTx/AssetTracker/Asset_Overlay.pm:713 msgid "Error in parameters to Asset->DeleteWatcher" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:705 lib/RTx/AssetTracker/Type_Overlay.pm:705 msgid "Error in parameters to Type->AddWatcher" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:864 lib/RTx/AssetTracker/Type_Overlay.pm:864 msgid "Error in parameters to Type->DeleteWatcher" msgstr "" #: NOT FOUND IN SOURCE msgid "Fields" msgstr "Дополнительные поля" #: html/AssetTracker/Elements/.svn/text-base/SelectAttachmentField.svn-base:51 html/AssetTracker/Elements/SelectAttachmentField:51 msgid "Filename" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Results.html.svn-base:110 html/AssetTracker/Search/Results.html:110 msgid "Find assets" msgstr "Поиск ресурсов" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:82 html/AssetTracker/Admin/Types/People.html:82 msgid "Find groups whose" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:78 html/AssetTracker/Admin/Types/People.html:78 msgid "Find people whose" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:82 html/AssetTracker/Elements/Tabs:82 msgid "First" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Results.html.svn-base:108 html/AssetTracker/Search/Results.html:108 #. ($assetcount) msgid "Found %quant(%1,asset)" msgstr "Найдено %quant(%1,asset)" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:868 lib/RTx/AssetTracker/Record.pm:868 msgid "Found Object" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowHistory.svn-base:62 html/AssetTracker/Asset/Elements/.svn/text-base/ShowHistory.svn-base:72 html/AssetTracker/Asset/Elements/ShowHistory:62 html/AssetTracker/Asset/Elements/ShowHistory:72 msgid "Full headers" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomFields.svn-base:55 html/AssetTracker/Admin/Elements/EditCustomFields:55 msgid "Global Custom Fields" msgstr "" #: html/AssetTracker/.svn/text-base/index.html.svn-base:15 html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:80 html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:84 html/AssetTracker/Admin/Types/.svn/text-base/index.html.svn-base:65 html/AssetTracker/Admin/Types/People.html:80 html/AssetTracker/Admin/Types/People.html:84 html/AssetTracker/Admin/Types/index.html:65 html/AssetTracker/Search/.svn/text-base/Results.html.svn-base:76 html/AssetTracker/Search/Results.html:76 html/AssetTracker/index.html:15 msgid "Go!" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Results.html.svn-base:81 html/AssetTracker/Search/Results.html:81 msgid "Grid Update multiple assets" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Grid.html.svn-base:46 html/AssetTracker/Search/.svn/text-base/Grid.html.svn-base:47 html/AssetTracker/Search/Grid.html:46 html/AssetTracker/Search/Grid.html:47 msgid "Grid asset update" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/TypeTabs.svn-base:71 html/AssetTracker/Admin/Elements/TypeTabs:71 html/Callbacks/AssetTracker/Admin/Elements/CustomFieldTabs/.svn/text-base/Default.svn-base:13 html/Callbacks/AssetTracker/Admin/Elements/CustomFieldTabs/Default:13 msgid "Group Rights" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:616 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:694 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:780 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:839 lib/RTx/AssetTracker/Asset_Overlay.pm:616 lib/RTx/AssetTracker/Asset_Overlay.pm:694 lib/RTx/AssetTracker/Type_Overlay.pm:780 lib/RTx/AssetTracker/Type_Overlay.pm:839 msgid "Group not found" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:104 html/AssetTracker/Admin/Types/People.html:104 msgid "Groups" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowHistory.svn-base:51 html/AssetTracker/Asset/Elements/ShowHistory:51 msgid "History" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:697 html/AssetTracker/Search/Build.html:697 msgid "I'm lost" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/SelectIPField.svn-base:47 html/AssetTracker/Elements/SelectIPField:47 msgid "IP Address" msgstr "Адрес IP" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowSummary.svn-base:68 html/AssetTracker/Asset/Elements/ShowSummary:68 msgid "IP Addresses" msgstr "Адреса IP" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:864 lib/RTx/AssetTracker/Transaction_Overlay.pm:864 #. ($self->NewValue) msgid "IP address %1 added" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:869 lib/RTx/AssetTracker/Transaction_Overlay.pm:869 #. ($self->OldValue) msgid "IP address %1 deleted" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1205 lib/RTx/AssetTracker/Asset_Overlay.pm:1205 msgid "IP address could not be created: $msg" msgstr "Невозможно создать адрес IP : $msg" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1165 lib/RTx/AssetTracker/Asset_Overlay.pm:1165 msgid "IP address could not be deleted" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1191 lib/RTx/AssetTracker/Asset_Overlay.pm:1191 msgid "IP address or interface must be specified" msgstr "" #: html/AssetTracker/Asset/.svn/text-base/ModifyAll.html.svn-base:71 html/AssetTracker/Asset/ModifyAll.html:71 msgid "IPs" msgstr "Адреса IP" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:48 html/AssetTracker/Asset/Elements/ShowBasics:48 lib/RTx/AssetTracker/.svn/text-base/Assets_Overlay.pm.svn-base:1074 lib/RTx/AssetTracker/.svn/text-base/Assets_Overlay.pm.svn-base:1100 lib/RTx/AssetTracker/Assets_Overlay.pm:1074 lib/RTx/AssetTracker/Assets_Overlay.pm:1100 msgid "Id" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:126 html/AssetTracker/Admin/Types/People.html:126 html/AssetTracker/Asset/.svn/text-base/Modify.html.svn-base:60 html/AssetTracker/Asset/.svn/text-base/ModifyAll.html.svn-base:84 html/AssetTracker/Asset/.svn/text-base/ModifyFields.html.svn-base:59 html/AssetTracker/Asset/.svn/text-base/ModifyIPs.html.svn-base:59 html/AssetTracker/Asset/.svn/text-base/ModifyPeople.html.svn-base:59 html/AssetTracker/Asset/Modify.html:60 html/AssetTracker/Asset/ModifyAll.html:84 html/AssetTracker/Asset/ModifyFields.html:59 html/AssetTracker/Asset/ModifyIPs.html:59 html/AssetTracker/Asset/ModifyPeople.html:59 msgid "If you've updated anything above, be sure to" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:860 lib/RTx/AssetTracker/Record.pm:860 msgid "Illegal value for %1" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:863 lib/RTx/AssetTracker/Record.pm:863 msgid "Immutable field" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/index.html.svn-base:64 html/AssetTracker/Admin/Types/index.html:64 msgid "Include disabled types in listing." msgstr "Показывать неиспользуемые категории" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:723 html/AssetTracker/Search/Build.html:723 msgid "Incomplete Query" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:720 html/AssetTracker/Search/Build.html:720 msgid "Incomplete query" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/SelectIPField.svn-base:49 html/AssetTracker/Elements/SelectIPField:49 msgid "Interface" msgstr "Интерфейс" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1020 lib/RTx/AssetTracker/Asset_Overlay.pm:1020 msgid "Internal Error" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:278 lib/RTx/AssetTracker/Record.pm:278 #. ($id->{error_message}) msgid "Internal Error: %1" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:865 lib/RTx/AssetTracker/Record.pm:865 msgid "Invalid data" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:253 lib/RTx/AssetTracker/Record.pm:253 #. ($key) msgid "Invalid value for %1" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1116 lib/RTx/AssetTracker/Record.pm:1116 msgid "Invalid value for custom field" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:199 lib/RTx/AssetTracker/Asset_Overlay.pm:199 msgid "Invalid value for status" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/EditFormat.svn-base:89 html/AssetTracker/Search/Elements/EditFormat:89 msgid "Italic" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/EditFormat.svn-base:83 html/AssetTracker/Search/Elements/EditFormat:83 msgid "Large" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:97 html/AssetTracker/Elements/Tabs:97 msgid "Last" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/SelectDateType.svn-base:48 html/AssetTracker/Elements/SelectDateType:48 msgid "Last Updated" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1586 lib/RTx/AssetTracker/Record.pm:1586 msgid "Link already exists" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1600 lib/RTx/AssetTracker/Record.pm:1600 msgid "Link could not be created" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1606 lib/RTx/AssetTracker/Record.pm:1606 #. ($TransString) msgid "Link created (%1)" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1667 lib/RTx/AssetTracker/Record.pm:1667 #. ($TransString) msgid "Link deleted (%1)" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1673 lib/RTx/AssetTracker/Record.pm:1673 msgid "Link not found" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowSummary.svn-base:116 html/AssetTracker/Asset/Elements/ShowSummary:116 html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:123 html/AssetTracker/Elements/Tabs:123 msgid "Links" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/EditSearches.svn-base:76 html/AssetTracker/Search/Elements/EditSearches:76 msgid "Load" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/EditSearches.svn-base:74 html/AssetTracker/Search/Elements/EditSearches:74 msgid "Load saved search:" msgstr "" #: html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:113 html/AssetTracker/Asset/Create.html:113 html/AssetTracker/Asset/Elements/.svn/text-base/EditBasics.svn-base:92 html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:64 html/AssetTracker/Asset/Elements/EditBasics:92 html/AssetTracker/Asset/Elements/ShowBasics:64 msgid "Location" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/SelectIPField.svn-base:48 html/AssetTracker/Elements/SelectIPField:48 msgid "MAC Address" msgstr "Адрес MAC" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:122 html/AssetTracker/Search/Bulk.html:122 msgid "Make Description" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:128 html/AssetTracker/Search/Bulk.html:128 msgid "Make Parent" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:126 html/AssetTracker/Search/Bulk.html:126 msgid "Make Status" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:124 html/AssetTracker/Search/Bulk.html:124 msgid "Make Type" msgstr "" #: html/Callbacks/AssetTracker/Admin/index.html/.svn/text-base/Default.svn-base:3 html/Callbacks/AssetTracker/Admin/index.html/Default:3 msgid "Manage asset types and asset-specific properties" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:156 html/AssetTracker/Search/Bulk.html:156 msgid "Message" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:727 html/AssetTracker/Search/Build.html:727 msgid "Mismatched parentheses" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:867 lib/RTx/AssetTracker/Record.pm:867 msgid "Missing a primary key?: %1" msgstr "" #: html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:105 html/AssetTracker/Asset/Create.html:105 html/AssetTracker/Asset/Elements/.svn/text-base/EditBasics.svn-base:84 html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:60 html/AssetTracker/Asset/Elements/EditBasics:84 html/AssetTracker/Asset/Elements/ShowBasics:60 msgid "Model" msgstr "" #: html/AssetTracker/Admin/Global/.svn/text-base/GroupRights.html.svn-base:49 html/AssetTracker/Admin/Global/GroupRights.html:49 html/Callbacks/AssetTracker/Admin/Global/index.html/.svn/text-base/Default.svn-base:3 html/Callbacks/AssetTracker/Admin/Global/index.html/Default:3 msgid "Modify Asset Tracker global group rights" msgstr "" #: html/AssetTracker/Admin/Global/.svn/text-base/GroupRights.html.svn-base:54 html/AssetTracker/Admin/Global/GroupRights.html:54 msgid "Modify Asset Tracker global group rights." msgstr "" #: html/AssetTracker/Admin/Global/.svn/text-base/UserRights.html.svn-base:46 html/AssetTracker/Admin/Global/UserRights.html:46 html/Callbacks/AssetTracker/Admin/Global/index.html/.svn/text-base/Default.svn-base:7 html/Callbacks/AssetTracker/Admin/Global/index.html/Default:7 msgid "Modify Asset Tracker global user rights" msgstr "" #: html/AssetTracker/Admin/Global/.svn/text-base/UserRights.html.svn-base:54 html/AssetTracker/Admin/Global/UserRights.html:54 msgid "Modify Asset Tracker global user rights." msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/ObjectCustomFields.svn-base:102 html/AssetTracker/Admin/Elements/ObjectCustomFields:102 #. (loc(lc($FriendlySubTypes)), loc(lc($Types))) msgid "Modify Custom Fields which apply to %1 for all %2" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/ObjectCustomFields.svn-base:104 html/AssetTracker/Admin/Elements/ObjectCustomFields:104 #. (loc(lc($Types))) msgid "Modify Custom Fields which apply to all %1" msgstr "" #: html/AssetTracker/Admin/Global/.svn/text-base/GroupRights.html.svn-base:106 html/AssetTracker/Admin/Global/GroupRights.html:106 html/AssetTracker/Admin/Types/.svn/text-base/GroupRights.html.svn-base:106 html/AssetTracker/Admin/Types/GroupRights.html:106 msgid "Modify Group Rights" msgstr "" #: html/AssetTracker/Asset/.svn/text-base/ModifyIPs.html.svn-base:46 html/AssetTracker/Asset/.svn/text-base/ModifyIPs.html.svn-base:50 html/AssetTracker/Asset/.svn/text-base/ModifyIPs.html.svn-base:56 html/AssetTracker/Asset/ModifyIPs.html:46 html/AssetTracker/Asset/ModifyIPs.html:50 html/AssetTracker/Asset/ModifyIPs.html:56 #. ($Asset->id) #. ($Asset->Id) msgid "Modify IP addresses related to asset #%1" msgstr "" #: html/AssetTracker/Admin/Global/.svn/text-base/UserRights.html.svn-base:75 html/AssetTracker/Admin/Global/UserRights.html:75 html/AssetTracker/Admin/Types/.svn/text-base/UserRights.html.svn-base:75 html/AssetTracker/Admin/Types/UserRights.html:75 msgid "Modify User Rights" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/CustomField.html.svn-base:66 html/AssetTracker/Admin/Types/CustomField.html:66 #. ($TypeObj->Name()) msgid "Modify a CustomField for type %1" msgstr "Изменение дополнительного поля для категории %1" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:42 lib/RTx/AssetTracker/Type_Overlay.pm:42 msgid "Modify administrators for type" msgstr "" #: html/AssetTracker/Asset/.svn/text-base/Modify.html.svn-base:46 html/AssetTracker/Asset/.svn/text-base/Modify.html.svn-base:50 html/AssetTracker/Asset/.svn/text-base/Modify.html.svn-base:56 html/AssetTracker/Asset/Modify.html:46 html/AssetTracker/Asset/Modify.html:50 html/AssetTracker/Asset/Modify.html:56 #. ($Asset->id) #. ($Asset->Id) msgid "Modify asset #%1" msgstr "Изменить ресурс #%1" #: html/AssetTracker/Asset/.svn/text-base/ModifyFields.html.svn-base:46 html/AssetTracker/Asset/.svn/text-base/ModifyFields.html.svn-base:50 html/AssetTracker/Asset/.svn/text-base/ModifyFields.html.svn-base:56 html/AssetTracker/Asset/ModifyFields.html:46 html/AssetTracker/Asset/ModifyFields.html:50 html/AssetTracker/Asset/ModifyFields.html:56 #. ($Asset->id) #. ($Asset->Id) msgid "Modify asset custom fields#%1" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:45 lib/RTx/AssetTracker/Type_Overlay.pm:45 msgid "Modify assets of this type" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/Objects.html.svn-base:78 html/AssetTracker/Admin/CustomFields/Objects.html:78 #. ($CF->Name) msgid "Modify associated objects for %1" msgstr "" #: html/Callbacks/AssetTracker/Admin/Elements/GlobalCustomFieldTabs/.svn/text-base/Default.svn-base:3 html/Callbacks/AssetTracker/Admin/Elements/GlobalCustomFieldTabs/Default:3 html/Callbacks/AssetTracker/Admin/Global/CustomFields/index.html/.svn/text-base/Default.svn-base:3 html/Callbacks/AssetTracker/Admin/Global/CustomFields/index.html/Default:3 msgid "Modify global asset rights" msgstr "" #: html/AssetTracker/Admin/Global/.svn/text-base/GroupRights.html.svn-base:46 html/AssetTracker/Admin/Global/GroupRights.html:46 msgid "Modify global group rights" msgstr "" #: html/AssetTracker/Admin/Global/.svn/text-base/UserRights.html.svn-base:49 html/AssetTracker/Admin/Global/UserRights.html:49 msgid "Modify global user rights" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:164 html/AssetTracker/Admin/CustomFields/GroupRights.html:164 #. ($CustomFieldObj->Name) msgid "Modify group rights for custom field %1" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/GroupRights.html.svn-base:46 html/AssetTracker/Admin/Types/.svn/text-base/GroupRights.html.svn-base:50 html/AssetTracker/Admin/Types/GroupRights.html:46 html/AssetTracker/Admin/Types/GroupRights.html:50 #. ($TypeObj->Name) msgid "Modify group rights for type %1" msgstr "" #: html/AssetTracker/Asset/.svn/text-base/ModifyPeople.html.svn-base:46 html/AssetTracker/Asset/.svn/text-base/ModifyPeople.html.svn-base:50 html/AssetTracker/Asset/.svn/text-base/ModifyPeople.html.svn-base:56 html/AssetTracker/Asset/ModifyPeople.html:46 html/AssetTracker/Asset/ModifyPeople.html:50 html/AssetTracker/Asset/ModifyPeople.html:56 #. ($Asset->id) #. ($Asset->Id) msgid "Modify people related to asset #%1" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:46 html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:50 html/AssetTracker/Admin/Types/People.html:46 html/AssetTracker/Admin/Types/People.html:50 #. ($TypeObj->Name) msgid "Modify people related to type %1" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/UserRights.html.svn-base:157 html/AssetTracker/Admin/CustomFields/UserRights.html:157 #. ($CustomFieldObj->Name) msgid "Modify user rights for custom field %1" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/UserRights.html.svn-base:46 html/AssetTracker/Admin/Types/.svn/text-base/UserRights.html.svn-base:50 html/AssetTracker/Admin/Types/UserRights.html:46 html/AssetTracker/Admin/Types/UserRights.html:50 #. ($TypeObj->Name) msgid "Modify user rights for type %1" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:45 lib/RTx/AssetTracker/Type_Overlay.pm:45 msgid "ModifyAsset" msgstr "ИзменятьРесурс" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:42 lib/RTx/AssetTracker/Type_Overlay.pm:42 msgid "ModifyTypeAdmins" msgstr "ИзменятьАдминистраторовКатегории" #: html/AssetTracker/Search/Elements/.svn/text-base/SelectSearchesForObjects.svn-base:53 html/AssetTracker/Search/Elements/SelectSearchesForObjects:53 msgid "My saved searches" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:55 html/AssetTracker/Admin/Elements/EditCustomField:55 html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:63 html/AssetTracker/Asset/Create.html:63 html/AssetTracker/Asset/Elements/.svn/text-base/EditBasics.svn-base:49 html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:52 html/AssetTracker/Asset/Elements/EditBasics:49 html/AssetTracker/Asset/Elements/ShowBasics:52 html/AssetTracker/Elements/.svn/text-base/SelectAttachmentField.svn-base:47 html/AssetTracker/Elements/SelectAttachmentField:47 msgid "Name" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:166 html/AssetTracker/Elements/Tabs:166 msgid "New Query" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/CustomField.html.svn-base:73 html/AssetTracker/Admin/Types/CustomField.html:73 msgid "New custom field" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/TypeTabs.svn-base:84 html/AssetTracker/Admin/Elements/TypeTabs:84 msgid "New type" msgstr "Новая категория" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:76 html/AssetTracker/Admin/Types/People.html:76 msgid "New watchers" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:93 html/AssetTracker/Elements/Tabs:93 msgid "Next" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/AssetList.svn-base:99 html/AssetTracker/Elements/AssetList:99 msgid "Next Page" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/UserRights.html.svn-base:145 html/AssetTracker/Admin/CustomFields/UserRights.html:145 msgid "No Class defined" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:119 html/AssetTracker/Admin/Elements/EditCustomField:119 msgid "No CustomField" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:103 html/AssetTracker/Admin/CustomFields/GroupRights.html:103 msgid "No CustomField defined" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Assets_Overlay_SQL.pm.svn-base:470 lib/RTx/AssetTracker/Assets_Overlay_SQL.pm:470 msgid "No Query" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/GroupRights.html.svn-base:117 html/AssetTracker/Admin/Types/.svn/text-base/UserRights.html.svn-base:89 html/AssetTracker/Admin/Types/GroupRights.html:117 html/AssetTracker/Admin/Types/UserRights.html:89 msgid "No Type defined" msgstr "Не указана категория" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:862 lib/RTx/AssetTracker/Record.pm:862 msgid "No column specified" msgstr "" #: html/AssetTracker/Elements/RTx__Asset/.svn/text-base/ColumnMap.svn-base:154 html/AssetTracker/Elements/RTx__Asset/ColumnMap:154 msgid "No description" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:240 lib/RTx/AssetTracker/Type_Overlay.pm:240 msgid "No permission to create asset types" msgstr "Отсутствуют права для создания категорий ресурсов" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:195 lib/RTx/AssetTracker/Asset_Overlay.pm:195 #. ($TypeObj->Name) msgid "No permission to create assets of this type '%1'" msgstr "Отсутствуют права для создания ресурсов в данной категории '%1'" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:221 html/AssetTracker/Search/.svn/text-base/Grid.html.svn-base:152 html/AssetTracker/Search/Bulk.html:221 html/AssetTracker/Search/Grid.html:152 msgid "No permission to perform bulk updates of assets." msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:674 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:827 lib/RTx/AssetTracker/Asset_Overlay.pm:674 lib/RTx/AssetTracker/Type_Overlay.pm:827 msgid "No principal specified" msgstr "Не указан принципал" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:175 html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:185 html/AssetTracker/Admin/Types/People.html:175 html/AssetTracker/Admin/Types/People.html:185 msgid "No principals selected." msgstr "Не выбран приницпал." #: NOT FOUND IN SOURCE msgid "No rights found" msgstr "Права не найдены" #: NOT FOUND IN SOURCE msgid "No rights granted." msgstr "Права не выданы" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:234 html/AssetTracker/Search/.svn/text-base/Grid.html.svn-base:214 html/AssetTracker/Search/Bulk.html:234 html/AssetTracker/Search/Grid.html:214 msgid "No search to operate on." msgstr "Нет запроса для выполнения" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:517 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:555 lib/RTx/AssetTracker/Transaction_Overlay.pm:517 lib/RTx/AssetTracker/Transaction_Overlay.pm:555 msgid "No transaction type specified" msgstr "Не указан тип транзакции" #: html/AssetTracker/Admin/Types/.svn/text-base/index.html.svn-base:56 html/AssetTracker/Admin/Types/index.html:56 msgid "No types matching search criteria found." msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:859 lib/RTx/AssetTracker/Record.pm:859 msgid "No value sent to _Set!\\n" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:864 lib/RTx/AssetTracker/Record.pm:864 msgid "Nonexistant field?" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:292 lib/RTx/AssetTracker/Record.pm:292 msgid "Object could not be created" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:124 lib/RTx/AssetTracker/Record.pm:124 msgid "Object could not be deleted" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:311 lib/RTx/AssetTracker/Record.pm:311 msgid "Object created" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:121 lib/RTx/AssetTracker/Record.pm:121 msgid "Object deleted" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/Objects.html.svn-base:72 html/AssetTracker/Admin/CustomFields/Objects.html:72 html/AssetTracker/Admin/Elements/.svn/text-base/ObjectCustomFields.svn-base:63 html/AssetTracker/Admin/Elements/ObjectCustomFields:63 #. ($ObjectType) #. ($LookupType) msgid "Object of type %1 cannot take custom fields" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/DisplayOptions.svn-base:56 html/AssetTracker/Search/Elements/DisplayOptions:56 msgid "Order by" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:47 lib/RTx/AssetTracker/Type_Overlay.pm:47 msgid "Own assets of this type" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:47 lib/RTx/AssetTracker/Type_Overlay.pm:47 msgid "OwnAsset" msgstr "БытьОтветственнымЗаРесурс" #: etc/AssetTracker/.svn/text-base/initialdata.svn-base:5 etc/AssetTracker/initialdata:5 html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:78 html/AssetTracker/Asset/Create.html:78 msgid "Owner" msgstr "Ответственный" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:221 lib/RTx/AssetTracker/Asset_Overlay.pm:221 msgid "Owner could not be set." msgstr "Невозможно назначить ответственного" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:65 html/AssetTracker/Admin/Types/People.html:65 html/AssetTracker/Asset/Elements/.svn/text-base/EditPeople.svn-base:52 html/AssetTracker/Asset/Elements/.svn/text-base/ShowPeople.svn-base:48 html/AssetTracker/Asset/Elements/EditPeople:52 html/AssetTracker/Asset/Elements/ShowPeople:48 msgid "Owners" msgstr "Ответственные" #: html/AssetTracker/Elements/.svn/text-base/AssetList.svn-base:73 html/AssetTracker/Elements/AssetList:73 #. ($Page, int($TotalFound/$Rows)+1) msgid "Page %1 of %2" msgstr "Страница %1 из %2" #: html/AssetTracker/Asset/Elements/.svn/text-base/EditBasics.svn-base:100 html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:76 html/AssetTracker/Asset/Elements/EditBasics:100 html/AssetTracker/Asset/Elements/ShowBasics:76 msgid "Parent" msgstr "Предок" #: html/AssetTracker/Admin/Elements/.svn/text-base/TypeTabs.svn-base:63 html/AssetTracker/Admin/Elements/TypeTabs:63 html/AssetTracker/Asset/.svn/text-base/ModifyAll.html.svn-base:64 html/AssetTracker/Asset/Elements/.svn/text-base/ShowSummary.svn-base:57 html/AssetTracker/Asset/Elements/ShowSummary:57 html/AssetTracker/Asset/ModifyAll.html:64 msgid "People" msgstr "Пользователи" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1014 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1138 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1183 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1299 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1308 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1395 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1458 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1498 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:550 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:557 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:572 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:706 lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:721 lib/RTx/AssetTracker/.svn/text-base/IP_Overlay.pm.svn-base:133 lib/RTx/AssetTracker/.svn/text-base/IP_Overlay.pm.svn-base:176 lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:1072 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:505 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:512 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:541 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:548 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:362 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:690 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:700 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:714 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:850 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:859 lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:872 lib/RTx/AssetTracker/Asset_Overlay.pm:1014 lib/RTx/AssetTracker/Asset_Overlay.pm:1138 lib/RTx/AssetTracker/Asset_Overlay.pm:1183 lib/RTx/AssetTracker/Asset_Overlay.pm:1299 lib/RTx/AssetTracker/Asset_Overlay.pm:1308 lib/RTx/AssetTracker/Asset_Overlay.pm:1395 lib/RTx/AssetTracker/Asset_Overlay.pm:1458 lib/RTx/AssetTracker/Asset_Overlay.pm:1498 lib/RTx/AssetTracker/Asset_Overlay.pm:550 lib/RTx/AssetTracker/Asset_Overlay.pm:557 lib/RTx/AssetTracker/Asset_Overlay.pm:572 lib/RTx/AssetTracker/Asset_Overlay.pm:706 lib/RTx/AssetTracker/Asset_Overlay.pm:721 lib/RTx/AssetTracker/IP_Overlay.pm:133 lib/RTx/AssetTracker/IP_Overlay.pm:176 lib/RTx/AssetTracker/Record.pm:1072 lib/RTx/AssetTracker/Transaction_Overlay.pm:505 lib/RTx/AssetTracker/Transaction_Overlay.pm:512 lib/RTx/AssetTracker/Transaction_Overlay.pm:541 lib/RTx/AssetTracker/Transaction_Overlay.pm:548 lib/RTx/AssetTracker/Type_Overlay.pm:362 lib/RTx/AssetTracker/Type_Overlay.pm:690 lib/RTx/AssetTracker/Type_Overlay.pm:700 lib/RTx/AssetTracker/Type_Overlay.pm:714 lib/RTx/AssetTracker/Type_Overlay.pm:850 lib/RTx/AssetTracker/Type_Overlay.pm:859 lib/RTx/AssetTracker/Type_Overlay.pm:872 msgid "Permission Denied" msgstr "Доступ запрещен" #: html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:155 html/AssetTracker/Search/Elements/PickBasics:155 msgid "Port" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:874 lib/RTx/AssetTracker/Transaction_Overlay.pm:874 #. ($self->NewValue) msgid "Port %1 added" msgstr "Порт %1 добавлен" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:879 lib/RTx/AssetTracker/Transaction_Overlay.pm:879 #. ($self->OldValue) msgid "Port %1 deleted" msgstr "Порт %1 удален" #: lib/RTx/AssetTracker/.svn/text-base/IP_Overlay.pm.svn-base:148 lib/RTx/AssetTracker/IP_Overlay.pm:148 msgid "Port could not be added" msgstr "Невозможно добавить порт" #: lib/RTx/AssetTracker/.svn/text-base/IP_Overlay.pm.svn-base:199 lib/RTx/AssetTracker/IP_Overlay.pm:199 msgid "Port could not be deleted" msgstr "Невозможно удалить порт" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:85 html/AssetTracker/Elements/Tabs:85 msgid "Prev" msgstr "Пред" #: html/AssetTracker/Elements/.svn/text-base/AssetList.svn-base:96 html/AssetTracker/Elements/AssetList:96 msgid "Previous Page" msgstr "Предыдущая страница" #: html/AssetTracker/Search/Elements/.svn/text-base/EditSearches.svn-base:50 html/AssetTracker/Search/Elements/EditSearches:50 msgid "Privacy:" msgstr "" #: html/AssetTracker/Elements/ShowLinks.andy:66 html/AssetTracker/Elements/ShowLinks:66 msgid "Problem with " msgstr "Проблема с " #: etc/AssetTracker/.svn/text-base/initialdata.svn-base:14 etc/AssetTracker/initialdata:14 msgid "Pseudogroup for internal use" msgstr "Псевдогруппа для внутреннего использования" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:168 html/AssetTracker/Elements/Tabs:168 html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:103 html/AssetTracker/Search/Build.html:103 msgid "Query Builder" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Quicksearch.orig.svn-base:46 html/AssetTracker/Elements/.svn/text-base/Quicksearch.svn-base:46 html/AssetTracker/Elements/Quicksearch.orig:46 html/AssetTracker/Elements/Quicksearch:46 msgid "Quick search" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Results.html.svn-base:84 html/AssetTracker/Search/Results.html:84 msgid "RSS Feed" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/ShowLinks.svn-base:48 html/AssetTracker/Elements/ShowLinks.andy:48 html/AssetTracker/Elements/ShowLinks.orig:48 html/AssetTracker/Elements/ShowLinks:48 msgid "Referred to by" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:113 html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:114 html/AssetTracker/Search/Bulk.html:113 html/AssetTracker/Search/Bulk.html:114 msgid "Remove Admin" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:109 html/AssetTracker/Search/Bulk.html:109 msgid "Remove Owner" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowTransaction.svn-base:182 html/AssetTracker/Asset/Elements/ShowTransaction:182 html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:149 html/AssetTracker/Elements/Tabs:149 msgid "Reply" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:142 html/AssetTracker/Search/Bulk.html:142 msgid "Reply to requestors" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:46 lib/RTx/AssetTracker/Type_Overlay.pm:46 msgid "Retire assets of this type" msgstr "Списать ресурсы в данной категории" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:46 lib/RTx/AssetTracker/Type_Overlay.pm:46 msgid "RetireAsset" msgstr "СписыватьРесурс" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1187 lib/RTx/AssetTracker/Asset_Overlay.pm:1187 msgid "Retired assets cannot have IP addresses" msgstr "Списанные ресурсы не могу иметь адреса IP" #: html/AssetTracker/Search/Elements/.svn/text-base/EditSearches.svn-base:61 html/AssetTracker/Search/Elements/EditSearches:61 msgid "Revert" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:129 html/AssetTracker/Admin/CustomFields/GroupRights.html:129 #. ($object_type) msgid "Rights could not be granted for %1" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:156 html/AssetTracker/Admin/CustomFields/GroupRights.html:156 #. ($object_type) msgid "Rights could not be revoked for %1" msgstr "" #: html/AssetTracker/Admin/Global/.svn/text-base/GroupRights.html.svn-base:72 html/AssetTracker/Admin/Global/GroupRights.html:72 html/AssetTracker/Admin/Types/.svn/text-base/GroupRights.html.svn-base:74 html/AssetTracker/Admin/Types/GroupRights.html:74 msgid "Roles" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/DisplayOptions.svn-base:83 html/AssetTracker/Search/Elements/DisplayOptions:83 msgid "Rows per page" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/EditSearches.svn-base:70 html/AssetTracker/Search/Elements/EditSearches:70 msgid "Save" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/Modify.html.svn-base:80 html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:126 html/AssetTracker/Admin/Types/Modify.html:80 html/AssetTracker/Admin/Types/People.html:126 html/AssetTracker/Asset/.svn/text-base/Modify.html.svn-base:60 html/AssetTracker/Asset/.svn/text-base/ModifyAll.html.svn-base:84 html/AssetTracker/Asset/.svn/text-base/ModifyFields.html.svn-base:59 html/AssetTracker/Asset/.svn/text-base/ModifyIPs.html.svn-base:59 html/AssetTracker/Asset/.svn/text-base/ModifyPeople.html.svn-base:59 html/AssetTracker/Asset/Modify.html:60 html/AssetTracker/Asset/ModifyAll.html:84 html/AssetTracker/Asset/ModifyFields.html:59 html/AssetTracker/Asset/ModifyIPs.html:59 html/AssetTracker/Asset/ModifyPeople.html:59 msgid "Save Changes" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/EditSearches.svn-base:46 html/AssetTracker/Search/Elements/EditSearches:46 msgid "Saved searches" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/GotoAsset.svn-base:20 html/AssetTracker/Elements/.svn/text-base/SimpleSearch.svn-base:48 html/AssetTracker/Elements/GotoAsset:20 html/AssetTracker/Elements/SimpleSearch:48 html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:92 html/AssetTracker/Search/Build.html:92 msgid "Search" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:43 lib/RTx/AssetTracker/Type_Overlay.pm:43 msgid "See asset details" msgstr "Дополнительная информация о ресурсе" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:38 lib/RTx/AssetTracker/Type_Overlay.pm:38 msgid "SeeType" msgstr "ВидетьКатегорию" #: html/AssetTracker/Admin/Elements/.svn/text-base/TypeTabs.svn-base:81 html/AssetTracker/Admin/Elements/TypeTabs:81 msgid "Select type" msgstr "Выберите категорию" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomFields.svn-base:58 html/AssetTracker/Admin/Elements/EditCustomFields:58 msgid "Selected Custom Fields" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/Objects.html.svn-base:59 html/AssetTracker/Admin/CustomFields/Objects.html:59 msgid "Selected objects" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowTransaction.svn-base:149 html/AssetTracker/Asset/Elements/.svn/text-base/ShowTransaction.svn-base:162 html/AssetTracker/Asset/Elements/ShowTransaction:149 html/AssetTracker/Asset/Elements/ShowTransaction:162 msgid "Show" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/EditFormat.svn-base:56 html/AssetTracker/Search/Elements/EditFormat:56 msgid "Show Columns" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:174 html/AssetTracker/Elements/Tabs:174 msgid "Show Results" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:43 lib/RTx/AssetTracker/Type_Overlay.pm:43 msgid "ShowAsset" msgstr "ВидетьРесурс" #: lib/RTx/AssetTracker/.svn/text-base/System.pm.svn-base:54 lib/RTx/AssetTracker/System.pm:54 msgid "ShowConfigTab" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/EditFormat.svn-base:82 html/AssetTracker/Search/Elements/EditFormat:82 msgid "Small" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Results.html.svn-base:83 html/AssetTracker/Search/Results.html:83 msgid "Spreadsheet" msgstr "" #: html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:70 html/AssetTracker/Asset/Create.html:70 html/AssetTracker/Asset/Elements/.svn/text-base/EditBasics.svn-base:56 html/AssetTracker/Asset/Elements/.svn/text-base/ShowBasics.svn-base:68 html/AssetTracker/Asset/Elements/EditBasics:56 html/AssetTracker/Asset/Elements/ShowBasics:68 html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:87 html/AssetTracker/Search/Elements/PickBasics:87 lib/RTx/AssetTracker/.svn/text-base/Assets_Overlay.pm.svn-base:1046 lib/RTx/AssetTracker/Assets_Overlay.pm:1046 msgid "Status" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:569 lib/RTx/AssetTracker/Transaction_Overlay.pm:569 #. ($self->loc($self->OldValue), $self->loc($self->NewValue)) msgid "Status changed from %1 to %2" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:145 html/AssetTracker/Search/Bulk.html:145 msgid "Subject" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/System.pm.svn-base:52 lib/RTx/AssetTracker/System.pm:52 msgid "SuperUser" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:128 html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:155 html/AssetTracker/Admin/CustomFields/.svn/text-base/UserRights.html.svn-base:128 html/AssetTracker/Admin/CustomFields/.svn/text-base/UserRights.html.svn-base:98 html/AssetTracker/Admin/CustomFields/GroupRights.html:128 html/AssetTracker/Admin/CustomFields/GroupRights.html:155 html/AssetTracker/Admin/CustomFields/UserRights.html:128 html/AssetTracker/Admin/CustomFields/UserRights.html:98 msgid "System Error" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:215 lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:221 lib/RTx/AssetTracker/Transaction_Overlay.pm:215 lib/RTx/AssetTracker/Transaction_Overlay.pm:221 #. ($msg) msgid "System Error: %1" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:58 html/AssetTracker/Admin/CustomFields/GroupRights.html:58 html/AssetTracker/Admin/Global/.svn/text-base/GroupRights.html.svn-base:56 html/AssetTracker/Admin/Global/GroupRights.html:56 html/AssetTracker/Admin/Types/.svn/text-base/GroupRights.html.svn-base:57 html/AssetTracker/Admin/Types/GroupRights.html:57 msgid "System groups" msgstr "" #: etc/AssetTracker/.svn/text-base/initialdata.svn-base:8 etc/AssetTracker/initialdata:8 msgid "SystemRolegroup for internal use" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:858 lib/RTx/AssetTracker/Record.pm:858 msgid "That is already the current value" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:621 lib/RTx/AssetTracker/Asset_Overlay.pm:621 #. ($self->loc($args{'Type'})) msgid "That principal is already a %1 for this asset" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:785 lib/RTx/AssetTracker/Type_Overlay.pm:785 #. ($args{'Type'}) msgid "That principal is already a %1 for this type" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Type_Overlay.pm.svn-base:883 lib/RTx/AssetTracker/Type_Overlay.pm:883 #. ($args{'Type'}) msgid "That principal is not a %1 for this type" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:1313 lib/RTx/AssetTracker/Asset_Overlay.pm:1313 msgid "That ticket has unresolved dependencies" msgstr "" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowSummary.svn-base:49 html/AssetTracker/Asset/Elements/ShowSummary:49 msgid "The Basics" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:861 lib/RTx/AssetTracker/Record.pm:861 msgid "The new value has been set." msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:288 lib/RTx/AssetTracker/Transaction_Overlay.pm:288 msgid "This transaction appears to have no content" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:886 lib/RTx/AssetTracker/Transaction_Overlay.pm:886 #. ($self->NewValue, $ticket->Subject, $ticket->Status) msgid "Ticket #%1 : %2 (%3)" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:885 lib/RTx/AssetTracker/Transaction_Overlay.pm:885 #. ($self->NewValue) msgid "Ticket #%1 linked" msgstr "" #: html/Callbacks/AssetTracker/Ticket/Create.html/.svn/text-base/CreateLinks.svn-base:1 html/Callbacks/AssetTracker/Ticket/Create.html/CreateLinks:1 msgid "Ticket of Asset" msgstr "Заявка ресурса" #: html/AssetTracker/Asset/Elements/.svn/text-base/ShowSummary.svn-base:88 html/AssetTracker/Asset/Elements/ShowSummary:88 msgid "Tickets" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:174 lib/RTx/AssetTracker/Transaction_Overlay.pm:174 msgid "Transaction Created" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:123 lib/RTx/AssetTracker/Transaction_Overlay.pm:123 msgid "Transaction->Create couldn't, as you didn't specify an object type and id" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Transaction_Overlay.pm.svn-base:627 lib/RTx/AssetTracker/Transaction_Overlay.pm:627 msgid "Transactions are immutable" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:165 html/AssetTracker/Search/Elements/PickBasics:165 msgid "Transport" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:65 html/AssetTracker/Admin/Elements/EditCustomField:65 html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:57 html/AssetTracker/Asset/Create.html:57 html/AssetTracker/Asset/Elements/.svn/text-base/EditBasics.svn-base:63 html/AssetTracker/Asset/Elements/EditBasics:63 html/AssetTracker/Elements/.svn/text-base/Quicksearch.orig.svn-base:50 html/AssetTracker/Elements/Quicksearch.orig:50 html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:71 html/AssetTracker/Search/Elements/PickBasics:71 lib/RTx/AssetTracker/.svn/text-base/Assets_Overlay.pm.svn-base:1018 lib/RTx/AssetTracker/Assets_Overlay.pm:1018 msgid "Type" msgstr "Категория" #: html/AssetTracker/Admin/Types/.svn/text-base/CustomField.html.svn-base:63 html/AssetTracker/Admin/Types/CustomField.html:63 #. ($Type) msgid "Type %1 not found" msgstr "Категория %1 не найдена" #: html/AssetTracker/Admin/Types/.svn/text-base/Modify.html.svn-base:64 html/AssetTracker/Admin/Types/Modify.html:64 msgid "Type Name" msgstr "Наименование категории" #: html/AssetTracker/Asset/.svn/text-base/Create.html.svn-base:155 html/AssetTracker/Asset/Create.html:155 msgid "Type could not be loaded." msgstr "Невозможно загрузить категорию" #: lib/RTx/AssetTracker/.svn/text-base/Record.pm.svn-base:783 lib/RTx/AssetTracker/Record.pm:783 #. ($ContentEncoding) msgid "Unknown ContentEncoding %1" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:692 html/AssetTracker/Search/Build.html:692 msgid "Unknown field: $key" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/SelectSearchesForObjects.svn-base:60 html/AssetTracker/Search/Elements/SelectSearchesForObjects:60 msgid "Unnamed search" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomFields.svn-base:60 html/AssetTracker/Admin/Elements/EditCustomFields:60 msgid "Unselected Custom Fields" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/Objects.html.svn-base:61 html/AssetTracker/Admin/CustomFields/Objects.html:61 msgid "Unselected objects" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:57 html/AssetTracker/Search/.svn/text-base/Grid.html.svn-base:59 html/AssetTracker/Search/Bulk.html:57 html/AssetTracker/Search/Grid.html:59 msgid "Update" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:214 html/AssetTracker/Search/.svn/text-base/Grid.html.svn-base:145 html/AssetTracker/Search/Bulk.html:214 html/AssetTracker/Search/Grid.html:145 msgid "Update All" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:139 html/AssetTracker/Search/Bulk.html:139 msgid "Update Type" msgstr "Обновить категорию" #: html/AssetTracker/Search/.svn/text-base/Bulk.html.svn-base:102 html/AssetTracker/Search/.svn/text-base/Grid.html.svn-base:53 html/AssetTracker/Search/Bulk.html:102 html/AssetTracker/Search/Grid.html:53 msgid "Update selected assets" msgstr "Обновить выбранные ресурсы" #: lib/RTx/AssetTracker/.svn/text-base/Asset_Overlay.pm.svn-base:222 lib/RTx/AssetTracker/Asset_Overlay.pm:222 #. ($args{'Owner'}) msgid "User '%1' could not be found." msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/TypeTabs.svn-base:74 html/AssetTracker/Admin/Elements/TypeTabs:74 html/Callbacks/AssetTracker/Admin/Elements/CustomFieldTabs/.svn/text-base/Default.svn-base:18 html/Callbacks/AssetTracker/Admin/Elements/CustomFieldTabs/Default:18 msgid "User Rights" msgstr "" #: html/AssetTracker/Admin/CustomFields/.svn/text-base/GroupRights.html.svn-base:74 html/AssetTracker/Admin/CustomFields/GroupRights.html:74 html/AssetTracker/Admin/Global/.svn/text-base/GroupRights.html.svn-base:88 html/AssetTracker/Admin/Global/GroupRights.html:88 html/AssetTracker/Admin/Types/.svn/text-base/GroupRights.html.svn-base:89 html/AssetTracker/Admin/Types/GroupRights.html:89 msgid "User defined groups" msgstr "" #: html/AssetTracker/Admin/Types/.svn/text-base/People.html.svn-base:89 html/AssetTracker/Admin/Types/People.html:89 msgid "Users" msgstr "" #: lib/RTx/AssetTracker/.svn/text-base/Assets_Overlay_SQL.pm.svn-base:486 lib/RTx/AssetTracker/Assets_Overlay_SQL.pm:486 msgid "Valid Query" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditCustomField.svn-base:78 html/AssetTracker/Admin/Elements/EditCustomField:78 msgid "Values" msgstr "" #: html/AssetTracker/Elements/.svn/text-base/Tabs.svn-base:128 html/AssetTracker/Elements/Tabs:128 msgid "View History" msgstr "История" #: html/AssetTracker/Search/.svn/text-base/Results.html.svn-base:85 html/AssetTracker/Search/Results.html:85 msgid "Work offline" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:112 html/AssetTracker/Search/Elements/PickBasics:112 msgid "belongs to" msgstr "" #: NOT FOUND IN SOURCE msgid "development" msgstr "в разработке" #: html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:113 html/AssetTracker/Search/Elements/PickBasics:113 msgid "does not belong to" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:61 html/AssetTracker/Search/Elements/PickBasics:61 msgid "does not match" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:371 html/AssetTracker/Search/Build.html:371 msgid "error: can't move down" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:393 html/AssetTracker/Search/Build.html:393 msgid "error: can't move left" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:352 html/AssetTracker/Search/Build.html:352 msgid "error: can't move up" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:435 html/AssetTracker/Search/Build.html:435 msgid "error: nothing to delete" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:357 html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:376 html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:398 html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:427 html/AssetTracker/Search/Build.html:357 html/AssetTracker/Search/Build.html:376 html/AssetTracker/Search/Build.html:398 html/AssetTracker/Search/Build.html:427 msgid "error: nothing to move" msgstr "" #: html/AssetTracker/Search/.svn/text-base/Build.html.svn-base:453 html/AssetTracker/Search/Build.html:453 msgid "error: nothing to toggle" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:48 html/AssetTracker/Search/Elements/PickBasics:48 msgid "id" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:136 html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:168 html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:74 html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:90 html/AssetTracker/Search/Elements/.svn/text-base/PickCFs.svn-base:53 html/AssetTracker/Search/Elements/PickBasics:136 html/AssetTracker/Search/Elements/PickBasics:168 html/AssetTracker/Search/Elements/PickBasics:74 html/AssetTracker/Search/Elements/PickBasics:90 html/AssetTracker/Search/Elements/PickCFs:53 msgid "is" msgstr "" #: html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:137 html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:169 html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:75 html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:91 html/AssetTracker/Search/Elements/.svn/text-base/PickCFs.svn-base:54 html/AssetTracker/Search/Elements/PickBasics:137 html/AssetTracker/Search/Elements/PickBasics:169 html/AssetTracker/Search/Elements/PickBasics:75 html/AssetTracker/Search/Elements/PickBasics:91 html/AssetTracker/Search/Elements/PickCFs:54 msgid "isn't" msgstr "" #: NOT FOUND IN SOURCE msgid "lost" msgstr "утеряно" #: html/AssetTracker/Search/Elements/.svn/text-base/PickBasics.svn-base:60 html/AssetTracker/Search/Elements/PickBasics:60 msgid "matches" msgstr "" #: html/AssetTracker/Admin/Elements/.svn/text-base/EditTypeWatchers.abk.svn-base:48 html/AssetTracker/Admin/Elements/.svn/text-base/EditTypeWatchers.svn-base:48 html/AssetTracker/Admin/Elements/EditTypeWatchers.abk:48 html/AssetTracker/Admin/Elements/EditTypeWatchers:48 html/AssetTracker/Asset/Elements/.svn/text-base/EditWatchers.svn-base:7 html/AssetTracker/Asset/Elements/.svn/text-base/ShowComponents.svn-base:3 html/AssetTracker/Asset/Elements/.svn/text-base/ShowTickets.svn-base:3 html/AssetTracker/Asset/Elements/EditWatchers:7 html/AssetTracker/Asset/Elements/ShowComponents:3 html/AssetTracker/Asset/Elements/ShowTickets:3 msgid "none" msgstr "" #: NOT FOUND IN SOURCE msgid "production" msgstr "используется" #: NOT FOUND IN SOURCE msgid "retired" msgstr "списано" #: NOT FOUND IN SOURCE msgid "saled" msgstr "продано" #: lib/RTx/AssetTracker/.svn/text-base/System.pm.svn-base:54 lib/RTx/AssetTracker/System.pm:54 msgid "show Configuration tab" msgstr "" rt-extension-assettracker-3.0.0/sbin/000077500000000000000000000000001222742774700176135ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/sbin/at-setup-database.in000077500000000000000000000436071222742774700234640ustar00rootroot00000000000000#!@PERL@ -w # BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} use strict; use vars qw($PROMPT $VERSION $Handle $Nobody $SystemUser $item); use vars qw(@Groups @Users @ACL @Types @ScripActions @ScripConditions @Templates @CustomFields @Scrips); use lib "@RT_LOCAL_PATH@/lib"; use lib "@RT_HOME_PATH@/lib"; #This drags in RT's config.pm # We do it in a begin block because RT::Handle needs to know the type to do its # inheritance use RT; use Carp; use RT::User; use RT::CurrentUser; use RT::Template; use RT::ScripAction; use RT::ACE; use RT::Group; use RT::User; use RT::Queue; use RT::ScripCondition; use RT::CustomField; use RT::Scrip; use RTx::AssetTracker; use RTx::AssetTracker::Type; RT::LoadConfig(); use Term::ReadKey; use Getopt::Long; my %args; GetOptions( \%args, 'prompt-for-dba-password', 'force', 'debug', 'action=s', 'dba=s', 'dba-password=s', 'datafile=s', 'datadir=s' ); $| = 1; #unbuffer that output. require RT::Handle; my $Handle = RT::Handle->new($RT::DatabaseType); $Handle->BuildDSN; my $dbh; if ( $args{'prompt-for-dba-password'} ) { $args{'dba-password'} = get_dba_password(); chomp( $args{'dba-password'} ); } unless ( $args{'action'} ) { help(); die; } if ( $args{'action'} eq 'init' ) { $dbh = DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} ) || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr"; if ($RT::DatabaseType eq "mysql") { # Check which version we're running my ($version) = $dbh->selectrow_hashref("show variables like 'version'")->{Value} =~ /^(\d\.\d+)/; print "*** Warning: RT is unsupported on MySQL versions before 4.0.x\n" if $version < 4; # MySQL must have InnoDB support my $innodb = $dbh->selectrow_hashref("show variables like 'have_innodb'")->{Value}; if ($innodb eq "NO") { die "RT requires that MySQL be compiled with InnoDB table support.\n". "See http://dev.mysql.com/doc/mysql/en/InnoDB.html\n"; } elsif ($innodb eq "DISABLED") { die "RT requires that MySQL InnoDB table support be enabled.\n". ($version < 4 ? "Add 'innodb_data_file_path=ibdata1:10M:autoextend' to the [mysqld] section of my.cnf\n" : "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n"); } } # SQLite can't deal with the disconnect/reconnect unless ($RT::DatabaseType eq 'SQLite') { $dbh->disconnect; $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} ) || die $DBI::errstr; } print "Now populating AssetTracker database schema.\n"; insert_schema("etc/AssetTracker"); print "Now inserting AssetTracker database ACLs\n"; insert_acl("etc/AssetTracker") unless ($RT::DatabaseType eq 'Oracle'); print "Now inserting AssetTracker data\n"; #insert_data( $AssetTracker::EtcPath . "/initialdata" ); insert_data( "etc/AssetTracker/initialdata" ); } elsif ( $args{'action'} eq 'drop' ) { unless ( $dbh = DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} ) ) { warn $DBI::errstr; warn "Database doesn't appear to exist. Aborting database drop."; exit(0); } drop_db(); } elsif ( $args{'action'} eq 'insert' ) { insert_data( $args{'datafile'} || ($args{'datadir'}."/content")); } elsif ($args{'action'} eq 'acl') { $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} ) || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr"; insert_acl($args{'datadir'}); } elsif ($args{'action'} eq 'schema') { $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} ) || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr"; insert_schema($args{'datadir'}); } else { print STDERR '$0 called with an invalid --action parameter'; exit(-1); } # {{{ sub insert_schema sub insert_schema { my $base_path = (shift || $RTx::AssetTracker::LocalEtcPath); my (@schema); print "Creating database schema.\n"; if ( -f $base_path . "/schema." . $RT::DatabaseType ) { no warnings 'unopened'; open( SCHEMA, "<" . $base_path . "/schema." . $RT::DatabaseType ); #open( SCHEMA_LOCAL, "<" . $RTx::AssetTracker::LocalEtcPath . "/schema." . $RT::DatabaseType ); my $statement = ""; #foreach my $line (, ($_ = ';;'), ) { foreach my $line () { $line =~ s/\#.*//g; $line =~ s/--.*//g; $statement .= $line; if ( $line =~ /;(\s*)$/ ) { $statement =~ s/;(\s*)$//g; push @schema, $statement; $statement = ""; } } local $SIG{__WARN__} = sub {}; my $is_local = 0; # local/etc/schema needs to be nonfatal. $dbh->begin_work or die $dbh->errstr; foreach my $statement (@schema) { if ($statement =~ /^\s*;$/) { $is_local = 1; next; } print STDERR "SQL: $statement\n" if defined $args{'debug'}; my $sth = $dbh->prepare($statement) or die $dbh->errstr; unless ( $sth->execute or $is_local ) { die "Problem with statement:\n $statement\n" . $sth->errstr; } } $dbh->commit or die $dbh->errstr; } else { die "Couldn't find schema file for " . $RT::DatabaseType . "\n"; } print "Done setting up database schema.\n"; } # }}} sub get_dba_password { print "In order to create or update your RT database,"; print "this script needs to connect to your " . $RT::DatabaseType . " instance on " . $RT::DatabaseHost . " as " . $args{'dba'} . ".\n"; print "Please specify that user's database password below. If the user has no database\n"; print "password, just press return.\n\n"; print "Password: "; ReadMode('noecho'); my $password = ReadLine(0); ReadMode('normal'); print "\n"; return ($password); } # {{{ sub _yesno sub _yesno { print "Proceed [y/N]:"; my $x = scalar(); $x =~ /^y/i; } # }}} # {{{ insert_acls sub insert_acl { my $base_path = (shift || $RTx::AssetTracker::LocalEtcPath); if ( $RT::DatabaseType =~ /^oracle$/i ) { do $base_path . "/acl.Oracle" || die "Couldn't find ACLS for Oracle\n" . $@; } elsif ( $RT::DatabaseType =~ /^pg$/i ) { do $base_path . "/acl.Pg" || die "Couldn't find ACLS for Pg\n" . $@; } elsif ( $RT::DatabaseType =~ /^mysql$/i ) { do $base_path . "/acl.mysql" || die "Couldn't find ACLS for mysql in $base_path\n" . $@; } elsif ( $RT::DatabaseType =~ /^Sybase$/i ) { do $base_path . "/acl.Sybase" || die "Couldn't find ACLS for Sybase in $base_path\n" . $@; } elsif ( $RT::DatabaseType =~ /^informix$/i ) { do $base_path . "/acl.Informix" || die "Couldn't find ACLS for Informix in $base_path\n" . $@; } elsif ( $RT::DatabaseType =~ /^SQLite$/i ) { return; } else { die "Unknown RT database type"; } my @acl = acl($dbh); foreach my $statement (@acl) { print STDERR $statement if $args{'debug'}; my $sth = $dbh->prepare($statement) or die $dbh->errstr; unless ( $sth->execute ) { die "Problem with statement:\n $statement\n" . $sth->errstr; } } print "Done setting up database ACLs.\n"; } # }}} =head2 get_system_dsn Returns a dsn suitable for database creates and drops and user creates and drops =cut sub get_system_dsn { my $dsn = $Handle->DSN; #with mysql, you want to connect sans database to funge things if ( $RT::DatabaseType eq 'mysql' ) { $dsn =~ s/dbname=$RT::DatabaseName//; # with postgres, you want to connect to database1 } elsif ( $RT::DatabaseType eq 'Pg' ) { $dsn =~ s/dbname=$RT::DatabaseName/dbname=template1/; } elsif ( $RT::DatabaseType eq 'Informix' ) { # with Informix, you want to connect sans database: $dsn =~ s/Informix:$RT::DatabaseName/Informix:/; } return $dsn; } # load some sort of data into the database sub insert_data { my $datafile = shift; #Connect to the database and get RT::SystemUser and RT::Nobody loaded RT::Init; my $CurrentUser = RT::CurrentUser->new(); $CurrentUser->LoadByName('RT_System'); #if ( $datafile eq $AssetTracker::EtcPath . "/initialdata" ) { if ( $datafile eq "etc/AssetTracker/initialdata" ) { print "Creating Superuser ACL..."; my $superuser_ace = RT::ACE->new($CurrentUser); $superuser_ace->_BootstrapCreate( PrincipalId => ACLEquivGroupId( $CurrentUser->Id ), PrincipalType => 'Group', RightName => 'SuperUser', ObjectType => 'RTx::AssetTracker::System', ObjectId => '1' ); print "done.\n"; } # Slurp in stuff to insert from the datafile. Possible things to go in here:- # @groups, @users, @acl, @queues, @ScripActions, @ScripConditions, @templates require $datafile || die "Couldn't find initial data for import\n" . $@; if (@Groups) { print "Creating groups..."; foreach $item (@Groups) { my $new_entry = RT::Group->new($CurrentUser); my ( $return, $msg ) = $new_entry->_Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } if (@Users) { print "Creating users..."; foreach $item (@Users) { my $new_entry = new RT::User($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } if (@Types) { print "Creating types..."; for $item (@Types) { my $new_entry = new RTx::AssetTracker::Type($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } if (@ACL) { print "Creating ACL..."; for my $item (@ACL) { my ($princ, $object); # Global rights or Queue rights? if ($item->{'Type'}) { $object = RTx::AssetTracker::Type->new($CurrentUser); $object->Load( $item->{'Type'} ); } else { $object = $RTx::AssetTracker::System; } # Group rights or user rights? if ($item->{'GroupDomain'}) { $princ = RT::Group->new($CurrentUser); if ($item->{'GroupDomain'} eq 'UserDefined') { $princ->LoadUserDefinedGroup( $item->{'GroupId'} ); } elsif ($item->{'GroupDomain'} eq 'SystemInternal') { $princ->LoadSystemInternalGroup( $item->{'GroupType'} ); } elsif ($item->{'GroupDomain'} eq 'RTx::AssetTracker::System-Role') { $princ->LoadSystemRoleGroup( $item->{'GroupType'} ); } elsif ($item->{'GroupDomain'} eq 'RTx::AssetTracker::Type-Role' && $item->{'Type'}) { $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'}, Type => $object->id); } else { $princ->Load( $item->{'GroupId'} ); } } else { $princ = RT::User->new($CurrentUser); $princ->Load( $item->{'UserId'} ); } # Grant it my ( $return, $msg ) = $princ->PrincipalObj->GrantRight( Right => $item->{'Right'}, Object => $object ); if ($return) { print $return. "."; } else { print $msg . "."; } } print "done.\n"; } if (@CustomFields) { print "Creating custom fields..."; for $item (@CustomFields) { my $new_entry = new RT::CustomField($CurrentUser); my $values = $item->{'Values'}; delete $item->{'Values'}; my $q = $item->{'Queue'}; my $q_obj = RT::Queue->new($CurrentUser); $q_obj->Load($q); if ( $q_obj->Id ) { $item->{'Queue'} = $q_obj->Id; } elsif ( $q == 0 ) { $item->{'Queue'} = 0; } else { print "(Error: Could not find queue " . $q . ")\n" unless ( $q_obj->Id ); next; } my ( $return, $msg ) = $new_entry->Create(%$item); foreach my $value ( @{$values} ) { my ( $eval, $emsg ) = $new_entry->AddValue(%$value); print "(Error: $emsg)\n" unless ($eval); } print "(Error: $msg)\n" unless ($return); print $return. "."; } print "done.\n"; } if (@ScripActions) { print "Creating ScripActions..."; for $item (@ScripActions) { my $new_entry = RT::ScripAction->new($CurrentUser); my $return = $new_entry->Create(%$item); print $return. "."; } print "done.\n"; } if (@ScripConditions) { print "Creating ScripConditions..."; for $item (@ScripConditions) { my $new_entry = RT::ScripCondition->new($CurrentUser); my $return = $new_entry->Create(%$item); print $return. "."; } print "done.\n"; } if (@Templates) { print "Creating templates..."; for $item (@Templates) { my $new_entry = new RT::Template($CurrentUser); my $return = $new_entry->Create(%$item); print $return. "."; } print "done.\n"; } if (@Scrips) { print "Creating scrips..."; for $item (@Scrips) { my $new_entry = new RT::Scrip($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); if ($return) { print $return. "."; } else { print "(Error: $msg)\n"; } } print "done.\n"; } $RT::Handle->Disconnect() unless ($RT::DatabaseType eq 'SQLite'); print "Done setting up database content.\n"; } =head2 ACLEquivGroupId Given a userid, return that user's acl equivalence group =cut sub ACLEquivGroupId { my $username = shift; my $user = RT::User->new($RT::SystemUser); $user->Load($username); my $equiv_group = RT::Group->new($RT::SystemUser); $equiv_group->LoadACLEquivalenceGroup($user); return ( $equiv_group->Id ); } sub help { print < # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} use DBI; use lib qw(@RT_LIB_PATH@); use RT; RT::LoadConfig(); my $database = $RT::DatabaseName; my $namespace = 'RTx::AssetTracker'; my $CollectionBaseclass = 'RTx::AssetTracker::SearchBuilder'; my $RecordBaseclass = 'RTx::AssetTracker::Record'; my $driver = $RT::DatabaseType; my $hostname = $RT::DatabaseHost; my $user = $RT::DatabaseUser; my $password = $RT::DatabasePassword; my $LicenseBlock = << '.'; # BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: # # This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) # # # LICENSE: # # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. # # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # # CONTRIBUTION SUBMISSION POLICY: # # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) # # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that # you are the copyright holder for those contributions and you grant # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} . my $Attribution = << '.'; # Autogenerated by DBIx::SearchBuilder factory (by ) # WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST. # # !! DO NOT EDIT THIS FILE !! # use strict; . my $dsn = "DBI:$driver:database=$database;host=$hostname"; my $dbh = DBI->connect( $dsn, $user, $password ); #get all tables out of database my @tables = $dbh->tables(); my ( %tablemap, $typemap, %modulemap ); foreach my $table (@tables) { $table =~ s/\`//g; next if ($table eq 'sessions'); next unless ($table =~ /^AT_/); $table = ucfirst($table); $tablemap{$table} = $table; $modulemap{$table} = $table; if ( $table =~ /^(.*)s$/ ) { $tablemap{$1} = $table; $modulemap{$1} = $1; } } $tablemap{'CreatedBy'} = 'User'; $tablemap{'UpdatedBy'} = 'User'; $typemap{'id'} = 'ro'; $typemap{'Creator'} = 'auto'; $typemap{'Created'} = 'auto'; $typemap{'Updated'} = 'auto'; $typemap{'UpdatedBy'} = 'auto'; $typemap{'LastUpdated'} = 'auto'; $typemap{'LastUpdatedBy'} = 'auto'; foreach my $table (@tables) { next if ($table eq 'sessions'); next unless ($table =~ /^AT_/); $table =~ s/AT_//; my $tablesingle = $table; $tablesingle =~ s/s$//; my $tableplural = $tablesingle . "s"; my %requirements; my $CollectionClassName = $namespace . "::" . $tableplural; my $RecordClassName = $namespace . "::" . $tablesingle; my $path = $namespace; $path =~ s/::/\//g; my $RecordClassPath = $path . "/" . $tablesingle . ".pm"; my $CollectionClassPath = $path . "/" . $tableplural . ".pm"; #create a collection class my $CreateInParams; my $CreateOutParams; my $ClassAccessible = ""; my $FieldsPod = ""; my $CreatePod = ""; my %fields; $introspection = $dbh->prepare("SELECT * from AT_$table where id is null"); $introspection->execute(); my @names =@{ $introspection->{'NAME'}}; my @types = @{$introspection->{'TYPE'}}; my @is_blob = @{$introspection->{'mysql_is_blob'}}; my @is_num = @{$introspection->{'mysql_is_num'}}; my %sqltypes = {}; my %numeric = {}; foreach my $name (@names) { $sqltypes{$name} = shift @types; $blobness{$name} = (shift @is_blob || "0"); $numeric{$name} = (shift @is_num || "0"); } my $sth = $dbh->prepare("DESCRIBE AT_$table"); $sth->execute; while ( my $row = $sth->fetchrow_hashref() ) { my $field = $row->{'Field'}; my $type = $row->{'Type'}; my $default = $row->{'Default'}; my $length = 0; if ($type =~ /^(?:.*?)\((\d+)\)$/) { $length = $1; } $fields{$field} = 1; #generate the 'accessible' datastructure if ( $typemap{$field} eq 'auto' ) { $ClassAccessible .= " $field => {read => 1, auto => 1,"; } elsif ( $typemap{$field} eq 'ro' ) { $ClassAccessible .= " $field => {read => 1,"; } else { $ClassAccessible .= " $field => {read => 1, write => 1,"; } $ClassAccessible .= " sql_type => $sqltypes{$field}, length => $length, is_blob => $blobness{$field}, is_numeric => $numeric{$field}, "; $ClassAccessible .= " type => '$type', default => '$default'},\n"; #generate pod for the accessible fields $FieldsPod .= " =head2 $field Returns the current value of $field. (In the database, $field is stored as $type.) "; unless ( exists $typemap{$field} && ( $typemap{$field} eq 'auto' || $typemap{$field} eq 'ro' )) { $FieldsPod .= " =head2 Set$field VALUE Set $field to VALUE. Returns (1, 'Status message') on success and (0, 'Error Message') on failure. (In the database, $field will be stored as a $type.) "; } $FieldsPod .= " =cut "; if ( $modulemap{$field} ) { $FieldsPod .= " =head2 ${field}Obj Returns the $modulemap{$field} Object which has the id returned by $field =cut sub ${field}Obj { my \$self = shift; my \$$field = ${namespace}::$modulemap{$field}->new(\$self->CurrentUser); \$$field->Load(\$self->__Value('$field')); return(\$$field); } "; $requirements{ $tablemap{$field} } = "use ${namespace}::$modulemap{$field};"; } unless ( $typemap{$field} eq 'auto' || $field eq 'id' ) { #generate create statement $CreateInParams .= " $field => '$default',\n"; $CreateOutParams .= " $field => \$args{'$field'},\n"; #gerenate pod for the create statement $CreatePod .= " $type '$field'"; $CreatePod .= " defaults to '$default'" if ($default); $CreatePod .= ".\n"; } } $Create = " sub Create { my \$self = shift; my \%args = ( $CreateInParams \@_); \$self->SUPER::Create( $CreateOutParams); } "; $CreatePod .= "\n=cut\n\n"; my $CollectionClass = $LicenseBlock . $Attribution . " =head1 NAME $CollectionClassName -- Class Description =head1 SYNOPSIS use $CollectionClassName =head1 DESCRIPTION =head1 METHODS =cut package $CollectionClassName; use $CollectionBaseclass; use $RecordClassName; use vars qw( \@ISA ); \@ISA= qw($CollectionBaseclass); sub _Init { my \$self = shift; \$self->{'table'} = 'AT_$table'; \$self->{'primary_key'} = 'id'; "; if ( $fields{'SortOrder'} ) { $CollectionClass .= " # By default, order by SortOrder \$self->OrderByCols( { ALIAS => 'main', FIELD => 'SortOrder', ORDER => 'ASC' }, { ALIAS => 'main', FIELD => 'id', ORDER => 'ASC' }, ); "; } $CollectionClass .= " return ( \$self->SUPER::_Init(\@_) ); } =head2 NewItem Returns an empty new $RecordClassName item =cut sub NewItem { my \$self = shift; return($RecordClassName->new(\$self->CurrentUser)); } " . MagicImport($CollectionClassName); my $RecordClassHeader = $Attribution . " =head1 NAME $RecordClassName =head1 SYNOPSIS =head1 DESCRIPTION =head1 METHODS =cut package $RecordClassName; use $RecordBaseclass; "; foreach my $key ( keys %requirements ) { $RecordClassHeader .= $requirements{$key} . "\n"; } $RecordClassHeader .= " use vars qw( \@ISA ); \@ISA= qw( $RecordBaseclass ); sub _Init { my \$self = shift; \$self->Table('AT_$table'); \$self->SUPER::_Init(\@_); } "; my $RecordClass = $LicenseBlock . $RecordClassHeader . " $RecordInit =head2 Create PARAMHASH Create takes a hash of values and creates a row in the database: $CreatePod $Create $FieldsPod sub _CoreAccessible { { $ClassAccessible } }; " . MagicImport($RecordClassName); print "About to make $RecordClassPath, $CollectionClassPath\n"; `mkdir -p $path`; open( RECORD, ">$RecordClassPath" ); print RECORD $RecordClass; close(RECORD); open( COL, ">$CollectionClassPath" ); print COL $CollectionClass; close(COL); } sub MagicImport { my $class = shift; #if (exists \$warnings::{unimport}) { # no warnings qw(redefine); my $path = $class; $path =~ s#::#/#gi; my $content = " eval \"require @{[$class]}_Overlay\"; if (\$@ && \$@ !~ qr{^Can't locate ".$path."_Overlay.pm}) { die \$@; }; eval \"require @{[$class]}_Vendor\"; if (\$@ && \$@ !~ qr{^Can't locate ".$path."_Vendor.pm}) { die \$@; }; eval \"require @{[$class]}_Local\"; if (\$@ && \$@ !~ qr{^Can't locate ".$path."_Local.pm}) { die \$@; }; =head1 SEE ALSO This class allows \"overlay\" methods to be placed into the following files _Overlay is for a System overlay by the original author, _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations. These overlay files can contain new subs or subs to replace existing subs in this module. Each of these files should begin with the line no warnings qw(redefine); so that perl does not kick and scream when you redefine a subroutine or variable in your overlay. @{[$class]}_Overlay, @{[$class]}_Vendor, @{[$class]}_Local =cut 1; "; return $content; } # }}} rt-extension-assettracker-3.0.0/t/000077500000000000000000000000001222742774700171235ustar00rootroot00000000000000rt-extension-assettracker-3.0.0/t/00-compile.t000066400000000000000000000023731222742774700211620ustar00rootroot00000000000000 use strict; use warnings; use RTx::AssetTracker::Test tests => 26; require_ok("RTx::AssetTracker"); require_ok("RTx::AssetTracker::Test"); require_ok("RTx::AssetTracker::Asset"); require_ok("RTx::AssetTracker::Assets"); require_ok("RTx::AssetTracker::IP"); require_ok("RTx::AssetTracker::IPs"); require_ok("RTx::AssetTracker::Port"); require_ok("RTx::AssetTracker::Ports"); require_ok("RTx::AssetTracker::Record"); require_ok("RTx::AssetTracker::Scrip"); require_ok("RTx::AssetTracker::Scrips"); require_ok("RTx::AssetTracker::ScripAction"); require_ok("RTx::AssetTracker::ScripActions"); require_ok("RTx::AssetTracker::ScripCondition"); require_ok("RTx::AssetTracker::ScripConditions"); require_ok("RTx::AssetTracker::Template"); require_ok("RTx::AssetTracker::Templates"); require_ok("RTx::AssetTracker::Type"); require_ok("RTx::AssetTracker::Types"); require_ok("RT::Graph::AssetTracker::Assets"); require_ok("RT::Shredder::Plugin::AssetObjects"); require_ok("RT::Shredder::Plugin::Assets"); require_ok("RT::URI::at"); # no the following doesn't work yet __END__ use File::Find::Rule; my @files = File::Find::Rule->file() ->name( '*.pm' ) ->in( 'lib' ); plan tests => scalar @files; for (@files) { local $SIG{__WARN__} = sub {}; require_ok($_); } rt-extension-assettracker-3.0.0/t/01_asset_link_searching.t000066400000000000000000000134131222742774700237710ustar00rootroot00000000000000#!/usr/bin/perl -w use RTx::AssetTracker::Test tests => 33; use strict; use RT; use RTx::AssetTracker; use RTx::AssetTracker::Type; use RTx::AssetTracker::Assets; # Load the config file RT::LoadConfig(); #Connect to the database and get RT::SystemUser and RT::Nobody loaded RT::Init(); #Get the current user all loaded my $CurrentUser = $RT::SystemUser; my $type = new RTx::AssetTracker::Type($CurrentUser); $type->Load('Servers') || Abort(loc("Type 'Servers' could not be loaded.")); my $child_asset = new RTx::AssetTracker::Asset( $CurrentUser ); my ( $childid ) = $child_asset->Create ( Name => "test child $$", Type => $type->Id); ok($childid != 0, "Asset created"); my $parent_asset = new RTx::AssetTracker::Asset( $CurrentUser ); my ( $parentid ) = $parent_asset->Create ( Name => "test parent $$", Type => $type->Id); ok($parentid != 0, "We created a parent asset"); my ($rv, $msg) = $parent_asset->AddLink(Base => $child_asset->URI, Type => 'ComponentOf'); ok($rv, "Relationship created"); my $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->LimitComponentOf ($parent_asset->URI); ok ($Collection->First); is ($Collection->First->id, $childid, "We found the collection of all children of $parentid with Limit"); is($Collection->Count,1, "We found only one result"); $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->FromSQL( "ComponentOf = '" . $parent_asset->URI . "'"); is ($Collection->First->id, $childid, "We found the collection of all children of $parentid with AssetSQL"); is($Collection->Count,1, "We found only one result"); $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->LimitHasComponent ($child_asset->URI); ok ($Collection->First); is ($Collection->First->id, $parentid, "We found the collection of all parents of $childid with Limit"); is($Collection->Count,1, "We found only one result"); $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->FromSQL("HasComponent = '" . $child_asset->URI . "'"); ok ($Collection->First); is ($Collection->First->id, $parentid, "We found the collection of all parents of $childid with AssetSQL"); is($Collection->Count,1, "We found only one result"); # Now we find a collection of all the assets which have no components. they should have no children. $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->LimitHasComponent(''); # must contain child; must not contain parent my %has; while (my $t = $Collection->Next) { ++$has{$t->id}; } ok ($has{$childid} , "The collection has our component - $childid"); ok( !$has{$parentid}, "The collection doesn't have our parent - $parentid"); # Now we find a collection of all the assets which are not components of anything. they should have no parents. $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->LimitComponentOf(''); # must contain parent; must not contain child %has = (); while (my $t = $Collection->Next) { ++$has{$t->id}; } ok ($has{$parentid} , "The collection has our parent - $parentid"); ok( !$has{$childid}, "The collection doesn't have our component - $childid"); # Do it all over with AssetSQL # # Now we find a collection of all the assets which have no components. they should have no children. $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->FromSQL ("HasComponent IS NULL"); # must contain parent; must not contain child %has = (); while (my $t = $Collection->Next) { ++$has{$t->id}; } ok (!$has{$parentid} , "The collection doesn't have our parent - $parentid"); ok( $has{$childid}, "The collection has our component - $childid"); # Now we find a collection of all the assets which have no components. they should have no children. # Alternate syntax $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->FromSQL ("HasComponent = ''"); # must contain parent; must not contain child %has = (); while (my $t = $Collection->Next) { ++$has{$t->id}; } ok (!$has{$parentid} , "The collection doesn't have our parent - $parentid"); ok( $has{$childid}, "The collection has our component - $childid"); # Now we find a collection of all the assets which are not components of anything. they should have no parents. $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->FromSQL("ComponentOf IS NULL"); # must not contain parent; must contain parent %has = (); while (my $t = $Collection->Next) { ++$has{$t->id}; } ok ($has{$parentid} , "The collection has our parent - $parentid"); ok(!$has{$childid}, "The collection doesn't have our component - $childid"); # Now we find a collection of all the assets which are not components of anything. they should have no parents. $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->FromSQL("ComponentOf = ''"); # must not contain parent; must contain parent %has = (); while (my $t = $Collection->Next) { ++$has{$t->id}; } ok ($has{$parentid} , "The collection has our parent - $parentid"); ok(!$has{$childid}, "The collection doesn't have our component - $childid"); my $nolink_asset = new RTx::AssetTracker::Asset( $CurrentUser ); my ( $nolinkid ) = $nolink_asset->Create ( Name => "test nolink $$", Type => $type->Id); ok($nolinkid != 0, "Asset created"); # Now we find a collection of all the assets which are not linked to anything. $Collection = RTx::AssetTracker::Assets->new($CurrentUser); $Collection->FromSQL("LinkedTo = ''"); %has = (); while (my $t = $Collection->Next) { ++$has{$t->id}; } TODO: { local $TODO = 'Waiting for Jesse to apply my LinkedTo patch'; ok(!$has{$parentid} , "The collection doesn't have our parent - $parentid"); ok(!$has{$childid}, "The collection doesn't have our component - $childid"); ok( $has{$nolinkid}, "The collection doesn't have our component - $nolinkid"); } 1; rt-extension-assettracker-3.0.0/t/03web_compilation_errors.t000066400000000000000000000034141222742774700242240ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use RTx::AssetTracker::Test no_plan => 1; use RT; RT::LoadConfig; RT::Init; our @a = RTx::AssetTracker::Test->create_assets( { Type => 1 }, { }, ); my ($url, $agent) = RT::Test->started_ok; $agent->get($url); is ($agent->{'status'}, 200, "Loaded a page"); # {{{ test a login ok($agent->login, 'logged in'); is($agent->{'status'}, 200, "Fetched the page ok"); ok( $agent->{'content'} =~ /Logout/i, "Found a logout link"); use File::Find; find ( \&wanted , 'html/AssetTracker/'); sub wanted { -f && /\.html$/ && $_ !~ /Logout.html$/ && test_get($File::Find::name); } sub test_get { my $file = shift; $file =~ s#^html/##; if ( $file =~ m#^AssetTracker/Admin/Types/Scrip\.html$# ) { $file .= '?AssetType=1'; } elsif ( $file =~ m#^AssetTracker/Admin/Global/Template\.html$# ) { $file .= '?Create=1'; } elsif ( $file =~ m#^AssetTracker/Admin/Types/Template\.html$# ) { $file .= '?Create=1&AssetType=1'; } elsif ( $file =~ m#^AssetTracker/Admin/Types/(.*)\.html$# ) { $file .= '?id=1'; } elsif ( $file =~ m#^AssetTracker/Asset/Create\.html$# ) { $file .= '?Type=1'; } elsif ( $file =~ m#^AssetTracker/Asset/(.*)\.html$# ) { $file .= '?id=' . $a[0]->Id; } elsif ( $file =~ m#^AssetTracker/Search/(.*)\.html$# ) { $file .= '?Query=id>0&Rows=10&Format=id'; } ok ($agent->get("$url/$file"), "GET $url/$file"); is ($agent->{'status'}, 200, "Loaded $file"); # ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file "); ok( $agent->{'content'} !~ /Not logged in/i, "Still logged in for $file"); ok( $agent->{'content'} !~ /System error/i, "Didn't get a Mason compilation error on $file"); } # }}} 1; rt-extension-assettracker-3.0.0/t/04interface.t000066400000000000000000000141321222742774700214150ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use RTx::AssetTracker::Test; use RT::CustomField; use RT::EmailParser; use RT::Queue; use RT::Ticket; use_ok 'RTx::AssetTracker::Type'; use_ok 'RTx::AssetTracker::Asset'; my ($url, $m) = RT::Test->started_ok; # Variables to test return values my ($ret, $msg); # Create a test type my $type = RTx::AssetTracker::Type->new($RT::SystemUser); ($ret, $msg) = $type->Create('Name' => 'tlaTestType-'.$$, 'Description' => 'A general-purpose test type'); ok($ret, "Test type created"); # Create some asset custom fields my $firstCF = RT::CustomField->new($RT::SystemUser); my $secondCF = RT::CustomField->new($RT::SystemUser); ($ret, $msg) = $firstCF->Create('Name' => 'First-'.$$, 'Type' => 'Text', 'MaxValues' => 1, 'LookupType' => 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset', 'Description' => 'A custom field', 'Disabled' => 0); ok($ret, "First CF created: $msg"); ($ret, $msg) = $secondCF->Create('Name' => 'Second-'.$$, 'Type' => 'Text', 'MaxValues' => 1, 'LookupType' => 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset', 'Description' => 'Another custom field', 'Disabled' => 0); ok($ret, "Second CF created: $msg"); # Attach the custom fields to our type ($ret, $msg) = $firstCF->AddToObject($type); ok($ret, "First CF added to type: $msg"); ($ret, $msg) = $secondCF->AddToObject($type); ok($ret, "Second CF added to type: $msg"); my ($aid, $bid) = ($firstCF->Id, $secondCF->Id); my %cvals = ('asset1a' => 'Some first about swallows', 'asset1b' => 'Some second about Europe and Africb', 'asset2a' => 'Another first about Monty Python', 'asset2b' => 'Romani ite domum', 'asset3a' => 'Why should I eat my supper?', 'asset3b' => 'There are starving children in Africb', 'asset4a' => 'What did Brian originally write?', 'asset4b' => 'Romanes eunt domus'); # Create an asset or two with our custom field values. my $asset1 = RTx::AssetTracker::Asset->new($RT::SystemUser); my $asset2 = RTx::AssetTracker::Asset->new($RT::SystemUser); my $asset3 = RTx::AssetTracker::Asset->new($RT::SystemUser); my $asset4 = RTx::AssetTracker::Asset->new($RT::SystemUser); ($ret, $msg) = $asset1->Create(Name => 'First asset '.$$, Description => 'blah blah 1', Type => $type->Id, "CustomField-$aid" => $cvals{'asset1a'}, "CustomField-$bid" => $cvals{'asset1b'}, ); ok($ret, "asset 1 created"); ($ret, $msg) = $asset2->Create(Name => 'Second asset '.$$, Description => 'foo bar 2', Type => $type->Id, "CustomField-$aid" => $cvals{'asset2a'}, "CustomField-$bid" => $cvals{'asset2b'}, ); ok($ret, "asset 2 created"); ($ret, $msg) = $asset3->Create(Name => 'Third asset '.$$, Description => 'ping pong 3', Type => $type->Id, "CustomField-$aid" => $cvals{'asset3a'}, "CustomField-$bid" => $cvals{'asset3b'}, ); ok($ret, "asset 3 created"); ($ret, $msg) = $asset4->Create(Name => 'Fourth asset '.$$, Description => 'hoi polloi 4', Type => $type->Id, "CustomField-$aid" => $cvals{'asset4a'}, "CustomField-$bid" => $cvals{'asset4b'}, ); ok($ret, "asset 4 created"); # Create a ticket. my $parser = RT::EmailParser->new(); $parser->ParseMIMEEntityFromScalar('From: root@localhost To: rt@example.com Subject: test ticket for assets This is some form of new request. There is a problem with an asset.'); my $ticket = RT::Ticket->new($RT::SystemUser); my $obj; ($ret, $obj, $msg) = $ticket->Create(Queue => 'General', Subject => 'test ticket for assets '.$$, MIMEObj => $parser->Entity); ok($ret, "Test ticket for assets created: $msg"); #### Right. That's our data. Now begin the real testing. isa_ok($m, 'Test::WWW::Mechanize'); ok($m->login, 'logged in'); $m->follow_link_ok({text => 'Assets'}, 'UI -> AssetTracker'); $m->content_contains($asset3->Name); $m->follow_link_ok( {text => $asset3->Name}, 'AssetTracker -> '. $asset3->Name ); $m->title_is("Asset #" . $asset3->Id . ": " . $asset3->Name); $m->follow_link_ok( {text => 'Links'}, 'Asset -> ModifyLinks' ); { my $a1uri = 'at://example.com/asset/'.$asset1->Id; my $a3uri = 'at://example.com/asset/'.$asset3->Id; $m->content_like(qr/Find assets/, "found asset search box"); $m->submit_form(with_fields => { 'AssetField' => $aid, 'AssetOp' => '=', 'AssetString' => $cvals{'asset1a'} } ); $m->content_contains($asset1->Name, "found " . $asset1->Name); $m->submit_form(with_fields => { "AddLink-Asset-$a1uri" => 'ComponentOf' } ); $m->content_contains("URI $a3uri component of URI $a1uri", "Asset component link was created"); } # Now try to create a linked ticket. $m->get_ok($url."/AssetTracker/Asset/Display.html?id=".$asset2->Id, "Loaded asset display"); $m->content_like(qr/New ticket in queue/, "Ticket creation link shows up"); $m->submit_form(form_name => 'child', fields => { 'Queue' => '1' } ); $m->submit_form(form_name => 'TicketCreate', fields => { 'Content' => 'This asset has a problem that needs fixing' } ); { my $a2name = $asset2->Name; my $a2id = $asset2->Id; $m->title_like(qr/Problem with $a2name/, 'Asset -> Linked ticket'); $m->content_contains("asset #$a2id: $a2name", "Ticket references originating asset"); } rt-extension-assettracker-3.0.0/t/api-asset.t000066400000000000000000000136531222742774700212060ustar00rootroot00000000000000 use strict; use warnings; use RTx::AssetTracker::Test tests => 65; use RT; { use_ok ('RTx::AssetTracker::Type'); ok(my $testtype = RTx::AssetTracker::Type->new($RT::SystemUser)); ok($testtype->Create( Name => 'asset tests')); isnt($testtype->Id , 0); use_ok('RT::CustomField'); ok(my $testcf = RT::CustomField->new($RT::SystemUser)); my ($ret, $cmsg) = $testcf->Create( Name => 'selectmulti', LookupType => 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset', Type => 'SelectMultiple'); ok($ret,"Created the custom field - ".$cmsg); ($ret, $cmsg) = $testcf->AddToObject($testtype); ok($ret, "CF added to type - $cmsg"); ($ret,$cmsg) = $testcf->AddValue ( Name => 'Value1', SortOrder => '1', Description => 'A testing value'); ok($ret, "Added a value - ".$cmsg); ok($testcf->AddValue ( Name => 'Value2', SortOrder => '2', Description => 'Another testing value')); ok($testcf->AddValue ( Name => 'Value3', SortOrder => '3', Description => 'Yet Another testing value')); is($testcf->Values->Count , 3); use_ok('RTx::AssetTracker::Asset'); my $u = RT::User->new($RT::SystemUser); $u->Load("root"); ok ($u->Id, "Found the root user"); ok(my $a = RTx::AssetTracker::Asset->new($RT::SystemUser)); ok(my ($id, $msg) = $a->Create( Type => $testtype->Id, Name => 'Testing', Owner => $u->Id )); isnt($id , 0); is ($a->OwnerRoleGroup->UserMembersObj->First->Id , $u->Id, "Root is the asset owner"); ok(my ($cfv, $cfm) =$a->AddCustomFieldValue(Field => $testcf->Id, Value => 'Value1')); isnt($cfv , 0, "Custom field creation didn't return an error: $cfm"); is($a->CustomFieldValues($testcf->Id)->Count , 1); ok($a->CustomFieldValues($testcf->Id)->First && $a->CustomFieldValues($testcf->Id)->First->Content eq 'Value1'); ok(my ($cfdv, $cfdm) = $a->DeleteCustomFieldValue(Field => $testcf->Id, Value => 'Value1')); isnt ($cfdv , 0, "Deleted a custom field value: $cfdm"); is($a->CustomFieldValues($testcf->Id)->Count , 0); ok(my $a2 = RTx::AssetTracker::Asset->new($RT::SystemUser)); ok($a2->Load($id)); is($a2->Name, 'Testing'); is($a2->TypeObj->Id, $testtype->id); is($a2->OwnerRoleGroup->UserMembersObj->First->Id, $u->Id); my $a3 = RTx::AssetTracker::Asset->new($RT::SystemUser); my ($id3, $msg3) = $a3->Create( Type => $testtype->Id, Name => 'Testing 2', Owner => $u->Id); my ($cfv1, $cfm1) = $a->AddCustomFieldValue(Field => $testcf->Id, Value => 'Value1'); isnt($cfv1 , 0, "Adding a custom field to asset 1 is successful: $cfm1"); my ($cfv2, $cfm2) = $a3->AddCustomFieldValue(Field => $testcf->Id, Value => 'Value2'); isnt($cfv2 , 0, "Adding a custom field to asset 2 is successful: $cfm2"); my ($cfv3, $cfm3) = $a->AddCustomFieldValue(Field => $testcf->Id, Value => 'Value3'); isnt($cfv3 , 0, "Adding a custom field to asset 1 is successful: $cfm3"); is($a->CustomFieldValues($testcf->Id)->Count , 2, "This asset has 2 custom field values"); is($a3->CustomFieldValues($testcf->Id)->Count , 1, "This asset has 1 custom field value"); } { ok(require RTx::AssetTracker::Asset, "Loading the RTx::AssetTracker::Asset library"); } { my $a = RTx::AssetTracker::Asset->new($RT::SystemUser); ok( $a->Create(Type => 'Servers', ReferredToBy => 'http://www.cpan.org', RefersTo => 'http://fsck.com', Name => 'This is a name'), "Asset Created"); ok ( my $id = $a->Id, "Got asset id"); like ($a->RefersTo->First->Target , qr/fsck.com/, "Got refers to"); like ($a->ReferredToBy->First->Base , qr/cpan.org/, "Got referredtoby"); } { ok(my $jesse = RT::User->new($RT::SystemUser), "Creating a jesse rt::user"); $jesse->LoadOrCreateByEmail('jesse@example.com'); ok($jesse->Id, "Found the jesse rt user"); my $asset = RTx::AssetTracker::Asset->new($RT::SystemUser); my ($id, $msg) = $asset->Create(Name => "Foo", Owner => $RT::SystemUser->Id, Status => 'production', Admin => ['jesse@example.com'], Type => '1' ); ok ($id, "Asset $id was created"); ok(my $group = $asset->LoadAssetRoleGroup(Type=> 'Admin')); ok ($group->Id, "Found the admins object for this asset"); ok ($asset->IsWatcher(Type => 'Admin', PrincipalId => $jesse->PrincipalId), "The asset actually has jesse at fsck.com as a admin"); ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user"); $bob->LoadOrCreateByEmail('bob@fsck.com'); ok($bob->Id, "Found the bob rt user"); ok (my ($add_id, $add_msg) = $asset->AddWatcher(Type => 'Admin', Email => 'bob@fsck.com'), "Added bob at fsck.com as a admin"); ok ($add_id, "Add succeeded: ($add_msg)"); ok ($asset->IsWatcher(Type => 'Admin', PrincipalId => $bob->PrincipalId), "The asset actually has bob at fsck.com as a admin"); ok ( ($add_id, $add_msg) = $asset->DeleteWatcher(Type =>'Admin', Email => 'bob@fsck.com'), "Added bob at fsck.com as a admin"); ok (!$asset->IsWatcher(Type => 'Admin', PrincipalId => $bob->PrincipalId), "The asset no longer has bob at fsck.com as a admin"); $group = $asset->LoadAssetRoleGroup(Asset => $id, Type=> 'Owner'); ok ($group->Id, "Found the Owner object for this asset"); ok($group->HasMember($RT::SystemUser->UserObj->PrincipalObj), "the owner group has the member 'RT_System'"); } { my $aa = RTx::AssetTracker::Asset->new($RT::SystemUser); my ($id, $tid, $msg)= $aa->Create(Type => 'servers', Name => 'test'); ok($id, $msg); is($aa->Status, 'production', "New asset is created as production"); ($id, $msg) = $aa->SetStatus('test'); ok($id, $msg); like($msg, qr/test/i, "Status message is correct"); ($id, $msg) = $aa->SetStatus('retired'); ok($id, $msg); like($msg, qr/retired/i, "Status message is correct"); ($id, $msg) = $aa->SetStatus('retired'); ok(!$id,$msg); } 1; rt-extension-assettracker-3.0.0/t/api-rights.t000066400000000000000000000024561222742774700213660ustar00rootroot00000000000000use RTx::AssetTracker::Test tests => 14; use strict; use warnings; my $type = RTx::AssetTracker::Test->load_or_create_type( Name => 'A' ); ok $type && $type->id, 'loaded or created type_a'; my $qid = $type->id; my $user = RTx::AssetTracker::Test->load_or_create_user( Name => 'user', Password => 'password', EmailAddress => 'test@example.com', ); ok $user && $user->id, 'loaded or created user'; { cleanup(); RTx::AssetTracker::Test->set_rights( { Principal => 'Everyone', Right => [qw(SeeType)] }, { Principal => 'Owner', Right => [qw(ShowAsset)] }, ); my ($a) = RTx::AssetTracker::Test->create_assets( { Type => $type->id }, { }, ); my $rights; $rights = $user->PrincipalObj->HasRights( Object => $a ); is_deeply( $rights, { SeeType => 1 }, 'got it' ); cleanup(); ($a) = RTx::AssetTracker::Test->create_assets( { Type => $type->id }, { Owner => $user->EmailAddress }, ); ok($a->OwnerRoleGroup->HasMember( $user->id ), 'user is owner'); $rights = $user->PrincipalObj->HasRights( Object => $a ); is_deeply( $rights, { SeeType => 1, ShowAsset => 1 }, 'got it' ) } sub cleanup { RTx::AssetTracker::Test->delete_assets( "Type = $qid" ); RTx::AssetTracker::Test->delete_type_watchers( $type ); }; rt-extension-assettracker-3.0.0/t/api-rights_show_asset.t000066400000000000000000000172351222742774700236260ustar00rootroot00000000000000 use RTx::AssetTracker::Test nodata => 1, tests => 267; use strict; use warnings; my $type_a = RTx::AssetTracker::Test->load_or_create_type( Name => 'A' ); ok $type_a && $type_a->id, 'loaded or created type_a'; my $qa_id = $type_a->id; my $type_b = RTx::AssetTracker::Test->load_or_create_type( Name => 'B' ); ok $type_b && $type_b->id, 'loaded or created type_b'; my $qb_id = $type_b->id; my $user_a = RTx::AssetTracker::Test->load_or_create_user( Name => 'user_a', Password => 'password', ); ok $user_a && $user_a->id, 'loaded or created user'; my $user_b = RTx::AssetTracker::Test->load_or_create_user( Name => 'user_b', Password => 'password', ); ok $user_b && $user_b->id, 'loaded or created user'; foreach my $option (0 .. 1 ) { RT->Config->Set( 'UseSQLForACLChecks' => $option ); diag "Testing with UseSQLForACLChecks => $option"; # Global Admin has right, a User is nobody { cleanup(); RTx::AssetTracker::Test->set_rights( { Principal => 'Everyone', Right => [qw(SeeType)] }, { Principal => 'Admin', Right => [qw(ShowAsset)] }, ); create_assets_set(); have_no_rights($user_a, $user_b); } # Global Admin has right, a User is Type Admin { cleanup(); RTx::AssetTracker::Test->set_rights( { Principal => 'Everyone', Right => [qw(SeeType)] }, { Principal => 'Admin', Right => [qw(ShowAsset)] }, ); create_assets_set(); have_no_rights($user_a, $user_b); my ($status, $msg) = $type_a->AddWatcher( Type => 'Admin', PrincipalId => $user_a->id ); ok($status, "user A is now type A watcher"); foreach my $q ( '', "Type = $qa_id OR Type = $qb_id", "Type = $qb_id OR Type = $qa_id", ) { my $assets = RTx::AssetTracker::Assets->new( RT::CurrentUser->new( $user_a ) ); $q? $assets->FromSQL($q) : $assets->UnLimit; my $found = 0; while ( my $t = $assets->Next ) { $found++; is( $t->Type, $type_a->id, "user sees assets only of type A" ); } is($found, 2, "user sees assets"); } have_no_rights( $user_b ); } # global Admin has right, a User is asset Admin { cleanup(); RTx::AssetTracker::Test->set_rights( { Principal => 'Everyone', Right => [qw(SeeType)] }, { Principal => 'Admin', Right => [qw(ShowAsset)] }, ); my @assets = create_assets_set(); have_no_rights($user_a, $user_b); my ($status, $msg) = $assets[1]->AddWatcher( Type => 'Admin', PrincipalId => $user_a->id ); ok($status, "user A is now type A watcher"); foreach my $q ( '', "Type = $qa_id OR Type = $qb_id", "Type = $qb_id OR Type = $qa_id", ) { my $assets = RTx::AssetTracker::Assets->new( RT::CurrentUser->new( $user_a ) ); $q? $assets->FromSQL($q) : $assets->UnLimit; my $found = 0; while ( my $t = $assets->Next ) { $found++; is( $t->Type, $type_a->id, "user sees assets only in type A" ); is( $t->id, $assets[1]->id, "correct asset"); } is($found, 1, "user sees assets"); } have_no_rights($user_b); } # Type Admin has right, a User is nobody { cleanup(); RTx::AssetTracker::Test->set_rights( { Principal => 'Everyone', Right => [qw(SeeType)] }, { Principal => 'Admin', Object => $type_a, Right => [qw(ShowAsset)] }, ); create_assets_set(); RT::Logger->info("START"); have_no_rights($user_a, $user_b); RT::Logger->info("END"); } # Type Admin has right, Users are Type Admins { cleanup(); RTx::AssetTracker::Test->set_rights( { Principal => 'Everyone', Right => [qw(SeeType)] }, { Principal => 'Admin', Object => $type_a, Right => [qw(ShowAsset)] }, ); create_assets_set(); have_no_rights($user_a, $user_b); my ($status, $msg) = $type_a->AddWatcher( Type => 'Admin', PrincipalId => $user_a->id ); ok($status, "user A is now type A watcher"); ($status, $msg) = $type_b->AddWatcher( Type => 'Admin', PrincipalId => $user_b->id ); ok($status, "user B is now type B watcher"); foreach my $q ( '', "Type = $qa_id OR Type = $qb_id", "Type = $qb_id OR Type = $qa_id", ) { my $assets = RTx::AssetTracker::Assets->new( RT::CurrentUser->new( $user_a ) ); $q? $assets->FromSQL($q) : $assets->UnLimit; my $found = 0; while ( my $t = $assets->Next ) { $found++; is( $t->Type, $type_a->id, "user sees assets only in type A" ); } is($found, 2, "user sees assets"); } have_no_rights( $user_b ); } # Type Admin has right, Users are asset Admins { cleanup(); RTx::AssetTracker::Test->set_rights( { Principal => 'Everyone', Right => [qw(SeeType)] }, { Principal => 'Admin', Object => $type_a, Right => [qw(ShowAsset)] }, ); my @assets = create_assets_set(); have_no_rights($user_a, $user_b); my ($status, $msg) = $assets[1]->AddWatcher( Type => 'Admin', PrincipalId => $user_a->id ); ok($status, "user A is now Admin on a asset in type A"); ($status, $msg) = $assets[2]->AddWatcher( Type => 'Admin', PrincipalId => $user_b->id ); ok($status, "user B is now Admin on a asset in type B"); foreach my $q ( '', "Type = $qa_id OR Type = $qb_id", "Type = $qb_id OR Type = $qa_id", ) { my $assets = RTx::AssetTracker::Assets->new( RT::CurrentUser->new( $user_a ) ); $q? $assets->FromSQL($q) : $assets->UnLimit; my $found = 0; while ( my $t = $assets->Next ) { $found++; is( $t->Type, $type_a->id, "user sees assets only in type A" ); is( $t->id, $assets[1]->id, ) } is($found, 1, "user sees assets"); } have_no_rights( $user_b ); } # Users has direct right on type { cleanup(); RTx::AssetTracker::Test->set_rights( { Principal => 'Everyone', Right => [qw(SeeType)] }, { Principal => $user_a, Object => $type_a, Right => [qw(ShowAsset)] }, ); my @assets = create_assets_set(); foreach my $q ( '', "Type = $qa_id OR Type = $qb_id", "Type = $qb_id OR Type = $qa_id", ) { my $assets = RTx::AssetTracker::Assets->new( RT::CurrentUser->new( $user_a ) ); $q? $assets->FromSQL($q) : $assets->UnLimit; my $found = 0; while ( my $t = $assets->Next ) { $found++; is( $t->Type, $type_a->id, "user sees assets only in type A" ); } is($found, 2, "user sees assets"); } have_no_rights( $user_b ); } } sub have_no_rights { $SIG{'INT'} = $SIG{'TERM'} = sub { print STDERR Carp::longmess('boo'); exit 1 }; local $Test::Builder::Level = $Test::Builder::Level + 1; foreach my $u ( @_ ) { foreach my $q ( '', "Type = $qa_id OR Type = $qb_id", "Type = $qb_id OR Type = $qa_id", ) { my $assets = RTx::AssetTracker::Assets->new( RT::CurrentUser->new( $u ) ); $q? $assets->FromSQL($q) : $assets->UnLimit; ok(!$assets->First, "no assets"); } } } sub create_assets_set{ local $Test::Builder::Level = $Test::Builder::Level + 1; my @res; foreach my $q ($type_a, $type_b) { foreach my $n (1 .. 2) { my $asset = RTx::AssetTracker::Asset->new( RT->SystemUser ); my ($tid) = $asset->Create( Type => $q->id, Name => $q->Name .' - '. $n ); ok( $tid, "created asset #$tid"); push @res, $asset; } } return @res; } sub cleanup { RTx::AssetTracker::Test->delete_assets( "Type = $qa_id OR Type = $qb_id" ); RTx::AssetTracker::Test->delete_type_watchers( $type_a, $type_b ); }; rt-extension-assettracker-3.0.0/t/api-scrip.t000066400000000000000000000073471222742774700212120ustar00rootroot00000000000000 use strict; use warnings; use RTx::AssetTracker::Test tests => 25; use RT; { ok (require RTx::AssetTracker::Scrip); my $t = RTx::AssetTracker::Type->new($RT::SystemUser); $t->Create(Name => 'ScripTest'); ok($t->Id, "Created a scriptest type"); my $s1 = RTx::AssetTracker::Scrip->new($RT::SystemUser); my ($val, $msg) =$s1->Create( AssetType => $t->Id, ScripAction => 'User Defined', ScripCondition => 'User Defined', CustomIsApplicableCode => 'if ($self->AssetObj->Name =~ /fire/) { return (1);} else { return(0)}', CustomPrepareCode => 'return 1', CustomCommitCode => '$self->AssetObj->SetDescription(Value => "firey");', Template => 'Blank' ); ok($val,$msg); my $asset = RTx::AssetTracker::Asset->new($RT::SystemUser); my ($av,$atv,$am) = $asset->Create(Type => $t->Id, Name => "hair on fire", ); ok($av, $am); is ($asset->Description , 'firey', "Asset description is set right"); my $asset2 = RTx::AssetTracker::Asset->new($RT::SystemUser); my ($t2v,$t2tv,$t2m) = $asset2->Create(Type => $t->Id, Name => "hair in water", ); ok($t2v, $t2m); isnt ($asset2->Description , 'firey', "Asset description is set right"); } { my $scrip = RTx::AssetTracker::Scrip->new($RT::SystemUser); my ( $val, $msg ) = $scrip->Create( ScripCondition => 'User Defined', ScripAction => 'User Defined', ); ok( !$val, "missing template: $msg" ); ( $val, $msg ) = $scrip->Create( ScripCondition => 'User Defined', ScripAction => 'User Defined', Template => 'not exists', ); ok( !$val, "invalid template: $msg" ); ( $val, $msg ) = $scrip->Create( ScripAction => 'User Defined', Template => 'Blank', ); ok( !$val, "missing condition: $msg" ); ( $val, $msg ) = $scrip->Create( ScripCondition => 'not exists', ScripAction => 'User Defined', Template => 'Blank', ); ok( !$val, "invalid condition: $msg" ); ( $val, $msg ) = $scrip->Create( ScripCondition => 'User Defined', Template => 'Blank', ); ok( !$val, "missing action: $msg" ); ( $val, $msg ) = $scrip->Create( ScripCondition => 'User Defined', ScripAction => 'not exists', Template => 'Blank', ); ok( !$val, "invalid action: $msg" ); ( $val, $msg ) = $scrip->Create( ScripAction => 'User Defined', ScripCondition => 'User Defined', Template => 'Blank', ); ok( $val, "created scrip: $msg" ); $scrip->Load($val); ok( $scrip->id, 'loaded scrip ' . $scrip->id ); ( $val, $msg ) = $scrip->SetScripCondition(); ok( !$val, "missing condition: $msg" ); ( $val, $msg ) = $scrip->SetScripCondition('not exists'); ok( !$val, "invalid condition: $msg" ); # ( $val, $msg ) = $scrip->SetScripCondition('On Correspond'); # ok( $val, "updated condition to 'On Correspond': $msg" ); ( $val, $msg ) = $scrip->SetScripAction(); ok( !$val, "missing action: $msg" ); ( $val, $msg ) = $scrip->SetScripAction('not exists'); ok( !$val, "invalid action: $msg" ); # ( $val, $msg ) = $scrip->SetScripAction('Notify AdminCcs'); # ok( $val, "updated action to 'Notify AdminCcs': $msg" ); ( $val, $msg ) = $scrip->SetTemplate(); ok( !$val, "missing template $msg" ); ( $val, $msg ) = $scrip->SetTemplate('not exists'); ok( !$val, "invalid template $msg" ); # ( $val, $msg ) = $scrip->SetTemplate('Forward'); # ok( $val, "updated template to 'Forward': $msg" ); ok( $scrip->Delete, 'delete the scrip' ); } 1; rt-extension-assettracker-3.0.0/t/import.t000066400000000000000000000304101222742774700206200ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; BEGIN { use RTx::AssetTracker::Test tests => 57; RT::LoadConfig(); RT::Init(); } use base qw(Test::Class); use RTx::AssetTracker; use RTx::AssetTracker::Assets; use RT::CurrentUser; use YAML; use Storable; use constant RUNSCRIPS => 1; use constant NOSCRIPS => 0; use constant DETAILED => 1; use constant NODETAIL => 0; sub startup :Test(startup=>3) { my ($self) = @_; my $user_obj = RT::User->new( $RT::SystemUser ); $user_obj->LoadOrCreateByEmail( 'todd@chaka.net' ); $user_obj->SetName( 'todd' ); $user_obj->SetPrivileged( 1 ); $user_obj->PrincipalObj->GrantRight( Right => 'SuperUser' ); my $cu = RT::CurrentUser->new(); $cu->LoadByName('todd'); $self->{cu} = $cu; my $group = RT::Group->new( $RT::SystemUser ); $group->LoadUserDefinedGroup( 'group foo' ); $group->Id || $group->CreateUserDefinedGroup( Name => 'group foo', Description => 'A test group' ); my $type = RTx::AssetTracker::Type->new($cu); $type->Load("Servers"); $type->Id || $type->Create(Name => "Servers"); ok($type->Id, "Asset type 'Servers' exists"); my $cf = RT::CustomField->new( $RT::SystemUser ); $cf->LoadByName( Type => 0, Name => 'Foo' ); $cf->Id || $cf->Create( Name => 'Foo', Type => 'FreeformSingle', LookupType => 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset', ); $cf->AddToObject( $type ); my $cf2 = RT::CustomField->new( $RT::SystemUser ); $cf2->LoadByName( Type => 0, Name => 'Bar' ); $cf2->Id || $cf2->Create( Name => 'Bar', Type => 'FreeformMultiple', LookupType => 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset', ); $cf2->AddToObject( $type ); $type = RTx::AssetTracker::Type->new($cu); $type->Load("Virtual"); $type->Id || $type->Create(Name => "Virtual"); ok($type->Id, "Asset type 'Virtual' exists"); # an asset all tests can count on my $asset = { id => 'new', Name => "The one true asset $$", Type => 'Servers', Status => 'production', }; my ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $asset); ok($rv, 'asset created'); $self->{asset} = RTx::AssetTracker::Asset->new($cu); $self->{asset}->Load($rv->[0]); #make sure CF exists #delete all IPs my $ips = RTx::AssetTracker::IPs->new($cu); $ips->UnLimit; while (my $ip = $ips->Next) { $ip->Delete; } } sub simple_import :Tests(2) { my ($self) = @_; my $before_count = $self->asset_count; my $good_asset = { id => 'new', Name => "Simple Asset $$", Type => 'Servers', Status => 'production', }; my ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $good_asset); ok($rv, 'simple asset create'); is($self->asset_count(), $before_count+1); } sub simple_update :Tests(8) { my ($self) = @_; my $before_count = $self->asset_count; my $good_asset = { id => 'new', Name => "Simple Asset for update $$", Description => "Simple Asset for update $$", Type => 'Servers', Status => 'production', }; my ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $good_asset); ok($rv, 'simple asset create'); is($self->asset_count(), $before_count+1); my $aid = $rv->[0]; my $asset_update = { id => $aid, Name => "Simple Asset updated $$", Type => 'Virtual', Status => 'test', }; ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $asset_update); ok($rv, 'simple asset update'); is($self->asset_count(), $before_count+1); is($rv->[0], $aid); my $asset = RTx::AssetTracker::Asset->new($self->{cu}); $asset->Load($aid); is($asset->Name, $asset_update->{Name}); is($asset->TypeObj->Name, $asset_update->{Type}); is($asset->Status, $asset_update->{Status}); } sub import_new :Tests(16) { my ($self) = @_; my $before_count = $self->asset_count; my $good_asset = { id => 'new', Name => "My Asset $$", Type => 'Servers', Description => "new asset $$", Status => 'dr', Owner => 'todd@chaka.net, root@localhost,@group foo', Admin => 'todd@chaka.net', Foo => 'foo value', RefersTo => $self->{asset}->URI, 'IP Address' => 'eth0:127.0.0.1:ffffffffffff:22,80:22' }; my ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $good_asset); ok($rv, 'asset created'); is($self->asset_count(), $before_count+1); my $a = RTx::AssetTracker::Asset->new($self->{cu}); is(ref($rv), 'ARRAY', "list of asset IDs returned"); $a->Load($rv->[0]); #basics is($a->Name, $good_asset->{Name}); is($a->TypeObj->Name, $good_asset->{Type}); is($a->Status, $good_asset->{Status}); is($a->Description, $good_asset->{Description}); #watchers ok($a->IsWatcher( Type => 'Owner', Email => 'todd@chaka.net' ), 'role watcher found'); ok($a->IsWatcher( Type => 'Owner', Email => 'root@localhost' ), 'role watcher found'); ok($a->IsWatcher( Type => 'Admin', Email => 'todd@chaka.net' ), 'role watcher found'); my $g = RT::Group->new($self->{cu}); $g->LoadUserDefinedGroup("group foo"); ok($a->IsWatcher( Type => 'Owner', PrincipalId => $g->PrincipalId ), 'group role watcher found'); #custom fields is($a->FirstCustomFieldValue('Foo'), $good_asset->{Foo}); #links my $refers = $a->RefersTo; is($refers->First->Target, $self->{asset}->URI, 'link created'); #ips my $ip = $a->IPs->First; is($ip->IP, '127.0.0.1', 'IP set'); is($ip->Interface, 'eth0'); is($ip->MAC, 'ffffffffffff'); } sub update :Tests(9) { my ($self) = @_; my $good_asset = { id => 'new', Name => "My Asset to Update $$", Type => 'Servers', Description => "new asset $$", Status => 'dr', Owner => 'todd@chaka.net, root@localhost,@group foo', Admin => 'todd@chaka.net', Foo => 'foo value', RefersTo => $self->{asset}->URI, 'IP Address' => 'eth0:127.0.0.3:ffffffffffff:22,80:22' }; my ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $good_asset); ok($rv, 'asset created'); my $updated_asset = Storable::dclone($good_asset); $updated_asset->{id} = $rv->[0]; $updated_asset->{Foo} = 'bar value'; $updated_asset->{Owner} = 'root@localhost,@group foo', $updated_asset->{'IP Address'} = 'eth0:127.0.0.4:ffffffffffff:22,80:22'; $updated_asset->{RefersTo} = undef; $updated_asset->{DependsOn} = $self->{asset}->URI; ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $updated_asset); ok($rv, 'asset updated'); my $asset = RTx::AssetTracker::Asset->new($self->{cu}); $asset->Load($rv->[0]); is($asset->FirstCustomFieldValue('Foo'), $updated_asset->{Foo}, 'custom field updated'); ok(!$asset->IsWatcher(Type => 'Owner', Email => 'todd@chaka.net'), 'watcher removed'); ok( $asset->IsWatcher(Type => 'Owner', Email => 'root@localhost'), 'watcher still there'); is($asset->RefersTo->Count, 0, 'link removed'); is($asset->DependsOn->Count, 1, 'link created'); my @ips = $asset->IPsAsList; is(@ips, 1); is($ips[0], '127.0.0.4', 'found updated IP'); } sub bad_import :Tests(2) { my ($self) = @_; my $before_count = $self->asset_count; my $good_asset = { id => 'new', Name => "My Asset $$", Type => 'Servers', Description => "new asset $$", Status => 'dr', Owner => 'todd@chaka.net, root@localhost', Admin => 'todd@chaka.net', Foo => 'foo value', }; my $bad_asset = Storable::dclone($good_asset); $bad_asset->{id} = 'bad'; #asset id must be an integer or new my ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $good_asset, $bad_asset); is($rv, 0, "not imported"); is($before_count, $self->asset_count()); } sub test_update_transactions :Tests(6) { my ($self) = @_; my $good_asset = { id => 'new', Name => "Simple Asset transactions $$", Type => 'Servers', Status => 'production', }; my ($rv, $msgs) = $self->Import(NOSCRIPS, DETAILED, $good_asset); ok($rv, 'simple asset create'); my $aid = $rv->[0]; my $asset = RTx::AssetTracker::Asset->new($self->{cu}); $asset->Load($aid); is($asset->Transactions->Count, 1, 'create transaction'); my $asset_update = { id => $aid, Name => "Simple Asset transactions $$", Type => 'Virtual', Status => 'test', }; ($rv, $msgs) = $self->Import(NOSCRIPS, DETAILED, $asset_update); ok($rv, 'simple asset update'); $asset = RTx::AssetTracker::Asset->new($self->{cu}); $asset->Load($aid); is($asset->Transactions->Count, 4, 'update, type, and status transactions'); $asset_update = { id => $aid, Name => "Simple Asset transactions $$", Type => 'Servers', Admin => 'todd@chaka.net', Status => 'test', }; ($rv, $msgs) = $self->Import(NOSCRIPS, DETAILED, $asset_update); ok($rv, 'simple asset update'); $asset = RTx::AssetTracker::Asset->new($self->{cu}); $asset->Load($aid); is($asset->Transactions->Count, 7, 'watcher, update and type transactions'); } sub test_multicf_import :Tests(8) { my ($self) = @_; my $before_count = $self->asset_count; my @vals = ( 'one', 'two', 'three' ); my $good_asset = { id => 'new', Name => "MultiCF Asset $$", Type => 'Servers', Status => 'production', Bar => join( "\n", @vals ) }; my ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $good_asset); ok($rv, 'multi-value cf import'); is($self->asset_count(), $before_count+1); my $a = RTx::AssetTracker::Asset->new($self->{cu}); is(ref($rv), 'ARRAY', "list of asset IDs returned"); $a->Load($rv->[0]); my @new_vals = map { $_->Content } @{ $a->CustomFieldValues('Bar')->ItemsArrayRef }; is_deeply( \@vals, \@new_vals, join(' ', @vals) ); push @vals, 'four'; $good_asset = { id => $a->Id, Name => "MultiCF Asset $$", Type => 'Servers', Status => 'production', Bar => join( "\n", @vals ) }; ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $good_asset); ok($rv, 'add cf value'); @new_vals = map { $_->Content } @{ $a->CustomFieldValues('Bar')->ItemsArrayRef }; is_deeply( \@vals, \@new_vals, join(' ', @vals) ); pop @vals; shift @vals; $good_asset = { id => $a->Id, Name => "MultiCF Asset $$", Type => 'Servers', Status => 'production', Bar => join( "\n", @vals ) }; ($rv, $msgs) = $self->Import(NOSCRIPS, NODETAIL, $good_asset); ok($rv, 'delete cf values'); @new_vals = map { $_->Content } @{ $a->CustomFieldValues('Bar')->ItemsArrayRef }; is_deeply( \@vals, \@new_vals, join(' ', @vals) ); } sub asset_count { my ($self) = @_; my $assets = RTx::AssetTracker::Assets->new($self->{cu}); $assets->UnLimit; return $assets->Count; } sub asset2headers { my ($self, $asset) = @_; my $headers = [ 'id', grep { $_ ne 'id' } keys %$asset ]; #id always has to be the first column return $headers; } sub asset2row { my ($self, $headers, $asset) = @_; my $row = [ map { $asset->{$_} } @$headers ]; return $row; } sub asset_name { } sub Import { my ($self, $runscrips, $detailed, @assets) = @_; my $headers = $self->asset2headers($assets[0]); my @rows; for (@assets) { push @rows, $self->asset2row($headers, $_); } my $assets = RTx::AssetTracker::Assets->new($self->{cu}); return $assets->Import($headers, \@rows, $runscrips, $detailed); } Test::Class->runtests; rt-extension-assettracker-3.0.0/t/web-rest.t000066400000000000000000000036411222742774700210440ustar00rootroot00000000000000#!/usr/bin/env perl use strict; use warnings; use RTx::AssetTracker::Test tests => 21; my ($baseurl, $m) = RTx::AssetTracker::Test->started_ok; for my $name ("severity", "fu()n:k/") { my $cf = RTx::AssetTracker::Test->load_or_create_asset_custom_field( Name => $name, Type => 'Freeform', AssetType => 'Servers', ); ok($cf->Id, "created a CustomField"); is($cf->Name, $name, "correct CF name"); } my $type = RTx::AssetTracker::Test->load_or_create_type(Name => 'Servers'); ok($type->Id, "loaded the Servers type"); $m->post("$baseurl/REST/1.0/asset/new", [ user => 'root', pass => 'password', format => 'l', ]); my $text = $m->content; my @lines = $text =~ m{.*}g; shift @lines; # header # CFs aren't in the default asset form push @lines, "CF-fu()n:k/: maximum"; # old style push @lines, "CF.{severity}: explosive"; # new style $text = join "\n", @lines; ok($text =~ s/Name:\s*$/Name: REST interface/m, "successfully replaced name"); $m->post("$baseurl/REST/1.0/asset/edit", [ user => 'root', pass => 'password', content => $text, ], Content_Type => 'form-data'); my ($id) = $m->content =~ /Asset (\d+) created/; ok($id, "got asset #$id"); my $asset = RTx::AssetTracker::Asset->new($RT::SystemUser); $asset->Load($id); is($asset->Id, $id, "loaded the REST-created asset"); is($asset->Name, "REST interface", "name successfully set"); is($asset->FirstCustomFieldValue("fu()n:k/"), "maximum", "CF successfully set"); $m->post("$baseurl/REST/1.0/search/asset", [ user => 'root', pass => 'password', query => "id=$id", fields => "Name,CF-fu()n:k/,CF.{severity},Status", ]); # the fields are interpreted server-side a hash (why?), so we can't depend # on order for ("id: asset/1", "Name: REST interface", "CF.{fu()n:k/}: maximum", "CF.{severity}: explosive", "Status: production") { $m->content_contains($_); }