Revision 1298:4f746d8966dd .svn/pristine/10

View differences:

.svn/pristine/10/1048cea83e08be09288d325f5a2d1908b807f592.svn-base
1
# Redmine - project management software
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

  
18
module Redmine
19
  module Helpers
20
    # Simple class to handle gantt chart data
21
    class Gantt
22
      include ERB::Util
23
      include Redmine::I18n
24

  
25
      # :nodoc:
26
      # Some utility methods for the PDF export
27
      class PDF
28
        MaxCharactorsForSubject = 45
29
        TotalWidth = 280
30
        LeftPaneWidth = 100
31

  
32
        def self.right_pane_width
33
          TotalWidth - LeftPaneWidth
34
        end
35
      end
36

  
37
      attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months, :truncated, :max_rows
38
      attr_accessor :query
39
      attr_accessor :project
40
      attr_accessor :view
41

  
42
      def initialize(options={})
43
        options = options.dup
44

  
45
        if options[:year] && options[:year].to_i >0
46
          @year_from = options[:year].to_i
47
          if options[:month] && options[:month].to_i >=1 && options[:month].to_i <= 12
48
            @month_from = options[:month].to_i
49
          else
50
            @month_from = 1
51
          end
52
        else
53
          @month_from ||= Date.today.month
54
          @year_from ||= Date.today.year
55
        end
56

  
57
        zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i
58
        @zoom = (zoom > 0 && zoom < 5) ? zoom : 2
59
        months = (options[:months] || User.current.pref[:gantt_months]).to_i
60
        @months = (months > 0 && months < 25) ? months : 6
61

  
62
        # Save gantt parameters as user preference (zoom and months count)
63
        if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
64
          User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
65
          User.current.preference.save
66
        end
67

  
68
        @date_from = Date.civil(@year_from, @month_from, 1)
69
        @date_to = (@date_from >> @months) - 1
70

  
71
        @subjects = ''
72
        @lines = ''
73
        @number_of_rows = nil
74

  
75
        @issue_ancestors = []
76

  
77
        @truncated = false
78
        if options.has_key?(:max_rows)
79
          @max_rows = options[:max_rows]
80
        else
81
          @max_rows = Setting.gantt_items_limit.blank? ? nil : Setting.gantt_items_limit.to_i
82
        end
83
      end
84

  
85
      def common_params
86
        { :controller => 'gantts', :action => 'show', :project_id => @project }
87
      end
88

  
89
      def params
90
        common_params.merge({  :zoom => zoom, :year => year_from, :month => month_from, :months => months })
91
      end
92

  
93
      def params_previous
94
        common_params.merge({:year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months })
95
      end
96

  
97
      def params_next
98
        common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months })
99
      end
100

  
101
      # Returns the number of rows that will be rendered on the Gantt chart
102
      def number_of_rows
103
        return @number_of_rows if @number_of_rows
104

  
105
        rows = projects.inject(0) {|total, p| total += number_of_rows_on_project(p)}
106
        rows > @max_rows ? @max_rows : rows
107
      end
108

  
109
      # Returns the number of rows that will be used to list a project on
110
      # the Gantt chart.  This will recurse for each subproject.
111
      def number_of_rows_on_project(project)
112
        return 0 unless projects.include?(project)
113

  
114
        count = 1
115
        count += project_issues(project).size
116
        count += project_versions(project).size
117
        count
118
      end
119

  
120
      # Renders the subjects of the Gantt chart, the left side.
121
      def subjects(options={})
122
        render(options.merge(:only => :subjects)) unless @subjects_rendered
123
        @subjects
124
      end
125

  
126
      # Renders the lines of the Gantt chart, the right side
127
      def lines(options={})
128
        render(options.merge(:only => :lines)) unless @lines_rendered
129
        @lines
130
      end
131

  
132
      # Returns issues that will be rendered
133
      def issues
134
        @issues ||= @query.issues(
135
          :include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
136
          :order => "#{Project.table_name}.lft ASC, #{Issue.table_name}.id ASC",
137
          :limit => @max_rows
138
        )
139
      end
140

  
141
      # Return all the project nodes that will be displayed
142
      def projects
143
        return @projects if @projects
144

  
145
        ids = issues.collect(&:project).uniq.collect(&:id)
146
        if ids.any?
147
          # All issues projects and their visible ancestors
148
          @projects = Project.visible.all(
149
            :joins => "LEFT JOIN #{Project.table_name} child ON #{Project.table_name}.lft <= child.lft AND #{Project.table_name}.rgt >= child.rgt",
150
            :conditions => ["child.id IN (?)", ids],
151
            :order => "#{Project.table_name}.lft ASC"
152
          ).uniq
153
        else
154
          @projects = []
155
        end
156
      end
157

  
158
      # Returns the issues that belong to +project+
159
      def project_issues(project)
160
        @issues_by_project ||= issues.group_by(&:project)
161
        @issues_by_project[project] || []
162
      end
163

  
164
      # Returns the distinct versions of the issues that belong to +project+
165
      def project_versions(project)
166
        project_issues(project).collect(&:fixed_version).compact.uniq
167
      end
168

  
169
      # Returns the issues that belong to +project+ and are assigned to +version+
170
      def version_issues(project, version)
171
        project_issues(project).select {|issue| issue.fixed_version == version}
172
      end
173

  
174
      def render(options={})
175
        options = {:top => 0, :top_increment => 20, :indent_increment => 20, :render => :subject, :format => :html}.merge(options)
176
        indent = options[:indent] || 4
177

  
178
        @subjects = '' unless options[:only] == :lines
179
        @lines = '' unless options[:only] == :subjects
180
        @number_of_rows = 0
181

  
182
        Project.project_tree(projects) do |project, level|
183
          options[:indent] = indent + level * options[:indent_increment]
184
          render_project(project, options)
185
          break if abort?
186
        end
187

  
188
        @subjects_rendered = true unless options[:only] == :lines
189
        @lines_rendered = true unless options[:only] == :subjects
190

  
191
        render_end(options)
192
      end
193

  
194
      def render_project(project, options={})
195
        subject_for_project(project, options) unless options[:only] == :lines
196
        line_for_project(project, options) unless options[:only] == :subjects
197

  
198
        options[:top] += options[:top_increment]
199
        options[:indent] += options[:indent_increment]
200
        @number_of_rows += 1
201
        return if abort?
202

  
203
        issues = project_issues(project).select {|i| i.fixed_version.nil?}
204
        sort_issues!(issues)
205
        if issues
206
          render_issues(issues, options)
207
          return if abort?
208
        end
209

  
210
        versions = project_versions(project)
211
        versions.each do |version|
212
          render_version(project, version, options)
213
        end
214

  
215
        # Remove indent to hit the next sibling
216
        options[:indent] -= options[:indent_increment]
217
      end
218

  
219
      def render_issues(issues, options={})
220
        @issue_ancestors = []
221

  
222
        issues.each do |i|
223
          subject_for_issue(i, options) unless options[:only] == :lines
224
          line_for_issue(i, options) unless options[:only] == :subjects
225

  
226
          options[:top] += options[:top_increment]
227
          @number_of_rows += 1
228
          break if abort?
229
        end
230

  
231
        options[:indent] -= (options[:indent_increment] * @issue_ancestors.size)
232
      end
233

  
234
      def render_version(project, version, options={})
235
        # Version header
236
        subject_for_version(version, options) unless options[:only] == :lines
237
        line_for_version(version, options) unless options[:only] == :subjects
238

  
239
        options[:top] += options[:top_increment]
