├── .gitignore ├── COPYRIGHT.txt ├── CREDITS.txt ├── GPL.txt ├── README.rdoc ├── Rakefile ├── VERSION ├── app ├── controllers │ └── customers_controller.rb ├── models │ └── customer.rb └── views │ └── customers │ ├── _form.html.erb │ ├── edit.html.erb │ ├── list.html.erb │ ├── new.html.erb │ ├── select.html.erb │ └── show.html.erb ├── config └── locales │ ├── ca.yml │ ├── de.yml │ ├── en.yml │ ├── es.yml │ ├── fr.yml │ ├── it.yml │ └── pt-br.yml ├── customer_plugin.gemspec ├── db └── migrate │ ├── 001_create_customers.rb │ ├── 002_link_customers_to_projects.rb │ └── 003_add_skype_contact.rb ├── init.rb ├── lang ├── ca.yml ├── de.yml ├── en.yml ├── es.yml ├── fr.yml ├── it.yml └── pt-br.yml ├── lib └── empty ├── rails └── init.rb ├── spec ├── controllers │ └── customers_controller_spec.rb ├── models │ └── customer_spec.rb ├── rcov.opts ├── sanity_spec.rb ├── spec.opts ├── spec_helper.rb └── views │ └── empty └── writeup.txt /.gitignore: -------------------------------------------------------------------------------- 1 | nbproject/ 2 | 3 | -------------------------------------------------------------------------------- /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | Redmine Customer is a Redmine plugin that can be used to track 2 | basic customer information. 3 | 4 | Copyright (C) 2007 Eric Davis, Little Stream Software 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License 8 | as published by the Free Software Foundation; either version 2 9 | of the License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | -------------------------------------------------------------------------------- /CREDITS.txt: -------------------------------------------------------------------------------- 1 | Thanks go to the following people for patches and contributions: 2 | 3 | Eric Davis 4 | - Project Maintainer 5 | 6 | Michele Franzin 7 | - added Italian translation 8 | - UI fixes and improvements 9 | - refactorings 10 | 11 | Pau Garcia i Quiles 12 | - Catalan translation 13 | -------------------------------------------------------------------------------- /GPL.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Redmine Customer plugin 2 | 3 | This is a plugin for Redmine that can be used to track basic customer information for each project. It was created so I could track which project belonged to which customer. 4 | 5 | == Features 6 | 7 | * Store Customer data 8 | * Associate a Customer for each project 9 | * Permissions to control user access to data 10 | 11 | == Getting the plugin 12 | 13 | A copy of the released version can be downloaded from {Little Stream Software}[https://projects.littlestreamsoftware.com/projects/redmine-customers/files]. A development copy can be downloaded from {GitHub}[http://github.com/edavis10/redmine-customer-plugin/tree/master] 14 | 15 | 16 | == Install 17 | 18 | 1. Follow the Redmine plugin installation steps at: http://www.redmine.org/wiki/redmine/Plugins Make sure the plugin is installed to +vendor/plugins/customer_plugin+ 19 | 2. Setup the database using the migrations. +rake db:migrate_plugins+ 20 | 3. Login to your Redmine install as an Administrator 21 | 4. Setup the permissions for your roles 22 | 5. Add the "Customer module" to the enabled modules for your project 23 | 6. The link to the plugin should appear on that project's navigation as Customer 24 | 25 | == Usage 26 | 27 | 1. Use the "New" link to create a Customer. 28 | 2. Once you have created a customer, use the "Assign" link to assign a customer to a Project. 29 | 3. Continue doing steps #1 and #2 until you have assigned customers to all your projects you need. 30 | 31 | == Upgrade 32 | 33 | === Zip file 34 | 35 | 1. Download the latest zip file from https://projects.littlestreamsoftware.com 36 | 2. Unzip the file to your Redmine into vendor/plugins 37 | 3. Restart your Redmine 38 | 39 | === Git 40 | 41 | 1. Open a shell to your Redmine's vendor/plugins/customer_plugin folder 42 | 2. Update your git copy with +git pull+ 43 | 3. Restart your Redmine 44 | 45 | == License 46 | 47 | This plugin is licensed under the GNU GPL v2. See LICENSE.txt and GPL.txt for details. 48 | 49 | == Project help 50 | 51 | If you need help you can contact the maintainer at the Bug Tracker. The bug tracker is located at: https://projects.littlestreamsoftware.com 52 | 53 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | require 'rubygems' 4 | gem 'rspec' 5 | gem 'rspec-rails' 6 | 7 | Dir[File.expand_path(File.dirname(__FILE__)) + "/lib/tasks/**/*.rake"].sort.each { |ext| load ext } 8 | 9 | # Modifided from the RSpec on Rails plugins 10 | PLUGIN_ROOT = File.expand_path(File.dirname(__FILE__)) 11 | 12 | # Allows loading of an environment config based on the environment 13 | REDMINE_ROOT = ENV["REDMINE_ROOT"] || File.dirname(__FILE__) + "/../../../.." 14 | REDMINE_APP = File.expand_path(REDMINE_ROOT + '/app') 15 | REDMINE_LIB = File.expand_path(REDMINE_ROOT + '/lib') 16 | 17 | require 'rake' 18 | require 'rake/clean' 19 | require 'rake/rdoctask' 20 | require 'spec/rake/spectask' 21 | 22 | PROJECT_NAME = 'customer_plugin' 23 | ZIP_FILE = PROJECT_NAME + ".zip" 24 | CLEAN.include('**/semantic.cache', ZIP_FILE) 25 | 26 | # No Database needed 27 | spec_prereq = :noop 28 | task :noop do 29 | end 30 | 31 | task :default => :spec 32 | task :stats => "spec:statsetup" 33 | 34 | desc "Run all specs in spec directory (excluding plugin specs)" 35 | Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t| 36 | t.spec_opts = ['--options', "\"#{PLUGIN_ROOT}/spec/spec.opts\""] 37 | t.spec_files = FileList['spec/**/*_spec.rb'] 38 | end 39 | 40 | namespace :spec do 41 | desc "Run all specs in spec directory with RCov (excluding plugin specs)" 42 | Spec::Rake::SpecTask.new(:rcov) do |t| 43 | t.spec_opts = ['--options', "\"#{PLUGIN_ROOT}/spec/spec.opts\""] 44 | t.spec_files = FileList['spec/**/*_spec.rb'] 45 | t.rcov = true 46 | t.rcov_opts << ["--rails", "--sort=coverage", "--exclude '/var/lib/gems,spec,#{REDMINE_APP},#{REDMINE_LIB}'"] 47 | end 48 | 49 | desc "Print Specdoc for all specs (excluding plugin specs)" 50 | Spec::Rake::SpecTask.new(:doc) do |t| 51 | t.spec_opts = ["--format", "specdoc", "--dry-run"] 52 | t.spec_files = FileList['spec/**/*_spec.rb'] 53 | end 54 | 55 | desc "Print Specdoc for all specs as HTML (excluding plugin specs)" 56 | Spec::Rake::SpecTask.new(:htmldoc) do |t| 57 | t.spec_opts = ["--format", "html:doc/rspec_report.html", "--loadby", "mtime"] 58 | t.spec_files = FileList['spec/**/*_spec.rb'] 59 | end 60 | 61 | [:models, :controllers, :views, :helpers, :lib].each do |sub| 62 | desc "Run the specs under spec/#{sub}" 63 | Spec::Rake::SpecTask.new(sub => spec_prereq) do |t| 64 | t.spec_opts = ['--options', "\"#{PLUGIN_ROOT}/spec/spec.opts\""] 65 | t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"] 66 | end 67 | end 68 | end 69 | 70 | desc 'Generate documentation for the plugin.' 71 | Rake::RDocTask.new(:rdoc) do |rdoc| 72 | rdoc.rdoc_dir = 'doc' 73 | rdoc.title = PROJECT_NAME 74 | rdoc.options << '--line-numbers' << '--inline-source' 75 | rdoc.rdoc_files.include('*.markdown') 76 | rdoc.rdoc_files.include('*.rdoc') 77 | rdoc.rdoc_files.include('*.txt') 78 | rdoc.rdoc_files.include('lib/**/*.rb') 79 | rdoc.rdoc_files.include('app/**/*.rb') 80 | end 81 | 82 | desc 'Uploads project documentation' 83 | task :upload_doc => ['spec:rcov', :doc, 'spec:htmldoc'] do |t| 84 | # TODO: Get rdoc working without frames 85 | `scp -r doc/ dev.littlestreamsoftware.com:/home/websites/projects.littlestreamsoftware.com/shared/embedded_docs/redmine-customers/doc` 86 | `scp -r coverage/ dev.littlestreamsoftware.com:/home/websites/projects.littlestreamsoftware.com/shared/embedded_docs/redmine-customers/coverage` 87 | end 88 | 89 | desc "Create release archives" 90 | task :release => [:clean, :rdoc, 'release:zip', 'release:tarball'] 91 | 92 | namespace :release do 93 | desc "Create a zip archive" 94 | task :zip => [:clean] do 95 | sh "git archive --format=zip --prefix=#{PROJECT_NAME}/ HEAD > #{PROJECT_NAME}.zip" 96 | end 97 | 98 | desc "Create a tarball archive" 99 | task :tarball => [:clean] do 100 | sh "git archive --format=tar --prefix=#{PROJECT_NAME}/ HEAD | gzip > #{PROJECT_NAME}.tar.gz" 101 | end 102 | end 103 | 104 | 105 | begin 106 | require 'jeweler' 107 | Jeweler::Tasks.new do |s| 108 | s.name = "customer_plugin" 109 | s.summary = "This is a plugin for Redmine that can be used to track basic customer information" 110 | s.email = "edavis@littlestreamsoftware.com" 111 | s.homepage = "https://projects.littlestreamsoftware.com/projects/TODO" 112 | s.description = "This is a plugin for Redmine that can be used to track basic customer information" 113 | s.authors = ["Eric Davis"] 114 | s.rubyforge_project = "customer_plugin" # TODO 115 | s.files = FileList[ 116 | "[A-Z]*", 117 | "init.rb", 118 | "rails/init.rb", 119 | "{bin,generators,lib,test,app,assets,config,lang}/**/*", 120 | 'lib/jeweler/templates/.gitignore' 121 | ] 122 | end 123 | Jeweler::GemcutterTasks.new 124 | Jeweler::RubyforgeTasks.new do |rubyforge| 125 | rubyforge.doc_task = "rdoc" 126 | end 127 | rescue LoadError 128 | puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" 129 | end 130 | 131 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.2.0 2 | -------------------------------------------------------------------------------- /app/controllers/customers_controller.rb: -------------------------------------------------------------------------------- 1 | class CustomersController < ApplicationController 2 | unloadable 3 | layout 'base' 4 | before_filter :find_project, :authorize 5 | before_filter :find_customer, :only => [:edit, :update, :destroy] 6 | before_filter :find_customers, :only => [:list, :select] 7 | 8 | def show 9 | @customer = Customer.find_by_id(@project.customer_id) 10 | end 11 | 12 | def list 13 | #@customers = Customer.find(:all) 14 | end 15 | 16 | def select 17 | #@customers = Customer.find(:all) 18 | end 19 | 20 | def assign 21 | @project.customer_id = params[:customer][:id] 22 | if @project.save 23 | flash[:notice] = l(:notice_successful_save) 24 | redirect_to :action => "show", :id => params[:id] 25 | else 26 | flash[:notice] = l(:notice_unsuccessful_save) 27 | redirect_to :action => "select", :id => params[:id] 28 | end 29 | end 30 | 31 | def edit 32 | #@customer = Customer.find_by_id(params[:customer_id]) 33 | end 34 | 35 | def update 36 | #@customer = Customer.find_by_id(params[:customer_id]) 37 | if @customer.update_attributes(params[:customer]) 38 | flash[:notice] = l(:notice_successful_update) 39 | redirect_to :action => "list", :id => params[:id] 40 | else 41 | render :action => "edit", :id => params[:id] 42 | end 43 | end 44 | 45 | def destroy 46 | #@customer = Customer.find_by_id(params[:customer_id]) 47 | if @customer.destroy 48 | flash[:notice] = l(:notice_successful_delete) 49 | else 50 | flash[:error] = l(:notice_unsuccessful_save) 51 | end 52 | redirect_to :action => "list", :id => params[:id] 53 | end 54 | 55 | def new 56 | @customer = Customer.new 57 | end 58 | 59 | def create 60 | @customer = Customer.new(params[:customer]) 61 | if @customer.save 62 | flash[:notice] = l(:notice_successful_create) 63 | redirect_to :action => "select", :id => params[:id] 64 | else 65 | render :action => "new", :id => params[:id] 66 | end 67 | end 68 | 69 | private 70 | 71 | def find_project 72 | @project = Project.find(params[:id]) 73 | rescue ActiveRecord::RecordNotFound 74 | render_404 75 | end 76 | 77 | def find_customer 78 | @customer = Customer.find_by_id(params[:customer_id]) 79 | rescue ActiveRecord::RecordNotFound 80 | render_404 81 | end 82 | 83 | def find_customers 84 | @customers = Customer.find(:all) || [] 85 | end 86 | 87 | end 88 | -------------------------------------------------------------------------------- /app/models/customer.rb: -------------------------------------------------------------------------------- 1 | class Customer < ActiveRecord::Base 2 | has_many :projects 3 | 4 | # name or company is mandatory 5 | validates_presence_of :name, :if => :company_unsetted 6 | validates_presence_of :company, :if => :name_unsetted 7 | validates_uniqueness_of :name, :scope => :company 8 | 9 | validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, 10 | :allow_nil => true, :allow_blank => true 11 | #TODO validate website address 12 | #TODO validate skype_name contact 13 | 14 | def pretty_name 15 | result = [] 16 | [self.name, self.company].each do |field| 17 | result << field unless field.blank? 18 | end 19 | 20 | return result.join(", ") 21 | end 22 | 23 | private 24 | 25 | def name_unsetted 26 | self.name.blank? 27 | end 28 | 29 | def company_unsetted 30 | self.company.blank? 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /app/views/customers/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= error_messages_for 'customer' %> 2 | 3 |
4 |

5 | <%= f.text_field 'name', :label=>l(:field_customer_name) -%> 6 |

7 |

8 | <%= f.text_field 'company', :label=>l(:field_customer_company) -%> 9 |

10 |

11 | <%= f.text_area 'address', :label=>l(:field_customer_address), :rows => 5 -%> 12 |

13 |

14 | <%= f.text_field 'phone', :label=>l(:field_customer_phone) -%> 15 |

16 |

17 | <%= f.text_field 'email', :label=>l(:field_customer_email) -%> 18 |

19 |

20 | <%= f.text_field 'website', :label=>l(:field_customer_website) -%> 21 |

22 |

23 | <%= f.text_field 'skype_name', :label=>l(:field_customer_skype) -%> 24 |

25 |
26 | -------------------------------------------------------------------------------- /app/views/customers/edit.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to_if_authorized l(:label_customer_view), {:controller => 'customers', :action => 'show', :id => @project}, :class => 'icon icon-user' %> 3 | <%= link_to_if_authorized l(:label_customer_assign), {:controller => 'customers', :action => 'select', :id => @project}, :class => 'icon icon-move' %> 4 | <%= link_to_if_authorized l(:label_customer_list), {:controller => 'customers', :action => 'list', :id => @project}, :class => 'icon icon-users' %> 5 | <%= link_to_if_authorized l(:label_customer_new), {:controller => 'customers', :action => 'new', :id => @project}, :class => 'icon icon-add' %> 6 |
7 | 8 |

<%= l(:label_customer_edit_information) %>

9 | 10 | <% labelled_tabular_form_for :customer, @customer, :url =>{:action => 'update', :id => @project, :customer_id => @customer.id} do |f| %> 11 | <%= render :partial => 'form', :locals => {:f => f} %> 12 | <%= submit_tag l(:button_save) -%> 13 | <% end -%> 14 | 15 | <% content_for(:header_tags) do %> 16 | 19 | <% end %> -------------------------------------------------------------------------------- /app/views/customers/list.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to_if_authorized l(:label_customer_view), {:controller => 'customers', :action => 'show', :id => @project}, :class => 'icon icon-user' %> 3 | <%= link_to_if_authorized l(:label_customer_assign), {:controller => 'customers', :action => 'select', :id => @project}, :class => 'icon icon-move' %> 4 | <%= link_to_if_authorized l(:label_customer_list), {:controller => 'customers', :action => 'list', :id => @project}, :class => 'icon icon-users' %> 5 | <%= link_to_if_authorized l(:label_customer_new), {:controller => 'customers', :action => 'new', :id => @project}, :class => 'icon icon-add' %> 6 |
7 | 8 |

<%= l(:label_customer_plural) %>

9 | 10 | <% unless @customers.empty? %> 11 | 12 | <% @customers.each do |customer| %> 13 | 14 | 15 | 21 | 22 | <% end %> 23 |
<%= customer.pretty_name %> 16 | 17 | <%= link_to_if_authorized l(:button_edit), {:controller => 'customers', :action => 'edit', :id => @project, :customer_id => customer.id}, :class => 'icon icon-edit' %> 18 | <%= link_to_if_authorized l(:button_delete), {:controller => 'customers', :action => 'destroy', :id => @project, :customer_id => customer.id}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> 19 | 20 |
24 | <% else %> 25 |

<%=l(:label_no_data)%>

26 | <% end %> 27 | 28 | <% content_for(:header_tags) do %> 29 | 32 | <% end %> 33 | 34 | -------------------------------------------------------------------------------- /app/views/customers/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to_if_authorized l(:label_customer_view), {:controller => 'customers', :action => 'show', :id => @project}, :class => 'icon icon-user' %> 3 | <%= link_to_if_authorized l(:label_customer_assign), {:controller => 'customers', :action => 'select', :id => @project}, :class => 'icon icon-move' %> 4 | <%= link_to_if_authorized l(:label_customer_list), {:controller => 'customers', :action => 'list', :id => @project}, :class => 'icon icon-users' %> 5 | <%= link_to_if_authorized l(:label_customer_new), {:controller => 'customers', :action => 'new', :id => @project}, :class => 'icon icon-add' %> 6 |
7 | 8 |

<%= l(:label_customer_new) %>

9 | 10 | <% labelled_tabular_form_for :customer, @customer, :url =>{:action => 'create', :id => @project} do |f| %> 11 | <%= render :partial => 'form', :locals => {:f => f} %> 12 | <%= submit_tag l(:button_save) -%> 13 | <% end -%> 14 | 15 | <% content_for(:header_tags) do %> 16 | 19 | <% end %> -------------------------------------------------------------------------------- /app/views/customers/select.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to_if_authorized l(:label_customer_view), {:controller => 'customers', :action => 'show', :id => @project}, :class => 'icon icon-user' %> 3 | <%= link_to_if_authorized l(:label_customer_assign), {:controller => 'customers', :action => 'select', :id => @project}, :class => 'icon icon-move' %> 4 | <%= link_to_if_authorized l(:label_customer_list), {:controller => 'customers', :action => 'list', :id => @project}, :class => 'icon icon-users' %> 5 | <%= link_to_if_authorized l(:label_customer_new), {:controller => 'customers', :action => 'new', :id => @project}, :class => 'icon icon-add' %> 6 |
7 | 8 |

<%= l(:label_customer_assign) %>

9 | 10 | <% form_for :customer, :url =>{:action => 'assign', :id => @project} do |f| %> 11 |
12 |

13 | 14 |

15 |

16 | <%= collection_select(:customer, :id, @customers, :id, :pretty_name, :include_blank => true) -%> 17 |

18 |
19 | <%= submit_tag l(:button_save) -%> 20 | 21 | <% end -%> 22 | 23 | <% content_for(:header_tags) do %> 24 | 27 | <% end %> 28 | -------------------------------------------------------------------------------- /app/views/customers/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to_if_authorized l(:button_edit), {:controller => 'customers', :action => 'edit', :id => @project, :customer_id => @customer.id}, :class => 'icon icon-edit' unless @customer.nil? %> 3 | <%= link_to_if_authorized l(:label_customer_view), {:controller => 'customers', :action => 'show', :id => @project}, :class => 'icon icon-user' %> 4 | <%= link_to_if_authorized l(:label_customer_assign), {:controller => 'customers', :action => 'select', :id => @project}, :class => 'icon icon-move' %> 5 | <%= link_to_if_authorized l(:label_customer_list), {:controller => 'customers', :action => 'list', :id => @project}, :class => 'icon icon-users' %> 6 | <%= link_to_if_authorized l(:label_customer_new), {:controller => 'customers', :action => 'new', :id => @project}, :class => 'icon icon-add' %> 7 |
8 | 9 |

<%= l(:label_customer) %>

10 | 11 | <% unless @customer.nil? -%> 12 |
13 |

<%= l(:field_customer_name) %>: <%= h @customer.name %>

14 |

<%= l(:field_customer_company) %>: <%= h @customer.company %>

15 |

<%= l(:field_customer_address) %>: <%= h @customer.address %>

16 |

<%= l(:field_customer_phone) %>: <%= h @customer.phone %>

17 |

<%= l(:field_customer_email) %>: <%= mail_to @customer.email unless @customer.email.blank? %>

18 |

<%= l(:field_customer_website) %>: <%= link_to @customer.website unless @customer.website.blank? %>

19 |

<%= l(:field_customer_skype) %>: <%= @customer.skype_name unless @customer.skype_name.blank? %>

20 |
21 | <% else %> 22 |

<%=l(:label_no_data)%>

23 | <% end %> 24 | 25 | <% content_for(:header_tags) do %> 26 | 29 | <% end %> -------------------------------------------------------------------------------- /config/locales/ca.yml: -------------------------------------------------------------------------------- 1 | ca: 2 | customer_title: Clients 3 | label_customer: Client 4 | label_customer_plural: Llista de clients 5 | label_customer_information: Informació de clients 6 | label_customer_edit_information: Edita informaci&ocuate; de client 7 | label_customer_assign_to_project: Assigna client a projecte 8 | label_customer_view: Veure 9 | label_customer_assign: Assigna 10 | label_customer_list: Llista 11 | label_customer_new: Nou 12 | field_customer_name: Nom 13 | field_customer_company: Empresa 14 | field_customer_address: Addreça 15 | field_customer_phone: Telèfon 16 | field_customer_email: Correu electrònic 17 | field_customer_website: Pàgina web 18 | field_customer_skype: Skype 19 | notice_successful_save: Desat 20 | notice_unsuccessful_save: No s'ha pogut desar 21 | -------------------------------------------------------------------------------- /config/locales/de.yml: -------------------------------------------------------------------------------- 1 | de: 2 | customer_title: Kunde 3 | label_customer: Kunde 4 | label_customer_plural: Kundenliste 5 | label_customer_information: Kundendetails 6 | label_customer_edit_information: Kundendetails bearbeiten 7 | label_customer_assign_to_project: Kunde zu Projekt zuordnen 8 | label_customer_view: Anzeigen 9 | label_customer_assign: Zuweisen 10 | label_customer_list: Auflisten 11 | label_customer_new: Neu 12 | field_customer_name: Name 13 | field_customer_company: Firma 14 | field_customer_address: Adresse 15 | field_customer_phone: Telefon 16 | field_customer_email: Email 17 | field_customer_website: Website 18 | field_customer_skype: Skype 19 | notice_successful_save: Gespeichert 20 | notice_unsuccessful_save: Fehler beim Speichern 21 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | customer_title: Customer 3 | label_customer: Customer 4 | label_customer_plural: Customer List 5 | label_customer_information: Customer Information 6 | label_customer_edit_information: Editing Customer Information 7 | label_customer_assign_to_project: Assign Customer to Project 8 | label_customer_view: View 9 | label_customer_assign: Assign 10 | label_customer_list: List 11 | label_customer_new: New 12 | field_customer_name: Name 13 | field_customer_company: Company 14 | field_customer_address: Address 15 | field_customer_phone: Phone 16 | field_customer_email: Email 17 | field_customer_website: Website 18 | field_customer_skype: Skype 19 | notice_successful_save: Saved 20 | notice_unsuccessful_save: Could not save 21 | -------------------------------------------------------------------------------- /config/locales/es.yml: -------------------------------------------------------------------------------- 1 | es: 2 | customer_title: Clientes 3 | label_customer: Customer 4 | label_customer_plural: Listado de Clientes 5 | label_customer_information: Informacion del Cliente 6 | label_customer_edit_information: Editar informacion del cliente 7 | label_customer_assign_to_project: Asignar cliente a un proyecto 8 | label_customer_view: Ver 9 | label_customer_assign: Asignar 10 | label_customer_list: Listar 11 | label_customer_new: Nuevo 12 | field_customer_name: Nombre 13 | field_customer_company: Empresa 14 | field_customer_address: Direccion 15 | field_customer_phone: Telefono 16 | field_customer_email: Correo Electronico 17 | field_customer_website: Sitio Web 18 | field_customer_skype: Skype 19 | notice_successful_save: Guardado 20 | notice_unsuccessful_save: No se pudo guardar 21 | -------------------------------------------------------------------------------- /config/locales/fr.yml: -------------------------------------------------------------------------------- 1 | fr: 2 | customer_title: Client 3 | label_customer: Client 4 | label_customer_plural: Liste des clients 5 | label_customer_information: Informations 6 | label_customer_edit_information: Modifier les informations 7 | label_customer_assign_to_project: Assigner un client au projet 8 | label_customer_view: Voir 9 | label_customer_assign: Assigner 10 | label_customer_list: Liste 11 | label_customer_new: Nouveau 12 | field_customer_name: Nom 13 | field_customer_company: Entreprise 14 | field_customer_address: Adresse 15 | field_customer_phone: Téléphone 16 | field_customer_email: Email 17 | field_customer_website: Site Web 18 | field_customer_skype: Skype 19 | notice_successful_save: Enregistré 20 | notice_unsuccessful_save: Enregistrement non effectué 21 | -------------------------------------------------------------------------------- /config/locales/it.yml: -------------------------------------------------------------------------------- 1 | it: 2 | customer_title: Cliente 3 | label_customer: Cliente 4 | label_customer_plural: Elenco clienti 5 | label_customer_information: Dettaglio cliente 6 | label_customer_edit_information: Modifica dettaglio cliente 7 | label_customer_assign_to_project: Assegna cliente al progetto 8 | label_customer_view: Mostra dettaglio 9 | label_customer_assign: Assegna 10 | label_customer_list: Elenco clienti 11 | label_customer_new: Nuovo cliente 12 | field_customer_name: Nominativo 13 | field_customer_company: Azienda 14 | field_customer_address: Indirizzo 15 | field_customer_phone: Telefono 16 | field_customer_email: Email 17 | field_customer_website: Sito web 18 | field_customer_skype: Skype 19 | notice_successful_save: Salvato con successo 20 | notice_unsuccessful_save: Impossibile salvare 21 | project_module_customer_module: Clienti 22 | permission_view_customer: Vedi cliente 23 | permission_assign_customer: Assegna cliente 24 | permission_see_customer_list: Vedi lista clienti 25 | permission_edit_customer: Modifica cliente 26 | -------------------------------------------------------------------------------- /config/locales/pt-br.yml: -------------------------------------------------------------------------------- 1 | "pt-BR": 2 | customer_title: Cliente 3 | label_customer: Cliente 4 | label_customer_plural: Lista de clientes 5 | label_customer_information: Dados do cliente 6 | label_customer_edit_information: Editando informações do cliente 7 | label_customer_assign_to_project: Atribuir cliente a projeto 8 | label_customer_view: Visualizar 9 | label_customer_assign: Atribuir 10 | label_customer_list: Listar 11 | label_customer_new: Novo 12 | field_customer_name: Nome 13 | field_customer_company: Empresa 14 | field_customer_address: Endereço 15 | field_customer_phone: Telefone 16 | field_customer_email: Email 17 | field_customer_website: Website 18 | field_customer_skype: Skype 19 | notice_successful_save: Salvo 20 | notice_unsuccessful_save: Não foi possível salvar -------------------------------------------------------------------------------- /customer_plugin.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec` 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{customer_plugin} 8 | s.version = "0.2.0" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Eric Davis"] 12 | s.date = %q{2009-10-14} 13 | s.description = %q{This is a plugin for Redmine that can be used to track basic customer information} 14 | s.email = %q{edavis@littlestreamsoftware.com} 15 | s.extra_rdoc_files = [ 16 | "README.rdoc" 17 | ] 18 | s.files = [ 19 | "COPYRIGHT.txt", 20 | "CREDITS.txt", 21 | "GPL.txt", 22 | "README.rdoc", 23 | "Rakefile", 24 | "TAGS", 25 | "VERSION", 26 | "app/controllers/customers_controller.rb", 27 | "app/models/customer.rb", 28 | "app/views/customers/_form.html.erb", 29 | "app/views/customers/edit.html.erb", 30 | "app/views/customers/list.html.erb", 31 | "app/views/customers/new.html.erb", 32 | "app/views/customers/select.html.erb", 33 | "app/views/customers/show.html.erb", 34 | "config/locales/ca.yml", 35 | "config/locales/de.yml", 36 | "config/locales/en.yml", 37 | "config/locales/es.yml", 38 | "config/locales/fr.yml", 39 | "config/locales/it.yml", 40 | "init.rb", 41 | "lang/ca.yml", 42 | "lang/de.yml", 43 | "lang/en.yml", 44 | "lang/es.yml", 45 | "lang/fr.yml", 46 | "lang/it.yml", 47 | "lib/empty", 48 | "rails/init.rb" 49 | ] 50 | s.homepage = %q{https://projects.littlestreamsoftware.com/projects/TODO} 51 | s.rdoc_options = ["--charset=UTF-8"] 52 | s.require_paths = ["lib"] 53 | s.rubyforge_project = %q{customer_plugin} 54 | s.rubygems_version = %q{1.3.5} 55 | s.summary = %q{This is a plugin for Redmine that can be used to track basic customer information} 56 | s.test_files = [ 57 | "spec/spec_helper.rb", 58 | "spec/models/customer_spec.rb", 59 | "spec/controllers/customers_controller_spec.rb", 60 | "spec/sanity_spec.rb" 61 | ] 62 | 63 | if s.respond_to? :specification_version then 64 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 65 | s.specification_version = 3 66 | 67 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then 68 | else 69 | end 70 | else 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /db/migrate/001_create_customers.rb: -------------------------------------------------------------------------------- 1 | # Use rake db:migrate_plugins to migrate installed plugins 2 | class CreateCustomers < ActiveRecord::Migration 3 | def self.up 4 | create_table :customers do |t| 5 | t.column :name, :string 6 | t.column :company, :string 7 | t.column :address, :text 8 | t.column :phone, :string 9 | t.column :email, :string 10 | t.column :website, :string 11 | end 12 | end 13 | 14 | def self.down 15 | drop_table :customers 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/002_link_customers_to_projects.rb: -------------------------------------------------------------------------------- 1 | # Use rake db:migrate_plugins to migrate installed plugins 2 | class LinkCustomersToProjects < ActiveRecord::Migration 3 | def self.up 4 | add_column :projects, :customer_id, :integer 5 | end 6 | 7 | def self.down 8 | remove_column :projects, :customer_id 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/003_add_skype_contact.rb: -------------------------------------------------------------------------------- 1 | # Use rake db:migrate_plugins to migrate installed plugins 2 | class AddSkypeContact < ActiveRecord::Migration 3 | def self.up 4 | add_column :customers, :skype_name, :string 5 | end 6 | 7 | def self.down 8 | remove_column :customers, :skype_name 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + "/rails/init" 2 | -------------------------------------------------------------------------------- /lang/ca.yml: -------------------------------------------------------------------------------- 1 | customer_title: Clients 2 | label_customer: Client 3 | label_customer_plural: Llista de clients 4 | label_customer_information: Informació de clients 5 | label_customer_edit_information: Edita informaci&ocuate; de client 6 | label_customer_assign_to_project: Assigna client a projecte 7 | label_customer_view: Veure 8 | label_customer_assign: Assigna 9 | label_customer_list: Llista 10 | label_customer_new: Nou 11 | field_customer_name: Nom 12 | field_customer_company: Empresa 13 | field_customer_address: Addreça 14 | field_customer_phone: Telèfon 15 | field_customer_email: Correu electrònic 16 | field_customer_website: Pàgina web 17 | field_customer_skype: Skype 18 | notice_successful_save: Desat 19 | notice_unsuccessful_save: No s'ha pogut desar 20 | -------------------------------------------------------------------------------- /lang/de.yml: -------------------------------------------------------------------------------- 1 | customer_title: Kunde 2 | label_customer: Kunde 3 | label_customer_plural: Kundenliste 4 | label_customer_information: Kundendaten 5 | label_customer_edit_information: Kundendatenbearbeiten 6 | label_customer_assign_to_project: Kunden einem Projekt zuordnen 7 | label_customer_view: Anzeigen 8 | label_customer_assign: Zuweisen 9 | label_customer_list: Auflisten 10 | label_customer_new: Neu 11 | field_customer_name: Name 12 | field_customer_company: Firma 13 | field_customer_address: Adresse 14 | field_customer_phone: Telefon 15 | field_customer_email: Email 16 | field_customer_website: Website 17 | field_customer_skype: Skype 18 | notice_successful_save: Gespeichert 19 | notice_unsuccessful_save: Fehler beim Speichern 20 | -------------------------------------------------------------------------------- /lang/en.yml: -------------------------------------------------------------------------------- 1 | customer_title: Customer 2 | label_customer: Customer 3 | label_customer_plural: Customer List 4 | label_customer_information: Customer Information 5 | label_customer_edit_information: Editing Customer Information 6 | label_customer_assign_to_project: Assign Customer to Project 7 | label_customer_view: View 8 | label_customer_assign: Assign 9 | label_customer_list: List 10 | label_customer_new: New 11 | field_customer_name: Name 12 | field_customer_company: Company 13 | field_customer_address: Address 14 | field_customer_phone: Phone 15 | field_customer_email: Email 16 | field_customer_website: Website 17 | field_customer_skype: Skype 18 | notice_successful_save: Saved 19 | notice_unsuccessful_save: Could not save 20 | -------------------------------------------------------------------------------- /lang/es.yml: -------------------------------------------------------------------------------- 1 | customer_title: Clientes 2 | label_customer: Customer 3 | label_customer_plural: Listado de Clientes 4 | label_customer_information: Informacion del Cliente 5 | label_customer_edit_information: Editar informacion del cliente 6 | label_customer_assign_to_project: Asignar cliente a un proyecto 7 | label_customer_view: Ver 8 | label_customer_assign: Asignar 9 | label_customer_list: Listar 10 | label_customer_new: Nuevo 11 | field_customer_name: Nombre 12 | field_customer_company: Empresa 13 | field_customer_address: Direccion 14 | field_customer_phone: Telefono 15 | field_customer_email: Correo Electronico 16 | field_customer_website: Sitio Web 17 | field_customer_skype: Skype 18 | notice_successful_save: Guardado 19 | notice_unsuccessful_save: No se pudo guardar 20 | -------------------------------------------------------------------------------- /lang/fr.yml: -------------------------------------------------------------------------------- 1 | customer_title: Client 2 | label_customer: Client 3 | label_customer_plural: Liste des clients 4 | label_customer_information: Informations 5 | label_customer_edit_information: Modifier les informations 6 | label_customer_assign_to_project: Assigner un client au projet 7 | label_customer_view: Voir 8 | label_customer_assign: Assigner 9 | label_customer_list: Liste 10 | label_customer_new: Nouveau 11 | field_customer_name: Nom 12 | field_customer_company: Entreprise 13 | field_customer_address: Adresse 14 | field_customer_phone: Téléphone 15 | field_customer_email: Email 16 | field_customer_website: Site Web 17 | field_customer_skype: Skype 18 | notice_successful_save: Enregistré 19 | notice_unsuccessful_save: Enregistrement non effectué 20 | -------------------------------------------------------------------------------- /lang/it.yml: -------------------------------------------------------------------------------- 1 | customer_title: Cliente 2 | label_customer: Cliente 3 | label_customer_plural: Elenco clienti 4 | label_customer_information: Dettaglio cliente 5 | label_customer_edit_information: Modifica dettaglio cliente 6 | label_customer_assign_to_project: Assegna cliente al progetto 7 | label_customer_view: Mostra dettaglio 8 | label_customer_assign: Assegna 9 | label_customer_list: Elenco clienti 10 | label_customer_new: Nuovo cliente 11 | field_customer_name: Nominativo 12 | field_customer_company: Azienda 13 | field_customer_address: Indirizzo 14 | field_customer_phone: Telefono 15 | field_customer_email: Email 16 | field_customer_website: Sito web 17 | field_customer_skype: Skype 18 | notice_successful_save: Salvato con successo 19 | notice_unsuccessful_save: Impossibile salvare 20 | project_module_customer_module: Clienti 21 | permission_view_customer: Vedi cliente 22 | permission_assign_customer: Assegna cliente 23 | permission_see_customer_list: Vedi lista clienti 24 | permission_edit_customer: Modifica cliente -------------------------------------------------------------------------------- /lang/pt-br.yml: -------------------------------------------------------------------------------- 1 | customer_title: Cliente 2 | label_customer: Cliente 3 | label_customer_plural: Lista de clientes 4 | label_customer_information: Dados do cliente 5 | label_customer_edit_information: Editando informações do cliente 6 | label_customer_assign_to_project: Atribuir cliente a projeto 7 | label_customer_view: Visualizar 8 | label_customer_assign: Atribuir 9 | label_customer_list: Listar 10 | label_customer_new: Novo 11 | field_customer_name: Nome 12 | field_customer_company: Empresa 13 | field_customer_address: Endereço 14 | field_customer_phone: Telefone 15 | field_customer_email: Email 16 | field_customer_website: Website 17 | field_customer_skype: Skype 18 | notice_successful_save: Salvo 19 | notice_unsuccessful_save: Não foi possível salvar -------------------------------------------------------------------------------- /lib/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edavis10/redmine-customer-plugin/10e5e6757e6a2c2d5b9523a06018c7645c2380f1/lib/empty -------------------------------------------------------------------------------- /rails/init.rb: -------------------------------------------------------------------------------- 1 | # Redmine customer plugin 2 | require 'redmine' 3 | 4 | RAILS_DEFAULT_LOGGER.info 'Starting Customer plugin for RedMine' 5 | 6 | Redmine::Plugin.register :customer_plugin do 7 | name 'Customer plugin' 8 | author 'Eric Davis' 9 | description 'This is a plugin for Redmine that can be used to track basic customer information' 10 | version '0.2.0' 11 | 12 | url 'https://projects.littlestreamsoftware.com/projects/redmine-customers' if respond_to? :url 13 | author_url 'http://www.littlestreamsoftware.com' if respond_to? :author_url 14 | 15 | 16 | project_module :customer_module do 17 | permission :view_customer, {:customers => [:show]} 18 | permission :assign_customer, {:customers => [:assign, :select]} 19 | permission :see_customer_list, {:customers => [:list]} 20 | permission :edit_customer, {:customers => [:edit, :update, :new, :create, :destroy]} 21 | end 22 | 23 | menu :project_menu, :customers, {:controller => 'customers', :action => 'show'}, :caption => :customer_title 24 | end 25 | -------------------------------------------------------------------------------- /spec/controllers/customers_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | -------------------------------------------------------------------------------- /spec/models/customer_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe Customer do 4 | before(:each) do 5 | @customer = Customer.new 6 | end 7 | 8 | it 'should not be valid without a name or company' do 9 | @customer.should_not be_valid 10 | end 11 | 12 | it 'should require a name if company is not set' do 13 | @customer.should_not be_valid 14 | @customer.errors.on(:name).should_not be_empty 15 | end 16 | 17 | it 'should require a company if name is not set' do 18 | @customer.should_not be_valid 19 | @customer.errors.on(:company).should_not be_empty 20 | end 21 | 22 | it 'should be valid if name is set but company is missing' do 23 | @customer.name = "John Customer" 24 | @customer.should be_valid 25 | end 26 | 27 | it 'should be valid if company is set but name is missing' do 28 | @customer.company = "ABC Company" 29 | @customer.should be_valid 30 | end 31 | 32 | it '#name should be unique within a company' do 33 | @customer.name = 'John Customer' 34 | @customer.company = "ABC Company" 35 | @customer.save! 36 | 37 | @conflict_customer = Customer.new( :name => 'John Customer', :company => "ABC Company") 38 | @conflict_customer.should_not be_valid 39 | @conflict_customer.errors.on(:name).should_not be_empty 40 | end 41 | 42 | describe 'with an invalid email' do 43 | before(:each) do 44 | @customer.attributes = { :name => 'John Customer', :company => "ABC Company"} 45 | end 46 | 47 | it 'would have more than one @' do 48 | @customer.email = "john@ABC@com" 49 | @customer.should_not be_valid 50 | end 51 | 52 | it 'would have no characters before @' do 53 | @customer.email = "@com" 54 | @customer.should_not be_valid 55 | end 56 | 57 | it 'would have no characters after @' do 58 | @customer.email = "john@" 59 | @customer.should_not be_valid 60 | end 61 | 62 | it 'would have non-alphanumerics for the domain' do 63 | @customer.email = "john@ABC,DEF.com" 64 | @customer.should_not be_valid 65 | end 66 | 67 | it 'should have a TLD at least 2 characters long' do 68 | @customer.email = "john@ABC.c" 69 | @customer.should_not be_valid 70 | end 71 | 72 | end 73 | 74 | describe 'with a valid email' do 75 | it 'can be blank' do 76 | @customer.attributes = { :name => 'John Customer', :company => "ABC Company"} 77 | @customer.email = "" 78 | @customer.should be_valid 79 | end 80 | 81 | it 'can be nil' do 82 | @customer.attributes = { :name => 'John Customer', :company => "ABC Company"} 83 | @customer.email = nil 84 | @customer.should be_valid 85 | end 86 | 87 | it 'should allow subdomains' do 88 | @customer.attributes = { :name => 'John Customer', :company => "ABC Company"} 89 | @customer.email = "john@sales.abc.com" 90 | @customer.should be_valid 91 | end 92 | 93 | it 'should allow a TLD with at least 2 characters' do 94 | @customer.attributes = { :name => 'John Customer', :company => "ABC Company"} 95 | @customer.email = "john@abc.us" 96 | @customer.should be_valid 97 | end 98 | end 99 | 100 | end 101 | 102 | describe Customer do 103 | it 'should have many projects' do 104 | association = Customer.reflect_on_association(:projects) 105 | association.should_not be_nil 106 | association.name.should eql(:projects) 107 | association.macro.should eql(:has_many) 108 | end 109 | end 110 | 111 | describe Customer, '.pretty_name' do 112 | it 'should return the name of the Customer' do 113 | @customer = Customer.new( :name => 'John Customer') 114 | @customer.pretty_name.should eql('John Customer') 115 | end 116 | 117 | it 'should add the company to the name if company is present' do 118 | @customer = Customer.new( :name => 'John Customer', :company => 'ABC Company') 119 | @customer.pretty_name.should match(/ABC Company/) 120 | end 121 | 122 | it 'should return only the company name if name is missing' do 123 | @customer = Customer.new( :company => 'ABC Company') 124 | @customer.pretty_name.should match(/ABC Company/) 125 | end 126 | 127 | it 'should separate the customer name from the company name by a space and comma if both are present' do 128 | @customer = Customer.new( :name => 'John Customer', :company => 'ABC Company') 129 | @customer.pretty_name.should eql("John Customer, ABC Company") 130 | end 131 | end 132 | -------------------------------------------------------------------------------- /spec/rcov.opts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edavis10/redmine-customer-plugin/10e5e6757e6a2c2d5b9523a06018c7645c2380f1/spec/rcov.opts -------------------------------------------------------------------------------- /spec/sanity_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/spec_helper' 2 | 3 | describe Class do 4 | it "should be a class of Class" do 5 | Class.class.should eql(Class) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --colour 2 | --format 3 | progress 4 | --loadby 5 | mtime 6 | --reverse 7 | --backtrace -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to ~/spec when you run 'ruby script/generate rspec' 2 | # from the project root directory. 3 | ENV["RAILS_ENV"] = "test" 4 | 5 | # Allows loading of an environment config based on the environment 6 | redmine_root = ENV["REDMINE_ROOT"] || File.dirname(__FILE__) + "/../../../.." 7 | require File.expand_path(redmine_root + "/config/environment") 8 | 9 | require 'spec' 10 | require 'spec/rails' 11 | 12 | Spec::Runner.configure do |config| 13 | # If you're not using ActiveRecord you should remove these 14 | # lines, delete config/database.yml and disable :active_record 15 | # in your config/boot.rb 16 | config.use_transactional_fixtures = true 17 | config.use_instantiated_fixtures = false 18 | config.fixture_path = RAILS_ROOT + '/spec/fixtures/' 19 | 20 | # == Fixtures 21 | # 22 | # You can declare fixtures for each example_group like this: 23 | # describe "...." do 24 | # fixtures :table_a, :table_b 25 | # 26 | # Alternatively, if you prefer to declare them only once, you can 27 | # do so right here. Just uncomment the next line and replace the fixture 28 | # names with your fixtures. 29 | # 30 | # config.global_fixtures = :table_a, :table_b 31 | # 32 | # If you declare global fixtures, be aware that they will be declared 33 | # for all of your examples, even those that don't use them. 34 | # 35 | # == Mock Framework 36 | # 37 | # RSpec uses it's own mocking framework by default. If you prefer to 38 | # use mocha, flexmock or RR, uncomment the appropriate line: 39 | # 40 | # config.mock_with :mocha 41 | # config.mock_with :flexmock 42 | # config.mock_with :rr 43 | end 44 | 45 | # require the entire app if we're running under coverage testing, 46 | # so we measure 0% covered files in the report 47 | # 48 | # http://www.pervasivecode.com/blog/2008/05/16/making-rcov-measure-your-whole-rails-app-even-if-tests-miss-entire-source-files/ 49 | if defined?(Rcov) 50 | all_app_files = Dir.glob('{app,lib}/**/*.rb') 51 | all_app_files.each{|rb| require rb} 52 | end 53 | -------------------------------------------------------------------------------- /spec/views/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edavis10/redmine-customer-plugin/10e5e6757e6a2c2d5b9523a06018c7645c2380f1/spec/views/empty -------------------------------------------------------------------------------- /writeup.txt: -------------------------------------------------------------------------------- 1 | * Copy the sample plugin 2 | * Clear out files 3 | 4 | . 5 | |-- README 6 | |-- app 7 | | |-- controllers 8 | | |-- models 9 | | `-- views 10 | | `-- settings 11 | |-- assets 12 | | |-- images 13 | | `-- stylesheets 14 | |-- db 15 | | `-- migrate 16 | |-- init.rb 17 | `-- lang 18 | 19 | * Configure your basic init file 20 | 21 |
 22 | # Redmine customer plugin
 23 | require 'redmine'
 24 | 
 25 | RAILS_DEFAULT_LOGGER.info 'Starting Customer plugin for RedMine'
 26 | 
 27 | Redmine::Plugin.register :customer do
 28 |   name 'Customer plugin'
 29 |   author 'Eric Davis'
 30 |   description 'This is a plugin for Redmine that can be used to track basic customer information'
 31 |   version '0.1.0'
 32 | end
 33 | 
34 | 35 | * Build your migration 36 | * Migrate your database `rake db:migrate_plugins` 37 | * Build a very basic Model so we can connect to the database 38 | 39 |
 40 | class Customer < ActiveRecord::Base
 41 | end
 42 | 
43 | 44 | * Lets try the console and see if we can create a few customers 45 | 46 |
 47 | $ script/console
 48 | Loading development environment (Rails 2.0.2)
 49 | GLoc v1.1 running in development mode. Strings can be modified at runtime.
 50 | >> Customer.create({:name => 'Eric Davis', :company => 'Little Stream Software', :address => 'Beaverton, OR', :phone => '888.499.2510', :website => 'http://www.littlestreamsoftware.com'})
 51 | => #
 52 | >> Customer.create({:name => 'Linux Torvalds', :company => 'Linux Foundation', :address => 'Portland, OR'})
 53 | => #
 54 | >> exit
 55 | 
56 | 57 | * Lets hook up a controller to see who is assigned to a project. Index would work best 58 | 59 |
 60 | class CustomersController < ApplicationController
 61 |   unloadable
 62 |   layout 'base'
 63 |   before_filter :find_project, :authorize
 64 | 
 65 |   def index
 66 |   end
 67 | 
 68 |   private
 69 |   def find_project
 70 |     @project=Project.find(params[:id])
 71 |   end
 72 | end
 73 | 
74 | 75 | * Need to add it to the Redmine init file now 76 | 77 |
 78 | # Redmine customer plugin
 79 | require 'redmine'
 80 | 
 81 | RAILS_DEFAULT_LOGGER.info 'Starting Customer plugin for RedMine'
 82 | 
 83 | Redmine::Plugin.register :customer do
 84 |   name 'Customer plugin'
 85 |   author 'Eric Davis'
 86 |   description 'This is a plugin for Redmine that can be used to track basic customer information'
 87 |   version '0.1.0'
 88 |   
 89 |   project_module :customer_module do
 90 |     permission :index, {:customers => [:index]}, :public => true
 91 |   end
 92 | 
 93 |   menu :project_menu, "Customer", :controller => 'customers', :action => 'index'
 94 | end
 95 | 
96 | 97 | * Now we need a view file to render 98 | mkdir app/views/customers 99 | 100 |
101 | #app/views/customers/index.html.erb 
102 | 

Customer

103 | 104 |
105 | 106 | * Opps, we are missing the FKs from customers to projects. Since we can't modify the Project's file, we can only setup a HABTM relationship from one side. 107 | 108 |
109 | # Use rake db:migrate_plugins to migrate installed plugins
110 | class LinkCustomersToProjects < ActiveRecord::Migration
111 |   def self.up
112 |     add_column :projects, :customer_id, :integer    
113 |   end
114 | 
115 |   def self.down
116 |     remove_column :projects, :customer_id
117 |   end
118 | end
119 | 
120 | 121 |
122 | class Customer < ActiveRecord::Base
123 |   has_many :projects
124 | end
125 | 
126 | 127 | * Migrate again to add the column 128 | * Lets assign the customer to my project by hand. My customer id is 2 129 | 130 | >> p = Project.find(16) 131 | => # 132 | >> p.customer_id = 2 133 | => 2 134 | >> p.save 135 | => true 136 | 137 | * Lets display it on our page now 138 | 139 |
140 | class CustomersController < ApplicationController
141 |   unloadable
142 |   layout 'base'
143 |   before_filter :find_project, :authorize
144 | 
145 |   def index
146 |     @customer = Customer.find_by_id(@project.customer_id)
147 |   end
148 | 
149 |   private
150 |   def find_project
151 |     @project=Project.find(params[:id])
152 |   end
153 | end
154 | 
155 | 156 |
157 | 

Customer

158 | 159 |

Name: <%= h @customer.name %>

160 |

Company: <%= h @customer.company %>

161 |

Address: <%= h @customer.address %>

162 |

Phone: <%= h @customer.phone %>

163 |

Website: <%= h @customer.website %>

164 | 165 |
166 | (((redmine_customer_1.png))) 167 | 168 | * It would be nice to be able to reassign the customer 169 |
170 | init.rb
171 |     permission :edit, {:customers => [:edit]}, :public => true
172 | 
173 | 174 |
175 |   def edit
176 |     case request.method
177 |     when :post
178 |       # Will fill out later
179 |     when :get
180 |       @customer = Customer.find_by_id(@project.customer_id)
181 |     end
182 |   end
183 | 
184 | 185 |
186 | 

Editing Customer

187 | 188 | <% form_for :customer, :url =>{:action => 'edit', :id => @project} do |f| %> 189 | 190 |

191 |
192 | <%= collection_select(:customer, :id, Customer.find(:all), :id, :name) -%> 193 |

194 | 195 | <%= submit_tag "Save" -%> 196 | 197 | <% end # form -%> 198 | 199 |
200 | 201 | * Now to hook up the reassignments 202 | 203 |
204 |   def edit
205 |     case request.method
206 |     when :post
207 |       @project.customer_id = params[:customer][:id]
208 |       if @project.save
209 |         flash[:notice] = "Saved"
210 |         redirect_to :action => "index", :id => params[:id]
211 |       else
212 |         flash[:notice] = "Could not save"
213 |       end
214 |     when :get
215 |       @customer = Customer.find_by_id(@project.customer_id)
216 |     end
217 |   end
218 | 
219 | 220 | * Add the ability to list and edit customer information itself 221 | 222 | project_module :customer_module do 223 | permission :index, {:customers => [:index]}, :public => true 224 | permission :edit, {:customers => [:edit]}, :public => true 225 | permission :list, {:customers => [:list]}, :public => true 226 | permission :edit_info, {:customers => [:edit_info]}, :public => true 227 | end 228 | 229 |

<%= link_to "Edit", :controller => 'customers', :action => 'edit', :id => @project -%>

230 |

<%= link_to "List", :controller => 'customers', :action => 'list', :id => @project -%>

231 | 232 | 233 | def list 234 | @customers = Customer.find(:all) 235 | end 236 | 237 |

Customer List

238 | 239 |
    240 | <% @customers.each do |customer| -%> 241 |
  • <%= link_to customer.name, 'edit_info', :id => @project, :customer_id => customer.id -%> from <%= h customer.company -%>
  • 242 | <% end -%> 243 |
244 | 245 | * Editing a customer record 246 | 247 | def edit_info 248 | case request.method 249 | when :post 250 | # Will add later 251 | when :get 252 | @customer = Customer.find_by_id(params[:customer_id]) 253 | end 254 | end 255 | 256 |

Editing Customer Information

257 | 258 | <% form_for :customer, :url =>{:action => 'edit_info', :id => @project} do |f| %> 259 | 260 | <%= f.hidden_field 'id' -%> 261 | 262 |

263 |
264 | <%= f.text_field 'name' -%> 265 |

266 |

267 |
268 | <%= f.text_field 'company' -%> 269 |

270 |

271 |
272 | <%= f.text_area 'address' -%> 273 |

274 |

275 |
276 | <%= f.text_field 'phone' -%> 277 |

278 |

279 |
280 | <%= f.text_field 'email' -%> 281 |

282 |

283 |
284 | <%= f.text_field 'website' -%> 285 |

286 | 287 | 288 | <%= submit_tag "Save" -%> 289 | 290 | <% end # form -%> 291 | 292 | * Saving an edited customer record 293 | 294 | def edit_info 295 | case request.method 296 | when :post 297 | @customer = Customer.find_by_id(params[:customer][:id]) 298 | if @customer.nil? 299 | # New record 300 | @customer = Customer.new(params[:customer]) 301 | end 302 | if @customer.update_attributes(params[:customer]) 303 | flash[:notice] = "Saved" 304 | redirect_to :action => "list", :id => params[:id] 305 | else 306 | flash[:notice] = "Could not save" 307 | end 308 | 309 | when :get 310 | @customer = Customer.find_by_id(params[:customer_id]) 311 | end 312 | end 313 | 314 | * Adding a new customer 315 | 316 | <%= link_to "New Customer", :action => 'edit_info', :id => @project %> 317 | 318 | * Security 319 | 320 | project_module :customer_module do 321 | permission :view_customer, {:customers => [:index]} 322 | permission :assign_customer, {:customers => [:edit]} 323 | permission :see_customer_list, {:customers => [:list]} 324 | permission :edit_customer, {:customers => [:edit_info]} 325 | end 326 | 327 | link_to_if_authorized 328 | 329 | * Simple menu 330 | 331 | <%= render(:partial => 'menu') %> 332 | 333 | 334 |

335 | Menu: 336 | <%= link_to_if_authorized "View", :controller => 'customers', :action => 'index', :id => @project -%> 337 | <%= link_to_if_authorized "Assign", :controller => 'customers', :action => 'edit', :id => @project -%> 338 | <%= link_to_if_authorized "List", :controller => 'customers', :action => 'list', :id => @project -%> 339 | <%= link_to_if_authorized "New", :action => 'edit_info', :id => @project %> 340 |

341 | --------------------------------------------------------------------------------