aboutsummaryrefslogtreecommitdiffstats
path: root/models/wiki.go
Commit message (Expand)AuthorAgeFilesLines
* Handle and propagate errors when checking if paths are Dirs, Files or Exist (...zeripath2020-11-271-2/+7
* Add owner_name column for table repository for maintaince reason (#9717)Lunny Xiao2020-01-121-2/+3
* Move wiki related funtions from models to services/wiki (#9355)Lunny Xiao2020-01-071-313/+0
* Add Close() method to gogitRepository (#8901)zeripath2019-11-131-0/+2
* Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631)zeripath2019-10-161-3/+18
* Use gitea forked macaron (#7933)Tamal Saha2019-08-231-1/+1
* Fix bug create/edit wiki pages when code master branch protected (#7580)Lunny Xiao2019-07-251-1/+7
* Add golangci (#6418)kolaente2019-06-121-2/+10
* Remove local clones & make hooks run on merge/edit/upload (#6672)zeripath2019-05-111-80/+151
* move code.gitea.io/git to code.gitea.io/gitea/modules/git (#6364)Lunny Xiao2019-03-271-4/+3
* Fix serving of raw wiki files other than .md (#5814)Gabriel Silva Simões2019-02-051-1/+1
* Fix deadlock when sqlite (#5118)Lunny Xiao2018-10-191-1/+1
* Fix to use only needed columns from tables to get repository git paths (#3870)Lauris BH2018-05-021-1/+1
* Fix rendering of wiki page list if wiki repo contains other files (#3454)Lauris BH2018-02-051-1/+1
* Remove hardcoded paths to fix randomly failing tests (#3347)Lauris BH2018-01-111-1/+9
* Various wiki bug fixes (#2996)Ethan Koenig2017-11-281-42/+55
* Update code.gitea.io/git (#1824)Ethan Koenig2017-05-301-2/+8
* fix wiki bugs (#1294)Lunny Xiao2017-03-201-1/+5
* Take back control of hooks (#1006)Lunny Xiao2017-02-231-2/+2
* Cleanup log messagingGabriel Jackson2017-02-021-2/+2
* Unit tests for models/wikiEthan Koenig2017-01-271-1/+1
* Add a reserved path check to the wiki (#720)Bwko2017-01-221-1/+18
* fixed bugs on Wiki and resolved #667 (#674)Lunny Xiao2017-01-211-15/+15
* In the wiki title replace tab with a space (#371)Bwko2016-12-111-0/+1
* Fix for #320Bwko2016-12-031-3/+1
* Catch os... errorsBwko2016-12-021-3/+13
* Fix lint errors in models/wiki (just add methods docs)Sandro Santilli2016-11-141-0/+6
* Update import paths from github.com/go-gitea to code.gitea.io (#135)Sandro Santilli2016-11-101-3/+3
* Replace gogits/git-module dependency with go-gitea/git (#94)Sandro Santilli2016-11-061-1/+1
* Change import reference to match gitea instead of gogs (#37)Rémy Boulanouar2016-11-031-2/+2
* modules/sync: rename SingleInstancePool to ExclusivePoolUnknwon2016-08-301-1/+1
* #3505 use user’s info for committer and authorUnknwon2016-08-271-2/+8
* #3467 fix clone fail when wiki is emptyUnknwon2016-08-151-1/+3
* Web editor: improve edit file and diff previewUnknwon2016-08-141-15/+2
* modules/sync: move sync objects to independent moduleUnknwon2016-08-141-5/+2
* Squashed commit of the following:Richard Mahn2016-08-141-39/+1
* #3233 code cleanup and minor issue fixUnknwon2016-08-111-0/+2
* Fix wiki vulnerabilitiesUnknwon2016-07-011-4/+13
* Minor fixes for #2746Unknwon2016-03-041-26/+25
* Add ability to delete single wiki pages.Josh Frye2016-03-041-0/+37
* Removed empty line, multi return argsJofkos2015-12-201-3/+3
* Wiki pages containing question marks in their name weren't loadingJofkos2015-12-201-2/+4
* rename import pathUnknwon2015-12-151-1/+1
* fix APIUnknwon2015-12-011-1/+1
* finish wikiUnknwon2015-11-301-0/+5
* wiki: finish editUnknwon2015-11-271-3/+22
* wiki: finish newUnknwon2015-11-271-3/+101
* introduce git-shellUnknwon2015-11-261-0/+56
} /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * SonarQube
 * Copyright (C) 2009-2017 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
import numeral from 'numeral';
import { translate, translateWithParameters } from './l10n';


const HOURS_IN_DAY = 8;

/**
 * Format a measure value for a given type
 * @param {string|number} value
 * @param {string} type
 */
export function formatMeasure (value, type) {
  const formatter = getFormatter(type);
  return useFormatter(value, formatter);
}

/**
 * Format a measure variation for a given type
 * @param {string|number} value
 * @param {string} type
 */
export function formatMeasureVariation (value, type) {
  const formatter = getVariationFormatter(type);
  return useFormatter(value, formatter);
}

/**
 * Return a localized metric name
 * @param {string} metricKey
 * @returns {string}
 */
export function localizeMetric (metricKey) {
  return translate('metric', metricKey, 'name');
}

/**
 * Return corresponding "short" for better display in UI
 * @param {string} type
 * @returns {string}
 */
export function getShortType (type) {
  if (type === 'INT') {
    return 'SHORT_INT';
  } else if (type === 'WORK_DUR') {
    return 'SHORT_WORK_DUR';
  }
  return type;
}

/**
 * Map metrics
 * @param {Array} measures
 * @param {Array} metrics
 * @returns {Array}
 */
export function enhanceMeasuresWithMetrics (measures, metrics) {
  return measures.map(measure => {
    const metric = metrics.find(metric => metric.key === measure.metric);
    return { ...measure, metric };
  });
}

/**
 * Get period value of a measure
 * @param measure
 * @param periodIndex
 */
export function getPeriodValue (measure, periodIndex) {
  const { periods } = measure;
  const period = periods.find(period => period.index === periodIndex);
  return period ? period.value : null;
}

/**
 * Check if metric is differential
 * @param {string} metricKey
 * @returns {boolean}
 */
export function isDiffMetric (metricKey) {
  return metricKey.indexOf('new_') === 0;
}

/*
 * Helpers
 */

function useFormatter (value, formatter) {
  return value != null && value !== '' && formatter != null ?
      formatter(value) : null;
}

function getFormatter (type) {
  const FORMATTERS = {
    'INT': intFormatter,
    'SHORT_INT': shortIntFormatter,
    'FLOAT': floatFormatter,
    'PERCENT': percentFormatter,
    'WORK_DUR': durationFormatter,
    'SHORT_WORK_DUR': shortDurationFormatter,
    'RATING': ratingFormatter,
    'LEVEL': levelFormatter,
    'MILLISEC': millisecondsFormatter
  };
  return FORMATTERS[type] || noFormatter;
}

function getVariationFormatter (type) {
  const FORMATTERS = {
    'INT': intVariationFormatter,
    'SHORT_INT': shortIntVariationFormatter,
    'FLOAT': floatVariationFormatter,
    'PERCENT': percentVariationFormatter,
    'WORK_DUR': durationVariationFormatter,
    'SHORT_WORK_DUR': shortDurationVariationFormatter,
    'RATING': emptyFormatter,
    'LEVEL': emptyFormatter,
    'MILLISEC': millisecondsVariationFormatter
  };
  return FORMATTERS[type] || noFormatter;
}

/*
 * Formatters
 */

function noFormatter (value) {
  return value;
}

function emptyFormatter () {
  return null;
}

function intFormatter (value) {
  return numeral(value).format('0,0');
}

function intVariationFormatter (value) {
  return numeral(value).format('+0,0');
}

function shortIntFormatter (value) {
  let format = '0,0';
  if (value >= 1000) {
    format = '0.[0]a';
  }
  if (value >= 10000) {
    format = '0a';
  }
  return numeral(value).format(format);
}

function shortIntVariationFormatter (value) {
  const formatted = shortIntFormatter(Math.abs(value));
  return value < 0 ? `-${formatted}` : `+${formatted}`;
}

function floatFormatter (value) {
  return numeral(value).format('0,0.0[0000]');
}

function floatVariationFormatter (value) {
  return value === 0 ? '+0.0' : numeral(value).format('+0,0.0[0000]');
}

function percentFormatter (value) {
  value = parseFloat(value);
  return value === 100 ? '100%' : numeral(value / 100).format('0,0.0%');
}

function percentVariationFormatter (value) {
  value = parseFloat(value);
  return value === 0 ? '+0.0%' : numeral(value / 100).format('+0,0.0%');
}

function ratingFormatter (value) {
  value = parseInt(value, 10);
  return String.fromCharCode(97 + value - 1).toUpperCase();
}

function levelFormatter (value) {
  const l10nKey = 'metric.level.' + value;
  const result = translate(l10nKey);

  // if couldn't translate, return the initial value
  return l10nKey !== result ? result : value;
}

function millisecondsFormatter (value) {
  const ONE_SECOND = 1000;
  const ONE_MINUTE = 60 * ONE_SECOND;
  if (value >= ONE_MINUTE) {
    const minutes = Math.round(value / ONE_MINUTE);
    return `${minutes}min`;
  } else if (value >= ONE_SECOND) {
    const seconds = Math.round(value / ONE_SECOND);
    return `${seconds}s`;
  } else {
    return `${value}ms`;
  }
}

function millisecondsVariationFormatter (value) {
  const absValue = Math.abs(value);
  const formattedValue = millisecondsFormatter(absValue);
  return value < 0 ? `-${formattedValue}` : `+${formattedValue}`;
}

/*
 * Debt Formatters
 */

function shouldDisplayDays (days) {
  return days > 0;
}

function shouldDisplayDaysInShortFormat (days) {
  return days > 0.9;
}

function shouldDisplayHours (days, hours) {
  return hours > 0 && days < 10;
}

function shouldDisplayHoursInShortFormat (hours) {
  return hours > 0.9;
}

function shouldDisplayMinutes (days, hours, minutes) {
  return minutes > 0 && hours < 10 && days === 0;
}

function addSpaceIfNeeded (value) {
  return value.length > 0 ? value + ' ' : value;
}

function formatDuration (isNegative, days, hours, minutes) {
  let formatted = '';
  if (shouldDisplayDays(days)) {
    formatted += translateWithParameters('work_duration.x_days', isNegative ? -1 * days : days);
  }
  if (shouldDisplayHours(days, hours)) {
    formatted = addSpaceIfNeeded(formatted);
    formatted += translateWithParameters('work_duration.x_hours',
        isNegative && formatted.length === 0 ? -1 * hours : hours);
  }
  if (shouldDisplayMinutes(days, hours, minutes)) {
    formatted = addSpaceIfNeeded(formatted);
    formatted += translateWithParameters('work_duration.x_minutes',
        isNegative && formatted.length === 0 ? -1 * minutes : minutes);
  }
  return formatted;
}

function formatDurationShort (isNegative, days, hours, minutes) {
  if (shouldDisplayDaysInShortFormat(days)) {
    const roundedDays = Math.round(days);
    const formattedDays = formatMeasure(isNegative ? -1 * roundedDays : roundedDays, 'SHORT_INT');
    return translateWithParameters('work_duration.x_days', formattedDays);
  }

  if (shouldDisplayHoursInShortFormat(hours)) {
    const roundedHours = Math.round(hours);
    const formattedHours = formatMeasure(isNegative ? -1 * roundedHours : roundedHours, 'SHORT_INT');
    return translateWithParameters('work_duration.x_hours', formattedHours);
  }

  const formattedMinutes = formatMeasure(isNegative ? -1 * minutes : minutes, 'SHORT_INT');
  return translateWithParameters('work_duration.x_minutes', formattedMinutes);
}

function durationFormatter (value) {
  if (value === 0 || value === '0') {
    return '0';
  }
  const hoursInDay = HOURS_IN_DAY;
  const isNegative = value < 0;
  const absValue = Math.abs(value);
  const days = Math.floor(absValue / hoursInDay / 60);
  let remainingValue = absValue - days * hoursInDay * 60;
  const hours = Math.floor(remainingValue / 60);
  remainingValue -= hours * 60;
  return formatDuration(isNegative, days, hours, remainingValue);
}

function shortDurationFormatter (value) {
  value = parseInt(value, 10);
  if (value === 0 || value === '0') {
    return '0';
  }
  const hoursInDay = HOURS_IN_DAY;
  const isNegative = value < 0;
  const absValue = Math.abs(value);
  const days = absValue / hoursInDay / 60;
  let remainingValue = absValue - Math.floor(days) * hoursInDay * 60;
  const hours = remainingValue / 60;
  remainingValue -= Math.floor(hours) * 60;
  return formatDurationShort(isNegative, days, hours, remainingValue);
}

function durationVariationFormatter (value) {
  if (value === 0 || value === '0') {
    return '+0';
  }
  const formatted = durationFormatter(value);
  return formatted[0] !== '-' ? '+' + formatted : formatted;
}

function shortDurationVariationFormatter (value) {
  if (value === 0 || value === '0') {
    return '+0';
  }
  const formatted = shortDurationFormatter(value);
  return formatted[0] !== '-' ? '+' + formatted : formatted;
}

function getRatingGrid () {
  // workaround cyclic dependencies
  const getStore = require('../app/utils/getStore').default;
  const { getSettingValue } = require('../store/rootReducer');

  const store = getStore();
  const settingValue = getSettingValue(store.getState(), 'sonar.technicalDebt.ratingGrid');
  return settingValue ? settingValue.value : '';
}

let maintainabilityRatingGrid;
function getMaintainabilityRatingGrid () {
  if (maintainabilityRatingGrid) {
    return maintainabilityRatingGrid;
  }

  const str = getRatingGrid();
  const numbers = str.split(',')
      .map(s => parseFloat(s))
      .filter(n => !isNaN(n));

  if (numbers.length === 4) {
    maintainabilityRatingGrid = numbers;
  } else {
    maintainabilityRatingGrid = [0, 0, 0, 0];
  }

  return maintainabilityRatingGrid;
}

function getMaintainabilityRatingTooltip (rating) {
  const maintainabilityGrid = getMaintainabilityRatingGrid();
  const maintainabilityRatingThreshold =
      maintainabilityGrid[Math.floor(rating) - 2];

  if (rating < 2) {
    return translateWithParameters(
        'metric.sqale_rating.tooltip.A',
        formatMeasure(maintainabilityGrid[0] * 100, 'PERCENT')
    );
  }

  const ratingLetter = formatMeasure(rating, 'RATING');

  return translateWithParameters(
      'metric.sqale_rating.tooltip',
      ratingLetter,
      formatMeasure(maintainabilityRatingThreshold * 100, 'PERCENT')
  );
}

export function getRatingTooltip (metricKey, value) {
  const ratingLetter = formatMeasure(value, 'RATING');

  const finalMetricKey = metricKey.startsWith('new_') ? metricKey.substr(4) : metricKey;

  return (finalMetricKey === 'sqale_rating' || finalMetricKey === 'maintainability_rating') ?
      getMaintainabilityRatingTooltip(value) :
      translate('metric', finalMetricKey, 'tooltip', ratingLetter);
}