240
        @number_of_rows += 1
241
        return if abort?
242

  
243
        issues = version_issues(project, version)
244
        if issues
245
          sort_issues!(issues)
246
          # Indent issues
247
          options[:indent] += options[:indent_increment]
248
          render_issues(issues, options)
249
          options[:indent] -= options[:indent_increment]
250
        end
251
      end
252

  
253
      def render_end(options={})
254
        case options[:format]
255
        when :pdf
256
          options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
257
        end
258
      end
259

  
260
      def subject_for_project(project, options)
261
        case options[:format]
262
        when :html
263
          subject = "<span class='icon icon-projects #{project.overdue? ? 'project-overdue' : ''}'>".html_safe
264
          subject << view.link_to_project(project).html_safe
265
          subject << '</span>'.html_safe
266
          html_subject(options, subject, :css => "project-name")
267
        when :image
268
          image_subject(options, project.name)
269
        when :pdf
270
          pdf_new_page?(options)
271
          pdf_subject(options, project.name)
272
        end
273
      end
274

  
275
      def line_for_project(project, options)
276
        # Skip versions that don't have a start_date or due date
277
        if project.is_a?(Project) && project.start_date && project.due_date
278
          options[:zoom] ||= 1
279
          options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
280

  
281
          coords = coordinates(project.start_date, project.due_date, nil, options[:zoom])
282
          label = h(project)
283

  
284
          case options[:format]
285
          when :html
286
            html_task(options, coords, :css => "project task", :label => label, :markers => true)
287
          when :image
288
            image_task(options, coords, :label => label, :markers => true, :height => 3)
289
          when :pdf
290
            pdf_task(options, coords, :label => label, :markers => true, :height => 0.8)
291
          end
292
        else
293
          ActiveRecord::Base.logger.debug "Gantt#line_for_project was not given a project with a start_date"
294
          ''
295
        end
296
      end
297

  
298
      def subject_for_version(version, options)
299
        case options[:format]
300
        when :html
301
          subject = "<span class='icon icon-package #{version.behind_schedule? ? 'version-behind-schedule' : ''} #{version.overdue? ? 'version-overdue' : ''}'>".html_safe
302
          subject << view.link_to_version(version).html_safe
303
          subject << '</span>'.html_safe
304
          html_subject(options, subject, :css => "version-name")
305
        when :image
306
          image_subject(options, version.to_s_with_project)
307
        when :pdf
308
          pdf_new_page?(options)
309
          pdf_subject(options, version.to_s_with_project)
310
        end
311
      end
312

  
313
      def line_for_version(version, options)
314
        # Skip versions that don't have a start_date
315
        if version.is_a?(Version) && version.start_date && version.due_date
316
          options[:zoom] ||= 1
317
          options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
318

  
319
          coords = coordinates(version.start_date, version.due_date, version.completed_pourcent, options[:zoom])
320
          label = "#{h version } #{h version.completed_pourcent.to_i.to_s}%"
321
          label = h("#{version.project} -") + label unless @project && @project == version.project
322

  
323
          case options[:format]
324
          when :html
325
            html_task(options, coords, :css => "version task", :label => label, :markers => true)
326
          when :image
327
            image_task(options, coords, :label => label, :markers => true, :height => 3)
328
          when :pdf
329
            pdf_task(options, coords, :label => label, :markers => true, :height => 0.8)
330
          end
331
        else
332
          ActiveRecord::Base.logger.debug "Gantt#line_for_version was not given a version with a start_date"
333
          ''
334
        end
335
      end
336

  
337
      def subject_for_issue(issue, options)
338
        while @issue_ancestors.any? && !issue.is_descendant_of?(@issue_ancestors.last)
339
          @issue_ancestors.pop
340
          options[:indent] -= options[:indent_increment]
341
        end
342

  
343
        output = case options[:format]
344
        when :html
345
          css_classes = ''
346
          css_classes << ' issue-overdue' if issue.overdue?
347
          css_classes << ' issue-behind-schedule' if issue.behind_schedule?
348
          css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
349

  
350
          subject = "<span class='#{css_classes}'>".html_safe
351
          if issue.assigned_to.present?
352
            assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
353
            subject << view.avatar(issue.assigned_to, :class => 'gravatar icon-gravatar', :size => 10, :title => assigned_string).to_s.html_safe
354
          end
355
          subject << view.link_to_issue(issue).html_safe
356
          subject << '</span>'.html_safe
357
          html_subject(options, subject, :css => "issue-subject", :title => issue.subject) + "\n"
358
        when :image
359
          image_subject(options, issue.subject)
360
        when :pdf
361
          pdf_new_page?(options)
362
          pdf_subject(options, issue.subject)
363
        end
364

  
365
        unless issue.leaf?
366
          @issue_ancestors << issue
367
          options[:indent] += options[:indent_increment]
368
        end
369

  
370
        output
371
      end
372

  
373
      def line_for_issue(issue, options)
374
        # Skip issues that don't have a due_before (due_date or version's due_date)
375
        if issue.is_a?(Issue) && issue.due_before
376
          coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom])
377
          label = "#{ issue.status.name } #{ issue.done_ratio }%"
378

  
379
          case options[:format]
380
          when :html
381
            html_task(options, coords, :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), :label => label, :issue => issue, :markers => !issue.leaf?)
382
          when :image
383
            image_task(options, coords, :label => label)
384
          when :pdf
385
            pdf_task(options, coords, :label => label)
386
        end
387
        else
388
          ActiveRecord::Base.logger.debug "GanttHelper#line_for_issue was not given an issue with a due_before"
389
          ''
390
        end
391
      end
392

  
393
      # Generates a gantt image
394
      # Only defined if RMagick is avalaible
395
      def to_image(format='PNG')
396
        date_to = (@date_from >> @months)-1
397
        show_weeks = @zoom > 1
398
        show_days = @zoom > 2
399

  
400
        subject_width = 400
401
        header_height = 18
402
        # width of one day in pixels
403
        zoom = @zoom*2
404
        g_width = (@date_to - @date_from + 1)*zoom
405
        g_height = 20 * number_of_rows + 30
406
        headers_height = (show_weeks ? 2*header_height : header_height)
407
        height = g_height + headers_height
408

  
409
        imgl = Magick::ImageList.new
410
        imgl.new_image(subject_width+g_width+1, height)
411
        gc = Magick::Draw.new
412

  
413
        # Subjects
414
        gc.stroke('transparent')
415
        subjects(:image => gc, :top => (headers_height + 20), :indent => 4, :format => :image)
416

  
417
        # Months headers
418
        month_f = @date_from
419
        left = subject_width
420
        @months.times do
421
          width = ((month_f >> 1) - month_f) * zoom
422
          gc.fill('white')
423
          gc.stroke('grey')
424
          gc.stroke_width(1)
425
          gc.rectangle(left, 0, left + width, height)
426
          gc.fill('black')
427
          gc.stroke('transparent')
428
          gc.stroke_width(1)
429
          gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}")
430
          left = left + width
431
          month_f = month_f >> 1
432
        end
433

  
434
        # Weeks headers
435
        if show_weeks
436
        	left = subject_width
437
        	height = header_height
438
        	if @date_from.cwday == 1
439
        	    # date_from is monday
440
                week_f = date_from
441
        	else
442
        	    # find next monday after date_from
443
        		week_f = @date_from + (7 - @date_from.cwday + 1)
444
        		width = (7 - @date_from.cwday + 1) * zoom
445
                gc.fill('white')
446
                gc.stroke('grey')
