SierraTecnologia /
fabrica
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * This file is part of Fabrica. |
||
| 5 | * |
||
| 6 | * (c) Alexandre Salomé <[email protected]> |
||
| 7 | * (c) Julien DIDIER <[email protected]> |
||
| 8 | * |
||
| 9 | * This source file is subject to the GPL license that is bundled |
||
| 10 | * with this source code in the file LICENSE. |
||
| 11 | */ |
||
| 12 | |||
| 13 | namespace Fabrica\Models\Code; |
||
| 14 | |||
| 15 | use Pedreiro\Models\Base; |
||
| 16 | |||
| 17 | View Code Duplication | class WikiPage extends Base |
|
|
0 ignored issues
–
show
|
|||
| 18 | { |
||
| 19 | |||
| 20 | protected $organizationPerspective = true; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * The attributes that are mass assignable. |
||
| 24 | * |
||
| 25 | * @var array |
||
| 26 | */ |
||
| 27 | protected $fillable = [ |
||
| 28 | 'author_id', |
||
| 29 | 'committer_id', |
||
| 30 | 'token', |
||
| 31 | 'company_token', |
||
| 32 | 'is_active' |
||
| 33 | ]; |
||
| 34 | /*# frozen_string_literal: true |
||
| 35 | |||
| 36 | # rubocop:disable Rails/ActiveRecordAliases |
||
| 37 | class WikiPage |
||
| 38 | PageChangedError = Class.new(StandardError) |
||
| 39 | PageRenameError = Class.new(StandardError) |
||
| 40 | |||
| 41 | include ActiveModel::Validations |
||
| 42 | include ActiveModel::Conversion |
||
| 43 | include StaticModel |
||
| 44 | extend ActiveModel::Naming |
||
| 45 | |||
| 46 | def self.primary_key |
||
| 47 | 'slug' |
||
| 48 | end |
||
| 49 | |||
| 50 | def self.model_name |
||
| 51 | ActiveModel::Name.new(self, nil, 'wiki') |
||
| 52 | end |
||
| 53 | |||
| 54 | # Sorts and groups pages by directory. |
||
| 55 | # |
||
| 56 | # pages - an array of WikiPage objects. |
||
| 57 | # |
||
| 58 | # Returns an array of WikiPage and WikiDirectory objects. The entries are |
||
| 59 | # sorted by alphabetical order (directories and pages inside each directory). |
||
| 60 | # Pages at the root level come before everything. |
||
| 61 | def self.group_by_directory(pages) |
||
| 62 | return [] if pages.blank? |
||
| 63 | |||
| 64 | pages.sort_by { |page| [page.directory, page.slug] } |
||
| 65 | .group_by(&:directory) |
||
| 66 | .map do |dir, pages| |
||
| 67 | if dir.present? |
||
| 68 | WikiDirectory.new(dir, pages) |
||
| 69 | else |
||
| 70 | pages |
||
| 71 | end |
||
| 72 | end |
||
| 73 | .flatten |
||
| 74 | end |
||
| 75 | |||
| 76 | def self.unhyphenize(name) |
||
| 77 | name.gsub(/-+/, ' ') |
||
| 78 | end |
||
| 79 | |||
| 80 | def to_key |
||
| 81 | [:slug] |
||
| 82 | end |
||
| 83 | |||
| 84 | validates :title, presence: true |
||
| 85 | validates :content, presence: true |
||
| 86 | |||
| 87 | # The GitLab ProjectWiki instance. |
||
| 88 | attr_reader :wiki |
||
| 89 | |||
| 90 | # The raw Gitlab::Git::WikiPage instance. |
||
| 91 | attr_reader :page |
||
| 92 | |||
| 93 | # The attributes Hash used for storing and validating |
||
| 94 | # new Page values before writing to the raw repository. |
||
| 95 | attr_accessor :attributes |
||
| 96 | |||
| 97 | def hook_attrs |
||
| 98 | Gitlab::HookData::WikiPageBuilder.new(self).build |
||
| 99 | end |
||
| 100 | |||
| 101 | def initialize(wiki, page = nil, persisted = false) |
||
| 102 | @wiki = wiki |
||
| 103 | @page = page |
||
| 104 | @persisted = persisted |
||
| 105 | @attributes = {}.with_indifferent_access |
||
| 106 | |||
| 107 | set_attributes if persisted? |
||
| 108 | end |
||
| 109 | |||
| 110 | # The escaped URL path of this page. |
||
| 111 | def slug |
||
| 112 | if @attributes[:slug].present? |
||
| 113 | @attributes[:slug] |
||
| 114 | else |
||
| 115 | wiki.wiki.preview_slug(title, format) |
||
| 116 | end |
||
| 117 | end |
||
| 118 | |||
| 119 | alias_method :to_param, :slug |
||
| 120 | |||
| 121 | def human_title |
||
| 122 | return 'Home' if title == 'home' |
||
| 123 | |||
| 124 | title |
||
| 125 | end |
||
| 126 | |||
| 127 | # The formatted title of this page. |
||
| 128 | def title |
||
| 129 | if @attributes[:title] |
||
| 130 | CGI.unescape_html(self.class.unhyphenize(@attributes[:title])) |
||
| 131 | else |
||
| 132 | "" |
||
| 133 | end |
||
| 134 | end |
||
| 135 | |||
| 136 | # Sets the title of this page. |
||
| 137 | def title=(new_title) |
||
| 138 | @attributes[:title] = new_title |
||
| 139 | end |
||
| 140 | |||
| 141 | # The raw content of this page. |
||
| 142 | def content |
||
| 143 | @attributes[:content] ||= @page&.text_data |
||
| 144 | end |
||
| 145 | |||
| 146 | # The hierarchy of the directory this page is contained in. |
||
| 147 | def directory |
||
| 148 | wiki.page_title_and_dir(slug)&.last.to_s |
||
| 149 | end |
||
| 150 | |||
| 151 | # The processed/formatted content of this page. |
||
| 152 | def formatted_content |
||
| 153 | @attributes[:formatted_content] ||= @wiki.page_formatted_data(@page) |
||
| 154 | end |
||
| 155 | |||
| 156 | # The markup format for the page. |
||
| 157 | def format |
||
| 158 | @attributes[:format] || :markdown |
||
| 159 | end |
||
| 160 | |||
| 161 | # The commit message for this page version. |
||
| 162 | def message |
||
| 163 | version.try(:message) |
||
| 164 | end |
||
| 165 | |||
| 166 | # The GitLab Commit instance for this page. |
||
| 167 | def version |
||
| 168 | return unless persisted? |
||
| 169 | |||
| 170 | @version ||= @page.version |
||
| 171 | end |
||
| 172 | |||
| 173 | def versions(options = {}) |
||
| 174 | return [] unless persisted? |
||
| 175 | |||
| 176 | wiki.wiki.page_versions(@page.path, options) |
||
| 177 | end |
||
| 178 | |||
| 179 | def count_versions |
||
| 180 | return [] unless persisted? |
||
| 181 | |||
| 182 | wiki.wiki.count_page_versions(@page.path) |
||
| 183 | end |
||
| 184 | |||
| 185 | def last_version |
||
| 186 | @last_version ||= versions(limit: 1).first |
||
| 187 | end |
||
| 188 | |||
| 189 | def last_commit_sha |
||
| 190 | last_version&.sha |
||
| 191 | end |
||
| 192 | |||
| 193 | # Returns boolean True or False if this instance |
||
| 194 | # is an old version of the page. |
||
| 195 | def historical? |
||
| 196 | return false unless last_commit_sha && version |
||
| 197 | |||
| 198 | @page.historical? && last_commit_sha != version.sha |
||
| 199 | end |
||
| 200 | |||
| 201 | # Returns boolean True or False if this instance |
||
| 202 | # is the latest commit version of the page. |
||
| 203 | def latest? |
||
| 204 | !historical? |
||
| 205 | end |
||
| 206 | |||
| 207 | # Returns boolean True or False if this instance |
||
| 208 | # has been fully created on disk or not. |
||
| 209 | def persisted? |
||
| 210 | @persisted == true |
||
| 211 | end |
||
| 212 | |||
| 213 | # Creates a new Wiki Page. |
||
| 214 | # |
||
| 215 | # attr - Hash of attributes to set on the new page. |
||
| 216 | # :title - The title (optionally including dir) for the new page. |
||
| 217 | # :content - The raw markup content. |
||
| 218 | # :format - Optional symbol representing the |
||
| 219 | # content format. Can be any type |
||
| 220 | # listed in the ProjectWiki::MARKUPS |
||
| 221 | # Hash. |
||
| 222 | # :message - Optional commit message to set on |
||
| 223 | # the new page. |
||
| 224 | # |
||
| 225 | # Returns the String SHA1 of the newly created page |
||
| 226 | # or False if the save was unsuccessful. |
||
| 227 | def create(attrs = {}) |
||
| 228 | update_attributes(attrs) |
||
| 229 | |||
| 230 | save(page_details: title) do |
||
| 231 | wiki.create_page(title, content, format, attrs[:message]) |
||
| 232 | end |
||
| 233 | end |
||
| 234 | |||
| 235 | # Updates an existing Wiki Page, creating a new version. |
||
| 236 | # |
||
| 237 | # attrs - Hash of attributes to be updated on the page. |
||
| 238 | # :content - The raw markup content to replace the existing. |
||
| 239 | # :format - Optional symbol representing the content format. |
||
| 240 | # See ProjectWiki::MARKUPS Hash for available formats. |
||
| 241 | # :message - Optional commit message to set on the new version. |
||
| 242 | # :last_commit_sha - Optional last commit sha to validate the page unchanged. |
||
| 243 | # :title - The Title (optionally including dir) to replace existing title |
||
| 244 | # |
||
| 245 | # Returns the String SHA1 of the newly created page |
||
| 246 | # or False if the save was unsuccessful. |
||
| 247 | def update(attrs = {}) |
||
| 248 | last_commit_sha = attrs.delete(:last_commit_sha) |
||
| 249 | |||
| 250 | if last_commit_sha && last_commit_sha != self.last_commit_sha |
||
| 251 | raise PageChangedError |
||
| 252 | end |
||
| 253 | |||
| 254 | update_attributes(attrs) |
||
| 255 | |||
| 256 | if title_changed? |
||
| 257 | page_details = title |
||
| 258 | |||
| 259 | if wiki.find_page(page_details).present? |
||
| 260 | @attributes[:title] = @page.url_path |
||
| 261 | raise PageRenameError |
||
| 262 | end |
||
| 263 | else |
||
| 264 | page_details = @page.url_path |
||
| 265 | end |
||
| 266 | |||
| 267 | save(page_details: page_details) do |
||
| 268 | wiki.update_page( |
||
| 269 | @page, |
||
| 270 | content: content, |
||
| 271 | format: format, |
||
| 272 | message: attrs[:message], |
||
| 273 | title: title |
||
| 274 | ) |
||
| 275 | end |
||
| 276 | end |
||
| 277 | |||
| 278 | # Destroys the Wiki Page. |
||
| 279 | # |
||
| 280 | # Returns boolean True or False. |
||
| 281 | def delete |
||
| 282 | if wiki.delete_page(@page) |
||
| 283 | true |
||
| 284 | else |
||
| 285 | false |
||
| 286 | end |
||
| 287 | end |
||
| 288 | |||
| 289 | # Relative path to the partial to be used when rendering collections |
||
| 290 | # of this object. |
||
| 291 | def to_partial_path |
||
| 292 | 'projects/wikis/wiki_page' |
||
| 293 | end |
||
| 294 | |||
| 295 | def id |
||
| 296 | page.version.to_s |
||
| 297 | end |
||
| 298 | |||
| 299 | def title_changed? |
||
| 300 | title.present? && self.class.unhyphenize(@page.url_path) != title |
||
| 301 | end |
||
| 302 | |||
| 303 | # Updates the current @attributes hash by merging a hash of params |
||
| 304 | def update_attributes(attrs) |
||
| 305 | attrs[:title] = process_title(attrs[:title]) if attrs[:title].present? |
||
| 306 | |||
| 307 | attrs.slice!(:content, :format, :message, :title) |
||
| 308 | |||
| 309 | @attributes.merge!(attrs) |
||
| 310 | end |
||
| 311 | |||
| 312 | private |
||
| 313 | |||
| 314 | # Process and format the title based on the user input. |
||
| 315 | def process_title(title) |
||
| 316 | return if title.blank? |
||
| 317 | |||
| 318 | title = deep_title_squish(title) |
||
| 319 | current_dirname = File.dirname(title) |
||
| 320 | |||
| 321 | if @page.present? |
||
| 322 | return title[1..-1] if current_dirname == '/' |
||
| 323 | return File.join([directory.presence, title].compact) if current_dirname == '.' |
||
| 324 | end |
||
| 325 | |||
| 326 | title |
||
| 327 | end |
||
| 328 | |||
| 329 | # This method squishes all the filename |
||
| 330 | # i.e: ' foo / bar / page_name' => 'foo/bar/page_name' |
||
| 331 | def deep_title_squish(title) |
||
| 332 | components = title.split(File::SEPARATOR).map(&:squish) |
||
| 333 | |||
| 334 | File.join(components) |
||
| 335 | end |
||
| 336 | |||
| 337 | def set_attributes |
||
| 338 | attributes[:slug] = @page.url_path |
||
| 339 | attributes[:title] = @page.title |
||
| 340 | attributes[:format] = @page.format |
||
| 341 | end |
||
| 342 | |||
| 343 | def save(page_details:) |
||
| 344 | return unless valid? |
||
| 345 | |||
| 346 | unless yield |
||
| 347 | errors.add(:base, wiki.error_message) |
||
| 348 | return false |
||
| 349 | end |
||
| 350 | |||
| 351 | page_title, page_dir = wiki.page_title_and_dir(page_details) |
||
| 352 | gitlab_git_wiki = wiki.wiki |
||
| 353 | @page = gitlab_git_wiki.page(title: page_title, dir: page_dir) |
||
| 354 | |||
| 355 | set_attributes |
||
| 356 | @persisted = errors.blank? |
||
| 357 | end |
||
| 358 | end |
||
| 359 | |||
| 360 | */ |
||
| 361 | } |
||
| 362 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.