447
                gc.stroke_width(1)
448
                gc.rectangle(left, header_height, left + width, 2*header_height + g_height-1)
449
        		left = left + width
450
        	end
451
        	while week_f <= date_to
452
        		width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom
453
                gc.fill('white')
454
                gc.stroke('grey')
455
                gc.stroke_width(1)
456
                gc.rectangle(left.round, header_height, left.round + width, 2*header_height + g_height-1)
457
                gc.fill('black')
458
                gc.stroke('transparent')
459
                gc.stroke_width(1)
460
                gc.text(left.round + 2, header_height + 14, week_f.cweek.to_s)
461
        		left = left + width
462
        		week_f = week_f+7
463
        	end
464
        end
465

  
466
        # Days details (week-end in grey)
467
        if show_days
468
        	left = subject_width
469
        	height = g_height + header_height - 1
470
        	wday = @date_from.cwday
471
        	(date_to - @date_from + 1).to_i.times do
472
              width =  zoom
473
              gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white')
474
              gc.stroke('#ddd')
475
              gc.stroke_width(1)
476
              gc.rectangle(left, 2*header_height, left + width, 2*header_height + g_height-1)
477
              left = left + width
478
              wday = wday + 1
479
              wday = 1 if wday > 7
480
        	end
481
        end
482

  
483
        # border
484
        gc.fill('transparent')
485
        gc.stroke('grey')
486
        gc.stroke_width(1)
487
        gc.rectangle(0, 0, subject_width+g_width, headers_height)
488
        gc.stroke('black')
489
        gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_height-1)
490

  
491
        # content
492
        top = headers_height + 20
493

  
494
        gc.stroke('transparent')
495
        lines(:image => gc, :top => top, :zoom => zoom, :subject_width => subject_width, :format => :image)
496

  
497
        # today red line
498
        if Date.today >= @date_from and Date.today <= date_to
499
          gc.stroke('red')
500
          x = (Date.today-@date_from+1)*zoom + subject_width
501
          gc.line(x, headers_height, x, headers_height + g_height-1)
502
        end
503

  
504
        gc.draw(imgl)
505
        imgl.format = format
506
        imgl.to_blob
507
      end if Object.const_defined?(:Magick)
508

  
509
      def to_pdf
510
        pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language)
511
        pdf.SetTitle("#{l(:label_gantt)} #{project}")
512
        pdf.alias_nb_pages
513
        pdf.footer_date = format_date(Date.today)
514
        pdf.AddPage("L")
515
        pdf.SetFontStyle('B',12)
516
        pdf.SetX(15)
517
        pdf.RDMCell(PDF::LeftPaneWidth, 20, project.to_s)
518
        pdf.Ln
519
        pdf.SetFontStyle('B',9)
520

  
521
        subject_width = PDF::LeftPaneWidth
522
        header_height = 5
523

  
524
        headers_height = header_height
525
        show_weeks = false
526
        show_days = false
527

  
528
        if self.months < 7
529
          show_weeks = true
530
          headers_height = 2*header_height
531
          if self.months < 3
532
            show_days = true
533
            headers_height = 3*header_height
534
          end
535
        end
536

  
537
        g_width = PDF.right_pane_width
538
        zoom = (g_width) / (self.date_to - self.date_from + 1)
539
        g_height = 120
540
        t_height = g_height + headers_height
541

  
542
        y_start = pdf.GetY
543

  
544
        # Months headers
545
        month_f = self.date_from
546
        left = subject_width
547
        height = header_height
548
        self.months.times do
549
          width = ((month_f >> 1) - month_f) * zoom
550
          pdf.SetY(y_start)
551
          pdf.SetX(left)
552
          pdf.RDMCell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
553
          left = left + width
554
          month_f = month_f >> 1
555
        end
556

  
557
        # Weeks headers
558
        if show_weeks
559
          left = subject_width
560
          height = header_height
561
          if self.date_from.cwday == 1
562
            # self.date_from is monday
563
            week_f = self.date_from
564
          else
565
            # find next monday after self.date_from
566
            week_f = self.date_from + (7 - self.date_from.cwday + 1)
567
            width = (7 - self.date_from.cwday + 1) * zoom-1
568
            pdf.SetY(y_start + header_height)
569
            pdf.SetX(left)
570
            pdf.RDMCell(width + 1, height, "", "LTR")
571
            left = left + width+1
572
          end
573
          while week_f <= self.date_to
574
            width = (week_f + 6 <= self.date_to) ? 7 * zoom : (self.date_to - week_f + 1) * zoom
575
            pdf.SetY(y_start + header_height)
576
            pdf.SetX(left)
577
            pdf.RDMCell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
578
            left = left + width
579
            week_f = week_f+7
580
          end
581
        end
582

  
583
        # Days headers
584
        if show_days
585
          left = subject_width
586
          height = header_height
587
          wday = self.date_from.cwday
588
          pdf.SetFontStyle('B',7)
589
          (self.date_to - self.date_from + 1).to_i.times do
590
            width = zoom
591
            pdf.SetY(y_start + 2 * header_height)
592
            pdf.SetX(left)
593
            pdf.RDMCell(width, height, day_name(wday).first, "LTR", 0, "C")
594
            left = left + width
595
            wday = wday + 1
596
            wday = 1 if wday > 7
597
          end
598
        end
599

  
600
        pdf.SetY(y_start)
601
        pdf.SetX(15)
602
        pdf.RDMCell(subject_width+g_width-15, headers_height, "", 1)
603

  
604
        # Tasks
605
        top = headers_height + y_start
606
        options = {
607
          :top => top,
608
          :zoom => zoom,
609
          :subject_width => subject_width,
610
          :g_width => g_width,
611
          :indent => 0,
612
          :indent_increment => 5,
613
          :top_increment => 5,
614
          :format => :pdf,
615
          :pdf => pdf
616
        }
617
        render(options)
618
        pdf.Output
619
      end
620

  
621
      private
622

  
623
      def coordinates(start_date, end_date, progress, zoom=nil)
624
        zoom ||= @zoom
625

  
626
        coords = {}
627
        if start_date && end_date && start_date < self.date_to && end_date > self.date_from
628
          if start_date > self.date_from
629
            coords[:start] = start_date - self.date_from
630
            coords[:bar_start] = start_date - self.date_from
631
          else
632
            coords[:bar_start] = 0
633
          end
634
          if end_date < self.date_to
635
            coords[:end] = end_date - self.date_from
636
            coords[:bar_end] = end_date - self.date_from + 1
637
          else
638
            coords[:bar_end] = self.date_to - self.date_from + 1
639
          end
640

  
641
          if progress
642
            progress_date = start_date + (end_date - start_date + 1) * (progress / 100.0)
643
            if progress_date > self.date_from && progress_date > start_date
644
              if progress_date < self.date_to
645
                coords[:bar_progress_end] = progress_date - self.date_from
646
              else
647
                coords[:bar_progress_end] = self.date_to - self.date_from + 1
648
              end
649
            end
650

  
651
            if progress_date < Date.today
652
              late_date = [Date.today, end_date].min
653
              if late_date > self.date_from && late_date > start_date
654
                if late_date < self.date_to
655
                  coords[:bar_late_end] = late_date - self.date_from + 1
656
                else
657
                  coords[:bar_late_end] = self.date_to - self.date_from + 1
658
                end
659
              end
660
            end
661
          end
662
        end
663

  
664
        # Transforms dates into pixels witdh
665
        coords.keys.each do |key|
666
          coords[key] = (coords[key] * zoom).floor
667
        end
668
        coords
669
      end
670

  
671
      # Sorts a collection of issues by start_date, due_date, id for gantt rendering
672
      def sort_issues!(issues)
673
        issues.sort! { |a, b| gantt_issue_compare(a, b, issues) }
674
      end
675

  
676
      # TODO: top level issues should be sorted by start date
677
      def gantt_issue_compare(x, y, issues)
678
        if x.root_id == y.root_id
679
          x.lft <=> y.lft
680
        else
681
          x.root_id <=> y.root_id
682
        end
683
      end
684

  
685
      def current_limit
686
        if @max_rows
687
          @max_rows - @number_of_rows
688
        else
689
          nil
690
        end
691
      end
692

  
693
      def abort?
694
        if @max_rows && @number_of_rows >= @max_rows
695
          @truncated = true
696
        end
697
      end
698

  
699
      def pdf_new_page?(options)
700
        if options[:top] > 180
701
          options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
702
          options[:pdf].AddPage("L")
703
          options[:top] = 15
704
          options[:pdf].Line(15, options[:top] - 0.1, PDF::TotalWidth, options[:top] - 0.1)
705
        end
706
      end
707

  
708
      def html_subject(params, subject, options={})
709
        style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;"
710
        style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width]
711

  
712
        output = view.content_tag 'div', subject, :class => options[:css], :style => style, :title => options[:title]
713
        @subjects << output
714
        output
715
      end
716

  
717
      def pdf_subject(params, subject, options={})
718
        params[:pdf].SetY(params[:top])
719
        params[:pdf].SetX(15)
720

  
721
        char_limit = PDF::MaxCharactorsForSubject - params[:indent]
722
        params[:pdf].RDMCell(params[:subject_width]-15, 5, (" " * params[:indent]) +  subject.to_s.sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR")
723

  
724
        params[:pdf].SetY(params[:top])
725
        params[:pdf].SetX(params[:subject_width])
726
        params[:pdf].RDMCell(params[:g_width], 5, "", "LR")
727
      end
728

  
729
      def image_subject(params, subject, options={})
730
        params[:image].fill('black')
731
        params[:image].stroke('transparent')
732
        params[:image].stroke_width(1)
733
        params[:image].text(params[:indent], params[:top] + 2, subject)
734
      end
735

  
736
      def html_task(params, coords, options={})
737
        output = ''
738
        # Renders the task bar, with progress and late
739
        if coords[:bar_start] && coords[:bar_end]
740
          output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_todo'>&nbsp;</div>".html_safe
741

  
742
          if coords[:bar_late_end]
743
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_late_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_late'>&nbsp;</div>".html_safe
744
          end
745
          if coords[:bar_progress_end]
746
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_progress_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_done'>&nbsp;</div>".html_safe
747
          end
748
        end
749
        # Renders the markers
750
        if options[:markers]
751
          if coords[:start]
752
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:start] }px;width:15px;' class='#{options[:css]} marker starting'>&nbsp;</div>".html_safe
753
          end
754
          if coords[:end]
755
            output << "<div style='top:#{ params[:top] }px;left:#{ coords[:end] + params[:zoom] }px;width:15px;' class='#{options[:css]} marker ending'>&nbsp;</div>".html_safe
756
          end
757
        end
758
        # Renders the label on the right
759
        if options[:label]
760
          output << "<div style='top:#{ params[:top] }px;left:#{ (coords[:bar_end] || 0) + 8 }px;' class='#{options[:css]} label'>".html_safe
761
          output << options[:label]
762
          output << "</div>".html_safe
763
        end
764
        # Renders the tooltip
765
        if options[:issue] && coords[:bar_start] && coords[:bar_end]
766
          output << "<div class='tooltip' style='position: absolute;top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] }px;height:12px;'>".html_safe
767
          output << '<span class="tip">'.html_safe
768
          output << view.render_issue_tooltip(options[:issue]).html_safe
769
          output << "</span></div>".html_safe
770
        end
771
        @lines << output
772
        output
773
      end
774

  
775
      def pdf_task(params, coords, options={})
776
        height = options[:height] || 2
777

  
778
        # Renders the task bar, with progress and late
779
        if coords[:bar_start] && coords[:bar_end]
780
          params[:pdf].SetY(params[:top]+1.5)
781
          params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
782
          params[:pdf].SetFillColor(200,200,200)
783
          params[:pdf].RDMCell(coords[:bar_end] - coords[:bar_start], height, "", 0, 0, "", 1)
784

  
785
          if coords[:bar_late_end]
786
            params[:pdf].SetY(params[:top]+1.5)
787
            params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
788
            params[:pdf].SetFillColor(255,100,100)
789
            params[:pdf].RDMCell(coords[:bar_late_end] - coords[:bar_start], height, "", 0, 0, "", 1)
790
          end
791
          if coords[:bar_progress_end]
792
            params[:pdf].SetY(params[:top]+1.5)
793
            params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
794
            params[:pdf].SetFillColor(90,200,90)
795
            params[:pdf].RDMCell(coords[:bar_progress_end] - coords[:bar_start], height, "", 0, 0, "", 1)
796
          end
797
        end
798
        # Renders the markers
799
        if options[:markers]
800
          if coords[:start]
801
            params[:pdf].SetY(params[:top] + 1)
802
            params[:pdf].SetX(params[:subject_width] + coords[:start] - 1)
803
            params[:pdf].SetFillColor(50,50,200)
804
            params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1)
805
          end
806
          if coords[:end]
807
            params[:pdf].SetY(params[:top] + 1)
808
            params[:pdf].SetX(params[:subject_width] + coords[:end] - 1)
809
            params[:pdf].SetFillColor(50,50,200)
810
            params[:pdf].RDMCell(2, 2, "", 0, 0, "", 1)
811
          end
812
        end
813
        # Renders the label on the right
814
        if options[:label]
815
          params[:pdf].SetX(params[:subject_width] + (coords[:bar_end] || 0) + 5)
816
          params[:pdf].RDMCell(30, 2, options[:label])
817
        end
818
      end
819

  
820
      def image_task(params, coords, options={})
821
        height = options[:height] || 6
822

  
823
        # Renders the task bar, with progress and late
824
        if coords[:bar_start] && coords[:bar_end]
825
          params[:image].fill('#aaa')
826
          params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_end], params[:top] - height)
827

  
828
          if coords[:bar_late_end]
829
            params[:image].fill('#f66')
830
            params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_late_end], params[:top] - height)
831
          end
832
          if coords[:bar_progress_end]
833
            params[:image].fill('#00c600')
834
            params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_progress_end], params[:top] - height)
835
          end
836
        end
837
        # Renders the markers
838
        if options[:markers]
839
          if coords[:start]
840
            x = params[:subject_width] + coords[:start]
841
            y = params[:top] - height / 2
842
            params[:image].fill('blue')
843
            params[:image].polygon(x-4, y, x, y-4, x+4, y, x, y+4)
844
          end
845
          if coords[:end]
846
            x = params[:subject_width] + coords[:end] + params[:zoom]
847
            y = params[:top] - height / 2
848
            params[:image].fill('blue')
849
            params[:image].polygon(x-4, y, x, y-4, x+4, y, x, y+4)
850
          end
851
        end
852
        # Renders the label on the right
853
        if options[:label]
854
          params[:image].fill('black')
855
          params[:image].text(params[:subject_width] + (coords[:bar_end] || 0) + 5,params[:top] + 1, options[:label])
856
        end
857
      end
858
    end
859
  end
860
end
.svn/pristine/10/105e4bad4371b49c4e052cc65ba6a1fae7edd15b.svn-base
1
# Serbian translations for Redmine
2
# by Vladimir Medarović (vlada@medarovic.com)
3
sr:
4
  direction: ltr
5
  date:
6
    formats:
7
      # Use the strftime parameters for formats.
8
      # When no format has been given, it uses default.
9
      # You can provide other formats here if you like!
10
      default: "%d.%m.%Y."
11
      short: "%e %b"
12
      long: "%B %e, %Y"
13
      
14
    day_names: [недеља, понедељак, уторак, среда, четвртак, петак, субота]
15
    abbr_day_names: [нед, пон, уто, сре, чет, пет, суб]
16
      
17
    # Don't forget the nil at the beginning; there's no such thing as a 0th month
18
    month_names: [~, јануар, фебруар, март, април, мај, јун, јул, август, септембар, октобар, новембар, децембар]
19
    abbr_month_names: [~, јан, феб, мар, апр, мај, јун, јул, авг, сеп, окт, нов, дец]
20
    # Used in date_select and datime_select.
21
    order:
22
      - :day
23
      - :month
24
      - :year
25

  
26
  time:
27
    formats:
28
      default: "%d.%m.%Y. у %H:%M"
29
      time: "%H:%M"
30
      short: "%d. %b у %H:%M"
31
      long: "%d. %B %Y у %H:%M"
32
    am: "am"
33
    pm: "pm"
34

  
35
  datetime:
36
    distance_in_words:
37
      half_a_minute: "пола минута"
38
      less_than_x_seconds:
39
        one:   "мање од једне секунде"
40
        other: "мање од %{count} сек."
41
      x_seconds:
42
        one:   "једна секунда"
43
        other: "%{count} сек."
44
      less_than_x_minutes:
45
        one:   "мање од минута"
46
        other: "мање од %{count} мин."
47
      x_minutes:
48
        one:   "један минут"
49
        other: "%{count} мин."
50
      about_x_hours:
51
        one:   "приближно један сат"
52
        other: "приближно %{count} сати"
53
      x_days:
54
        one:   "један дан"
55
        other: "%{count} дана"
56
      about_x_months:
57
        one:   "приближно један месец"
58
        other: "приближно %{count} месеци"
59
      x_months:
60
        one:   "један месец"
61
        other: "%{count} месеци"
62
      about_x_years:
63
        one:   "приближно годину дана"
64
        other: "приближно %{count} год."
65
      over_x_years:
66
        one:   "преко годину дана"
67
        other: "преко %{count} год."
68
      almost_x_years:
69
        one:   "скоро годину дана"
70
        other: "скоро %{count} год."
71

  
72
  number:
73
    format:
74
      separator: ","
75
      delimiter: ""
76
      precision: 3
77
    human:
78
      format:
79
        delimiter: ""
80
        precision: 1
81
      storage_units:
82
        format: "%n %u"
83
        units:
84
          byte:
85
            one: "Byte"
86
            other: "Bytes"
87
          kb: "KB"
88
          mb: "MB"
89
          gb: "GB"
90
          tb: "TB"
91

  
92

  
93
# Used in array.to_sentence.
94
  support:
95
    array:
96
      sentence_connector: "и"
97
      skip_last_comma: false
98
      
99
  activerecord:
100
    errors:
101
      template:
102
        header:
103
          one:    "1 error prohibited this %{model} from being saved"
104
          other:  "%{count} errors prohibited this %{model} from being saved"
105
      messages:
106
        inclusion: "није укључен у списак"
107
        exclusion: "је резервисан"
108
        invalid: "је неисправан"
109
        confirmation: "потврда не одговара"
110
        accepted: "мора бити прихваћен"
111
        empty: "не може бити празно"
112
        blank: "не може бити празно"
113
        too_long: "је предугачка (максимум знакова је %{count})"
114
        too_short: "је прекратка (минимум знакова је %{count})"
115
        wrong_length: "је погрешне дужине (број знакова мора бити %{count})"
116
        taken: "је већ у употреби"
117
        not_a_number: "није број"
118
        not_a_date: "није исправан датум"
119
        greater_than: "мора бити већи од %{count}"
120
        greater_than_or_equal_to: "мора бити већи или једнак %{count}"
121
        equal_to: "мора бити једнак %{count}"
122
        less_than: "мора бити мањи од %{count}"
123
        less_than_or_equal_to: "мора бити мањи или једнак %{count}"
124
        odd: "мора бити паран"
125
        even: "мора бити непаран"
126
        greater_than_start_date: "мора бити већи од почетног датума"
127
        not_same_project: "не припада истом пројекту"
128
        circular_dependency: "Ова веза ће створити кружну референцу"
129
        cant_link_an_issue_with_a_descendant: "Проблем не може бити повезан са једним од својих подзадатака"
130

  
131
  actionview_instancetag_blank_option: Молим одаберите
132
  
133
  general_text_No: 'Не'
134
  general_text_Yes: 'Да'
135
  general_text_no: 'не'
136
  general_text_yes: 'да'
137
  general_lang_name: 'Српски'
138
  general_csv_separator: ','
139
  general_csv_decimal_separator: '.'
140
  general_csv_encoding: UTF-8
141
  general_pdf_encoding: UTF-8
142
  general_first_day_of_week: '1'
143
  
144
  notice_account_updated: Налог је успешно ажуриран.
145
  notice_account_invalid_creditentials: Неисправно корисничко име или лозинка.
146
  notice_account_password_updated: Лозинка је успешно ажурирана.
147
  notice_account_wrong_password: Погрешна лозинка
148
  notice_account_register_done: Кориснички налог је успешно креиран. Кликните на линк који сте добили у е-поруци за активацију.
149
  notice_account_unknown_email: Непознат корисник.
150
  notice_can_t_change_password: Овај кориснички налог за потврду идентитета користи спољни извор. Немогуће је променити лозинку.
151
  notice_account_lost_email_sent: Послата вам је е-порука са упутством за избор нове лозинке
152
  notice_account_activated: Ваш кориснички налог је активиран. Сада се можете пријавити.
153
  notice_successful_create: Успешно креирање.
154
  notice_successful_update: Успешно ажурирање.
155
  notice_successful_delete: Успешно брисање.
156
  notice_successful_connection: Успешно повезивање.
157
  notice_file_not_found: Страна којој желите приступити не постоји или је уклоњена.
158
  notice_locking_conflict: Податак је ажуриран од стране другог корисника.
159
  notice_not_authorized: Нисте овлашћени за приступ овој страни.
160
  notice_email_sent: "E-порука је послата на %{value}"
161
  notice_email_error: "Догодила се грешка приликом слања е-поруке (%{value})"
162
  notice_feeds_access_key_reseted: Ваш RSS приступни кључ је поништен.
163
  notice_api_access_key_reseted: Ваш API приступни кључ је поништен.
164
  notice_failed_to_save_issues: "Неуспешно снимање %{count} проблема од %{total} одабраних: %{ids}."
165
  notice_failed_to_save_members: "Неуспешно снимање члана(ова): %{errors}."
166
  notice_no_issue_selected: "Ни један проблем није одабран! Молимо, одаберите проблем који желите да мењате."
167
  notice_account_pending: "Ваш налог је креиран и чека на одобрење администратора."
168
  notice_default_data_loaded: Подразумевано конфигурисање је успешно учитано.
169
  notice_unable_delete_version: Верзију је немогуће избрисати.
170
  notice_unable_delete_time_entry: Ставку евиденције времена је немогуће избрисати.
171
  notice_issue_done_ratios_updated: Однос решених проблема је ажуриран.
172
  
173
  error_can_t_load_default_data: "Подразумевано конфигурисање је немогуће учитати: %{value}"
174
  error_scm_not_found: "Ставка или исправка нису пронађене у спремишту."
175
  error_scm_command_failed: "Грешка се јавила приликом покушаја приступа спремишту: %{value}"
176
  error_scm_annotate: "Ставка не постоји или не може бити означена."
177
  error_issue_not_found_in_project: 'Проблем није пронађен или не припада овом пројекту.'
178
  error_no_tracker_in_project: 'Ни једно праћење није повезано са овим пројектом. Молимо проверите подешавања пројекта.'
179
  error_no_default_issue_status: 'Подразумевани статус проблема није дефинисан. Молимо проверите ваше конфигурисање (идите на "Администрација -> Статуси проблема").'
180
  error_can_not_delete_custom_field: Немогуће је избрисати прилагођено поље
181
  error_can_not_delete_tracker: "Ово праћење садржи проблеме и не може бити обрисано."
182
  error_can_not_remove_role: "Ова улога је у употреби и не може бити обрисана."
183
  error_can_not_reopen_issue_on_closed_version: 'Проблем додељен затвореној верзији не може бити поново отворен'
184
  error_can_not_archive_project: Овај пројекат се не може архивирати
185
  error_issue_done_ratios_not_updated: "Однос решених проблема није ажуриран."
186
  error_workflow_copy_source: 'Молимо одаберите изворно праћење или улогу'
187
  error_workflow_copy_target: 'Молимо одаберите одредишно праћење и улогу'
188
  error_unable_delete_issue_status: 'Статус проблема је немогуће обрисати'
189
  error_unable_to_connect: "Повезивање са (%{value}) је немогуће"  
190
  warning_attachments_not_saved: "%{count} датотека не може бити снимљена."
191
  
192
  mail_subject_lost_password: "Ваша %{value} лозинка"
193
  mail_body_lost_password: 'За промену ваше лозинке, кликните на следећи линк:'
194
  mail_subject_register: "Активација вашег %{value} налога"
195
  mail_body_register: 'За активацију вашег налога, кликните на следећи линк:'
196
  mail_body_account_information_external: "Ваш налог %{value} можете користити за пријаву."
197
  mail_body_account_information: Информације о вашем налогу
198
  mail_subject_account_activation_request: "Захтев за активацију налога %{value}"
199
  mail_body_account_activation_request: "Нови корисник (%{value}) је регистрован. Налог чека на ваше одобрење:"
200
  mail_subject_reminder: "%{count} проблема доспева наредних %{days} дана"
201
  mail_body_reminder: "%{count} проблема додељених вама доспева у наредних %{days} дана:"
202
  mail_subject_wiki_content_added: "Wiki страница '%{id}' је додата"
203
  mail_body_wiki_content_added: "%{author} је додао wiki страницу '%{id}'."
204
  mail_subject_wiki_content_updated: "Wiki страница '%{id}' је ажурирана"
205
  mail_body_wiki_content_updated: "%{author} је ажурирао wiki страницу '%{id}'."
206
  
207
  gui_validation_error: једна грешка
208
  gui_validation_error_plural: "%{count} грешака"
209
  
210
  field_name: Назив
211
  field_description: Опис
212
  field_summary: Резиме
213
  field_is_required: Обавезно
214
  field_firstname: Име
215
  field_lastname: Презиме
216
  field_mail: Е-адреса
217
  field_filename: Датотека
218
  field_filesize: Величина
219
  field_downloads: Преузимања
220
  field_author: Аутор
221
  field_created_on: Креирано
222
  field_updated_on: Ажурирано
223
  field_field_format: Формат
224
  field_is_for_all: За све пројекте
225
  field_possible_values: Могуће вредности
226
  field_regexp: Регуларан израз
227
  field_min_length: Минимална дужина
228
  field_max_length: Максимална дужина
229
  field_value: Вредност
230
  field_category: Категорија
231
  field_title: Наслов
232
  field_project: Пројекат
233
  field_issue: Проблем
234
  field_status: Статус
235
  field_notes: Белешке
236
  field_is_closed: Затворен проблем
237
  field_is_default: Подразумевана вредност
238
  field_tracker: Праћење
239
  field_subject: Предмет
240
  field_due_date: Крајњи рок
241
  field_assigned_to: Додељено
242
  field_priority: Приоритет
243
  field_fixed_version: Одредишна верзија
244
  field_user: Корисник
245
  field_principal: Главни
246
  field_role: Улога
247
  field_homepage: Почетна страница
248
  field_is_public: Јавно објављивање
249
  field_parent: Потпројекат од
250
  field_is_in_roadmap: Проблеми приказани у плану рада
251
  field_login: Корисничко име
252
  field_mail_notification: Обавештења путем е-поште
253
  field_admin: Администратор
254
  field_last_login_on: Последње повезивање
255
  field_language: Језик
256
  field_effective_date: Датум
257
  field_password: Лозинка
258
  field_new_password: Нова лозинка
259
  field_password_confirmation: Потврда лозинке
260
  field_version: Верзија
261
  field_type: Тип
262
  field_host: Главни рачунар
263
  field_port: Порт
264
  field_account: Кориснички налог
265
  field_base_dn: Базни DN
266
  field_attr_login: Атрибут пријављивања
267
  field_attr_firstname: Атрибут имена
268
  field_attr_lastname: Атрибут презимена
269
  field_attr_mail: Атрибут е-адресе
270
  field_onthefly: Креирање корисника у току рада
271
  field_start_date: Почетак
272
  field_done_ratio: "% урађено"
273
  field_auth_source: Режим потврде идентитета
274
  field_hide_mail: Сакриј моју е-адресу
275
  field_comments: Коментар
276
  field_url: URL
277
  field_start_page: Почетна страница
278
  field_subproject: Потпројекат
279
  field_hours: сати
280
  field_activity: Активност
281
  field_spent_on: Датум
282
  field_identifier: Идентификатор
283
  field_is_filter: Употреби као филтер
284
  field_issue_to: Сродни проблеми
285
  field_delay: Кашњење
286
  field_assignable: Проблем може бити додељен овој улози
287
  field_redirect_existing_links: Преусмери постојеће везе
288
  field_estimated_hours: Протекло време
289
  field_column_names: Колоне
290
  field_time_zone: Временска зона
291
  field_searchable: Може да се претражује
292
  field_default_value: Подразумевана вредност
293
  field_comments_sorting: Прикажи коментаре
294
  field_parent_title: Матична страница
295
  field_editable: Изменљиво
296
  field_watcher: Посматрач
297
  field_identity_url: OpenID URL
298
  field_content: Садржај
299
  field_group_by: Груписање резултата по
300
  field_sharing: Дељење
301
  field_parent_issue: Матични задатак
302
  
303
  setting_app_title: Наслов апликације
304
  setting_app_subtitle: Поднаслов апликације
305
  setting_welcome_text: Текст добродошлице
306
  setting_default_language: Подразумевани језик
307
  setting_login_required: Обавезна потврда идентитета
308
  setting_self_registration: Саморегистрација
309
  setting_attachment_max_size: Макс. величина приложене датотеке
310
  setting_issues_export_limit: Ограничење извоза „проблема“
311
  setting_mail_from: Е-адреса пошиљаоца
312
  setting_bcc_recipients: Примаоци „Bcc“ копије
313
  setting_plain_text_mail: Порука са чистим текстом (без HTML-а)
314
  setting_host_name: Путања и назив главног рачунара
315
  setting_text_formatting: Обликовање текста
316
  setting_wiki_compression: Компресија Wiki историје
317
  setting_feeds_limit: Ограничење садржаја извора вести
318
  setting_default_projects_public: Подразумева се јавно приказивање нових пројеката
319
  setting_autofetch_changesets: Извршавање аутоматског преузимања
320
  setting_sys_api_enabled: Омогућавање WS за управљање спремиштем
321
  setting_commit_ref_keywords: Референцирање кључних речи
322
  setting_commit_fix_keywords: Поправљање кључних речи
323
  setting_autologin: Аутоматска пријава
324
  setting_date_format: Формат датума
325
  setting_time_format: Формат времена
326
  setting_cross_project_issue_relations: Дозволи повезивање проблема из унакрсних пројеката
327
  setting_issue_list_default_columns: Подразумеване колоне приказане на списку проблема
328
  setting_emails_footer: Подножје странице е-поруке
329
  setting_protocol: Протокол
330
  setting_per_page_options: Опције приказа објеката по страници
331
  setting_user_format: Формат приказа корисника
332
  setting_activity_days_default: Број дана приказаних на пројектној активности
333
  setting_display_subprojects_issues: Приказуј проблеме из потпројеката на главном пројекту, уколико није другачије наведено
334
  setting_enabled_scm: Омогућавање SCM
335
  setting_mail_handler_body_delimiters: "Скраћивање е-поруке након једне од ових линија"
336
  setting_mail_handler_api_enabled: Омогућавање WS долазне е-поруке
337
  setting_mail_handler_api_key: API кључ
338
  setting_sequential_project_identifiers: Генерисање секвенцијалног имена пројекта
339
  setting_gravatar_enabled: Користи Gravatar корисничке иконе
340
  setting_gravatar_default: Подразумевана Gravatar слика
341
  setting_diff_max_lines_displayed: Макс. број приказаних различитих линија
342
  setting_file_max_size_displayed: Макс. величина текст. датотека приказаних уметнуто
343
  setting_repository_log_display_limit: Макс. број ревизија приказаних у датотеци за евиденцију
344
  setting_openid: Дозволи OpenID пријаву и регистрацију
345
  setting_password_min_length: Минимална дужина лозинке
346
  setting_new_project_user_role_id: Креатору пројекта (који није администратор) додељује је улога
347
  setting_default_projects_modules: Подразумевано омогућени модули за нове пројекте
348
  setting_issue_done_ratio: Израчунај однос решених проблема
349
  setting_issue_done_ratio_issue_field: користећи поље проблема
350
  setting_issue_done_ratio_issue_status: користећи статус проблема
351
  setting_start_of_week: Први дан у седмици
352
  setting_rest_api_enabled: Омогући REST web услуге
353
  setting_cache_formatted_text: Кеширање обрађеног текста
354
  
355
  permission_add_project: Креирање пројекта
356
  permission_add_subprojects: Креирање потпојекта
357
  permission_edit_project: Измена пројеката
358
  permission_select_project_modules: Одабирање модула пројекта
359
  permission_manage_members: Управљање члановима
360
  permission_manage_project_activities: Управљање пројектним активностима
361
  permission_manage_versions: Управљање верзијама
362
  permission_manage_categories: Управљање категоријама проблема
363
  permission_view_issues: Преглед проблема
364
  permission_add_issues: Додавање проблема
365
  permission_edit_issues: Измена проблема
366
  permission_manage_issue_relations: Управљање везама између проблема
367
  permission_add_issue_notes: Додавање белешки
368
  permission_edit_issue_notes: Измена белешки
369
  permission_edit_own_issue_notes: Измена сопствених белешки
370
  permission_move_issues: Померање проблема
371
  permission_delete_issues: Брисање проблема
372
  permission_manage_public_queries: Управљање јавним упитима
373
  permission_save_queries: Снимање упита
374
  permission_view_gantt: Прегледање Гантовог дијаграма
375
  permission_view_calendar: Прегледање календара
376
  permission_view_issue_watchers: Прегледање списка посматрача
377
  permission_add_issue_watchers: Додавање посматрача
378
  permission_delete_issue_watchers: Брисање посматрача
379
  permission_log_time: Бележење утрошеног времена
380
  permission_view_time_entries: Прегледање утрошеног времена
381
  permission_edit_time_entries: Измена утрошеног времена
382
  permission_edit_own_time_entries: Измена сопственог утрошеног времена
383
  permission_manage_news: Управљање вестима
384
  permission_comment_news: Коментарисање вести
385
  permission_manage_documents: Управљање документима
386
  permission_view_documents: Прегледање докумената
387
  permission_manage_files: Управљање датотекама
388
  permission_view_files: Прегледање датотека
389
  permission_manage_wiki: Управљање wiki страницама
390
  permission_rename_wiki_pages: Промена имена wiki страницама
391
  permission_delete_wiki_pages: Брисање wiki страница
392
  permission_view_wiki_pages: Прегледање wiki страница
393
  permission_view_wiki_edits: Прегледање wiki историје
394
  permission_edit_wiki_pages: Измена wiki страница
395
  permission_delete_wiki_pages_attachments: Брисање приложених датотека
396
  permission_protect_wiki_pages: Заштита wiki страница
397
  permission_manage_repository: Управљање спремиштем
398
  permission_browse_repository: Прегледање спремишта
399
  permission_view_changesets: Прегледање скупа промена
400
  permission_commit_access: Потврда приступа
401
  permission_manage_boards: Управљање форумима
402
  permission_view_messages: Прегледање порука
403
  permission_add_messages: Слање порука
404
  permission_edit_messages: Измена порука
405
  permission_edit_own_messages: Измена сопствених порука
406
  permission_delete_messages: Брисање порука
407
  permission_delete_own_messages: Брисање сопствених порука
408
  permission_export_wiki_pages: Извоз wiki страница
409
  permission_manage_subtasks: Управљање подзадацима
410
  
411
  project_module_issue_tracking: Праћење проблема
412
  project_module_time_tracking: Праћење времена
413
  project_module_news: Вести
414
  project_module_documents: Документи
415
  project_module_files: Датотеке
416
  project_module_wiki: Wiki
417
  project_module_repository: Спремиште
418
  project_module_boards: Форуми
419
  
420
  label_user: Корисник
421
  label_user_plural: Корисници
422
  label_user_new: Нови корисник
423
  label_user_anonymous: Анониман
424
  label_project: Пројекат
425
  label_project_new: Нови пројекат
426
  label_project_plural: Пројекти
427
  label_x_projects:
428
    zero:  нема пројеката
429
    one:   један пројекат
430
    other: "%{count} пројеката"
431
  label_project_all: Сви пројекти
432
  label_project_latest: Последњи пројекти
433
  label_issue: Проблем
434
  label_issue_new: Нови проблем
435
  label_issue_plural: Проблеми
436
  label_issue_view_all: Приказ свих проблема
437
  label_issues_by: "Проблеми (%{value})"
438
  label_issue_added: Проблем је додат
439
  label_issue_updated: Проблем је ажуриран
440
  label_document: Документ
441
  label_document_new: Нови документ
442
  label_document_plural: Документи
443
  label_document_added: Документ је додат
444
  label_role: Улога
445
  label_role_plural: Улоге
446
  label_role_new: Нова улога
447
  label_role_and_permissions: Улоге и дозволе
448
  label_member: Члан
449
  label_member_new: Нови члан
450
  label_member_plural: Чланови
451
  label_tracker: Праћење
452
  label_tracker_plural: Праћења
453
  label_tracker_new: Ново праћење
454
  label_workflow: Ток посла
455
  label_issue_status: Статус проблема
456
  label_issue_status_plural: Статуси проблема
457
  label_issue_status_new: Нови статус
458
  label_issue_category: Категорија проблема
459
  label_issue_category_plural: Категорије проблема
460
  label_issue_category_new: Нова категорија
461
  label_custom_field: Прилагођено поље
462
  label_custom_field_plural: Прилагођена поља
463
  label_custom_field_new: Ново прилагођено поље
464
  label_enumerations: Набројива листа
465
  label_enumeration_new: Нова вредност
466
  label_information: Информација
467
  label_information_plural: Информације
468
  label_please_login: Молимо, пријавите се
469
  label_register: Регистрација
470
  label_login_with_open_id_option: или пријава са OpenID
471
  label_password_lost: Изгубљена лозинка
472
  label_home: Почетак
473
  label_my_page: Моја страница
474
  label_my_account: Мој налог
475
  label_my_projects: Моји пројекти
476
  label_my_page_block: My page block
477
  label_administration: Администрација
478
  label_login: Пријава
479
  label_logout: Одјава
480
  label_help: Помоћ
481
  label_reported_issues: Пријављени проблеми
482
  label_assigned_to_me_issues: Проблеми додељени мени
483
  label_last_login: Последње повезивање
484
  label_registered_on: Регистрован
485
  label_activity: Активност
486
  label_overall_activity: Целокупна активност
487
  label_user_activity: "Активност корисника %{value}"
488
  label_new: Ново
489
  label_logged_as: Пријављени сте као
490
  label_environment: Окружење
491
  label_authentication: Потврда идентитета
492
  label_auth_source: Режим потврде идентитета
493
  label_auth_source_new: Нови режим потврде идентитета
494
  label_auth_source_plural: Режими потврде идентитета
495
  label_subproject_plural: Потпројекти
496
  label_subproject_new: Нови потпројекат
497
  label_and_its_subprojects: "%{value} и његови потпројекти"
498
  label_min_max_length: Мин. - Макс. дужина
499
  label_list: Списак
500
  label_date: Датум
501
  label_integer: Цео број
502
  label_float: Са покретним зарезом
503
  label_boolean: Логички оператор
504
  label_string: Текст
505
  label_text: Дуги текст
506
  label_attribute: Особина
507
  label_attribute_plural: Особине
508
  label_download: "%{count} преузимање"
509
  label_download_plural: "%{count} преузимања"
510
  label_no_data: Нема података за приказивање
511
  label_change_status: Промена статуса
512
  label_history: Историја
513
  label_attachment: Датотека
514
  label_attachment_new: Нова датотека
515
  label_attachment_delete: Брисање датотеке
516
  label_attachment_plural: Датотеке
517
  label_file_added: Датотека је додата
518
  label_report: Извештај
519
  label_report_plural: Извештаји
520
  label_news: Вести
521
  label_news_new: Додавање вести
522
  label_news_plural: Вести
523
  label_news_latest: Последње вести
524
  label_news_view_all: Приказ свих вести
525
  label_news_added: Вести су додате
526
  label_settings: Подешавања
527
  label_overview: Преглед
528
  label_version: Верзија
529
  label_version_new: Нова верзија
530
  label_version_plural: Верзије
531
  label_close_versions: Затвори завршене верзије
532
  label_confirmation: Потврда
533
  label_export_to: 'Такође доступно и у варијанти:'
534
  label_read: Читање...
535
  label_public_projects: Јавни пројекти
536
  label_open_issues: отворен
537
  label_open_issues_plural: отворених
538
  label_closed_issues: затворен
539
  label_closed_issues_plural: затворених
540
  label_x_open_issues_abbr_on_total:
541
    zero:  0 отворених / %{total}
542
    one:   1 отворен / %{total}
543
    other: "%{count} отворених / %{total}"
544
  label_x_open_issues_abbr:
545
    zero:  0 отворених
546
    one:   1 отворен
547
    other: "%{count} отворених"
548
  label_x_closed_issues_abbr:
549
    zero:  0 затворених
550
    one:   1 затворен
551
    other: "%{count} затворених"
552
  label_total: Укупно
553
  label_permissions: Дозволе
554
  label_current_status: Тренутни статус
555
  label_new_statuses_allowed: Нови статуси дозвољени
556
  label_all: сви
557
  label_none: ниједан
558
  label_nobody: никоме
559
  label_next: Следеће
560
  label_previous: Претходно
561
  label_used_by: Користио
562
  label_details: Детаљи
563
  label_add_note: Додај белешку
564
  label_per_page: По страни
565
  label_calendar: Календар
566
  label_months_from: месеци од
567
  label_gantt: Гантов дијаграм
568
  label_internal: Унутрашњи
569
  label_last_changes: "последњих %{count} промена"
570
  label_change_view_all: Прикажи све промене
571
  label_personalize_page: Персонализуј ову страну
572
  label_comment: Коментар
573
  label_comment_plural: Коментари
574
  label_x_comments:
575
    zero: без коментара
576
    one: један коментар
577
    other: "%{count} коментара"
578
  label_comment_add: Додај коментар
579
  label_comment_added: Коментар додат
580
  label_comment_delete: Обриши коментаре
581
  label_query: Прилагођен упит
582
  label_query_plural: Прилагођени упити
583
  label_query_new: Нови упит
584
  label_filter_add: Додавање филтера
585
  label_filter_plural: Филтери
586
  label_equals: је
587
  label_not_equals: није
588
  label_in_less_than: мање од
589
  label_in_more_than: више од
590
  label_greater_or_equal: '>='
591
  label_less_or_equal: '<='
592
  label_in: у
593
  label_today: данас
594
  label_all_time: све време
595
  label_yesterday: јуче
596
  label_this_week: ове седмице
597
  label_last_week: последње седмице
598
  label_last_n_days: "последњих %{count} дана"
599
  label_this_month: овог месеца
600
  label_last_month: последњег месеца
601
  label_this_year: ове године
602
  label_date_range: Временски период
603
  label_less_than_ago: пре мање од неколико дана
604
  label_more_than_ago: пре више од неколико дана
605
  label_ago: пре неколико дана
606
  label_contains: садржи
607
  label_not_contains: не садржи
608
  label_day_plural: дана
609
  label_repository: Спремиште
610
  label_repository_plural: Спремишта
611
  label_browse: Прегледање
612
  label_modification: "%{count} промена"
613
  label_modification_plural: "%{count} промена"
614
  label_branch: Грана
615
  label_tag: Ознака
616
  label_revision: Ревизија
617
  label_revision_plural: Ревизије
618
  label_revision_id: "Ревизија %{value}"
619
  label_associated_revisions: Придружене ревизије
620
  label_added: додато
621
  label_modified: промењено
622
  label_copied: копирано
623
  label_renamed: преименовано
624
  label_deleted: избрисано
625
  label_latest_revision: Последња ревизија
626
  label_latest_revision_plural: Последње ревизије
627
  label_view_revisions: Преглед ревизија
628
  label_view_all_revisions: Преглед свих ревизија
629
  label_max_size: Максимална величина
630
  label_sort_highest: Премештање на врх
631
  label_sort_higher: Премештање на горе
632
  label_sort_lower: Премештање на доле
633
  label_sort_lowest: Премештање на дно
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff