##// END OF EJS Templates
Pull request !2362 Created on Thu, 01 Jul 2021 12:06:29, by
 # import all the libraries

import numpy as np
import re
import nltk
from sklearn.datasets import load_files
nltk.download('stopwords')
import pickle
import pandas as pd
from sklearn.multioutput import MultiOutputClassifier
from sklearn.preprocessing import LabelEncoder
from nltk.corpus import stopwords
from collections import defaultdict
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import make_pipeline
import joblib 


pd.options.display.float_format = "{:,.2f}".format

df = pd.read_csv(r'/Users/tabetibrahim/Desktop/Vona/vef/facture_mobile_mars_avril_2021.csv', sep=";")

data_set = df[['Tem section 1', 'Tem section 2','Tem section 3','Description facture operateur']]
data_set.head()

tem_section= data_set[['Tem section 1', 'Tem section 2','Tem section 3']]

tem_en= data_set[['Tem section 1', 'Tem section 2','Tem section 3']]

labelencoder = LabelEncoder()
le = LabelEncoder()
encoder_dict = defaultdict(LabelEncoder)

df_encode = tem_en.apply(lambda x: encoder_dict[x.name].fit_transform(x))

inverse_transform_lambda = df_encode.apply(lambda x: encoder_dict[x.name].inverse_transform(x))

X, y =  data_set[['Description facture operateur']], tem_section


import nltk
import pandas as pd
import numpy as np
from nltk.stem import PorterStemmer
from nltk.corpus import stopwords

# If not previously performed:
# nltk.download('stopwords')

stemming = PorterStemmer()
stops = set(stopwords.words("french"))

def apply_cleaning_function_to_list(X):
    cleaned_X = []
    for element in X:
        cleaned_X.append(clean_text(element))
    return cleaned_X


def clean_text(raw_text):
    """This function works on a raw text string, and:
        1) changes to lower case
        2) tokenizes (breaks down into words
        3) removes punctuation and non-word text
        4) finds word stems
        5) removes stop words
        6) rejoins meaningful stem words"""

    # Convert to lower case
    text = raw_text.lower()

    # Tokenize
    tokens = nltk.word_tokenize(text)

    # Keep only words (removes punctuation + numbers)
    # use .isalnum to keep also numbers
    token_words = [w for w in tokens if w.isalpha()]

    # Stemming
    stemmed_words = [stemming.stem(w) for w in token_words]

    # Remove stop words
    meaningful_words = [w for w in stemmed_words if not w in stops]

    # Rejoin meaningful stemmed words
    joined_words = ( " ".join(meaningful_words))

    # Return cleaned data
    return joined_words




# Get text to clean
text_to_clean = list(data_set['Description facture operateur'])

# Clean text
cleaned_text = apply_cleaning_function_to_list(text_to_clean)

# Add cleaned data back into DataFrame
data_set['cleaned_review'] = cleaned_text

# Remove temporary cleaned_text list (after transfer to DataFrame)
del cleaned_text


from sklearn.model_selection import train_test_split
X = list(data_set['cleaned_review'])
y = df_encode
X_train, X_test, y_train, y_test = train_test_split(
    X,y, test_size = 0.25)

def create_bag_of_words(X):
    from sklearn.feature_extraction.text import CountVectorizer

    print ('Creating bag of words...')
    # Initialize the "CountVectorizer" object, which is scikit-learn's
    # bag of words tool.  

    # In this example features may be single words or two consecutive words
    # (as shown by ngram_range = 1,2)
    vectorizer = CountVectorizer(analyzer = "word",   \
                                 tokenizer = None,    \
                                 preprocessor = None, \
                                 stop_words = None,   \
                                 ngram_range = (1,2), \
                                 max_features = 10000
                                ) 

    # fit_transform() does two functions: First, it fits the model
    # and learns the vocabulary; second, it transforms our training data
    # into feature vectors. The input to fit_transform should be a list of 
    # strings. The output is a sparse array
    train_data_features = vectorizer.fit_transform(X)

    # Convert to a NumPy array for easy of handling
    train_data_features = train_data_features.toarray()

    # tfidf transform
    from sklearn.feature_extraction.text import TfidfTransformer
    tfidf = TfidfTransformer()
    tfidf_features = tfidf.fit_transform(train_data_features).toarray()

    # Get words in the vocabulary
    vocab = vectorizer.get_feature_names()

    return vectorizer, vocab, train_data_features, tfidf_features, tfidf


vectorizer, vocab, train_data_features, tfidf_features, tfidf  = \
    create_bag_of_words(X_train)

def train_logistic_regression(features, label):
    print ("Training the logistic regression model...")
    from sklearn.linear_model import LogisticRegression

    ml_model =  MultiOutputClassifier(LogisticRegression(C = 0.1, solver = 'newton-cg', penalty = 'l2',multi_class = 'multinomial'))


    ml_model.fit(features, label)

    print ('Finished')
    return  ml_model 

ml_model = train_logistic_regression(tfidf_features, y_train)


test_data_features = vectorizer.transform(X_test)
# Convert to numpy array
test_data_features = test_data_features.toarray()


test_data_tfidf_features = tfidf.fit_transform(test_data_features)
# Convert to numpy array
test_data_tfidf_features = test_data_tfidf_features.toarray()


predicted_y = ml_model.predict(test_data_tfidf_features)
correctly_identified_y = predicted_y == y_test
accuracy = np.mean(correctly_identified_y) * 100
1 version available for this pull request, show versions.
There are new changes for `branch:stable` in source repository, please consider updating this pull request.
ver Time Author Commit Description
24 commits hidden, click expand to show them.
@@ -0,0 +1,40 b''
1 |RCE| 4.25.1 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2021-04-06
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18
19
20 Security
21 ^^^^^^^^
22
23
24
25 Performance
26 ^^^^^^^^^^^
27
28
29
30 Fixes
31 ^^^^^
32
33 - Artifacts: fixed admin panel bad urls generated for the new artifacts admin view in CE edition.
34
35
36
37 Upgrade notes
38 ^^^^^^^^^^^^^
39
40 - Un-scheduled release addressing problems in 4.25.X releases.
@@ -0,0 +1,53 b''
1 |RCE| 4.25.2 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2021-04-14
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18 - Comments: refresh on draft sidebar on draft submit.
19 - Vcsserver: log exceptions into the logs
20 - Archiving: make it explicit archiving a repo is irreversible.
21 - My-account: updated bookmarks UX
22 - Pull requests: added awaiting my review filter for users pull-requests.
23 Additionally the awaiting my review now properly filters pull requests that have no review votes on them.
24
25
26 Security
27 ^^^^^^^^
28
29
30
31 Performance
32 ^^^^^^^^^^^
33
34
35
36 Fixes
37 ^^^^^
38
39 - Draft comments: fixed logic in toggle all draft for submit.
40 - Draft comments: when submitting edited drafts also clear the history to prevent DB problems.
41 - Mercurial: fixed a case of lookup branches that had 40 characters in length.
42 - Gists: block id input for public gists.
43 - Pull requests: fixed problems with unicode characters in branches.
44 - Pull requests: small ui fix for grid.
45 - Summary: fixed ui on summary page for non-admins.
46 The setup instructions were broken if user had no write permissions.
47 - Users: make user data loading more resilient to errors.
48
49
50 Upgrade notes
51 ^^^^^^^^^^^^^
52
53 - Scheduled release addressing problems in 4.25.X releases.
@@ -0,0 +1,40 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
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 Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from rhodecode.apps._base import BaseAppView, DataGridAppView
24 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
25
26 log = logging.getLogger(__name__)
27
28
29 class AdminArtifactsView(BaseAppView, DataGridAppView):
30
31 def load_default_context(self):
32 c = self._get_local_tmpl_context()
33 return c
34
35 @LoginRequired()
36 @HasPermissionAllDecorator('hg.admin')
37 def artifacts(self):
38 c = self.load_default_context()
39 c.active = 'artifacts'
40 return self._get_template_context(c)
@@ -0,0 +1,39 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
3
4 <%def name="title()">
5 ${_('Artifacts Admin')}
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
9 </%def>
10
11 <%def name="breadcrumbs_links()"></%def>
12
13 <%def name="menu_bar_nav()">
14 ${self.menu_items(active='admin')}
15 </%def>
16
17 <%def name="menu_bar_subnav()">
18 ${self.admin_menu(active='artifacts')}
19 </%def>
20
21 <%def name="main()">
22
23 <div class="box">
24
25 <div class="panel panel-default">
26 <div class="panel-heading">
27 <h3 class="panel-title">${_('Artifacts Administration.')}</h3>
28 </div>
29 <div class="panel-body">
30 <h4>${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='<a href="mailto:sales@rhodecode.com">sales@rhodecode.com</a>')|n}</h4>
31
32 </div>
33 </div>
34
35 </div>
36
37
38 </%def>
39
@@ -1,5 +1,6 b''
1 1 [bumpversion]
2 current_version = 4.25.0
2 current_version = 4.25.2
3 3 message = release: Bump version {current_version} to {new_version}
4 4
5 5 [bumpversion:file:rhodecode/VERSION]
6
@@ -1,77 +1,79 b''
1 1 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0
2 2 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1
3 3 c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0
4 4 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1
5 5 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2
6 6 baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0
7 7 32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1
8 8 fa695cdb411d294679ac081d595ac654e5613b03 v4.3.0
9 9 0e4dc11b58cad833c513fe17bac39e6850edf959 v4.3.1
10 10 8a876f48f5cb1d018b837db28ff928500cb32cfb v4.4.0
11 11 8dd86b410b1aac086ffdfc524ef300f896af5047 v4.4.1
12 12 d2514226abc8d3b4f6fb57765f47d1b6fb360a05 v4.4.2
13 13 27d783325930af6dad2741476c0d0b1b7c8415c2 v4.5.0
14 14 7f2016f352abcbdba4a19d4039c386e9629449da v4.5.1
15 15 416fec799314c70a5c780fb28b3357b08869333a v4.5.2
16 16 27c3b85fafc83143e6678fbc3da69e1615bcac55 v4.6.0
17 17 5ad13deb9118c2a5243d4032d4d9cc174e5872db v4.6.1
18 18 2be921e01fa24bb102696ada596f87464c3666f6 v4.7.0
19 19 7198bdec29c2872c974431d55200d0398354cdb1 v4.7.1
20 20 bd1c8d230fe741c2dfd7100a0ef39fd0774fd581 v4.7.2
21 21 9731914f89765d9628dc4dddc84bc9402aa124c8 v4.8.0
22 22 c5a2b7d0e4bbdebc4a62d7b624befe375207b659 v4.9.0
23 23 d9aa3b27ac9f7e78359775c75fedf7bfece232f1 v4.9.1
24 24 4ba4d74981cec5d6b28b158f875a2540952c2f74 v4.10.0
25 25 0a6821cbd6b0b3c21503002f88800679fa35ab63 v4.10.1
26 26 434ad90ec8d621f4416074b84f6e9ce03964defb v4.10.2
27 27 68baee10e698da2724c6e0f698c03a6abb993bf2 v4.10.3
28 28 00821d3afd1dce3f4767cc353f84a17f7d5218a1 v4.10.4
29 29 22f6744ad8cc274311825f63f953e4dee2ea5cb9 v4.10.5
30 30 96eb24bea2f5f9258775245e3f09f6fa0a4dda01 v4.10.6
31 31 3121217a812c956d7dd5a5875821bd73e8002a32 v4.11.0
32 32 fa98b454715ac5b912f39e84af54345909a2a805 v4.11.1
33 33 3982abcfdcc229a723cebe52d3a9bcff10bba08e v4.11.2
34 34 33195f145db9172f0a8f1487e09207178a6ab065 v4.11.3
35 35 194c74f33e32bbae6fc4d71ec5a999cff3c13605 v4.11.4
36 36 8fbd8b0c3ddc2fa4ac9e4ca16942a03eb593df2d v4.11.5
37 37 f0609aa5d5d05a1ca2f97c3995542236131c9d8a v4.11.6
38 38 b5b30547d90d2e088472a70c84878f429ffbf40d v4.12.0
39 39 9072253aa8894d20c00b4a43dc61c2168c1eff94 v4.12.1
40 40 6a517543ea9ef9987d74371bd2a315eb0b232dc9 v4.12.2
41 41 7fc0731b024c3114be87865eda7ab621cc957e32 v4.12.3
42 42 6d531c0b068c6eda62dddceedc9f845ecb6feb6f v4.12.4
43 43 3d6bf2d81b1564830eb5e83396110d2a9a93eb1e v4.13.0
44 44 5468fc89e708bd90e413cd0d54350017abbdbc0e v4.13.1
45 45 610d621550521c314ee97b3d43473ac0bcf06fb8 v4.13.2
46 46 7dc62c090881fb5d03268141e71e0940d7c3295d v4.13.3
47 47 9151328c1c46b72ba6f00d7640d9141e75aa1ca2 v4.14.0
48 48 a47eeac5dfa41fa6779d90452affba4091c3ade8 v4.14.1
49 49 4b34ce0d2c3c10510626b3b65044939bb7a2cddf v4.15.0
50 50 14502561d22e6b70613674cd675ae9a604b7989f v4.15.1
51 51 4aaa40b605b01af78a9f6882eca561c54b525ef0 v4.15.2
52 52 797744642eca86640ed20bef2cd77445780abaec v4.16.0
53 53 6c3452c7c25ed35ff269690929e11960ed6ad7d3 v4.16.1
54 54 5d8057df561c4b6b81b6401aed7d2f911e6e77f7 v4.16.2
55 55 13acfc008896ef4c62546bab5074e8f6f89b4fa7 v4.17.0
56 56 45b9b610976f483877142fe75321808ce9ebac59 v4.17.1
57 57 ad5bd0c4bd322fdbd04bb825a3d027e08f7a3901 v4.17.2
58 58 037f5794b55a6236d68f6485a485372dde6566e0 v4.17.3
59 59 83bc3100cfd6094c1d04f475ddb299b7dc3d0b33 v4.17.4
60 60 e3de8c95baf8cc9109ca56aee8193a2cb6a54c8a v4.17.4
61 61 f37a3126570477543507f0bc9d245ce75546181a v4.18.0
62 62 71d8791463e87b64c1a18475de330ee600d37561 v4.18.1
63 63 4bd6b75dac1d25c64885d4d49385e5533f21c525 v4.18.2
64 64 12ed92fe57f2e9fc7b71dc0b65e26c2da5c7085f v4.18.3
65 65 ddef396a6567117de531d67d44c739cbbfc3eebb v4.19.0
66 66 c0c65acd73914bf4368222d510afe1161ab8c07c v4.19.1
67 67 7ac623a4a2405917e2af660d645ded662011e40d v4.19.2
68 68 ef7ffda65eeb90c3ba88590a6cb816ef9b0bc232 v4.19.3
69 69 3e635489bb7961df93b01e42454ad1a8730ae968 v4.20.0
70 70 7e2eb896a02ca7cd2cd9f0f853ef3dac3f0039e3 v4.20.1
71 71 8bb5fece08ab65986225b184e46f53d2a71729cb v4.21.0
72 72 90734aac31ee4563bbe665a43ff73190cc762275 v4.22.0
73 73 a9655707f7cf4146affc51c12fe5ed8e02898a57 v4.23.0
74 74 56310d93b33b97535908ef9c7b0985b89bb7fad2 v4.23.1
75 75 7637c38528fa38c1eabc1fde6a869c20995a0da7 v4.23.2
76 76 6aeb4ac3ef7f0ac699c914740dad3688c9495e83 v4.24.0
77 77 6eaf953da06e468a4c4e5239d3d0e700bda6b163 v4.24.1
78 f8161cbc2d94a935d3c395a0e758d9a094287169 v4.25.0
79 77fe47b5b39338e71b2c040de2c0359b529b6251 v4.25.1
@@ -1,33 +1,33 b''
1 1 [DEFAULT]
2 2 done = false
3 3
4 4 [task:bump_version]
5 5 done = true
6 6
7 7 [task:rc_tools_pinned]
8 8 done = true
9 9
10 10 [task:fixes_on_stable]
11 11 done = true
12 12
13 13 [task:pip2nix_generated]
14 14 done = true
15 15
16 16 [task:changelog_updated]
17 17 done = true
18 18
19 19 [task:generate_api_docs]
20 20 done = true
21 21
22 22 [release]
23 23 state = prepared
24 version = 4.11.6
24 version = 4.25.2
25 25
26 26 [task:updated_translation]
27 27
28 28 [task:generate_js_routes]
29 29
30 30 [task:updated_trial_license]
31 31
32 32 [task:generate_oss_licenses]
33 33
@@ -1,154 +1,156 b''
1 1 .. _rhodecode-release-notes-ref:
2 2
3 3 Release Notes
4 4 =============
5 5
6 6 |RCE| 4.x Versions
7 7 ------------------
8 8
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-4.25.2.rst
13 release-notes-4.25.1.rst
12 14 release-notes-4.25.0.rst
13 15 release-notes-4.24.1.rst
14 16 release-notes-4.24.0.rst
15 17 release-notes-4.23.2.rst
16 18 release-notes-4.23.1.rst
17 19 release-notes-4.23.0.rst
18 20 release-notes-4.22.0.rst
19 21 release-notes-4.21.0.rst
20 22 release-notes-4.20.1.rst
21 23 release-notes-4.20.0.rst
22 24 release-notes-4.19.3.rst
23 25 release-notes-4.19.2.rst
24 26 release-notes-4.19.1.rst
25 27 release-notes-4.19.0.rst
26 28 release-notes-4.18.3.rst
27 29 release-notes-4.18.2.rst
28 30 release-notes-4.18.1.rst
29 31 release-notes-4.18.0.rst
30 32 release-notes-4.17.4.rst
31 33 release-notes-4.17.3.rst
32 34 release-notes-4.17.2.rst
33 35 release-notes-4.17.1.rst
34 36 release-notes-4.17.0.rst
35 37 release-notes-4.16.2.rst
36 38 release-notes-4.16.1.rst
37 39 release-notes-4.16.0.rst
38 40 release-notes-4.15.2.rst
39 41 release-notes-4.15.1.rst
40 42 release-notes-4.15.0.rst
41 43 release-notes-4.14.1.rst
42 44 release-notes-4.14.0.rst
43 45 release-notes-4.13.3.rst
44 46 release-notes-4.13.2.rst
45 47 release-notes-4.13.1.rst
46 48 release-notes-4.13.0.rst
47 49 release-notes-4.12.4.rst
48 50 release-notes-4.12.3.rst
49 51 release-notes-4.12.2.rst
50 52 release-notes-4.12.1.rst
51 53 release-notes-4.12.0.rst
52 54 release-notes-4.11.6.rst
53 55 release-notes-4.11.5.rst
54 56 release-notes-4.11.4.rst
55 57 release-notes-4.11.3.rst
56 58 release-notes-4.11.2.rst
57 59 release-notes-4.11.1.rst
58 60 release-notes-4.11.0.rst
59 61 release-notes-4.10.6.rst
60 62 release-notes-4.10.5.rst
61 63 release-notes-4.10.4.rst
62 64 release-notes-4.10.3.rst
63 65 release-notes-4.10.2.rst
64 66 release-notes-4.10.1.rst
65 67 release-notes-4.10.0.rst
66 68 release-notes-4.9.1.rst
67 69 release-notes-4.9.0.rst
68 70 release-notes-4.8.0.rst
69 71 release-notes-4.7.2.rst
70 72 release-notes-4.7.1.rst
71 73 release-notes-4.7.0.rst
72 74 release-notes-4.6.1.rst
73 75 release-notes-4.6.0.rst
74 76 release-notes-4.5.2.rst
75 77 release-notes-4.5.1.rst
76 78 release-notes-4.5.0.rst
77 79 release-notes-4.4.2.rst
78 80 release-notes-4.4.1.rst
79 81 release-notes-4.4.0.rst
80 82 release-notes-4.3.1.rst
81 83 release-notes-4.3.0.rst
82 84 release-notes-4.2.1.rst
83 85 release-notes-4.2.0.rst
84 86 release-notes-4.1.2.rst
85 87 release-notes-4.1.1.rst
86 88 release-notes-4.1.0.rst
87 89 release-notes-4.0.1.rst
88 90 release-notes-4.0.0.rst
89 91
90 92 |RCE| 3.x Versions
91 93 ------------------
92 94
93 95 .. toctree::
94 96 :maxdepth: 1
95 97
96 98 release-notes-3.8.4.rst
97 99 release-notes-3.8.3.rst
98 100 release-notes-3.8.2.rst
99 101 release-notes-3.8.1.rst
100 102 release-notes-3.8.0.rst
101 103 release-notes-3.7.1.rst
102 104 release-notes-3.7.0.rst
103 105 release-notes-3.6.1.rst
104 106 release-notes-3.6.0.rst
105 107 release-notes-3.5.2.rst
106 108 release-notes-3.5.1.rst
107 109 release-notes-3.5.0.rst
108 110 release-notes-3.4.1.rst
109 111 release-notes-3.4.0.rst
110 112 release-notes-3.3.4.rst
111 113 release-notes-3.3.3.rst
112 114 release-notes-3.3.2.rst
113 115 release-notes-3.3.1.rst
114 116 release-notes-3.3.0.rst
115 117 release-notes-3.2.3.rst
116 118 release-notes-3.2.2.rst
117 119 release-notes-3.2.1.rst
118 120 release-notes-3.2.0.rst
119 121 release-notes-3.1.1.rst
120 122 release-notes-3.1.0.rst
121 123 release-notes-3.0.2.rst
122 124 release-notes-3.0.1.rst
123 125 release-notes-3.0.0.rst
124 126
125 127 |RCE| 2.x Versions
126 128 ------------------
127 129
128 130 .. toctree::
129 131 :maxdepth: 1
130 132
131 133 release-notes-2.2.8.rst
132 134 release-notes-2.2.7.rst
133 135 release-notes-2.2.6.rst
134 136 release-notes-2.2.5.rst
135 137 release-notes-2.2.4.rst
136 138 release-notes-2.2.3.rst
137 139 release-notes-2.2.2.rst
138 140 release-notes-2.2.1.rst
139 141 release-notes-2.2.0.rst
140 142 release-notes-2.1.0.rst
141 143 release-notes-2.0.2.rst
142 144 release-notes-2.0.1.rst
143 145 release-notes-2.0.0.rst
144 146
145 147 |RCE| 1.x Versions
146 148 ------------------
147 149
148 150 .. toctree::
149 151 :maxdepth: 1
150 152
151 153 release-notes-1.7.2.rst
152 154 release-notes-1.7.1.rst
153 155 release-notes-1.7.0.rst
154 156 release-notes-1.6.0.rst
@@ -1,2520 +1,2520 b''
1 1 # Generated by pip2nix 0.8.0.dev1
2 2 # See https://github.com/johbo/pip2nix
3 3
4 4 { pkgs, fetchurl, fetchgit, fetchhg }:
5 5
6 6 self: super: {
7 7 "alembic" = super.buildPythonPackage {
8 8 name = "alembic-1.4.2";
9 9 doCheck = false;
10 10 propagatedBuildInputs = [
11 11 self."sqlalchemy"
12 12 self."mako"
13 13 self."python-editor"
14 14 self."python-dateutil"
15 15 ];
16 16 src = fetchurl {
17 17 url = "https://files.pythonhosted.org/packages/60/1e/cabc75a189de0fbb2841d0975243e59bde8b7822bacbb95008ac6fe9ad47/alembic-1.4.2.tar.gz";
18 18 sha256 = "1gsdrzx9h7wfva200qvvsc9sn4w79mk2vs0bbnzjhxi1jw2b0nh3";
19 19 };
20 20 meta = {
21 21 license = [ pkgs.lib.licenses.mit ];
22 22 };
23 23 };
24 24 "amqp" = super.buildPythonPackage {
25 25 name = "amqp-2.5.2";
26 26 doCheck = false;
27 27 propagatedBuildInputs = [
28 28 self."vine"
29 29 ];
30 30 src = fetchurl {
31 31 url = "https://files.pythonhosted.org/packages/92/1d/433541994a5a69f4ad2fff39746ddbb0bdedb0ea0d85673eb0db68a7edd9/amqp-2.5.2.tar.gz";
32 32 sha256 = "13dhhfxjrqcjybnq4zahg92mydhpg2l76nxcmq7d560687wsxwbp";
33 33 };
34 34 meta = {
35 35 license = [ pkgs.lib.licenses.bsdOriginal ];
36 36 };
37 37 };
38 38 "apispec" = super.buildPythonPackage {
39 39 name = "apispec-1.0.0";
40 40 doCheck = false;
41 41 propagatedBuildInputs = [
42 42 self."PyYAML"
43 43 ];
44 44 src = fetchurl {
45 45 url = "https://files.pythonhosted.org/packages/67/15/346c04988dd67d36007e28145504c520491930c878b1f484a97b27a8f497/apispec-1.0.0.tar.gz";
46 46 sha256 = "1712w1anvqrvadjjpvai84vbaygaxabd3zz5lxihdzwzs4gvi9sp";
47 47 };
48 48 meta = {
49 49 license = [ pkgs.lib.licenses.mit ];
50 50 };
51 51 };
52 52 "appenlight-client" = super.buildPythonPackage {
53 53 name = "appenlight-client-0.6.26";
54 54 doCheck = false;
55 55 propagatedBuildInputs = [
56 56 self."webob"
57 57 self."requests"
58 58 self."six"
59 59 ];
60 60 src = fetchurl {
61 61 url = "https://files.pythonhosted.org/packages/2e/56/418fc10379b96e795ee39a15e69a730c222818af04c3821fa354eaa859ec/appenlight_client-0.6.26.tar.gz";
62 62 sha256 = "0s9xw3sb8s3pk73k78nnq4jil3q4mk6bczfa1fmgfx61kdxl2712";
63 63 };
64 64 meta = {
65 65 license = [ pkgs.lib.licenses.bsdOriginal ];
66 66 };
67 67 };
68 68 "asn1crypto" = super.buildPythonPackage {
69 69 name = "asn1crypto-0.24.0";
70 70 doCheck = false;
71 71 src = fetchurl {
72 72 url = "https://files.pythonhosted.org/packages/fc/f1/8db7daa71f414ddabfa056c4ef792e1461ff655c2ae2928a2b675bfed6b4/asn1crypto-0.24.0.tar.gz";
73 73 sha256 = "0jaf8rf9dx1lf23xfv2cdd5h52f1qr3w8k63985bc35g3d220p4x";
74 74 };
75 75 meta = {
76 76 license = [ pkgs.lib.licenses.mit ];
77 77 };
78 78 };
79 79 "atomicwrites" = super.buildPythonPackage {
80 80 name = "atomicwrites-1.3.0";
81 81 doCheck = false;
82 82 src = fetchurl {
83 83 url = "https://files.pythonhosted.org/packages/ec/0f/cd484ac8820fed363b374af30049adc8fd13065720fd4f4c6be8a2309da7/atomicwrites-1.3.0.tar.gz";
84 84 sha256 = "19ngcscdf3jsqmpcxn6zl5b6anmsajb6izp1smcd1n02midl9abm";
85 85 };
86 86 meta = {
87 87 license = [ pkgs.lib.licenses.mit ];
88 88 };
89 89 };
90 90 "attrs" = super.buildPythonPackage {
91 91 name = "attrs-19.3.0";
92 92 doCheck = false;
93 93 src = fetchurl {
94 94 url = "https://files.pythonhosted.org/packages/98/c3/2c227e66b5e896e15ccdae2e00bbc69aa46e9a8ce8869cc5fa96310bf612/attrs-19.3.0.tar.gz";
95 95 sha256 = "0wky4h28n7xnr6xv69p9z6kv8bzn50d10c3drmd9ds8gawbcxdzp";
96 96 };
97 97 meta = {
98 98 license = [ pkgs.lib.licenses.mit ];
99 99 };
100 100 };
101 101 "babel" = super.buildPythonPackage {
102 102 name = "babel-1.3";
103 103 doCheck = false;
104 104 propagatedBuildInputs = [
105 105 self."pytz"
106 106 ];
107 107 src = fetchurl {
108 108 url = "https://files.pythonhosted.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
109 109 sha256 = "0bnin777lc53nxd1hp3apq410jj5wx92n08h7h4izpl4f4sx00lz";
110 110 };
111 111 meta = {
112 112 license = [ pkgs.lib.licenses.bsdOriginal ];
113 113 };
114 114 };
115 115 "backports.shutil-get-terminal-size" = super.buildPythonPackage {
116 116 name = "backports.shutil-get-terminal-size-1.0.0";
117 117 doCheck = false;
118 118 src = fetchurl {
119 119 url = "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
120 120 sha256 = "107cmn7g3jnbkp826zlj8rrj19fam301qvaqf0f3905f5217lgki";
121 121 };
122 122 meta = {
123 123 license = [ pkgs.lib.licenses.mit ];
124 124 };
125 125 };
126 126 "beaker" = super.buildPythonPackage {
127 127 name = "beaker-1.9.1";
128 128 doCheck = false;
129 129 propagatedBuildInputs = [
130 130 self."funcsigs"
131 131 ];
132 132 src = fetchurl {
133 133 url = "https://files.pythonhosted.org/packages/ca/14/a626188d0d0c7b55dd7cf1902046c2743bd392a7078bb53073e13280eb1e/Beaker-1.9.1.tar.gz";
134 134 sha256 = "08arsn61r255lhz6hcpn2lsiqpg30clla805ysx06wmbhvb6w9rj";
135 135 };
136 136 meta = {
137 137 license = [ pkgs.lib.licenses.bsdOriginal ];
138 138 };
139 139 };
140 140 "beautifulsoup4" = super.buildPythonPackage {
141 141 name = "beautifulsoup4-4.6.3";
142 142 doCheck = false;
143 143 src = fetchurl {
144 144 url = "https://files.pythonhosted.org/packages/88/df/86bffad6309f74f3ff85ea69344a078fc30003270c8df6894fca7a3c72ff/beautifulsoup4-4.6.3.tar.gz";
145 145 sha256 = "041dhalzjciw6qyzzq7a2k4h1yvyk76xigp35hv5ibnn448ydy4h";
146 146 };
147 147 meta = {
148 148 license = [ pkgs.lib.licenses.mit ];
149 149 };
150 150 };
151 151 "billiard" = super.buildPythonPackage {
152 152 name = "billiard-3.6.1.0";
153 153 doCheck = false;
154 154 src = fetchurl {
155 155 url = "https://files.pythonhosted.org/packages/68/1d/2aea8fbb0b1e1260a8a2e77352de2983d36d7ac01207cf14c2b9c6cc860e/billiard-3.6.1.0.tar.gz";
156 156 sha256 = "09hzy3aqi7visy4vmf4xiish61n0rq5nd3iwjydydps8yrs9r05q";
157 157 };
158 158 meta = {
159 159 license = [ pkgs.lib.licenses.bsdOriginal ];
160 160 };
161 161 };
162 162 "bleach" = super.buildPythonPackage {
163 163 name = "bleach-3.1.3";
164 164 doCheck = false;
165 165 propagatedBuildInputs = [
166 166 self."six"
167 167 self."webencodings"
168 168 ];
169 169 src = fetchurl {
170 170 url = "https://files.pythonhosted.org/packages/de/09/5267f8577a92487ed43bc694476c4629c6eca2e3c93fcf690a26bfe39e1d/bleach-3.1.3.tar.gz";
171 171 sha256 = "0al437aw4p2xp83az5hhlrp913nsf0cg6kg4qj3fjhv4wakxipzq";
172 172 };
173 173 meta = {
174 174 license = [ pkgs.lib.licenses.asl20 ];
175 175 };
176 176 };
177 177 "bumpversion" = super.buildPythonPackage {
178 178 name = "bumpversion-0.5.3";
179 179 doCheck = false;
180 180 src = fetchurl {
181 181 url = "https://files.pythonhosted.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
182 182 sha256 = "0zn7694yfipxg35ikkfh7kvgl2fissha3dnqad2c5bvsvmrwhi37";
183 183 };
184 184 meta = {
185 185 license = [ pkgs.lib.licenses.mit ];
186 186 };
187 187 };
188 188 "cachetools" = super.buildPythonPackage {
189 189 name = "cachetools-3.1.1";
190 190 doCheck = false;
191 191 src = fetchurl {
192 192 url = "https://files.pythonhosted.org/packages/ae/37/7fd45996b19200e0cb2027a0b6bef4636951c4ea111bfad36c71287247f6/cachetools-3.1.1.tar.gz";
193 193 sha256 = "16m69l6n6y1r1y7cklm92rr7v69ldig2n3lbl3j323w5jz7d78lf";
194 194 };
195 195 meta = {
196 196 license = [ pkgs.lib.licenses.mit ];
197 197 };
198 198 };
199 199 "celery" = super.buildPythonPackage {
200 200 name = "celery-4.3.0";
201 201 doCheck = false;
202 202 propagatedBuildInputs = [
203 203 self."pytz"
204 204 self."billiard"
205 205 self."kombu"
206 206 self."vine"
207 207 ];
208 208 src = fetchurl {
209 209 url = "https://files.pythonhosted.org/packages/a2/4b/d020836f751617e907e84753a41c92231cd4b673ff991b8ee9da52361323/celery-4.3.0.tar.gz";
210 210 sha256 = "1y8y0gbgkwimpxqnxq2rm5qz2vy01fvjiybnpm00y5rzd2m34iac";
211 211 };
212 212 meta = {
213 213 license = [ pkgs.lib.licenses.bsdOriginal ];
214 214 };
215 215 };
216 216 "certifi" = super.buildPythonPackage {
217 217 name = "certifi-2020.4.5.1";
218 218 doCheck = false;
219 219 src = fetchurl {
220 220 url = "https://files.pythonhosted.org/packages/b8/e2/a3a86a67c3fc8249ed305fc7b7d290ebe5e4d46ad45573884761ef4dea7b/certifi-2020.4.5.1.tar.gz";
221 221 sha256 = "06b5gfs7wmmipln8f3z928d2mmx2j4b3x7pnqmj6cvmyfh8v7z2i";
222 222 };
223 223 meta = {
224 224 license = [ pkgs.lib.licenses.mpl20 { fullName = "Mozilla Public License 2.0 (MPL 2.0)"; } ];
225 225 };
226 226 };
227 227 "cffi" = super.buildPythonPackage {
228 228 name = "cffi-1.12.3";
229 229 doCheck = false;
230 230 propagatedBuildInputs = [
231 231 self."pycparser"
232 232 ];
233 233 src = fetchurl {
234 234 url = "https://files.pythonhosted.org/packages/93/1a/ab8c62b5838722f29f3daffcc8d4bd61844aa9b5f437341cc890ceee483b/cffi-1.12.3.tar.gz";
235 235 sha256 = "0x075521fxwv0mfp4cqzk7lvmw4n94bjw601qkcv314z5s182704";
236 236 };
237 237 meta = {
238 238 license = [ pkgs.lib.licenses.mit ];
239 239 };
240 240 };
241 241 "chameleon" = super.buildPythonPackage {
242 242 name = "chameleon-2.24";
243 243 doCheck = false;
244 244 src = fetchurl {
245 245 url = "https://files.pythonhosted.org/packages/5a/9e/637379ffa13c5172b5c0e704833ffea6bf51cec7567f93fd6e903d53ed74/Chameleon-2.24.tar.gz";
246 246 sha256 = "0ykqr7syxfa6h9adjfnsv1gdsca2xzm22vmic8859n0f0j09abj5";
247 247 };
248 248 meta = {
249 249 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
250 250 };
251 251 };
252 252 "channelstream" = super.buildPythonPackage {
253 253 name = "channelstream-0.6.14";
254 254 doCheck = false;
255 255 propagatedBuildInputs = [
256 256 self."gevent"
257 257 self."ws4py"
258 258 self."marshmallow"
259 259 self."python-dateutil"
260 260 self."pyramid"
261 261 self."pyramid-jinja2"
262 262 self."pyramid-apispec"
263 263 self."itsdangerous"
264 264 self."requests"
265 265 self."six"
266 266 ];
267 267 src = fetchurl {
268 268 url = "https://files.pythonhosted.org/packages/d4/2d/86d6757ccd06ce673ee224123471da3d45251d061da7c580bfc259bad853/channelstream-0.6.14.tar.gz";
269 269 sha256 = "0qgy5j3rj6c8cslzidh32glhkrhbbdxjc008y69v8a0y3zyaz2d3";
270 270 };
271 271 meta = {
272 272 license = [ pkgs.lib.licenses.bsdOriginal ];
273 273 };
274 274 };
275 275 "chardet" = super.buildPythonPackage {
276 276 name = "chardet-3.0.4";
277 277 doCheck = false;
278 278 src = fetchurl {
279 279 url = "https://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz";
280 280 sha256 = "1bpalpia6r5x1kknbk11p1fzph56fmmnp405ds8icksd3knr5aw4";
281 281 };
282 282 meta = {
283 283 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
284 284 };
285 285 };
286 286 "click" = super.buildPythonPackage {
287 287 name = "click-7.0";
288 288 doCheck = false;
289 289 src = fetchurl {
290 290 url = "https://files.pythonhosted.org/packages/f8/5c/f60e9d8a1e77005f664b76ff8aeaee5bc05d0a91798afd7f53fc998dbc47/Click-7.0.tar.gz";
291 291 sha256 = "1mzjixd4vjbjvzb6vylki9w1556a9qmdh35kzmq6cign46av952v";
292 292 };
293 293 meta = {
294 294 license = [ pkgs.lib.licenses.bsdOriginal ];
295 295 };
296 296 };
297 297 "colander" = super.buildPythonPackage {
298 298 name = "colander-1.7.0";
299 299 doCheck = false;
300 300 propagatedBuildInputs = [
301 301 self."translationstring"
302 302 self."iso8601"
303 303 self."enum34"
304 304 ];
305 305 src = fetchurl {
306 306 url = "https://files.pythonhosted.org/packages/db/e4/74ab06f54211917b41865cafc987ce511e35503de48da9bfe9358a1bdc3e/colander-1.7.0.tar.gz";
307 307 sha256 = "1wl1bqab307lbbcjx81i28s3yl6dlm4rf15fxawkjb6j48x1cn6p";
308 308 };
309 309 meta = {
310 310 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
311 311 };
312 312 };
313 313 "configobj" = super.buildPythonPackage {
314 314 name = "configobj-5.0.6";
315 315 doCheck = false;
316 316 propagatedBuildInputs = [
317 317 self."six"
318 318 ];
319 319 src = fetchurl {
320 320 url = "https://code.rhodecode.com/upstream/configobj/artifacts/download/0-012de99a-b1e1-4f64-a5c0-07a98a41b324.tar.gz?md5=6a513f51fe04b2c18cf84c1395a7c626";
321 321 sha256 = "0kqfrdfr14mw8yd8qwq14dv2xghpkjmd3yjsy8dfcbvpcc17xnxp";
322 322 };
323 323 meta = {
324 324 license = [ pkgs.lib.licenses.bsdOriginal ];
325 325 };
326 326 };
327 327 "configparser" = super.buildPythonPackage {
328 328 name = "configparser-4.0.2";
329 329 doCheck = false;
330 330 src = fetchurl {
331 331 url = "https://files.pythonhosted.org/packages/16/4f/48975536bd488d3a272549eb795ac4a13a5f7fcdc8995def77fbef3532ee/configparser-4.0.2.tar.gz";
332 332 sha256 = "1priacxym85yjcf68hh38w55nqswaxp71ryjyfdk222kg9l85ln7";
333 333 };
334 334 meta = {
335 335 license = [ pkgs.lib.licenses.mit ];
336 336 };
337 337 };
338 338 "contextlib2" = super.buildPythonPackage {
339 339 name = "contextlib2-0.6.0.post1";
340 340 doCheck = false;
341 341 src = fetchurl {
342 342 url = "https://files.pythonhosted.org/packages/02/54/669207eb72e3d8ae8b38aa1f0703ee87a0e9f88f30d3c0a47bebdb6de242/contextlib2-0.6.0.post1.tar.gz";
343 343 sha256 = "0bhnr2ac7wy5l85ji909gyljyk85n92w8pdvslmrvc8qih4r1x01";
344 344 };
345 345 meta = {
346 346 license = [ pkgs.lib.licenses.psfl ];
347 347 };
348 348 };
349 349 "cov-core" = super.buildPythonPackage {
350 350 name = "cov-core-1.15.0";
351 351 doCheck = false;
352 352 propagatedBuildInputs = [
353 353 self."coverage"
354 354 ];
355 355 src = fetchurl {
356 356 url = "https://files.pythonhosted.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
357 357 sha256 = "0k3np9ymh06yv1ib96sb6wfsxjkqhmik8qfsn119vnhga9ywc52a";
358 358 };
359 359 meta = {
360 360 license = [ pkgs.lib.licenses.mit ];
361 361 };
362 362 };
363 363 "coverage" = super.buildPythonPackage {
364 364 name = "coverage-4.5.4";
365 365 doCheck = false;
366 366 src = fetchurl {
367 367 url = "https://files.pythonhosted.org/packages/85/d5/818d0e603685c4a613d56f065a721013e942088047ff1027a632948bdae6/coverage-4.5.4.tar.gz";
368 368 sha256 = "0p0j4di6h8k6ica7jwwj09azdcg4ycxq60i9qsskmsg94cd9yzg0";
369 369 };
370 370 meta = {
371 371 license = [ pkgs.lib.licenses.asl20 ];
372 372 };
373 373 };
374 374 "cryptography" = super.buildPythonPackage {
375 375 name = "cryptography-2.6.1";
376 376 doCheck = false;
377 377 propagatedBuildInputs = [
378 378 self."asn1crypto"
379 379 self."six"
380 380 self."cffi"
381 381 self."enum34"
382 382 self."ipaddress"
383 383 ];
384 384 src = fetchurl {
385 385 url = "https://files.pythonhosted.org/packages/07/ca/bc827c5e55918ad223d59d299fff92f3563476c3b00d0a9157d9c0217449/cryptography-2.6.1.tar.gz";
386 386 sha256 = "19iwz5avym5zl6jrrrkym1rdaa9h61j20ph4cswsqgv8xg5j3j16";
387 387 };
388 388 meta = {
389 389 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD or Apache License, Version 2.0"; } pkgs.lib.licenses.asl20 ];
390 390 };
391 391 };
392 392 "cssselect" = super.buildPythonPackage {
393 393 name = "cssselect-1.0.3";
394 394 doCheck = false;
395 395 src = fetchurl {
396 396 url = "https://files.pythonhosted.org/packages/52/ea/f31e1d2e9eb130fda2a631e22eac369dc644e8807345fbed5113f2d6f92b/cssselect-1.0.3.tar.gz";
397 397 sha256 = "011jqa2jhmydhi0iz4v1w3cr540z5zas8g2bw8brdw4s4b2qnv86";
398 398 };
399 399 meta = {
400 400 license = [ pkgs.lib.licenses.bsdOriginal ];
401 401 };
402 402 };
403 403 "cssutils" = super.buildPythonPackage {
404 404 name = "cssutils-1.0.2";
405 405 doCheck = false;
406 406 src = fetchurl {
407 407 url = "https://files.pythonhosted.org/packages/5c/0b/c5f29d29c037e97043770b5e7c740b6252993e4b57f029b3cd03c78ddfec/cssutils-1.0.2.tar.gz";
408 408 sha256 = "1bxchrbqzapwijap0yhlxdil1w9bmwvgx77aizlkhc2mcxjg1z52";
409 409 };
410 410 meta = {
411 411 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL 2.1 or later, see also http://cthedot.de/cssutils/"; } ];
412 412 };
413 413 };
414 414 "decorator" = super.buildPythonPackage {
415 415 name = "decorator-4.1.2";
416 416 doCheck = false;
417 417 src = fetchurl {
418 418 url = "https://files.pythonhosted.org/packages/bb/e0/f6e41e9091e130bf16d4437dabbac3993908e4d6485ecbc985ef1352db94/decorator-4.1.2.tar.gz";
419 419 sha256 = "1d8npb11kxyi36mrvjdpcjij76l5zfyrz2f820brf0l0rcw4vdkw";
420 420 };
421 421 meta = {
422 422 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "new BSD License"; } ];
423 423 };
424 424 };
425 425 "deform" = super.buildPythonPackage {
426 426 name = "deform-2.0.8";
427 427 doCheck = false;
428 428 propagatedBuildInputs = [
429 429 self."chameleon"
430 430 self."colander"
431 431 self."iso8601"
432 432 self."peppercorn"
433 433 self."translationstring"
434 434 self."zope.deprecation"
435 435 ];
436 436 src = fetchurl {
437 437 url = "https://files.pythonhosted.org/packages/21/d0/45fdf891a82722c02fc2da319cf2d1ae6b5abf9e470ad3762135a895a868/deform-2.0.8.tar.gz";
438 438 sha256 = "0wbjv98sib96649aqaygzxnrkclyy50qij2rha6fn1i4c86bfdl9";
439 439 };
440 440 meta = {
441 441 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
442 442 };
443 443 };
444 444 "defusedxml" = super.buildPythonPackage {
445 445 name = "defusedxml-0.6.0";
446 446 doCheck = false;
447 447 src = fetchurl {
448 448 url = "https://files.pythonhosted.org/packages/a4/5f/f8aa58ca0cf01cbcee728abc9d88bfeb74e95e6cb4334cfd5bed5673ea77/defusedxml-0.6.0.tar.gz";
449 449 sha256 = "1xbp8fivl3wlbyg2jrvs4lalaqv1xp9a9f29p75wdx2s2d6h717n";
450 450 };
451 451 meta = {
452 452 license = [ pkgs.lib.licenses.psfl ];
453 453 };
454 454 };
455 455 "dm.xmlsec.binding" = super.buildPythonPackage {
456 456 name = "dm.xmlsec.binding-1.3.7";
457 457 doCheck = false;
458 458 propagatedBuildInputs = [
459 459 self."setuptools"
460 460 self."lxml"
461 461 ];
462 462 src = fetchurl {
463 463 url = "https://files.pythonhosted.org/packages/2c/9e/7651982d50252692991acdae614af821fd6c79bc8dcd598ad71d55be8fc7/dm.xmlsec.binding-1.3.7.tar.gz";
464 464 sha256 = "03jjjscx1pz2nc0dwiw9nia02qbz1c6f0f9zkyr8fmvys2n5jkb3";
465 465 };
466 466 meta = {
467 467 license = [ pkgs.lib.licenses.bsdOriginal ];
468 468 };
469 469 };
470 470 "docutils" = super.buildPythonPackage {
471 471 name = "docutils-0.16";
472 472 doCheck = false;
473 473 src = fetchurl {
474 474 url = "https://files.pythonhosted.org/packages/2f/e0/3d435b34abd2d62e8206171892f174b180cd37b09d57b924ca5c2ef2219d/docutils-0.16.tar.gz";
475 475 sha256 = "1z3qliszqca9m719q3qhdkh0ghh90g500avzdgi7pl77x5h3mpn2";
476 476 };
477 477 meta = {
478 478 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.publicDomain pkgs.lib.licenses.gpl1 { fullName = "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"; } pkgs.lib.licenses.psfl ];
479 479 };
480 480 };
481 481 "dogpile.cache" = super.buildPythonPackage {
482 482 name = "dogpile.cache-0.9.0";
483 483 doCheck = false;
484 484 propagatedBuildInputs = [
485 485 self."decorator"
486 486 ];
487 487 src = fetchurl {
488 488 url = "https://files.pythonhosted.org/packages/ac/6a/9ac405686a94b7f009a20a50070a5786b0e1aedc707b88d40d0c4b51a82e/dogpile.cache-0.9.0.tar.gz";
489 489 sha256 = "0sr1fn6b4k5bh0cscd9yi8csqxvj4ngzildav58x5p694mc86j5k";
490 490 };
491 491 meta = {
492 492 license = [ pkgs.lib.licenses.bsdOriginal ];
493 493 };
494 494 };
495 495 "dogpile.core" = super.buildPythonPackage {
496 496 name = "dogpile.core-0.4.1";
497 497 doCheck = false;
498 498 src = fetchurl {
499 499 url = "https://files.pythonhosted.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
500 500 sha256 = "0xpdvg4kr1isfkrh1rfsh7za4q5a5s6l2kf9wpvndbwf3aqjyrdy";
501 501 };
502 502 meta = {
503 503 license = [ pkgs.lib.licenses.bsdOriginal ];
504 504 };
505 505 };
506 506 "ecdsa" = super.buildPythonPackage {
507 507 name = "ecdsa-0.13.2";
508 508 doCheck = false;
509 509 src = fetchurl {
510 510 url = "https://files.pythonhosted.org/packages/51/76/139bf6e9b7b6684d5891212cdbd9e0739f2bfc03f380a1a6ffa700f392ac/ecdsa-0.13.2.tar.gz";
511 511 sha256 = "116qaq7bh4lcynzi613960jhsnn19v0kmsqwahiwjfj14gx4y0sw";
512 512 };
513 513 meta = {
514 514 license = [ pkgs.lib.licenses.mit ];
515 515 };
516 516 };
517 517 "elasticsearch" = super.buildPythonPackage {
518 518 name = "elasticsearch-6.3.1";
519 519 doCheck = false;
520 520 propagatedBuildInputs = [
521 521 self."urllib3"
522 522 ];
523 523 src = fetchurl {
524 524 url = "https://files.pythonhosted.org/packages/9d/ce/c4664e8380e379a9402ecfbaf158e56396da90d520daba21cfa840e0eb71/elasticsearch-6.3.1.tar.gz";
525 525 sha256 = "12y93v0yn7a4xmf969239g8gb3l4cdkclfpbk1qc8hx5qkymrnma";
526 526 };
527 527 meta = {
528 528 license = [ pkgs.lib.licenses.asl20 ];
529 529 };
530 530 };
531 531 "elasticsearch-dsl" = super.buildPythonPackage {
532 532 name = "elasticsearch-dsl-6.3.1";
533 533 doCheck = false;
534 534 propagatedBuildInputs = [
535 535 self."six"
536 536 self."python-dateutil"
537 537 self."elasticsearch"
538 538 self."ipaddress"
539 539 ];
540 540 src = fetchurl {
541 541 url = "https://files.pythonhosted.org/packages/4c/0d/1549f50c591db6bb4e66cbcc8d34a6e537c3d89aa426b167c244fd46420a/elasticsearch-dsl-6.3.1.tar.gz";
542 542 sha256 = "1gh8a0shqi105k325hgwb9avrpdjh0mc6mxwfg9ba7g6lssb702z";
543 543 };
544 544 meta = {
545 545 license = [ pkgs.lib.licenses.asl20 ];
546 546 };
547 547 };
548 548 "elasticsearch1" = super.buildPythonPackage {
549 549 name = "elasticsearch1-1.10.0";
550 550 doCheck = false;
551 551 propagatedBuildInputs = [
552 552 self."urllib3"
553 553 ];
554 554 src = fetchurl {
555 555 url = "https://files.pythonhosted.org/packages/a6/eb/73e75f9681fa71e3157b8ee878534235d57f24ee64f0e77f8d995fb57076/elasticsearch1-1.10.0.tar.gz";
556 556 sha256 = "0g89444kd5zwql4vbvyrmi2m6l6dcj6ga98j4hqxyyyz6z20aki2";
557 557 };
558 558 meta = {
559 559 license = [ pkgs.lib.licenses.asl20 ];
560 560 };
561 561 };
562 562 "elasticsearch1-dsl" = super.buildPythonPackage {
563 563 name = "elasticsearch1-dsl-0.0.12";
564 564 doCheck = false;
565 565 propagatedBuildInputs = [
566 566 self."six"
567 567 self."python-dateutil"
568 568 self."elasticsearch1"
569 569 ];
570 570 src = fetchurl {
571 571 url = "https://files.pythonhosted.org/packages/eb/9d/785342775cb10eddc9b8d7457d618a423b4f0b89d8b2b2d1bc27190d71db/elasticsearch1-dsl-0.0.12.tar.gz";
572 572 sha256 = "0ig1ly39v93hba0z975wnhbmzwj28w6w1sqlr2g7cn5spp732bhk";
573 573 };
574 574 meta = {
575 575 license = [ pkgs.lib.licenses.asl20 ];
576 576 };
577 577 };
578 578 "elasticsearch2" = super.buildPythonPackage {
579 579 name = "elasticsearch2-2.5.1";
580 580 doCheck = false;
581 581 propagatedBuildInputs = [
582 582 self."urllib3"
583 583 ];
584 584 src = fetchurl {
585 585 url = "https://files.pythonhosted.org/packages/f6/09/f9b24aa6b1120bea371cd57ef6f57c7694cf16660469456a8be6c2bdbe22/elasticsearch2-2.5.1.tar.gz";
586 586 sha256 = "19k2znpjfyp0hrq73cz7pjyj289040xpsxsm0xhh4jfh6y551g7k";
587 587 };
588 588 meta = {
589 589 license = [ pkgs.lib.licenses.asl20 ];
590 590 };
591 591 };
592 592 "entrypoints" = super.buildPythonPackage {
593 593 name = "entrypoints-0.2.2";
594 594 doCheck = false;
595 595 propagatedBuildInputs = [
596 596 self."configparser"
597 597 ];
598 598 src = fetchurl {
599 599 url = "https://code.rhodecode.com/upstream/entrypoints/artifacts/download/0-8e9ee9e4-c4db-409c-b07e-81568fd1832d.tar.gz?md5=3a027b8ff1d257b91fe257de6c43357d";
600 600 sha256 = "0qih72n2myclanplqipqxpgpj9d2yhff1pz5d02zq1cfqyd173w5";
601 601 };
602 602 meta = {
603 603 license = [ pkgs.lib.licenses.mit ];
604 604 };
605 605 };
606 606 "enum34" = super.buildPythonPackage {
607 607 name = "enum34-1.1.10";
608 608 doCheck = false;
609 609 src = fetchurl {
610 610 url = "https://files.pythonhosted.org/packages/11/c4/2da1f4952ba476677a42f25cd32ab8aaf0e1c0d0e00b89822b835c7e654c/enum34-1.1.10.tar.gz";
611 611 sha256 = "0j7ji699fwswm4vg6w1v07fkbf8dkzdm6gfh88jvs5nqgr3sgrnc";
612 612 };
613 613 meta = {
614 614 license = [ pkgs.lib.licenses.bsdOriginal ];
615 615 };
616 616 };
617 617 "formencode" = super.buildPythonPackage {
618 618 name = "formencode-1.2.4";
619 619 doCheck = false;
620 620 src = fetchurl {
621 621 url = "https://files.pythonhosted.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
622 622 sha256 = "1fgy04sdy4yry5xcjls3x3xy30dqwj58ycnkndim819jx0788w42";
623 623 };
624 624 meta = {
625 625 license = [ pkgs.lib.licenses.psfl ];
626 626 };
627 627 };
628 628 "funcsigs" = super.buildPythonPackage {
629 629 name = "funcsigs-1.0.2";
630 630 doCheck = false;
631 631 src = fetchurl {
632 632 url = "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz";
633 633 sha256 = "0l4g5818ffyfmfs1a924811azhjj8ax9xd1cffr1mzd3ycn0zfx7";
634 634 };
635 635 meta = {
636 636 license = [ { fullName = "ASL"; } pkgs.lib.licenses.asl20 ];
637 637 };
638 638 };
639 639 "functools32" = super.buildPythonPackage {
640 640 name = "functools32-3.2.3.post2";
641 641 doCheck = false;
642 642 src = fetchurl {
643 643 url = "https://files.pythonhosted.org/packages/c5/60/6ac26ad05857c601308d8fb9e87fa36d0ebf889423f47c3502ef034365db/functools32-3.2.3-2.tar.gz";
644 644 sha256 = "0v8ya0b58x47wp216n1zamimv4iw57cxz3xxhzix52jkw3xks9gn";
645 645 };
646 646 meta = {
647 647 license = [ pkgs.lib.licenses.psfl ];
648 648 };
649 649 };
650 650 "future" = super.buildPythonPackage {
651 651 name = "future-0.14.3";
652 652 doCheck = false;
653 653 src = fetchurl {
654 654 url = "https://files.pythonhosted.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
655 655 sha256 = "1savk7jx7hal032f522c5ajhh8fra6gmnadrj9adv5qxi18pv1b2";
656 656 };
657 657 meta = {
658 658 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
659 659 };
660 660 };
661 661 "futures" = super.buildPythonPackage {
662 662 name = "futures-3.0.2";
663 663 doCheck = false;
664 664 src = fetchurl {
665 665 url = "https://files.pythonhosted.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
666 666 sha256 = "0mz2pbgxbc2nbib1szifi07whjbfs4r02pv2z390z7p410awjgyw";
667 667 };
668 668 meta = {
669 669 license = [ pkgs.lib.licenses.bsdOriginal ];
670 670 };
671 671 };
672 672 "gevent" = super.buildPythonPackage {
673 673 name = "gevent-1.5.0";
674 674 doCheck = false;
675 675 propagatedBuildInputs = [
676 676 self."greenlet"
677 677 ];
678 678 src = fetchurl {
679 679 url = "https://files.pythonhosted.org/packages/5a/79/2c63d385d017b5dd7d70983a463dfd25befae70c824fedb857df6e72eff2/gevent-1.5.0.tar.gz";
680 680 sha256 = "0aac3d4vhv5n4rsb6cqzq0d1xx9immqz4fmpddw35yxkwdc450dj";
681 681 };
682 682 meta = {
683 683 license = [ pkgs.lib.licenses.mit ];
684 684 };
685 685 };
686 686 "gnureadline" = super.buildPythonPackage {
687 687 name = "gnureadline-6.3.8";
688 688 doCheck = false;
689 689 src = fetchurl {
690 690 url = "https://files.pythonhosted.org/packages/50/64/86085c823cd78f9df9d8e33dce0baa71618016f8860460b82cf6610e1eb3/gnureadline-6.3.8.tar.gz";
691 691 sha256 = "0ddhj98x2nv45iz4aadk4b9m0b1kpsn1xhcbypn5cd556knhiqjq";
692 692 };
693 693 meta = {
694 694 license = [ { fullName = "GNU General Public License v3 (GPLv3)"; } pkgs.lib.licenses.gpl1 ];
695 695 };
696 696 };
697 697 "gprof2dot" = super.buildPythonPackage {
698 698 name = "gprof2dot-2017.9.19";
699 699 doCheck = false;
700 700 src = fetchurl {
701 701 url = "https://files.pythonhosted.org/packages/9d/36/f977122502979f3dfb50704979c9ed70e6b620787942b089bf1af15f5aba/gprof2dot-2017.9.19.tar.gz";
702 702 sha256 = "17ih23ld2nzgc3xwgbay911l6lh96jp1zshmskm17n1gg2i7mg6f";
703 703 };
704 704 meta = {
705 705 license = [ { fullName = "GNU Lesser General Public License v3 or later (LGPLv3+)"; } { fullName = "LGPL"; } ];
706 706 };
707 707 };
708 708 "greenlet" = super.buildPythonPackage {
709 709 name = "greenlet-0.4.15";
710 710 doCheck = false;
711 711 src = fetchurl {
712 712 url = "https://files.pythonhosted.org/packages/f8/e8/b30ae23b45f69aa3f024b46064c0ac8e5fcb4f22ace0dca8d6f9c8bbe5e7/greenlet-0.4.15.tar.gz";
713 713 sha256 = "1g4g1wwc472ds89zmqlpyan3fbnzpa8qm48z3z1y6mlk44z485ll";
714 714 };
715 715 meta = {
716 716 license = [ pkgs.lib.licenses.mit ];
717 717 };
718 718 };
719 719 "gunicorn" = super.buildPythonPackage {
720 720 name = "gunicorn-19.9.0";
721 721 doCheck = false;
722 722 src = fetchurl {
723 723 url = "https://files.pythonhosted.org/packages/47/52/68ba8e5e8ba251e54006a49441f7ccabca83b6bef5aedacb4890596c7911/gunicorn-19.9.0.tar.gz";
724 724 sha256 = "1wzlf4xmn6qjirh5w81l6i6kqjnab1n1qqkh7zsj1yb6gh4n49ps";
725 725 };
726 726 meta = {
727 727 license = [ pkgs.lib.licenses.mit ];
728 728 };
729 729 };
730 730 "hupper" = super.buildPythonPackage {
731 731 name = "hupper-1.10.2";
732 732 doCheck = false;
733 733 src = fetchurl {
734 734 url = "https://files.pythonhosted.org/packages/41/24/ea90fef04706e54bd1635c05c50dc9cf87cda543c59303a03e7aa7dda0ce/hupper-1.10.2.tar.gz";
735 735 sha256 = "0am0p6g5cz6xmcaf04xq8q6dzdd9qz0phj6gcmpsckf2mcyza61q";
736 736 };
737 737 meta = {
738 738 license = [ pkgs.lib.licenses.mit ];
739 739 };
740 740 };
741 741 "idna" = super.buildPythonPackage {
742 742 name = "idna-2.8";
743 743 doCheck = false;
744 744 src = fetchurl {
745 745 url = "https://files.pythonhosted.org/packages/ad/13/eb56951b6f7950cadb579ca166e448ba77f9d24efc03edd7e55fa57d04b7/idna-2.8.tar.gz";
746 746 sha256 = "01rlkigdxg17sf9yar1jl8n18ls59367wqh59hnawlyg53vb6my3";
747 747 };
748 748 meta = {
749 749 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD-like"; } ];
750 750 };
751 751 };
752 752 "importlib-metadata" = super.buildPythonPackage {
753 753 name = "importlib-metadata-1.6.0";
754 754 doCheck = false;
755 755 propagatedBuildInputs = [
756 756 self."zipp"
757 757 self."pathlib2"
758 758 self."contextlib2"
759 759 self."configparser"
760 760 ];
761 761 src = fetchurl {
762 762 url = "https://files.pythonhosted.org/packages/b4/1b/baab42e3cd64c9d5caac25a9d6c054f8324cdc38975a44d600569f1f7158/importlib_metadata-1.6.0.tar.gz";
763 763 sha256 = "07icyggasn38yv2swdrd8z6i0plazmc9adavsdkbqqj91j53ll9l";
764 764 };
765 765 meta = {
766 766 license = [ pkgs.lib.licenses.asl20 ];
767 767 };
768 768 };
769 769 "infrae.cache" = super.buildPythonPackage {
770 770 name = "infrae.cache-1.0.1";
771 771 doCheck = false;
772 772 propagatedBuildInputs = [
773 773 self."beaker"
774 774 self."repoze.lru"
775 775 ];
776 776 src = fetchurl {
777 777 url = "https://files.pythonhosted.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
778 778 sha256 = "1dvqsjn8vw253wz9d1pz17j79mf4bs53dvp2qxck2qdp1am1njw4";
779 779 };
780 780 meta = {
781 781 license = [ pkgs.lib.licenses.zpl21 ];
782 782 };
783 783 };
784 784 "invoke" = super.buildPythonPackage {
785 785 name = "invoke-0.13.0";
786 786 doCheck = false;
787 787 src = fetchurl {
788 788 url = "https://files.pythonhosted.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
789 789 sha256 = "0794vhgxfmkh0vzkkg5cfv1w82g3jc3xr18wim29far9qpx9468s";
790 790 };
791 791 meta = {
792 792 license = [ pkgs.lib.licenses.bsdOriginal ];
793 793 };
794 794 };
795 795 "ipaddress" = super.buildPythonPackage {
796 796 name = "ipaddress-1.0.23";
797 797 doCheck = false;
798 798 src = fetchurl {
799 799 url = "https://files.pythonhosted.org/packages/b9/9a/3e9da40ea28b8210dd6504d3fe9fe7e013b62bf45902b458d1cdc3c34ed9/ipaddress-1.0.23.tar.gz";
800 800 sha256 = "1qp743h30s04m3cg3yk3fycad930jv17q7dsslj4mfw0jlvf1y5p";
801 801 };
802 802 meta = {
803 803 license = [ pkgs.lib.licenses.psfl ];
804 804 };
805 805 };
806 806 "ipdb" = super.buildPythonPackage {
807 807 name = "ipdb-0.13.2";
808 808 doCheck = false;
809 809 propagatedBuildInputs = [
810 810 self."setuptools"
811 811 self."ipython"
812 812 ];
813 813 src = fetchurl {
814 814 url = "https://files.pythonhosted.org/packages/2c/bb/a3e1a441719ebd75c6dac8170d3ddba884b7ee8a5c0f9aefa7297386627a/ipdb-0.13.2.tar.gz";
815 815 sha256 = "0jcd849rx30y3wcgzsqbn06v0yjlzvb9x3076q0yxpycdwm1ryvp";
816 816 };
817 817 meta = {
818 818 license = [ pkgs.lib.licenses.bsdOriginal ];
819 819 };
820 820 };
821 821 "ipython" = super.buildPythonPackage {
822 822 name = "ipython-5.1.0";
823 823 doCheck = false;
824 824 propagatedBuildInputs = [
825 825 self."setuptools"
826 826 self."decorator"
827 827 self."pickleshare"
828 828 self."simplegeneric"
829 829 self."traitlets"
830 830 self."prompt-toolkit"
831 831 self."pygments"
832 832 self."pexpect"
833 833 self."backports.shutil-get-terminal-size"
834 834 self."pathlib2"
835 835 self."pexpect"
836 836 ];
837 837 src = fetchurl {
838 838 url = "https://files.pythonhosted.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
839 839 sha256 = "0qdrf6aj9kvjczd5chj1my8y2iq09am9l8bb2a1334a52d76kx3y";
840 840 };
841 841 meta = {
842 842 license = [ pkgs.lib.licenses.bsdOriginal ];
843 843 };
844 844 };
845 845 "ipython-genutils" = super.buildPythonPackage {
846 846 name = "ipython-genutils-0.2.0";
847 847 doCheck = false;
848 848 src = fetchurl {
849 849 url = "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz";
850 850 sha256 = "1a4bc9y8hnvq6cp08qs4mckgm6i6ajpndp4g496rvvzcfmp12bpb";
851 851 };
852 852 meta = {
853 853 license = [ pkgs.lib.licenses.bsdOriginal ];
854 854 };
855 855 };
856 856 "iso8601" = super.buildPythonPackage {
857 857 name = "iso8601-0.1.12";
858 858 doCheck = false;
859 859 src = fetchurl {
860 860 url = "https://files.pythonhosted.org/packages/45/13/3db24895497345fb44c4248c08b16da34a9eb02643cea2754b21b5ed08b0/iso8601-0.1.12.tar.gz";
861 861 sha256 = "10nyvvnrhw2w3p09v1ica4lgj6f4g9j3kkfx17qmraiq3w7b5i29";
862 862 };
863 863 meta = {
864 864 license = [ pkgs.lib.licenses.mit ];
865 865 };
866 866 };
867 867 "isodate" = super.buildPythonPackage {
868 868 name = "isodate-0.6.0";
869 869 doCheck = false;
870 870 propagatedBuildInputs = [
871 871 self."six"
872 872 ];
873 873 src = fetchurl {
874 874 url = "https://files.pythonhosted.org/packages/b1/80/fb8c13a4cd38eb5021dc3741a9e588e4d1de88d895c1910c6fc8a08b7a70/isodate-0.6.0.tar.gz";
875 875 sha256 = "1n7jkz68kk5pwni540pr5zdh99bf6ywydk1p5pdrqisrawylldif";
876 876 };
877 877 meta = {
878 878 license = [ pkgs.lib.licenses.bsdOriginal ];
879 879 };
880 880 };
881 881 "itsdangerous" = super.buildPythonPackage {
882 882 name = "itsdangerous-1.1.0";
883 883 doCheck = false;
884 884 src = fetchurl {
885 885 url = "https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz";
886 886 sha256 = "068zpbksq5q2z4dckh2k1zbcq43ay74ylqn77rni797j0wyh66rj";
887 887 };
888 888 meta = {
889 889 license = [ pkgs.lib.licenses.bsdOriginal ];
890 890 };
891 891 };
892 892 "jinja2" = super.buildPythonPackage {
893 893 name = "jinja2-2.9.6";
894 894 doCheck = false;
895 895 propagatedBuildInputs = [
896 896 self."markupsafe"
897 897 ];
898 898 src = fetchurl {
899 899 url = "https://files.pythonhosted.org/packages/90/61/f820ff0076a2599dd39406dcb858ecb239438c02ce706c8e91131ab9c7f1/Jinja2-2.9.6.tar.gz";
900 900 sha256 = "1zzrkywhziqffrzks14kzixz7nd4yh2vc0fb04a68vfd2ai03anx";
901 901 };
902 902 meta = {
903 903 license = [ pkgs.lib.licenses.bsdOriginal ];
904 904 };
905 905 };
906 906 "jsonschema" = super.buildPythonPackage {
907 907 name = "jsonschema-2.6.0";
908 908 doCheck = false;
909 909 propagatedBuildInputs = [
910 910 self."functools32"
911 911 ];
912 912 src = fetchurl {
913 913 url = "https://files.pythonhosted.org/packages/58/b9/171dbb07e18c6346090a37f03c7e74410a1a56123f847efed59af260a298/jsonschema-2.6.0.tar.gz";
914 914 sha256 = "00kf3zmpp9ya4sydffpifn0j0mzm342a2vzh82p6r0vh10cg7xbg";
915 915 };
916 916 meta = {
917 917 license = [ pkgs.lib.licenses.mit ];
918 918 };
919 919 };
920 920 "jupyter-client" = super.buildPythonPackage {
921 921 name = "jupyter-client-5.0.0";
922 922 doCheck = false;
923 923 propagatedBuildInputs = [
924 924 self."traitlets"
925 925 self."jupyter-core"
926 926 self."pyzmq"
927 927 self."python-dateutil"
928 928 ];
929 929 src = fetchurl {
930 930 url = "https://files.pythonhosted.org/packages/e5/6f/65412ed462202b90134b7e761b0b7e7f949e07a549c1755475333727b3d0/jupyter_client-5.0.0.tar.gz";
931 931 sha256 = "0nxw4rqk4wsjhc87gjqd7pv89cb9dnimcfnmcmp85bmrvv1gjri7";
932 932 };
933 933 meta = {
934 934 license = [ pkgs.lib.licenses.bsdOriginal ];
935 935 };
936 936 };
937 937 "jupyter-core" = super.buildPythonPackage {
938 938 name = "jupyter-core-4.5.0";
939 939 doCheck = false;
940 940 propagatedBuildInputs = [
941 941 self."traitlets"
942 942 ];
943 943 src = fetchurl {
944 944 url = "https://files.pythonhosted.org/packages/4a/de/ff4ca734656d17ebe0450807b59d728f45277e2e7f4b82bc9aae6cb82961/jupyter_core-4.5.0.tar.gz";
945 945 sha256 = "1xr4pbghwk5hayn5wwnhb7z95380r45p79gf5if5pi1akwg7qvic";
946 946 };
947 947 meta = {
948 948 license = [ pkgs.lib.licenses.bsdOriginal ];
949 949 };
950 950 };
951 951 "kombu" = super.buildPythonPackage {
952 952 name = "kombu-4.6.6";
953 953 doCheck = false;
954 954 propagatedBuildInputs = [
955 955 self."amqp"
956 956 self."importlib-metadata"
957 957 ];
958 958 src = fetchurl {
959 959 url = "https://files.pythonhosted.org/packages/20/e6/bc2d9affba6138a1dc143f77fef253e9e08e238fa7c0688d917c09005e96/kombu-4.6.6.tar.gz";
960 960 sha256 = "11mxpcy8mg1l35bgbhba70v29bydr2hrhdbdlb4lg98m3m5vaq0p";
961 961 };
962 962 meta = {
963 963 license = [ pkgs.lib.licenses.bsdOriginal ];
964 964 };
965 965 };
966 966 "lxml" = super.buildPythonPackage {
967 967 name = "lxml-4.2.5";
968 968 doCheck = false;
969 969 src = fetchurl {
970 970 url = "https://files.pythonhosted.org/packages/4b/20/ddf5eb3bd5c57582d2b4652b4bbcf8da301bdfe5d805cb94e805f4d7464d/lxml-4.2.5.tar.gz";
971 971 sha256 = "0zw0y9hs0nflxhl9cs6ipwwh53szi3w2x06wl0k9cylyqac0cwin";
972 972 };
973 973 meta = {
974 974 license = [ pkgs.lib.licenses.bsdOriginal ];
975 975 };
976 976 };
977 977 "mako" = super.buildPythonPackage {
978 978 name = "mako-1.1.0";
979 979 doCheck = false;
980 980 propagatedBuildInputs = [
981 981 self."markupsafe"
982 982 ];
983 983 src = fetchurl {
984 984 url = "https://files.pythonhosted.org/packages/b0/3c/8dcd6883d009f7cae0f3157fb53e9afb05a0d3d33b3db1268ec2e6f4a56b/Mako-1.1.0.tar.gz";
985 985 sha256 = "0jqa3qfpykyn4fmkn0kh6043sfls7br8i2bsdbccazcvk9cijsd3";
986 986 };
987 987 meta = {
988 988 license = [ pkgs.lib.licenses.mit ];
989 989 };
990 990 };
991 991 "markdown" = super.buildPythonPackage {
992 992 name = "markdown-2.6.11";
993 993 doCheck = false;
994 994 src = fetchurl {
995 995 url = "https://files.pythonhosted.org/packages/b3/73/fc5c850f44af5889192dff783b7b0d8f3fe8d30b65c8e3f78f8f0265fecf/Markdown-2.6.11.tar.gz";
996 996 sha256 = "108g80ryzykh8bj0i7jfp71510wrcixdi771lf2asyghgyf8cmm8";
997 997 };
998 998 meta = {
999 999 license = [ pkgs.lib.licenses.bsdOriginal ];
1000 1000 };
1001 1001 };
1002 1002 "markupsafe" = super.buildPythonPackage {
1003 1003 name = "markupsafe-1.1.1";
1004 1004 doCheck = false;
1005 1005 src = fetchurl {
1006 1006 url = "https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz";
1007 1007 sha256 = "0sqipg4fk7xbixqd8kq6rlkxj664d157bdwbh93farcphf92x1r9";
1008 1008 };
1009 1009 meta = {
1010 1010 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd3 ];
1011 1011 };
1012 1012 };
1013 1013 "marshmallow" = super.buildPythonPackage {
1014 1014 name = "marshmallow-2.18.0";
1015 1015 doCheck = false;
1016 1016 src = fetchurl {
1017 1017 url = "https://files.pythonhosted.org/packages/ad/0b/5799965d1c6d5f608d684e2c0dce8a828e0309a3bfe8327d9418a89f591c/marshmallow-2.18.0.tar.gz";
1018 1018 sha256 = "1g0aafpjn7yaxq06yndy8c7rs9n42adxkqq1ayhlr869pr06d3lm";
1019 1019 };
1020 1020 meta = {
1021 1021 license = [ pkgs.lib.licenses.mit ];
1022 1022 };
1023 1023 };
1024 1024 "mistune" = super.buildPythonPackage {
1025 1025 name = "mistune-0.8.4";
1026 1026 doCheck = false;
1027 1027 src = fetchurl {
1028 1028 url = "https://files.pythonhosted.org/packages/2d/a4/509f6e7783ddd35482feda27bc7f72e65b5e7dc910eca4ab2164daf9c577/mistune-0.8.4.tar.gz";
1029 1029 sha256 = "0vkmsh0x480rni51lhyvigfdf06b9247z868pk3bal1wnnfl58sr";
1030 1030 };
1031 1031 meta = {
1032 1032 license = [ pkgs.lib.licenses.bsdOriginal ];
1033 1033 };
1034 1034 };
1035 1035 "mock" = super.buildPythonPackage {
1036 1036 name = "mock-3.0.5";
1037 1037 doCheck = false;
1038 1038 propagatedBuildInputs = [
1039 1039 self."six"
1040 1040 self."funcsigs"
1041 1041 ];
1042 1042 src = fetchurl {
1043 1043 url = "https://files.pythonhosted.org/packages/2e/ab/4fe657d78b270aa6a32f027849513b829b41b0f28d9d8d7f8c3d29ea559a/mock-3.0.5.tar.gz";
1044 1044 sha256 = "1hrp6j0yrx2xzylfv02qa8kph661m6yq4p0mc8fnimch9j4psrc3";
1045 1045 };
1046 1046 meta = {
1047 1047 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "OSI Approved :: BSD License"; } ];
1048 1048 };
1049 1049 };
1050 1050 "more-itertools" = super.buildPythonPackage {
1051 1051 name = "more-itertools-5.0.0";
1052 1052 doCheck = false;
1053 1053 propagatedBuildInputs = [
1054 1054 self."six"
1055 1055 ];
1056 1056 src = fetchurl {
1057 1057 url = "https://files.pythonhosted.org/packages/dd/26/30fc0d541d9fdf55faf5ba4b0fd68f81d5bd2447579224820ad525934178/more-itertools-5.0.0.tar.gz";
1058 1058 sha256 = "1r12cm6mcdwdzz7d47a6g4l437xsvapdlgyhqay3i2nrlv03da9q";
1059 1059 };
1060 1060 meta = {
1061 1061 license = [ pkgs.lib.licenses.mit ];
1062 1062 };
1063 1063 };
1064 1064 "msgpack-python" = super.buildPythonPackage {
1065 1065 name = "msgpack-python-0.5.6";
1066 1066 doCheck = false;
1067 1067 src = fetchurl {
1068 1068 url = "https://files.pythonhosted.org/packages/8a/20/6eca772d1a5830336f84aca1d8198e5a3f4715cd1c7fc36d3cc7f7185091/msgpack-python-0.5.6.tar.gz";
1069 1069 sha256 = "16wh8qgybmfh4pjp8vfv78mdlkxfmcasg78lzlnm6nslsfkci31p";
1070 1070 };
1071 1071 meta = {
1072 1072 license = [ pkgs.lib.licenses.asl20 ];
1073 1073 };
1074 1074 };
1075 1075 "mysql-python" = super.buildPythonPackage {
1076 1076 name = "mysql-python-1.2.5";
1077 1077 doCheck = false;
1078 1078 src = fetchurl {
1079 1079 url = "https://files.pythonhosted.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
1080 1080 sha256 = "0x0c2jg0bb3pp84njaqiic050qkyd7ymwhfvhipnimg58yv40441";
1081 1081 };
1082 1082 meta = {
1083 1083 license = [ pkgs.lib.licenses.gpl1 ];
1084 1084 };
1085 1085 };
1086 1086 "nbconvert" = super.buildPythonPackage {
1087 1087 name = "nbconvert-5.3.1";
1088 1088 doCheck = false;
1089 1089 propagatedBuildInputs = [
1090 1090 self."mistune"
1091 1091 self."jinja2"
1092 1092 self."pygments"
1093 1093 self."traitlets"
1094 1094 self."jupyter-core"
1095 1095 self."nbformat"
1096 1096 self."entrypoints"
1097 1097 self."bleach"
1098 1098 self."pandocfilters"
1099 1099 self."testpath"
1100 1100 ];
1101 1101 src = fetchurl {
1102 1102 url = "https://files.pythonhosted.org/packages/b9/a4/d0a0938ad6f5eeb4dea4e73d255c617ef94b0b2849d51194c9bbdb838412/nbconvert-5.3.1.tar.gz";
1103 1103 sha256 = "1f9dkvpx186xjm4xab0qbph588mncp4vqk3fmxrsnqs43mks9c8j";
1104 1104 };
1105 1105 meta = {
1106 1106 license = [ pkgs.lib.licenses.bsdOriginal ];
1107 1107 };
1108 1108 };
1109 1109 "nbformat" = super.buildPythonPackage {
1110 1110 name = "nbformat-4.4.0";
1111 1111 doCheck = false;
1112 1112 propagatedBuildInputs = [
1113 1113 self."ipython-genutils"
1114 1114 self."traitlets"
1115 1115 self."jsonschema"
1116 1116 self."jupyter-core"
1117 1117 ];
1118 1118 src = fetchurl {
1119 1119 url = "https://files.pythonhosted.org/packages/6e/0e/160754f7ae3e984863f585a3743b0ed1702043a81245907c8fae2d537155/nbformat-4.4.0.tar.gz";
1120 1120 sha256 = "00nlf08h8yc4q73nphfvfhxrcnilaqanb8z0mdy6nxk0vzq4wjgp";
1121 1121 };
1122 1122 meta = {
1123 1123 license = [ pkgs.lib.licenses.bsdOriginal ];
1124 1124 };
1125 1125 };
1126 1126 "packaging" = super.buildPythonPackage {
1127 1127 name = "packaging-20.3";
1128 1128 doCheck = false;
1129 1129 propagatedBuildInputs = [
1130 1130 self."pyparsing"
1131 1131 self."six"
1132 1132 ];
1133 1133 src = fetchurl {
1134 1134 url = "https://files.pythonhosted.org/packages/65/37/83e3f492eb52d771e2820e88105f605335553fe10422cba9d256faeb1702/packaging-20.3.tar.gz";
1135 1135 sha256 = "18xpablq278janh03bai9xd4kz9b0yfp6vflazn725ns9x3jna9w";
1136 1136 };
1137 1137 meta = {
1138 1138 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "BSD or Apache License, Version 2.0"; } pkgs.lib.licenses.asl20 ];
1139 1139 };
1140 1140 };
1141 1141 "pandocfilters" = super.buildPythonPackage {
1142 1142 name = "pandocfilters-1.4.2";
1143 1143 doCheck = false;
1144 1144 src = fetchurl {
1145 1145 url = "https://files.pythonhosted.org/packages/4c/ea/236e2584af67bb6df960832731a6e5325fd4441de001767da328c33368ce/pandocfilters-1.4.2.tar.gz";
1146 1146 sha256 = "1a8d9b7s48gmq9zj0pmbyv2sivn5i7m6mybgpkk4jm5vd7hp1pdk";
1147 1147 };
1148 1148 meta = {
1149 1149 license = [ pkgs.lib.licenses.bsdOriginal ];
1150 1150 };
1151 1151 };
1152 1152 "paste" = super.buildPythonPackage {
1153 1153 name = "paste-3.4.0";
1154 1154 doCheck = false;
1155 1155 propagatedBuildInputs = [
1156 1156 self."six"
1157 1157 ];
1158 1158 src = fetchurl {
1159 1159 url = "https://files.pythonhosted.org/packages/79/4a/45821b71dd40000507549afd1491546afad8279c0a87527c88776a794158/Paste-3.4.0.tar.gz";
1160 1160 sha256 = "16sichvhyci1gaarkjs35mai8vphh7b244qm14hj1isw38nx4c03";
1161 1161 };
1162 1162 meta = {
1163 1163 license = [ pkgs.lib.licenses.mit ];
1164 1164 };
1165 1165 };
1166 1166 "pastedeploy" = super.buildPythonPackage {
1167 1167 name = "pastedeploy-2.1.0";
1168 1168 doCheck = false;
1169 1169 src = fetchurl {
1170 1170 url = "https://files.pythonhosted.org/packages/c4/e9/972a1c20318b3ae9edcab11a6cef64308fbae5d0d45ab52c6f8b2b8f35b8/PasteDeploy-2.1.0.tar.gz";
1171 1171 sha256 = "16qsq5y6mryslmbp5pn35x4z8z3ndp5rpgl42h226879nrw9hmg7";
1172 1172 };
1173 1173 meta = {
1174 1174 license = [ pkgs.lib.licenses.mit ];
1175 1175 };
1176 1176 };
1177 1177 "pastescript" = super.buildPythonPackage {
1178 1178 name = "pastescript-3.2.0";
1179 1179 doCheck = false;
1180 1180 propagatedBuildInputs = [
1181 1181 self."paste"
1182 1182 self."pastedeploy"
1183 1183 self."six"
1184 1184 ];
1185 1185 src = fetchurl {
1186 1186 url = "https://files.pythonhosted.org/packages/ff/47/45c6f5a3cb8f5abf786fea98dbb8d02400a55768a9b623afb7df12346c61/PasteScript-3.2.0.tar.gz";
1187 1187 sha256 = "1b3jq7xh383nvrrlblk05m37345bv97xrhx77wshllba3h7mq3wv";
1188 1188 };
1189 1189 meta = {
1190 1190 license = [ pkgs.lib.licenses.mit ];
1191 1191 };
1192 1192 };
1193 1193 "pathlib2" = super.buildPythonPackage {
1194 1194 name = "pathlib2-2.3.5";
1195 1195 doCheck = false;
1196 1196 propagatedBuildInputs = [
1197 1197 self."six"
1198 1198 self."scandir"
1199 1199 ];
1200 1200 src = fetchurl {
1201 1201 url = "https://files.pythonhosted.org/packages/94/d8/65c86584e7e97ef824a1845c72bbe95d79f5b306364fa778a3c3e401b309/pathlib2-2.3.5.tar.gz";
1202 1202 sha256 = "0s4qa8c082fdkb17izh4mfgwrjd1n5pya18wvrbwqdvvb5xs9nbc";
1203 1203 };
1204 1204 meta = {
1205 1205 license = [ pkgs.lib.licenses.mit ];
1206 1206 };
1207 1207 };
1208 1208 "peppercorn" = super.buildPythonPackage {
1209 1209 name = "peppercorn-0.6";
1210 1210 doCheck = false;
1211 1211 src = fetchurl {
1212 1212 url = "https://files.pythonhosted.org/packages/e4/77/93085de7108cdf1a0b092ff443872a8f9442c736d7ddebdf2f27627935f4/peppercorn-0.6.tar.gz";
1213 1213 sha256 = "1ip4bfwcpwkq9hz2dai14k2cyabvwrnvcvrcmzxmqm04g8fnimwn";
1214 1214 };
1215 1215 meta = {
1216 1216 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1217 1217 };
1218 1218 };
1219 1219 "pexpect" = super.buildPythonPackage {
1220 1220 name = "pexpect-4.8.0";
1221 1221 doCheck = false;
1222 1222 propagatedBuildInputs = [
1223 1223 self."ptyprocess"
1224 1224 ];
1225 1225 src = fetchurl {
1226 1226 url = "https://files.pythonhosted.org/packages/e5/9b/ff402e0e930e70467a7178abb7c128709a30dfb22d8777c043e501bc1b10/pexpect-4.8.0.tar.gz";
1227 1227 sha256 = "032cg337h8awydgypz6f4wx848lw8dyrj4zy988x0lyib4ws8rgw";
1228 1228 };
1229 1229 meta = {
1230 1230 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
1231 1231 };
1232 1232 };
1233 1233 "pickleshare" = super.buildPythonPackage {
1234 1234 name = "pickleshare-0.7.5";
1235 1235 doCheck = false;
1236 1236 propagatedBuildInputs = [
1237 1237 self."pathlib2"
1238 1238 ];
1239 1239 src = fetchurl {
1240 1240 url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz";
1241 1241 sha256 = "1jmghg3c53yp1i8cm6pcrm280ayi8621rwyav9fac7awjr3kss47";
1242 1242 };
1243 1243 meta = {
1244 1244 license = [ pkgs.lib.licenses.mit ];
1245 1245 };
1246 1246 };
1247 1247 "plaster" = super.buildPythonPackage {
1248 1248 name = "plaster-1.0";
1249 1249 doCheck = false;
1250 1250 propagatedBuildInputs = [
1251 1251 self."setuptools"
1252 1252 ];
1253 1253 src = fetchurl {
1254 1254 url = "https://files.pythonhosted.org/packages/37/e1/56d04382d718d32751017d32f351214384e529b794084eee20bb52405563/plaster-1.0.tar.gz";
1255 1255 sha256 = "1hy8k0nv2mxq94y5aysk6hjk9ryb4bsd13g83m60hcyzxz3wflc3";
1256 1256 };
1257 1257 meta = {
1258 1258 license = [ pkgs.lib.licenses.mit ];
1259 1259 };
1260 1260 };
1261 1261 "plaster-pastedeploy" = super.buildPythonPackage {
1262 1262 name = "plaster-pastedeploy-0.7";
1263 1263 doCheck = false;
1264 1264 propagatedBuildInputs = [
1265 1265 self."pastedeploy"
1266 1266 self."plaster"
1267 1267 ];
1268 1268 src = fetchurl {
1269 1269 url = "https://files.pythonhosted.org/packages/99/69/2d3bc33091249266a1bd3cf24499e40ab31d54dffb4a7d76fe647950b98c/plaster_pastedeploy-0.7.tar.gz";
1270 1270 sha256 = "1zg7gcsvc1kzay1ry5p699rg2qavfsxqwl17mqxzr0gzw6j9679r";
1271 1271 };
1272 1272 meta = {
1273 1273 license = [ pkgs.lib.licenses.mit ];
1274 1274 };
1275 1275 };
1276 1276 "pluggy" = super.buildPythonPackage {
1277 1277 name = "pluggy-0.13.1";
1278 1278 doCheck = false;
1279 1279 propagatedBuildInputs = [
1280 1280 self."importlib-metadata"
1281 1281 ];
1282 1282 src = fetchurl {
1283 1283 url = "https://files.pythonhosted.org/packages/f8/04/7a8542bed4b16a65c2714bf76cf5a0b026157da7f75e87cc88774aa10b14/pluggy-0.13.1.tar.gz";
1284 1284 sha256 = "1c35qyhvy27q9ih9n899f3h4sdnpgq027dbiilly2qb5cvgarchm";
1285 1285 };
1286 1286 meta = {
1287 1287 license = [ pkgs.lib.licenses.mit ];
1288 1288 };
1289 1289 };
1290 1290 "premailer" = super.buildPythonPackage {
1291 1291 name = "premailer-3.6.1";
1292 1292 doCheck = false;
1293 1293 propagatedBuildInputs = [
1294 1294 self."lxml"
1295 1295 self."cssselect"
1296 1296 self."cssutils"
1297 1297 self."requests"
1298 1298 self."cachetools"
1299 1299 ];
1300 1300 src = fetchurl {
1301 1301 url = "https://files.pythonhosted.org/packages/62/da/2f43cdf9d3d79c80c4856a12389a1f257d65fe9ccc44bc6b4383c8a18e33/premailer-3.6.1.tar.gz";
1302 1302 sha256 = "08pshx7a110k4ll20x0xhpvyn3kkipkrbgxjjn7ncdxs54ihdhgw";
1303 1303 };
1304 1304 meta = {
1305 1305 license = [ pkgs.lib.licenses.psfl { fullName = "Python"; } ];
1306 1306 };
1307 1307 };
1308 1308 "prompt-toolkit" = super.buildPythonPackage {
1309 1309 name = "prompt-toolkit-1.0.18";
1310 1310 doCheck = false;
1311 1311 propagatedBuildInputs = [
1312 1312 self."six"
1313 1313 self."wcwidth"
1314 1314 ];
1315 1315 src = fetchurl {
1316 1316 url = "https://files.pythonhosted.org/packages/c5/64/c170e5b1913b540bf0c8ab7676b21fdd1d25b65ddeb10025c6ca43cccd4c/prompt_toolkit-1.0.18.tar.gz";
1317 1317 sha256 = "09h1153wgr5x2ny7ds0w2m81n3bb9j8hjb8sjfnrg506r01clkyx";
1318 1318 };
1319 1319 meta = {
1320 1320 license = [ pkgs.lib.licenses.bsdOriginal ];
1321 1321 };
1322 1322 };
1323 1323 "psutil" = super.buildPythonPackage {
1324 1324 name = "psutil-5.7.0";
1325 1325 doCheck = false;
1326 1326 src = fetchurl {
1327 1327 url = "https://files.pythonhosted.org/packages/c4/b8/3512f0e93e0db23a71d82485ba256071ebef99b227351f0f5540f744af41/psutil-5.7.0.tar.gz";
1328 1328 sha256 = "03jykdi3dgf1cdal9bv4fq9zjvzj9l9bs99gi5ar81sdl5nc2pk8";
1329 1329 };
1330 1330 meta = {
1331 1331 license = [ pkgs.lib.licenses.bsdOriginal ];
1332 1332 };
1333 1333 };
1334 1334 "psycopg2" = super.buildPythonPackage {
1335 1335 name = "psycopg2-2.8.4";
1336 1336 doCheck = false;
1337 1337 src = fetchurl {
1338 1338 url = "https://files.pythonhosted.org/packages/84/d7/6a93c99b5ba4d4d22daa3928b983cec66df4536ca50b22ce5dcac65e4e71/psycopg2-2.8.4.tar.gz";
1339 1339 sha256 = "1djvh98pi4hjd8rxbq8qzc63bg8v78k33yg6pl99wak61b6fb67q";
1340 1340 };
1341 1341 meta = {
1342 1342 license = [ pkgs.lib.licenses.zpl21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1343 1343 };
1344 1344 };
1345 1345 "ptyprocess" = super.buildPythonPackage {
1346 1346 name = "ptyprocess-0.6.0";
1347 1347 doCheck = false;
1348 1348 src = fetchurl {
1349 1349 url = "https://files.pythonhosted.org/packages/7d/2d/e4b8733cf79b7309d84c9081a4ab558c89d8c89da5961bf4ddb050ca1ce0/ptyprocess-0.6.0.tar.gz";
1350 1350 sha256 = "1h4lcd3w5nrxnsk436ar7fwkiy5rfn5wj2xwy9l0r4mdqnf2jgwj";
1351 1351 };
1352 1352 meta = {
1353 1353 license = [ ];
1354 1354 };
1355 1355 };
1356 1356 "py" = super.buildPythonPackage {
1357 1357 name = "py-1.8.0";
1358 1358 doCheck = false;
1359 1359 src = fetchurl {
1360 1360 url = "https://files.pythonhosted.org/packages/f1/5a/87ca5909f400a2de1561f1648883af74345fe96349f34f737cdfc94eba8c/py-1.8.0.tar.gz";
1361 1361 sha256 = "0lsy1gajva083pzc7csj1cvbmminb7b4l6a0prdzyb3fd829nqyw";
1362 1362 };
1363 1363 meta = {
1364 1364 license = [ pkgs.lib.licenses.mit ];
1365 1365 };
1366 1366 };
1367 1367 "py-bcrypt" = super.buildPythonPackage {
1368 1368 name = "py-bcrypt-0.4";
1369 1369 doCheck = false;
1370 1370 src = fetchurl {
1371 1371 url = "https://files.pythonhosted.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
1372 1372 sha256 = "0y6smdggwi5s72v6p1nn53dg6w05hna3d264cq6kas0lap73p8az";
1373 1373 };
1374 1374 meta = {
1375 1375 license = [ pkgs.lib.licenses.bsdOriginal ];
1376 1376 };
1377 1377 };
1378 1378 "py-gfm" = super.buildPythonPackage {
1379 1379 name = "py-gfm-0.1.4";
1380 1380 doCheck = false;
1381 1381 propagatedBuildInputs = [
1382 1382 self."setuptools"
1383 1383 self."markdown"
1384 1384 ];
1385 1385 src = fetchurl {
1386 1386 url = "https://files.pythonhosted.org/packages/06/ee/004a03a1d92bb386dae44f6dd087db541bc5093374f1637d4d4ae5596cc2/py-gfm-0.1.4.tar.gz";
1387 1387 sha256 = "0zip06g2isivx8fzgqd4n9qzsa22c25jas1rsb7m2rnjg72m0rzg";
1388 1388 };
1389 1389 meta = {
1390 1390 license = [ pkgs.lib.licenses.bsdOriginal ];
1391 1391 };
1392 1392 };
1393 1393 "pyasn1" = super.buildPythonPackage {
1394 1394 name = "pyasn1-0.4.8";
1395 1395 doCheck = false;
1396 1396 src = fetchurl {
1397 1397 url = "https://files.pythonhosted.org/packages/a4/db/fffec68299e6d7bad3d504147f9094830b704527a7fc098b721d38cc7fa7/pyasn1-0.4.8.tar.gz";
1398 1398 sha256 = "1fnhbi3rmk47l9851gbik0flfr64vs5j0hbqx24cafjap6gprxxf";
1399 1399 };
1400 1400 meta = {
1401 1401 license = [ pkgs.lib.licenses.bsdOriginal ];
1402 1402 };
1403 1403 };
1404 1404 "pyasn1-modules" = super.buildPythonPackage {
1405 1405 name = "pyasn1-modules-0.2.6";
1406 1406 doCheck = false;
1407 1407 propagatedBuildInputs = [
1408 1408 self."pyasn1"
1409 1409 ];
1410 1410 src = fetchurl {
1411 1411 url = "https://files.pythonhosted.org/packages/f1/a9/a1ef72a0e43feff643cf0130a08123dea76205e7a0dda37e3efb5f054a31/pyasn1-modules-0.2.6.tar.gz";
1412 1412 sha256 = "08hph9j1r018drnrny29l7dl2q0cin78csswrhwrh8jmq61pmha3";
1413 1413 };
1414 1414 meta = {
1415 1415 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
1416 1416 };
1417 1417 };
1418 1418 "pycparser" = super.buildPythonPackage {
1419 1419 name = "pycparser-2.20";
1420 1420 doCheck = false;
1421 1421 src = fetchurl {
1422 1422 url = "https://files.pythonhosted.org/packages/0f/86/e19659527668d70be91d0369aeaa055b4eb396b0f387a4f92293a20035bd/pycparser-2.20.tar.gz";
1423 1423 sha256 = "1w0m3xvlrzq4lkbvd1ngfm8mdw64r1yxy6n7djlw6qj5d0km6ird";
1424 1424 };
1425 1425 meta = {
1426 1426 license = [ pkgs.lib.licenses.bsdOriginal ];
1427 1427 };
1428 1428 };
1429 1429 "pycrypto" = super.buildPythonPackage {
1430 1430 name = "pycrypto-2.6.1";
1431 1431 doCheck = false;
1432 1432 src = fetchurl {
1433 1433 url = "https://files.pythonhosted.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1434 1434 sha256 = "0g0ayql5b9mkjam8hym6zyg6bv77lbh66rv1fyvgqb17kfc1xkpj";
1435 1435 };
1436 1436 meta = {
1437 1437 license = [ pkgs.lib.licenses.publicDomain ];
1438 1438 };
1439 1439 };
1440 1440 "pycurl" = super.buildPythonPackage {
1441 1441 name = "pycurl-7.43.0.3";
1442 1442 doCheck = false;
1443 1443 src = fetchurl {
1444 1444 url = "https://files.pythonhosted.org/packages/ac/b3/0f3979633b7890bab6098d84c84467030b807a1e2b31f5d30103af5a71ca/pycurl-7.43.0.3.tar.gz";
1445 1445 sha256 = "13nsvqhvnmnvfk75s8iynqsgszyv06cjp4drd3psi7zpbh63623g";
1446 1446 };
1447 1447 meta = {
1448 1448 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1449 1449 };
1450 1450 };
1451 1451 "pygments" = super.buildPythonPackage {
1452 1452 name = "pygments-2.4.2";
1453 1453 doCheck = false;
1454 1454 src = fetchurl {
1455 1455 url = "https://files.pythonhosted.org/packages/7e/ae/26808275fc76bf2832deb10d3a3ed3107bc4de01b85dcccbe525f2cd6d1e/Pygments-2.4.2.tar.gz";
1456 1456 sha256 = "15v2sqm5g12bqa0c7wikfh9ck2nl97ayizy1hpqhmws5gqalq748";
1457 1457 };
1458 1458 meta = {
1459 1459 license = [ pkgs.lib.licenses.bsdOriginal ];
1460 1460 };
1461 1461 };
1462 1462 "pymysql" = super.buildPythonPackage {
1463 1463 name = "pymysql-0.8.1";
1464 1464 doCheck = false;
1465 1465 src = fetchurl {
1466 1466 url = "https://files.pythonhosted.org/packages/44/39/6bcb83cae0095a31b6be4511707fdf2009d3e29903a55a0494d3a9a2fac0/PyMySQL-0.8.1.tar.gz";
1467 1467 sha256 = "0a96crz55bw4h6myh833skrli7b0ck89m3x673y2z2ryy7zrpq9l";
1468 1468 };
1469 1469 meta = {
1470 1470 license = [ pkgs.lib.licenses.mit ];
1471 1471 };
1472 1472 };
1473 1473 "pyotp" = super.buildPythonPackage {
1474 1474 name = "pyotp-2.3.0";
1475 1475 doCheck = false;
1476 1476 src = fetchurl {
1477 1477 url = "https://files.pythonhosted.org/packages/f7/15/395c4945ea6bc37e8811280bb675615cb4c2b2c1cd70bdc43329da91a386/pyotp-2.3.0.tar.gz";
1478 1478 sha256 = "18d13ikra1iq0xyfqfm72zhgwxi2qi9ps6z1a6zmqp4qrn57wlzw";
1479 1479 };
1480 1480 meta = {
1481 1481 license = [ pkgs.lib.licenses.mit ];
1482 1482 };
1483 1483 };
1484 1484 "pyparsing" = super.buildPythonPackage {
1485 1485 name = "pyparsing-2.4.7";
1486 1486 doCheck = false;
1487 1487 src = fetchurl {
1488 1488 url = "https://files.pythonhosted.org/packages/c1/47/dfc9c342c9842bbe0036c7f763d2d6686bcf5eb1808ba3e170afdb282210/pyparsing-2.4.7.tar.gz";
1489 1489 sha256 = "1hgc8qrbq1ymxbwfbjghv01fm3fbpjwpjwi0bcailxxzhf3yq0y2";
1490 1490 };
1491 1491 meta = {
1492 1492 license = [ pkgs.lib.licenses.mit ];
1493 1493 };
1494 1494 };
1495 1495 "pyramid" = super.buildPythonPackage {
1496 1496 name = "pyramid-1.10.4";
1497 1497 doCheck = false;
1498 1498 propagatedBuildInputs = [
1499 1499 self."hupper"
1500 1500 self."plaster"
1501 1501 self."plaster-pastedeploy"
1502 1502 self."setuptools"
1503 1503 self."translationstring"
1504 1504 self."venusian"
1505 1505 self."webob"
1506 1506 self."zope.deprecation"
1507 1507 self."zope.interface"
1508 1508 self."repoze.lru"
1509 1509 ];
1510 1510 src = fetchurl {
1511 1511 url = "https://files.pythonhosted.org/packages/c2/43/1ae701c9c6bb3a434358e678a5e72c96e8aa55cf4cb1d2fa2041b5dd38b7/pyramid-1.10.4.tar.gz";
1512 1512 sha256 = "0rkxs1ajycg2zh1c94xlmls56mx5m161sn8112skj0amza6cn36q";
1513 1513 };
1514 1514 meta = {
1515 1515 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1516 1516 };
1517 1517 };
1518 1518 "pyramid-debugtoolbar" = super.buildPythonPackage {
1519 1519 name = "pyramid-debugtoolbar-4.6.1";
1520 1520 doCheck = false;
1521 1521 propagatedBuildInputs = [
1522 1522 self."pyramid"
1523 1523 self."pyramid-mako"
1524 1524 self."repoze.lru"
1525 1525 self."pygments"
1526 1526 self."ipaddress"
1527 1527 ];
1528 1528 src = fetchurl {
1529 1529 url = "https://files.pythonhosted.org/packages/99/f6/b8603f82c18275be293921bc3a2184205056ca505747bf64ab8a0c08e124/pyramid_debugtoolbar-4.6.1.tar.gz";
1530 1530 sha256 = "185z7q8n959ga5331iczwra2iljwkidfx4qn6bbd7vm3rm4w6llv";
1531 1531 };
1532 1532 meta = {
1533 1533 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1534 1534 };
1535 1535 };
1536 1536 "pyramid-jinja2" = super.buildPythonPackage {
1537 1537 name = "pyramid-jinja2-2.7";
1538 1538 doCheck = false;
1539 1539 propagatedBuildInputs = [
1540 1540 self."pyramid"
1541 1541 self."zope.deprecation"
1542 1542 self."jinja2"
1543 1543 self."markupsafe"
1544 1544 ];
1545 1545 src = fetchurl {
1546 1546 url = "https://files.pythonhosted.org/packages/d8/80/d60a7233823de22ce77bd864a8a83736a1fe8b49884b08303a2e68b2c853/pyramid_jinja2-2.7.tar.gz";
1547 1547 sha256 = "1sz5s0pp5jqhf4w22w9527yz8hgdi4mhr6apd6vw1gm5clghh8aw";
1548 1548 };
1549 1549 meta = {
1550 1550 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1551 1551 };
1552 1552 };
1553 1553 "pyramid-apispec" = super.buildPythonPackage {
1554 1554 name = "pyramid-apispec-0.3.2";
1555 1555 doCheck = false;
1556 1556 propagatedBuildInputs = [
1557 1557 self."apispec"
1558 1558 ];
1559 1559 src = fetchurl {
1560 1560 url = "https://files.pythonhosted.org/packages/2a/30/1dea5d81ea635449572ba60ec3148310d75ae4530c3c695f54b0991bb8c7/pyramid_apispec-0.3.2.tar.gz";
1561 1561 sha256 = "0ffrcqp9dkykivhfcq0v9lgy6w0qhwl6x78925vfjmayly9r8da0";
1562 1562 };
1563 1563 meta = {
1564 1564 license = [ pkgs.lib.licenses.bsdOriginal ];
1565 1565 };
1566 1566 };
1567 1567 "pyramid-mailer" = super.buildPythonPackage {
1568 1568 name = "pyramid-mailer-0.15.1";
1569 1569 doCheck = false;
1570 1570 propagatedBuildInputs = [
1571 1571 self."pyramid"
1572 1572 self."repoze.sendmail"
1573 1573 self."transaction"
1574 1574 ];
1575 1575 src = fetchurl {
1576 1576 url = "https://files.pythonhosted.org/packages/a0/f2/6febf5459dff4d7e653314d575469ad2e11b9d2af2c3606360e1c67202f2/pyramid_mailer-0.15.1.tar.gz";
1577 1577 sha256 = "16vg8jb203jgb7b0hd6wllfqvp542qh2ry1gjai2m6qpv5agy2pc";
1578 1578 };
1579 1579 meta = {
1580 1580 license = [ pkgs.lib.licenses.bsdOriginal ];
1581 1581 };
1582 1582 };
1583 1583 "pyramid-mako" = super.buildPythonPackage {
1584 1584 name = "pyramid-mako-1.1.0";
1585 1585 doCheck = false;
1586 1586 propagatedBuildInputs = [
1587 1587 self."pyramid"
1588 1588 self."mako"
1589 1589 ];
1590 1590 src = fetchurl {
1591 1591 url = "https://files.pythonhosted.org/packages/63/7b/5e2af68f675071a6bad148c1c393928f0ef5fcd94e95cbf53b89d6471a83/pyramid_mako-1.1.0.tar.gz";
1592 1592 sha256 = "1qj0m091mnii86j2q1d82yir22nha361rvhclvg3s70z8iiwhrh0";
1593 1593 };
1594 1594 meta = {
1595 1595 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1596 1596 };
1597 1597 };
1598 1598 "pysqlite" = super.buildPythonPackage {
1599 1599 name = "pysqlite-2.8.3";
1600 1600 doCheck = false;
1601 1601 src = fetchurl {
1602 1602 url = "https://files.pythonhosted.org/packages/42/02/981b6703e3c83c5b25a829c6e77aad059f9481b0bbacb47e6e8ca12bd731/pysqlite-2.8.3.tar.gz";
1603 1603 sha256 = "1424gwq9sil2ffmnizk60q36vydkv8rxs6m7xs987kz8cdc37lqp";
1604 1604 };
1605 1605 meta = {
1606 1606 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1607 1607 };
1608 1608 };
1609 1609 "pytest" = super.buildPythonPackage {
1610 1610 name = "pytest-4.6.5";
1611 1611 doCheck = false;
1612 1612 propagatedBuildInputs = [
1613 1613 self."py"
1614 1614 self."six"
1615 1615 self."packaging"
1616 1616 self."attrs"
1617 1617 self."atomicwrites"
1618 1618 self."pluggy"
1619 1619 self."importlib-metadata"
1620 1620 self."wcwidth"
1621 1621 self."funcsigs"
1622 1622 self."pathlib2"
1623 1623 self."more-itertools"
1624 1624 ];
1625 1625 src = fetchurl {
1626 1626 url = "https://files.pythonhosted.org/packages/2a/c6/1d1f32f6a5009900521b12e6560fb6b7245b0d4bc3fb771acd63d10e30e1/pytest-4.6.5.tar.gz";
1627 1627 sha256 = "0iykwwfp4h181nd7rsihh2120b0rkawlw7rvbl19sgfspncr3hwg";
1628 1628 };
1629 1629 meta = {
1630 1630 license = [ pkgs.lib.licenses.mit ];
1631 1631 };
1632 1632 };
1633 1633 "pytest-cov" = super.buildPythonPackage {
1634 1634 name = "pytest-cov-2.7.1";
1635 1635 doCheck = false;
1636 1636 propagatedBuildInputs = [
1637 1637 self."pytest"
1638 1638 self."coverage"
1639 1639 ];
1640 1640 src = fetchurl {
1641 1641 url = "https://files.pythonhosted.org/packages/bb/0f/3db7ff86801883b21d5353b258c994b1b8e2abbc804e2273b8d0fd19004b/pytest-cov-2.7.1.tar.gz";
1642 1642 sha256 = "0filvmmyqm715azsl09ql8hy2x7h286n6d8z5x42a1wpvvys83p0";
1643 1643 };
1644 1644 meta = {
1645 1645 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1646 1646 };
1647 1647 };
1648 1648 "pytest-profiling" = super.buildPythonPackage {
1649 1649 name = "pytest-profiling-1.7.0";
1650 1650 doCheck = false;
1651 1651 propagatedBuildInputs = [
1652 1652 self."six"
1653 1653 self."pytest"
1654 1654 self."gprof2dot"
1655 1655 ];
1656 1656 src = fetchurl {
1657 1657 url = "https://files.pythonhosted.org/packages/39/70/22a4b33739f07f1732a63e33bbfbf68e0fa58cfba9d200e76d01921eddbf/pytest-profiling-1.7.0.tar.gz";
1658 1658 sha256 = "0abz9gi26jpcfdzgsvwad91555lpgdc8kbymicmms8k2fqa8z4wk";
1659 1659 };
1660 1660 meta = {
1661 1661 license = [ pkgs.lib.licenses.mit ];
1662 1662 };
1663 1663 };
1664 1664 "pytest-runner" = super.buildPythonPackage {
1665 1665 name = "pytest-runner-5.1";
1666 1666 doCheck = false;
1667 1667 src = fetchurl {
1668 1668 url = "https://files.pythonhosted.org/packages/d9/6d/4b41a74b31720e25abd4799be72d54811da4b4d0233e38b75864dcc1f7ad/pytest-runner-5.1.tar.gz";
1669 1669 sha256 = "0ykfcnpp8c22winj63qzc07l5axwlc9ikl8vn05sc32gv3417815";
1670 1670 };
1671 1671 meta = {
1672 1672 license = [ pkgs.lib.licenses.mit ];
1673 1673 };
1674 1674 };
1675 1675 "pytest-sugar" = super.buildPythonPackage {
1676 1676 name = "pytest-sugar-0.9.2";
1677 1677 doCheck = false;
1678 1678 propagatedBuildInputs = [
1679 1679 self."pytest"
1680 1680 self."termcolor"
1681 1681 self."packaging"
1682 1682 ];
1683 1683 src = fetchurl {
1684 1684 url = "https://files.pythonhosted.org/packages/55/59/f02f78d1c80f7e03e23177f60624c8106d4f23d124c921df103f65692464/pytest-sugar-0.9.2.tar.gz";
1685 1685 sha256 = "1asq7yc4g8bx2sn7yy974mhc9ywvaihasjab4inkirdwn9s7mn7w";
1686 1686 };
1687 1687 meta = {
1688 1688 license = [ pkgs.lib.licenses.bsdOriginal ];
1689 1689 };
1690 1690 };
1691 1691 "pytest-timeout" = super.buildPythonPackage {
1692 1692 name = "pytest-timeout-1.3.3";
1693 1693 doCheck = false;
1694 1694 propagatedBuildInputs = [
1695 1695 self."pytest"
1696 1696 ];
1697 1697 src = fetchurl {
1698 1698 url = "https://files.pythonhosted.org/packages/13/48/7a166eaa29c1dca6cc253e3ba5773ff2e4aa4f567c1ea3905808e95ac5c1/pytest-timeout-1.3.3.tar.gz";
1699 1699 sha256 = "1cczcjhw4xx5sjkhxlhc5c1bkr7x6fcyx12wrnvwfckshdvblc2a";
1700 1700 };
1701 1701 meta = {
1702 1702 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1703 1703 };
1704 1704 };
1705 1705 "python-dateutil" = super.buildPythonPackage {
1706 1706 name = "python-dateutil-2.8.1";
1707 1707 doCheck = false;
1708 1708 propagatedBuildInputs = [
1709 1709 self."six"
1710 1710 ];
1711 1711 src = fetchurl {
1712 1712 url = "https://files.pythonhosted.org/packages/be/ed/5bbc91f03fa4c839c4c7360375da77f9659af5f7086b7a7bdda65771c8e0/python-dateutil-2.8.1.tar.gz";
1713 1713 sha256 = "0g42w7k5007iv9dam6gnja2ry8ydwirh99mgdll35s12pyfzxsvk";
1714 1714 };
1715 1715 meta = {
1716 1716 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.asl20 { fullName = "Dual License"; } ];
1717 1717 };
1718 1718 };
1719 1719 "python-editor" = super.buildPythonPackage {
1720 1720 name = "python-editor-1.0.4";
1721 1721 doCheck = false;
1722 1722 src = fetchurl {
1723 1723 url = "https://files.pythonhosted.org/packages/0a/85/78f4a216d28343a67b7397c99825cff336330893f00601443f7c7b2f2234/python-editor-1.0.4.tar.gz";
1724 1724 sha256 = "0yrjh8w72ivqxi4i7xsg5b1vz15x8fg51xra7c3bgfyxqnyadzai";
1725 1725 };
1726 1726 meta = {
1727 1727 license = [ pkgs.lib.licenses.asl20 { fullName = "Apache"; } ];
1728 1728 };
1729 1729 };
1730 1730 "python-ldap" = super.buildPythonPackage {
1731 1731 name = "python-ldap-3.2.0";
1732 1732 doCheck = false;
1733 1733 propagatedBuildInputs = [
1734 1734 self."pyasn1"
1735 1735 self."pyasn1-modules"
1736 1736 ];
1737 1737 src = fetchurl {
1738 1738 url = "https://files.pythonhosted.org/packages/ea/93/596f875e003c770447f4b99267820a0c769dd2dc3ae3ed19afe460fcbad0/python-ldap-3.2.0.tar.gz";
1739 1739 sha256 = "13nvrhp85yr0jyxixcjj012iw8l9wynxxlykm9j3alss6waln73x";
1740 1740 };
1741 1741 meta = {
1742 1742 license = [ pkgs.lib.licenses.psfl ];
1743 1743 };
1744 1744 };
1745 1745 "python-memcached" = super.buildPythonPackage {
1746 1746 name = "python-memcached-1.59";
1747 1747 doCheck = false;
1748 1748 propagatedBuildInputs = [
1749 1749 self."six"
1750 1750 ];
1751 1751 src = fetchurl {
1752 1752 url = "https://files.pythonhosted.org/packages/90/59/5faf6e3cd8a568dd4f737ddae4f2e54204fd8c51f90bf8df99aca6c22318/python-memcached-1.59.tar.gz";
1753 1753 sha256 = "0kvyapavbirk2x3n1jx4yb9nyigrj1s3x15nm3qhpvhkpqvqdqm2";
1754 1754 };
1755 1755 meta = {
1756 1756 license = [ pkgs.lib.licenses.psfl ];
1757 1757 };
1758 1758 };
1759 1759 "python-pam" = super.buildPythonPackage {
1760 1760 name = "python-pam-1.8.4";
1761 1761 doCheck = false;
1762 1762 src = fetchurl {
1763 1763 url = "https://files.pythonhosted.org/packages/01/16/544d01cae9f28e0292dbd092b6b8b0bf222b528f362ee768a5bed2140111/python-pam-1.8.4.tar.gz";
1764 1764 sha256 = "16whhc0vr7gxsbzvsnq65nq8fs3wwmx755cavm8kkczdkz4djmn8";
1765 1765 };
1766 1766 meta = {
1767 1767 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1768 1768 };
1769 1769 };
1770 1770 "python-saml" = super.buildPythonPackage {
1771 1771 name = "python-saml-2.4.2";
1772 1772 doCheck = false;
1773 1773 propagatedBuildInputs = [
1774 1774 self."dm.xmlsec.binding"
1775 1775 self."isodate"
1776 1776 self."defusedxml"
1777 1777 ];
1778 1778 src = fetchurl {
1779 1779 url = "https://files.pythonhosted.org/packages/79/a8/a6611017e0883102fd5e2b73c9d90691b8134e38247c04ee1531d3dc647c/python-saml-2.4.2.tar.gz";
1780 1780 sha256 = "0dls4hwvf13yg7x5yfjrghbywg8g38vn5vr0rsf70hli3ydbfm43";
1781 1781 };
1782 1782 meta = {
1783 1783 license = [ pkgs.lib.licenses.mit ];
1784 1784 };
1785 1785 };
1786 1786 "pytz" = super.buildPythonPackage {
1787 1787 name = "pytz-2019.3";
1788 1788 doCheck = false;
1789 1789 src = fetchurl {
1790 1790 url = "https://files.pythonhosted.org/packages/82/c3/534ddba230bd4fbbd3b7a3d35f3341d014cca213f369a9940925e7e5f691/pytz-2019.3.tar.gz";
1791 1791 sha256 = "1ghrk1wg45d3nymj7bf4zj03n3bh64xmczhk4pfi577hdkdhcb5h";
1792 1792 };
1793 1793 meta = {
1794 1794 license = [ pkgs.lib.licenses.mit ];
1795 1795 };
1796 1796 };
1797 1797 "pyzmq" = super.buildPythonPackage {
1798 1798 name = "pyzmq-14.6.0";
1799 1799 doCheck = false;
1800 1800 src = fetchurl {
1801 1801 url = "https://files.pythonhosted.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1802 1802 sha256 = "1frmbjykvhmdg64g7sn20c9fpamrsfxwci1nhhg8q7jgz5pq0ikp";
1803 1803 };
1804 1804 meta = {
1805 1805 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1806 1806 };
1807 1807 };
1808 1808 "PyYAML" = super.buildPythonPackage {
1809 1809 name = "PyYAML-5.3.1";
1810 1810 doCheck = false;
1811 1811 src = fetchurl {
1812 1812 url = "https://files.pythonhosted.org/packages/64/c2/b80047c7ac2478f9501676c988a5411ed5572f35d1beff9cae07d321512c/PyYAML-5.3.1.tar.gz";
1813 1813 sha256 = "0pb4zvkfxfijkpgd1b86xjsqql97ssf1knbd1v53wkg1qm9cgsmq";
1814 1814 };
1815 1815 meta = {
1816 1816 license = [ pkgs.lib.licenses.mit ];
1817 1817 };
1818 1818 };
1819 1819 "regex" = super.buildPythonPackage {
1820 1820 name = "regex-2020.9.27";
1821 1821 doCheck = false;
1822 1822 src = fetchurl {
1823 1823 url = "https://files.pythonhosted.org/packages/93/8c/17f45cdfb39b13d4b5f909e4b4c2917abcbdef9c0036919a0399769148cf/regex-2020.9.27.tar.gz";
1824 1824 sha256 = "179ngfzwbsjvn5vhyzdahvmg0f7acahkwwy9bpjy1pv08bm2mwx6";
1825 1825 };
1826 1826 meta = {
1827 1827 license = [ pkgs.lib.licenses.psfl ];
1828 1828 };
1829 1829 };
1830 1830 "redis" = super.buildPythonPackage {
1831 1831 name = "redis-3.5.3";
1832 1832 doCheck = false;
1833 1833 src = fetchurl {
1834 1834 url = "https://files.pythonhosted.org/packages/b3/17/1e567ff78c83854e16b98694411fe6e08c3426af866ad11397cddceb80d3/redis-3.5.3.tar.gz";
1835 1835 sha256 = "0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2";
1836 1836 };
1837 1837 meta = {
1838 1838 license = [ pkgs.lib.licenses.mit ];
1839 1839 };
1840 1840 };
1841 1841 "repoze.lru" = super.buildPythonPackage {
1842 1842 name = "repoze.lru-0.7";
1843 1843 doCheck = false;
1844 1844 src = fetchurl {
1845 1845 url = "https://files.pythonhosted.org/packages/12/bc/595a77c4b5e204847fdf19268314ef59c85193a9dc9f83630fc459c0fee5/repoze.lru-0.7.tar.gz";
1846 1846 sha256 = "0xzz1aw2smy8hdszrq8yhnklx6w1r1mf55061kalw3iq35gafa84";
1847 1847 };
1848 1848 meta = {
1849 1849 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1850 1850 };
1851 1851 };
1852 1852 "repoze.sendmail" = super.buildPythonPackage {
1853 1853 name = "repoze.sendmail-4.4.1";
1854 1854 doCheck = false;
1855 1855 propagatedBuildInputs = [
1856 1856 self."setuptools"
1857 1857 self."zope.interface"
1858 1858 self."transaction"
1859 1859 ];
1860 1860 src = fetchurl {
1861 1861 url = "https://files.pythonhosted.org/packages/12/4e/8ef1fd5c42765d712427b9c391419a77bd48877886d2cbc5e9f23c8cad9b/repoze.sendmail-4.4.1.tar.gz";
1862 1862 sha256 = "096ln02jr2afk7ab9j2czxqv2ryqq7m86ah572nqplx52iws73ks";
1863 1863 };
1864 1864 meta = {
1865 1865 license = [ pkgs.lib.licenses.zpl21 ];
1866 1866 };
1867 1867 };
1868 1868 "requests" = super.buildPythonPackage {
1869 1869 name = "requests-2.22.0";
1870 1870 doCheck = false;
1871 1871 propagatedBuildInputs = [
1872 1872 self."chardet"
1873 1873 self."idna"
1874 1874 self."urllib3"
1875 1875 self."certifi"
1876 1876 ];
1877 1877 src = fetchurl {
1878 1878 url = "https://files.pythonhosted.org/packages/01/62/ddcf76d1d19885e8579acb1b1df26a852b03472c0e46d2b959a714c90608/requests-2.22.0.tar.gz";
1879 1879 sha256 = "1d5ybh11jr5sm7xp6mz8fyc7vrp4syifds91m7sj60xalal0gq0i";
1880 1880 };
1881 1881 meta = {
1882 1882 license = [ pkgs.lib.licenses.asl20 ];
1883 1883 };
1884 1884 };
1885 1885 "rhodecode-enterprise-ce" = super.buildPythonPackage {
1886 name = "rhodecode-enterprise-ce-4.25.0";
1886 name = "rhodecode-enterprise-ce-4.25.2";
1887 1887 buildInputs = [
1888 1888 self."pytest"
1889 1889 self."py"
1890 1890 self."pytest-cov"
1891 1891 self."pytest-sugar"
1892 1892 self."pytest-runner"
1893 1893 self."pytest-profiling"
1894 1894 self."pytest-timeout"
1895 1895 self."gprof2dot"
1896 1896 self."mock"
1897 1897 self."cov-core"
1898 1898 self."coverage"
1899 1899 self."webtest"
1900 1900 self."beautifulsoup4"
1901 1901 self."configobj"
1902 1902 ];
1903 1903 doCheck = true;
1904 1904 propagatedBuildInputs = [
1905 1905 self."amqp"
1906 1906 self."babel"
1907 1907 self."beaker"
1908 1908 self."bleach"
1909 1909 self."celery"
1910 1910 self."channelstream"
1911 1911 self."click"
1912 1912 self."colander"
1913 1913 self."configobj"
1914 1914 self."cssselect"
1915 1915 self."cryptography"
1916 1916 self."decorator"
1917 1917 self."deform"
1918 1918 self."docutils"
1919 1919 self."dogpile.cache"
1920 1920 self."dogpile.core"
1921 1921 self."formencode"
1922 1922 self."future"
1923 1923 self."futures"
1924 1924 self."infrae.cache"
1925 1925 self."iso8601"
1926 1926 self."itsdangerous"
1927 1927 self."kombu"
1928 1928 self."lxml"
1929 1929 self."mako"
1930 1930 self."markdown"
1931 1931 self."markupsafe"
1932 1932 self."msgpack-python"
1933 1933 self."pyotp"
1934 1934 self."packaging"
1935 1935 self."pathlib2"
1936 1936 self."paste"
1937 1937 self."pastedeploy"
1938 1938 self."pastescript"
1939 1939 self."peppercorn"
1940 1940 self."premailer"
1941 1941 self."psutil"
1942 1942 self."py-bcrypt"
1943 1943 self."pycurl"
1944 1944 self."pycrypto"
1945 1945 self."pygments"
1946 1946 self."pyparsing"
1947 1947 self."pyramid-debugtoolbar"
1948 1948 self."pyramid-mako"
1949 1949 self."pyramid"
1950 1950 self."pyramid-mailer"
1951 1951 self."python-dateutil"
1952 1952 self."python-ldap"
1953 1953 self."python-memcached"
1954 1954 self."python-pam"
1955 1955 self."python-saml"
1956 1956 self."pytz"
1957 1957 self."tzlocal"
1958 1958 self."pyzmq"
1959 1959 self."py-gfm"
1960 1960 self."regex"
1961 1961 self."redis"
1962 1962 self."repoze.lru"
1963 1963 self."requests"
1964 1964 self."routes"
1965 1965 self."simplejson"
1966 1966 self."six"
1967 1967 self."sqlalchemy"
1968 1968 self."sshpubkeys"
1969 1969 self."subprocess32"
1970 1970 self."supervisor"
1971 1971 self."translationstring"
1972 1972 self."urllib3"
1973 1973 self."urlobject"
1974 1974 self."venusian"
1975 1975 self."weberror"
1976 1976 self."webhelpers2"
1977 1977 self."webob"
1978 1978 self."whoosh"
1979 1979 self."wsgiref"
1980 1980 self."zope.cachedescriptors"
1981 1981 self."zope.deprecation"
1982 1982 self."zope.event"
1983 1983 self."zope.interface"
1984 1984 self."mysql-python"
1985 1985 self."pymysql"
1986 1986 self."pysqlite"
1987 1987 self."psycopg2"
1988 1988 self."nbconvert"
1989 1989 self."nbformat"
1990 1990 self."jupyter-client"
1991 1991 self."jupyter-core"
1992 1992 self."alembic"
1993 1993 self."invoke"
1994 1994 self."bumpversion"
1995 1995 self."gevent"
1996 1996 self."greenlet"
1997 1997 self."gunicorn"
1998 1998 self."waitress"
1999 1999 self."ipdb"
2000 2000 self."ipython"
2001 2001 self."rhodecode-tools"
2002 2002 self."appenlight-client"
2003 2003 self."pytest"
2004 2004 self."py"
2005 2005 self."pytest-cov"
2006 2006 self."pytest-sugar"
2007 2007 self."pytest-runner"
2008 2008 self."pytest-profiling"
2009 2009 self."pytest-timeout"
2010 2010 self."gprof2dot"
2011 2011 self."mock"
2012 2012 self."cov-core"
2013 2013 self."coverage"
2014 2014 self."webtest"
2015 2015 self."beautifulsoup4"
2016 2016 ];
2017 2017 src = ./.;
2018 2018 meta = {
2019 2019 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
2020 2020 };
2021 2021 };
2022 2022 "rhodecode-tools" = super.buildPythonPackage {
2023 2023 name = "rhodecode-tools-1.4.0";
2024 2024 doCheck = false;
2025 2025 propagatedBuildInputs = [
2026 2026 self."click"
2027 2027 self."future"
2028 2028 self."six"
2029 2029 self."mako"
2030 2030 self."markupsafe"
2031 2031 self."requests"
2032 2032 self."urllib3"
2033 2033 self."whoosh"
2034 2034 self."elasticsearch"
2035 2035 self."elasticsearch-dsl"
2036 2036 self."elasticsearch2"
2037 2037 self."elasticsearch1-dsl"
2038 2038 ];
2039 2039 src = fetchurl {
2040 2040 url = "https://code.rhodecode.com/rhodecode-tools-ce/artifacts/download/0-ed54e749-2ef5-4bc7-ae7f-7900e3c2aa15.tar.gz?sha256=76f024bad3a1e55fdb3d64f13f5b77ff21a12fee699918de2110fe21effd5a3a";
2041 2041 sha256 = "0fjszppj3zhh47g1i6b9xqps28gzfxdkzwb47pdmzrd1sfx29w3n";
2042 2042 };
2043 2043 meta = {
2044 2044 license = [ { fullName = "Apache 2.0 and Proprietary"; } ];
2045 2045 };
2046 2046 };
2047 2047 "routes" = super.buildPythonPackage {
2048 2048 name = "routes-2.4.1";
2049 2049 doCheck = false;
2050 2050 propagatedBuildInputs = [
2051 2051 self."six"
2052 2052 self."repoze.lru"
2053 2053 ];
2054 2054 src = fetchurl {
2055 2055 url = "https://files.pythonhosted.org/packages/33/38/ea827837e68d9c7dde4cff7ec122a93c319f0effc08ce92a17095576603f/Routes-2.4.1.tar.gz";
2056 2056 sha256 = "1zamff3m0kc4vyfniyhxpkkcqv1rrgnmh37ykxv34nna1ws47vi6";
2057 2057 };
2058 2058 meta = {
2059 2059 license = [ pkgs.lib.licenses.mit ];
2060 2060 };
2061 2061 };
2062 2062 "scandir" = super.buildPythonPackage {
2063 2063 name = "scandir-1.10.0";
2064 2064 doCheck = false;
2065 2065 src = fetchurl {
2066 2066 url = "https://files.pythonhosted.org/packages/df/f5/9c052db7bd54d0cbf1bc0bb6554362bba1012d03e5888950a4f5c5dadc4e/scandir-1.10.0.tar.gz";
2067 2067 sha256 = "1bkqwmf056pkchf05ywbnf659wqlp6lljcdb0y88wr9f0vv32ijd";
2068 2068 };
2069 2069 meta = {
2070 2070 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ];
2071 2071 };
2072 2072 };
2073 2073 "setproctitle" = super.buildPythonPackage {
2074 2074 name = "setproctitle-1.1.10";
2075 2075 doCheck = false;
2076 2076 src = fetchurl {
2077 2077 url = "https://files.pythonhosted.org/packages/5a/0d/dc0d2234aacba6cf1a729964383e3452c52096dc695581248b548786f2b3/setproctitle-1.1.10.tar.gz";
2078 2078 sha256 = "163kplw9dcrw0lffq1bvli5yws3rngpnvrxrzdw89pbphjjvg0v2";
2079 2079 };
2080 2080 meta = {
2081 2081 license = [ pkgs.lib.licenses.bsdOriginal ];
2082 2082 };
2083 2083 };
2084 2084 "setuptools" = super.buildPythonPackage {
2085 2085 name = "setuptools-44.1.0";
2086 2086 doCheck = false;
2087 2087 src = fetchurl {
2088 2088 url = "https://files.pythonhosted.org/packages/ed/7b/bbf89ca71e722b7f9464ebffe4b5ee20a9e5c9a555a56e2d3914bb9119a6/setuptools-44.1.0.zip";
2089 2089 sha256 = "1jja896zvd1ppccnjbhkgagxbwchgq6vfamp6qn1hvywq6q9cjkr";
2090 2090 };
2091 2091 meta = {
2092 2092 license = [ pkgs.lib.licenses.mit ];
2093 2093 };
2094 2094 };
2095 2095 "setuptools-scm" = super.buildPythonPackage {
2096 2096 name = "setuptools-scm-3.5.0";
2097 2097 doCheck = false;
2098 2098 src = fetchurl {
2099 2099 url = "https://files.pythonhosted.org/packages/b2/f7/60a645aae001a2e06cf4b8db2fba9d9f36b8fd378f10647e3e218b61b74b/setuptools_scm-3.5.0.tar.gz";
2100 2100 sha256 = "5bdf21a05792903cafe7ae0c9501182ab52497614fa6b1750d9dbae7b60c1a87";
2101 2101 };
2102 2102 meta = {
2103 2103 license = [ pkgs.lib.licenses.psfl ];
2104 2104 };
2105 2105 };
2106 2106 "simplegeneric" = super.buildPythonPackage {
2107 2107 name = "simplegeneric-0.8.1";
2108 2108 doCheck = false;
2109 2109 src = fetchurl {
2110 2110 url = "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
2111 2111 sha256 = "0wwi1c6md4vkbcsfsf8dklf3vr4mcdj4mpxkanwgb6jb1432x5yw";
2112 2112 };
2113 2113 meta = {
2114 2114 license = [ pkgs.lib.licenses.zpl21 ];
2115 2115 };
2116 2116 };
2117 2117 "simplejson" = super.buildPythonPackage {
2118 2118 name = "simplejson-3.16.0";
2119 2119 doCheck = false;
2120 2120 src = fetchurl {
2121 2121 url = "https://files.pythonhosted.org/packages/e3/24/c35fb1c1c315fc0fffe61ea00d3f88e85469004713dab488dee4f35b0aff/simplejson-3.16.0.tar.gz";
2122 2122 sha256 = "19cws1syk8jzq2pw43878dv6fjkb0ifvjpx0i9aajix6kc9jkwxi";
2123 2123 };
2124 2124 meta = {
2125 2125 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
2126 2126 };
2127 2127 };
2128 2128 "six" = super.buildPythonPackage {
2129 2129 name = "six-1.11.0";
2130 2130 doCheck = false;
2131 2131 src = fetchurl {
2132 2132 url = "https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz";
2133 2133 sha256 = "1scqzwc51c875z23phj48gircqjgnn3af8zy2izjwmnlxrxsgs3h";
2134 2134 };
2135 2135 meta = {
2136 2136 license = [ pkgs.lib.licenses.mit ];
2137 2137 };
2138 2138 };
2139 2139 "sqlalchemy" = super.buildPythonPackage {
2140 2140 name = "sqlalchemy-1.3.15";
2141 2141 doCheck = false;
2142 2142 src = fetchurl {
2143 2143 url = "https://files.pythonhosted.org/packages/8c/30/4134e726dd5ed13728ff814fa91fc01c447ad8700504653fe99d91fdd34b/SQLAlchemy-1.3.15.tar.gz";
2144 2144 sha256 = "0iglkvymfp35zm5pxy5kzqvcv96kkas0chqdx7xpla86sspa9k64";
2145 2145 };
2146 2146 meta = {
2147 2147 license = [ pkgs.lib.licenses.mit ];
2148 2148 };
2149 2149 };
2150 2150 "sshpubkeys" = super.buildPythonPackage {
2151 2151 name = "sshpubkeys-3.1.0";
2152 2152 doCheck = false;
2153 2153 propagatedBuildInputs = [
2154 2154 self."cryptography"
2155 2155 self."ecdsa"
2156 2156 ];
2157 2157 src = fetchurl {
2158 2158 url = "https://files.pythonhosted.org/packages/00/23/f7508a12007c96861c3da811992f14283d79c819d71a217b3e12d5196649/sshpubkeys-3.1.0.tar.gz";
2159 2159 sha256 = "105g2li04nm1hb15a2y6hm9m9k7fbrkd5l3gy12w3kgcmsf3k25k";
2160 2160 };
2161 2161 meta = {
2162 2162 license = [ pkgs.lib.licenses.bsdOriginal ];
2163 2163 };
2164 2164 };
2165 2165 "subprocess32" = super.buildPythonPackage {
2166 2166 name = "subprocess32-3.5.4";
2167 2167 doCheck = false;
2168 2168 src = fetchurl {
2169 2169 url = "https://files.pythonhosted.org/packages/32/c8/564be4d12629b912ea431f1a50eb8b3b9d00f1a0b1ceff17f266be190007/subprocess32-3.5.4.tar.gz";
2170 2170 sha256 = "17f7mvwx2271s1wrl0qac3wjqqnrqag866zs3qc8v5wp0k43fagb";
2171 2171 };
2172 2172 meta = {
2173 2173 license = [ pkgs.lib.licenses.psfl ];
2174 2174 };
2175 2175 };
2176 2176 "supervisor" = super.buildPythonPackage {
2177 2177 name = "supervisor-4.1.0";
2178 2178 doCheck = false;
2179 2179 src = fetchurl {
2180 2180 url = "https://files.pythonhosted.org/packages/de/87/ee1ad8fa533a4b5f2c7623f4a2b585d3c1947af7bed8e65bc7772274320e/supervisor-4.1.0.tar.gz";
2181 2181 sha256 = "10q36sa1jqljyyyl7cif52akpygl5kmlqq9x91hmx53f8zh6zj1d";
2182 2182 };
2183 2183 meta = {
2184 2184 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
2185 2185 };
2186 2186 };
2187 2187 "tempita" = super.buildPythonPackage {
2188 2188 name = "tempita-0.5.2";
2189 2189 doCheck = false;
2190 2190 src = fetchurl {
2191 2191 url = "https://files.pythonhosted.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
2192 2192 sha256 = "177wwq45slfyajd8csy477bmdmzipyw0dm7i85k3akb7m85wzkna";
2193 2193 };
2194 2194 meta = {
2195 2195 license = [ pkgs.lib.licenses.mit ];
2196 2196 };
2197 2197 };
2198 2198 "termcolor" = super.buildPythonPackage {
2199 2199 name = "termcolor-1.1.0";
2200 2200 doCheck = false;
2201 2201 src = fetchurl {
2202 2202 url = "https://files.pythonhosted.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
2203 2203 sha256 = "0fv1vq14rpqwgazxg4981904lfyp84mnammw7y046491cv76jv8x";
2204 2204 };
2205 2205 meta = {
2206 2206 license = [ pkgs.lib.licenses.mit ];
2207 2207 };
2208 2208 };
2209 2209 "testpath" = super.buildPythonPackage {
2210 2210 name = "testpath-0.4.4";
2211 2211 doCheck = false;
2212 2212 src = fetchurl {
2213 2213 url = "https://files.pythonhosted.org/packages/2c/b3/5d57205e896d8998d77ad12aa42ebce75cd97d8b9a97d00ba078c4c9ffeb/testpath-0.4.4.tar.gz";
2214 2214 sha256 = "0zpcmq22dz79ipvvsfnw1ykpjcaj6xyzy7ws77s5b5ql3hka7q30";
2215 2215 };
2216 2216 meta = {
2217 2217 license = [ ];
2218 2218 };
2219 2219 };
2220 2220 "traitlets" = super.buildPythonPackage {
2221 2221 name = "traitlets-4.3.3";
2222 2222 doCheck = false;
2223 2223 propagatedBuildInputs = [
2224 2224 self."ipython-genutils"
2225 2225 self."six"
2226 2226 self."decorator"
2227 2227 self."enum34"
2228 2228 ];
2229 2229 src = fetchurl {
2230 2230 url = "https://files.pythonhosted.org/packages/75/b0/43deb021bc943f18f07cbe3dac1d681626a48997b7ffa1e7fb14ef922b21/traitlets-4.3.3.tar.gz";
2231 2231 sha256 = "1xsrwgivpkxlbr4dfndfsi098s29yqgswgjc1qqn69yxklvfw8yh";
2232 2232 };
2233 2233 meta = {
2234 2234 license = [ pkgs.lib.licenses.bsdOriginal ];
2235 2235 };
2236 2236 };
2237 2237 "transaction" = super.buildPythonPackage {
2238 2238 name = "transaction-2.4.0";
2239 2239 doCheck = false;
2240 2240 propagatedBuildInputs = [
2241 2241 self."zope.interface"
2242 2242 ];
2243 2243 src = fetchurl {
2244 2244 url = "https://files.pythonhosted.org/packages/9d/7d/0e8af0d059e052b9dcf2bb5a08aad20ae3e238746bdd3f8701a60969b363/transaction-2.4.0.tar.gz";
2245 2245 sha256 = "17wz1y524ca07vr03yddy8dv0gbscs06dbdywmllxv5rc725jq3j";
2246 2246 };
2247 2247 meta = {
2248 2248 license = [ pkgs.lib.licenses.zpl21 ];
2249 2249 };
2250 2250 };
2251 2251 "translationstring" = super.buildPythonPackage {
2252 2252 name = "translationstring-1.3";
2253 2253 doCheck = false;
2254 2254 src = fetchurl {
2255 2255 url = "https://files.pythonhosted.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
2256 2256 sha256 = "0bdpcnd9pv0131dl08h4zbcwmgc45lyvq3pa224xwan5b3x4rr2f";
2257 2257 };
2258 2258 meta = {
2259 2259 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
2260 2260 };
2261 2261 };
2262 2262 "tzlocal" = super.buildPythonPackage {
2263 2263 name = "tzlocal-1.5.1";
2264 2264 doCheck = false;
2265 2265 propagatedBuildInputs = [
2266 2266 self."pytz"
2267 2267 ];
2268 2268 src = fetchurl {
2269 2269 url = "https://files.pythonhosted.org/packages/cb/89/e3687d3ed99bc882793f82634e9824e62499fdfdc4b1ae39e211c5b05017/tzlocal-1.5.1.tar.gz";
2270 2270 sha256 = "0kiciwiqx0bv0fbc913idxibc4ygg4cb7f8rcpd9ij2shi4bigjf";
2271 2271 };
2272 2272 meta = {
2273 2273 license = [ pkgs.lib.licenses.mit ];
2274 2274 };
2275 2275 };
2276 2276 "urllib3" = super.buildPythonPackage {
2277 2277 name = "urllib3-1.25.2";
2278 2278 doCheck = false;
2279 2279 src = fetchurl {
2280 2280 url = "https://files.pythonhosted.org/packages/9a/8b/ea6d2beb2da6e331e9857d0a60b79ed4f72dcbc4e2c7f2d2521b0480fda2/urllib3-1.25.2.tar.gz";
2281 2281 sha256 = "1nq2k4pss1ihsjh02r41sqpjpm5rfqkjfysyq7g7n2i1p7c66c55";
2282 2282 };
2283 2283 meta = {
2284 2284 license = [ pkgs.lib.licenses.mit ];
2285 2285 };
2286 2286 };
2287 2287 "urlobject" = super.buildPythonPackage {
2288 2288 name = "urlobject-2.4.3";
2289 2289 doCheck = false;
2290 2290 src = fetchurl {
2291 2291 url = "https://files.pythonhosted.org/packages/e2/b8/1d0a916f4b34c4618846e6da0e4eeaa8fcb4a2f39e006434fe38acb74b34/URLObject-2.4.3.tar.gz";
2292 2292 sha256 = "1ahc8ficzfvr2avln71immfh4ls0zyv6cdaa5xmkdj5rd87f5cj7";
2293 2293 };
2294 2294 meta = {
2295 2295 license = [ pkgs.lib.licenses.publicDomain ];
2296 2296 };
2297 2297 };
2298 2298 "venusian" = super.buildPythonPackage {
2299 2299 name = "venusian-1.2.0";
2300 2300 doCheck = false;
2301 2301 src = fetchurl {
2302 2302 url = "https://files.pythonhosted.org/packages/7e/6f/40a9d43ac77cb51cb62be5b5662d170f43f8037bdc4eab56336c4ca92bb7/venusian-1.2.0.tar.gz";
2303 2303 sha256 = "0ghyx66g8ikx9nx1mnwqvdcqm11i1vlq0hnvwl50s48bp22q5v34";
2304 2304 };
2305 2305 meta = {
2306 2306 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
2307 2307 };
2308 2308 };
2309 2309 "vine" = super.buildPythonPackage {
2310 2310 name = "vine-1.3.0";
2311 2311 doCheck = false;
2312 2312 src = fetchurl {
2313 2313 url = "https://files.pythonhosted.org/packages/1c/e1/79fb8046e607dd6c2ad05c9b8ebac9d0bd31d086a08f02699e96fc5b3046/vine-1.3.0.tar.gz";
2314 2314 sha256 = "11ydsbhl1vabndc2r979dv61s6j2b0giq6dgvryifvq1m7bycghk";
2315 2315 };
2316 2316 meta = {
2317 2317 license = [ pkgs.lib.licenses.bsdOriginal ];
2318 2318 };
2319 2319 };
2320 2320 "waitress" = super.buildPythonPackage {
2321 2321 name = "waitress-1.3.1";
2322 2322 doCheck = false;
2323 2323 src = fetchurl {
2324 2324 url = "https://files.pythonhosted.org/packages/a6/e6/708da7bba65898e5d759ade8391b1077e49d07be0b0223c39f5be04def56/waitress-1.3.1.tar.gz";
2325 2325 sha256 = "1iysl8ka3l4cdrr0r19fh1cv28q41mwpvgsb81ji7k4shkb0k3i7";
2326 2326 };
2327 2327 meta = {
2328 2328 license = [ pkgs.lib.licenses.zpl21 ];
2329 2329 };
2330 2330 };
2331 2331 "wcwidth" = super.buildPythonPackage {
2332 2332 name = "wcwidth-0.1.9";
2333 2333 doCheck = false;
2334 2334 src = fetchurl {
2335 2335 url = "https://files.pythonhosted.org/packages/25/9d/0acbed6e4a4be4fc99148f275488580968f44ddb5e69b8ceb53fc9df55a0/wcwidth-0.1.9.tar.gz";
2336 2336 sha256 = "1wf5ycjx8s066rdvr0fgz4xds9a8zhs91c4jzxvvymm1c8l8cwzf";
2337 2337 };
2338 2338 meta = {
2339 2339 license = [ pkgs.lib.licenses.mit ];
2340 2340 };
2341 2341 };
2342 2342 "webencodings" = super.buildPythonPackage {
2343 2343 name = "webencodings-0.5.1";
2344 2344 doCheck = false;
2345 2345 src = fetchurl {
2346 2346 url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz";
2347 2347 sha256 = "08qrgrc4hrximb2gqnl69g01s93rhf2842jfxdjljc1dbwj1qsmk";
2348 2348 };
2349 2349 meta = {
2350 2350 license = [ pkgs.lib.licenses.bsdOriginal ];
2351 2351 };
2352 2352 };
2353 2353 "weberror" = super.buildPythonPackage {
2354 2354 name = "weberror-0.13.1";
2355 2355 doCheck = false;
2356 2356 propagatedBuildInputs = [
2357 2357 self."webob"
2358 2358 self."tempita"
2359 2359 self."pygments"
2360 2360 self."paste"
2361 2361 ];
2362 2362 src = fetchurl {
2363 2363 url = "https://files.pythonhosted.org/packages/07/0a/09ca5eb0fab5c0d17b380026babe81c96ecebb13f2b06c3203432dd7be72/WebError-0.13.1.tar.gz";
2364 2364 sha256 = "0r4qvnf2r92gfnpa1kwygh4j2x6j3axg2i4an6hyxwg2gpaqp7y1";
2365 2365 };
2366 2366 meta = {
2367 2367 license = [ pkgs.lib.licenses.mit ];
2368 2368 };
2369 2369 };
2370 2370 "webhelpers2" = super.buildPythonPackage {
2371 2371 name = "webhelpers2-2.0";
2372 2372 doCheck = false;
2373 2373 propagatedBuildInputs = [
2374 2374 self."markupsafe"
2375 2375 self."six"
2376 2376 ];
2377 2377 src = fetchurl {
2378 2378 url = "https://files.pythonhosted.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
2379 2379 sha256 = "0aphva1qmxh83n01p53f5fd43m4srzbnfbz5ajvbx9aj2aipwmcs";
2380 2380 };
2381 2381 meta = {
2382 2382 license = [ pkgs.lib.licenses.mit ];
2383 2383 };
2384 2384 };
2385 2385 "webob" = super.buildPythonPackage {
2386 2386 name = "webob-1.8.5";
2387 2387 doCheck = false;
2388 2388 src = fetchurl {
2389 2389 url = "https://files.pythonhosted.org/packages/9d/1a/0c89c070ee2829c934cb6c7082287c822e28236a4fcf90063e6be7c35532/WebOb-1.8.5.tar.gz";
2390 2390 sha256 = "11khpzaxc88q31v25ic330gsf56fwmbdc9b30br8mvp0fmwspah5";
2391 2391 };
2392 2392 meta = {
2393 2393 license = [ pkgs.lib.licenses.mit ];
2394 2394 };
2395 2395 };
2396 2396 "webtest" = super.buildPythonPackage {
2397 2397 name = "webtest-2.0.34";
2398 2398 doCheck = false;
2399 2399 propagatedBuildInputs = [
2400 2400 self."six"
2401 2401 self."webob"
2402 2402 self."waitress"
2403 2403 self."beautifulsoup4"
2404 2404 ];
2405 2405 src = fetchurl {
2406 2406 url = "https://files.pythonhosted.org/packages/2c/74/a0e63feee438735d628631e2b70d82280276a930637ac535479e5fad9427/WebTest-2.0.34.tar.gz";
2407 2407 sha256 = "0x1y2c8z4fmpsny4hbp6ka37si2g10r5r2jwxhvv5mx7g3blq4bi";
2408 2408 };
2409 2409 meta = {
2410 2410 license = [ pkgs.lib.licenses.mit ];
2411 2411 };
2412 2412 };
2413 2413 "whoosh" = super.buildPythonPackage {
2414 2414 name = "whoosh-2.7.4";
2415 2415 doCheck = false;
2416 2416 src = fetchurl {
2417 2417 url = "https://files.pythonhosted.org/packages/25/2b/6beed2107b148edc1321da0d489afc4617b9ed317ef7b72d4993cad9b684/Whoosh-2.7.4.tar.gz";
2418 2418 sha256 = "10qsqdjpbc85fykc1vgcs8xwbgn4l2l52c8d83xf1q59pwyn79bw";
2419 2419 };
2420 2420 meta = {
2421 2421 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
2422 2422 };
2423 2423 };
2424 2424 "ws4py" = super.buildPythonPackage {
2425 2425 name = "ws4py-0.5.1";
2426 2426 doCheck = false;
2427 2427 src = fetchurl {
2428 2428 url = "https://files.pythonhosted.org/packages/53/20/4019a739b2eefe9282d3822ef6a225250af964b117356971bd55e274193c/ws4py-0.5.1.tar.gz";
2429 2429 sha256 = "10slbbf2jm4hpr92jx7kh7mhf48sjl01v2w4d8z3f1p0ybbp7l19";
2430 2430 };
2431 2431 meta = {
2432 2432 license = [ pkgs.lib.licenses.bsdOriginal ];
2433 2433 };
2434 2434 };
2435 2435 "wsgiref" = super.buildPythonPackage {
2436 2436 name = "wsgiref-0.1.2";
2437 2437 doCheck = false;
2438 2438 src = fetchurl {
2439 2439 url = "https://files.pythonhosted.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
2440 2440 sha256 = "0y8fyjmpq7vwwm4x732w97qbkw78rjwal5409k04cw4m03411rn7";
2441 2441 };
2442 2442 meta = {
2443 2443 license = [ { fullName = "PSF or ZPL"; } ];
2444 2444 };
2445 2445 };
2446 2446 "zipp" = super.buildPythonPackage {
2447 2447 name = "zipp-1.2.0";
2448 2448 doCheck = false;
2449 2449 propagatedBuildInputs = [
2450 2450 self."contextlib2"
2451 2451 ];
2452 2452 src = fetchurl {
2453 2453 url = "https://files.pythonhosted.org/packages/78/08/d52f0ea643bc1068d6dc98b412f4966a9b63255d20911a23ac3220c033c4/zipp-1.2.0.tar.gz";
2454 2454 sha256 = "1c91lnv1bxjimh8as27hz7bghsjkkbxn1d37xq7in9c82iai0167";
2455 2455 };
2456 2456 meta = {
2457 2457 license = [ pkgs.lib.licenses.mit ];
2458 2458 };
2459 2459 };
2460 2460 "zope.cachedescriptors" = super.buildPythonPackage {
2461 2461 name = "zope.cachedescriptors-4.3.1";
2462 2462 doCheck = false;
2463 2463 propagatedBuildInputs = [
2464 2464 self."setuptools"
2465 2465 ];
2466 2466 src = fetchurl {
2467 2467 url = "https://files.pythonhosted.org/packages/2f/89/ebe1890cc6d3291ebc935558fa764d5fffe571018dbbee200e9db78762cb/zope.cachedescriptors-4.3.1.tar.gz";
2468 2468 sha256 = "0jhr3m5p74c6r7k8iv0005b8bfsialih9d7zl5vx38rf5xq1lk8z";
2469 2469 };
2470 2470 meta = {
2471 2471 license = [ pkgs.lib.licenses.zpl21 ];
2472 2472 };
2473 2473 };
2474 2474 "zope.deprecation" = super.buildPythonPackage {
2475 2475 name = "zope.deprecation-4.4.0";
2476 2476 doCheck = false;
2477 2477 propagatedBuildInputs = [
2478 2478 self."setuptools"
2479 2479 ];
2480 2480 src = fetchurl {
2481 2481 url = "https://files.pythonhosted.org/packages/34/da/46e92d32d545dd067b9436279d84c339e8b16de2ca393d7b892bc1e1e9fd/zope.deprecation-4.4.0.tar.gz";
2482 2482 sha256 = "1pz2cv7gv9y1r3m0bdv7ks1alagmrn5msm5spwdzkb2by0w36i8d";
2483 2483 };
2484 2484 meta = {
2485 2485 license = [ pkgs.lib.licenses.zpl21 ];
2486 2486 };
2487 2487 };
2488 2488 "zope.event" = super.buildPythonPackage {
2489 2489 name = "zope.event-4.4";
2490 2490 doCheck = false;
2491 2491 propagatedBuildInputs = [
2492 2492 self."setuptools"
2493 2493 ];
2494 2494 src = fetchurl {
2495 2495 url = "https://files.pythonhosted.org/packages/4c/b2/51c0369adcf5be2334280eed230192ab3b03f81f8efda9ddea6f65cc7b32/zope.event-4.4.tar.gz";
2496 2496 sha256 = "1ksbc726av9xacml6jhcfyn828hlhb9xlddpx6fcvnlvmpmpvhk9";
2497 2497 };
2498 2498 meta = {
2499 2499 license = [ pkgs.lib.licenses.zpl21 ];
2500 2500 };
2501 2501 };
2502 2502 "zope.interface" = super.buildPythonPackage {
2503 2503 name = "zope.interface-4.6.0";
2504 2504 doCheck = false;
2505 2505 propagatedBuildInputs = [
2506 2506 self."setuptools"
2507 2507 ];
2508 2508 src = fetchurl {
2509 2509 url = "https://files.pythonhosted.org/packages/4e/d0/c9d16bd5b38de44a20c6dc5d5ed80a49626fafcb3db9f9efdc2a19026db6/zope.interface-4.6.0.tar.gz";
2510 2510 sha256 = "1rgh2x3rcl9r0v0499kf78xy86rnmanajf4ywmqb943wpk50sg8v";
2511 2511 };
2512 2512 meta = {
2513 2513 license = [ pkgs.lib.licenses.zpl21 ];
2514 2514 };
2515 2515 };
2516 2516
2517 2517 ### Test requirements
2518 2518
2519 2519
2520 2520 }
@@ -1,1 +1,1 b''
1 4.25.0 No newline at end of file
1 4.25.2 No newline at end of file
@@ -1,1055 +1,1084 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 from rhodecode.apps._base import ADMIN_PREFIX
23 23
24 24
25 25 def admin_routes(config):
26 26 """
27 27 Admin prefixed routes
28 28 """
29 29 from rhodecode.apps.admin.views.audit_logs import AdminAuditLogsView
30 from rhodecode.apps.admin.views.artifacts import AdminArtifactsView
30 31 from rhodecode.apps.admin.views.defaults import AdminDefaultSettingsView
31 32 from rhodecode.apps.admin.views.exception_tracker import ExceptionsTrackerView
32 33 from rhodecode.apps.admin.views.main_views import AdminMainView
33 34 from rhodecode.apps.admin.views.open_source_licenses import OpenSourceLicensesAdminSettingsView
34 35 from rhodecode.apps.admin.views.permissions import AdminPermissionsView
35 36 from rhodecode.apps.admin.views.process_management import AdminProcessManagementView
36 37 from rhodecode.apps.admin.views.repo_groups import AdminRepoGroupsView
37 38 from rhodecode.apps.admin.views.repositories import AdminReposView
38 39 from rhodecode.apps.admin.views.sessions import AdminSessionSettingsView
39 40 from rhodecode.apps.admin.views.settings import AdminSettingsView
40 41 from rhodecode.apps.admin.views.svn_config import AdminSvnConfigView
41 42 from rhodecode.apps.admin.views.system_info import AdminSystemInfoSettingsView
42 43 from rhodecode.apps.admin.views.user_groups import AdminUserGroupsView
43 44 from rhodecode.apps.admin.views.users import AdminUsersView, UsersView
44 45
45 46 config.add_route(
46 47 name='admin_audit_logs',
47 48 pattern='/audit_logs')
48 49 config.add_view(
49 50 AdminAuditLogsView,
50 51 attr='admin_audit_logs',
51 52 route_name='admin_audit_logs', request_method='GET',
52 53 renderer='rhodecode:templates/admin/admin_audit_logs.mako')
53 54
54 55 config.add_route(
55 56 name='admin_audit_log_entry',
56 57 pattern='/audit_logs/{audit_log_id}')
57 58 config.add_view(
58 59 AdminAuditLogsView,
59 60 attr='admin_audit_log_entry',
60 61 route_name='admin_audit_log_entry', request_method='GET',
61 62 renderer='rhodecode:templates/admin/admin_audit_log_entry.mako')
62 63
64 # Artifacts EE feature
65 config.add_route(
66 'admin_artifacts',
67 pattern=ADMIN_PREFIX + '/artifacts')
68 config.add_route(
69 'admin_artifacts_show_all',
70 pattern=ADMIN_PREFIX + '/artifacts')
71 config.add_view(
72 AdminArtifactsView,
73 attr='artifacts',
74 route_name='admin_artifacts', request_method='GET',
75 renderer='rhodecode:templates/admin/artifacts/artifacts.mako')
76 config.add_view(
77 AdminArtifactsView,
78 attr='artifacts',
79 route_name='admin_artifacts_show_all', request_method='GET',
80 renderer='rhodecode:templates/admin/artifacts/artifacts.mako')
81 # EE views
82 config.add_route(
83 name='admin_artifacts_show_info',
84 pattern=ADMIN_PREFIX + '/artifacts/{uid}')
85 config.add_route(
86 name='admin_artifacts_delete',
87 pattern=ADMIN_PREFIX + '/artifacts/{uid}/delete')
88 config.add_route(
89 name='admin_artifacts_update',
90 pattern=ADMIN_PREFIX + '/artifacts/{uid}/update')
91
63 92 config.add_route(
64 93 name='admin_settings_open_source',
65 94 pattern='/settings/open_source')
66 95 config.add_view(
67 96 OpenSourceLicensesAdminSettingsView,
68 97 attr='open_source_licenses',
69 98 route_name='admin_settings_open_source', request_method='GET',
70 99 renderer='rhodecode:templates/admin/settings/settings.mako')
71 100
72 101 config.add_route(
73 102 name='admin_settings_vcs_svn_generate_cfg',
74 103 pattern='/settings/vcs/svn_generate_cfg')
75 104 config.add_view(
76 105 AdminSvnConfigView,
77 106 attr='vcs_svn_generate_config',
78 107 route_name='admin_settings_vcs_svn_generate_cfg',
79 108 request_method='POST', renderer='json')
80 109
81 110 config.add_route(
82 111 name='admin_settings_system',
83 112 pattern='/settings/system')
84 113 config.add_view(
85 114 AdminSystemInfoSettingsView,
86 115 attr='settings_system_info',
87 116 route_name='admin_settings_system', request_method='GET',
88 117 renderer='rhodecode:templates/admin/settings/settings.mako')
89 118
90 119 config.add_route(
91 120 name='admin_settings_system_update',
92 121 pattern='/settings/system/updates')
93 122 config.add_view(
94 123 AdminSystemInfoSettingsView,
95 124 attr='settings_system_info_check_update',
96 125 route_name='admin_settings_system_update', request_method='GET',
97 126 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
98 127
99 128 config.add_route(
100 129 name='admin_settings_exception_tracker',
101 130 pattern='/settings/exceptions')
102 131 config.add_view(
103 132 ExceptionsTrackerView,
104 133 attr='browse_exceptions',
105 134 route_name='admin_settings_exception_tracker', request_method='GET',
106 135 renderer='rhodecode:templates/admin/settings/settings.mako')
107 136
108 137 config.add_route(
109 138 name='admin_settings_exception_tracker_delete_all',
110 139 pattern='/settings/exceptions_delete_all')
111 140 config.add_view(
112 141 ExceptionsTrackerView,
113 142 attr='exception_delete_all',
114 143 route_name='admin_settings_exception_tracker_delete_all', request_method='POST',
115 144 renderer='rhodecode:templates/admin/settings/settings.mako')
116 145
117 146 config.add_route(
118 147 name='admin_settings_exception_tracker_show',
119 148 pattern='/settings/exceptions/{exception_id}')
120 149 config.add_view(
121 150 ExceptionsTrackerView,
122 151 attr='exception_show',
123 152 route_name='admin_settings_exception_tracker_show', request_method='GET',
124 153 renderer='rhodecode:templates/admin/settings/settings.mako')
125 154
126 155 config.add_route(
127 156 name='admin_settings_exception_tracker_delete',
128 157 pattern='/settings/exceptions/{exception_id}/delete')
129 158 config.add_view(
130 159 ExceptionsTrackerView,
131 160 attr='exception_delete',
132 161 route_name='admin_settings_exception_tracker_delete', request_method='POST',
133 162 renderer='rhodecode:templates/admin/settings/settings.mako')
134 163
135 164 config.add_route(
136 165 name='admin_settings_sessions',
137 166 pattern='/settings/sessions')
138 167 config.add_view(
139 168 AdminSessionSettingsView,
140 169 attr='settings_sessions',
141 170 route_name='admin_settings_sessions', request_method='GET',
142 171 renderer='rhodecode:templates/admin/settings/settings.mako')
143 172
144 173 config.add_route(
145 174 name='admin_settings_sessions_cleanup',
146 175 pattern='/settings/sessions/cleanup')
147 176 config.add_view(
148 177 AdminSessionSettingsView,
149 178 attr='settings_sessions_cleanup',
150 179 route_name='admin_settings_sessions_cleanup', request_method='POST')
151 180
152 181 config.add_route(
153 182 name='admin_settings_process_management',
154 183 pattern='/settings/process_management')
155 184 config.add_view(
156 185 AdminProcessManagementView,
157 186 attr='process_management',
158 187 route_name='admin_settings_process_management', request_method='GET',
159 188 renderer='rhodecode:templates/admin/settings/settings.mako')
160 189
161 190 config.add_route(
162 191 name='admin_settings_process_management_data',
163 192 pattern='/settings/process_management/data')
164 193 config.add_view(
165 194 AdminProcessManagementView,
166 195 attr='process_management_data',
167 196 route_name='admin_settings_process_management_data', request_method='GET',
168 197 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
169 198
170 199 config.add_route(
171 200 name='admin_settings_process_management_signal',
172 201 pattern='/settings/process_management/signal')
173 202 config.add_view(
174 203 AdminProcessManagementView,
175 204 attr='process_management_signal',
176 205 route_name='admin_settings_process_management_signal',
177 206 request_method='POST', renderer='json_ext')
178 207
179 208 config.add_route(
180 209 name='admin_settings_process_management_master_signal',
181 210 pattern='/settings/process_management/master_signal')
182 211 config.add_view(
183 212 AdminProcessManagementView,
184 213 attr='process_management_master_signal',
185 214 route_name='admin_settings_process_management_master_signal',
186 215 request_method='POST', renderer='json_ext')
187 216
188 217 # default settings
189 218 config.add_route(
190 219 name='admin_defaults_repositories',
191 220 pattern='/defaults/repositories')
192 221 config.add_view(
193 222 AdminDefaultSettingsView,
194 223 attr='defaults_repository_show',
195 224 route_name='admin_defaults_repositories', request_method='GET',
196 225 renderer='rhodecode:templates/admin/defaults/defaults.mako')
197 226
198 227 config.add_route(
199 228 name='admin_defaults_repositories_update',
200 229 pattern='/defaults/repositories/update')
201 230 config.add_view(
202 231 AdminDefaultSettingsView,
203 232 attr='defaults_repository_update',
204 233 route_name='admin_defaults_repositories_update', request_method='POST',
205 234 renderer='rhodecode:templates/admin/defaults/defaults.mako')
206 235
207 236 # admin settings
208 237
209 238 config.add_route(
210 239 name='admin_settings',
211 240 pattern='/settings')
212 241 config.add_view(
213 242 AdminSettingsView,
214 243 attr='settings_global',
215 244 route_name='admin_settings', request_method='GET',
216 245 renderer='rhodecode:templates/admin/settings/settings.mako')
217 246
218 247 config.add_route(
219 248 name='admin_settings_update',
220 249 pattern='/settings/update')
221 250 config.add_view(
222 251 AdminSettingsView,
223 252 attr='settings_global_update',
224 253 route_name='admin_settings_update', request_method='POST',
225 254 renderer='rhodecode:templates/admin/settings/settings.mako')
226 255
227 256 config.add_route(
228 257 name='admin_settings_global',
229 258 pattern='/settings/global')
230 259 config.add_view(
231 260 AdminSettingsView,
232 261 attr='settings_global',
233 262 route_name='admin_settings_global', request_method='GET',
234 263 renderer='rhodecode:templates/admin/settings/settings.mako')
235 264
236 265 config.add_route(
237 266 name='admin_settings_global_update',
238 267 pattern='/settings/global/update')
239 268 config.add_view(
240 269 AdminSettingsView,
241 270 attr='settings_global_update',
242 271 route_name='admin_settings_global_update', request_method='POST',
243 272 renderer='rhodecode:templates/admin/settings/settings.mako')
244 273
245 274 config.add_route(
246 275 name='admin_settings_vcs',
247 276 pattern='/settings/vcs')
248 277 config.add_view(
249 278 AdminSettingsView,
250 279 attr='settings_vcs',
251 280 route_name='admin_settings_vcs', request_method='GET',
252 281 renderer='rhodecode:templates/admin/settings/settings.mako')
253 282
254 283 config.add_route(
255 284 name='admin_settings_vcs_update',
256 285 pattern='/settings/vcs/update')
257 286 config.add_view(
258 287 AdminSettingsView,
259 288 attr='settings_vcs_update',
260 289 route_name='admin_settings_vcs_update', request_method='POST',
261 290 renderer='rhodecode:templates/admin/settings/settings.mako')
262 291
263 292 config.add_route(
264 293 name='admin_settings_vcs_svn_pattern_delete',
265 294 pattern='/settings/vcs/svn_pattern_delete')
266 295 config.add_view(
267 296 AdminSettingsView,
268 297 attr='settings_vcs_delete_svn_pattern',
269 298 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
270 299 renderer='json_ext', xhr=True)
271 300
272 301 config.add_route(
273 302 name='admin_settings_mapping',
274 303 pattern='/settings/mapping')
275 304 config.add_view(
276 305 AdminSettingsView,
277 306 attr='settings_mapping',
278 307 route_name='admin_settings_mapping', request_method='GET',
279 308 renderer='rhodecode:templates/admin/settings/settings.mako')
280 309
281 310 config.add_route(
282 311 name='admin_settings_mapping_update',
283 312 pattern='/settings/mapping/update')
284 313 config.add_view(
285 314 AdminSettingsView,
286 315 attr='settings_mapping_update',
287 316 route_name='admin_settings_mapping_update', request_method='POST',
288 317 renderer='rhodecode:templates/admin/settings/settings.mako')
289 318
290 319 config.add_route(
291 320 name='admin_settings_visual',
292 321 pattern='/settings/visual')
293 322 config.add_view(
294 323 AdminSettingsView,
295 324 attr='settings_visual',
296 325 route_name='admin_settings_visual', request_method='GET',
297 326 renderer='rhodecode:templates/admin/settings/settings.mako')
298 327
299 328 config.add_route(
300 329 name='admin_settings_visual_update',
301 330 pattern='/settings/visual/update')
302 331 config.add_view(
303 332 AdminSettingsView,
304 333 attr='settings_visual_update',
305 334 route_name='admin_settings_visual_update', request_method='POST',
306 335 renderer='rhodecode:templates/admin/settings/settings.mako')
307 336
308 337 config.add_route(
309 338 name='admin_settings_issuetracker',
310 339 pattern='/settings/issue-tracker')
311 340 config.add_view(
312 341 AdminSettingsView,
313 342 attr='settings_issuetracker',
314 343 route_name='admin_settings_issuetracker', request_method='GET',
315 344 renderer='rhodecode:templates/admin/settings/settings.mako')
316 345
317 346 config.add_route(
318 347 name='admin_settings_issuetracker_update',
319 348 pattern='/settings/issue-tracker/update')
320 349 config.add_view(
321 350 AdminSettingsView,
322 351 attr='settings_issuetracker_update',
323 352 route_name='admin_settings_issuetracker_update', request_method='POST',
324 353 renderer='rhodecode:templates/admin/settings/settings.mako')
325 354
326 355 config.add_route(
327 356 name='admin_settings_issuetracker_test',
328 357 pattern='/settings/issue-tracker/test')
329 358 config.add_view(
330 359 AdminSettingsView,
331 360 attr='settings_issuetracker_test',
332 361 route_name='admin_settings_issuetracker_test', request_method='POST',
333 362 renderer='string', xhr=True)
334 363
335 364 config.add_route(
336 365 name='admin_settings_issuetracker_delete',
337 366 pattern='/settings/issue-tracker/delete')
338 367 config.add_view(
339 368 AdminSettingsView,
340 369 attr='settings_issuetracker_delete',
341 370 route_name='admin_settings_issuetracker_delete', request_method='POST',
342 371 renderer='json_ext', xhr=True)
343 372
344 373 config.add_route(
345 374 name='admin_settings_email',
346 375 pattern='/settings/email')
347 376 config.add_view(
348 377 AdminSettingsView,
349 378 attr='settings_email',
350 379 route_name='admin_settings_email', request_method='GET',
351 380 renderer='rhodecode:templates/admin/settings/settings.mako')
352 381
353 382 config.add_route(
354 383 name='admin_settings_email_update',
355 384 pattern='/settings/email/update')
356 385 config.add_view(
357 386 AdminSettingsView,
358 387 attr='settings_email_update',
359 388 route_name='admin_settings_email_update', request_method='POST',
360 389 renderer='rhodecode:templates/admin/settings/settings.mako')
361 390
362 391 config.add_route(
363 392 name='admin_settings_hooks',
364 393 pattern='/settings/hooks')
365 394 config.add_view(
366 395 AdminSettingsView,
367 396 attr='settings_hooks',
368 397 route_name='admin_settings_hooks', request_method='GET',
369 398 renderer='rhodecode:templates/admin/settings/settings.mako')
370 399
371 400 config.add_route(
372 401 name='admin_settings_hooks_update',
373 402 pattern='/settings/hooks/update')
374 403 config.add_view(
375 404 AdminSettingsView,
376 405 attr='settings_hooks_update',
377 406 route_name='admin_settings_hooks_update', request_method='POST',
378 407 renderer='rhodecode:templates/admin/settings/settings.mako')
379 408
380 409 config.add_route(
381 410 name='admin_settings_hooks_delete',
382 411 pattern='/settings/hooks/delete')
383 412 config.add_view(
384 413 AdminSettingsView,
385 414 attr='settings_hooks_update',
386 415 route_name='admin_settings_hooks_delete', request_method='POST',
387 416 renderer='rhodecode:templates/admin/settings/settings.mako')
388 417
389 418 config.add_route(
390 419 name='admin_settings_search',
391 420 pattern='/settings/search')
392 421 config.add_view(
393 422 AdminSettingsView,
394 423 attr='settings_search',
395 424 route_name='admin_settings_search', request_method='GET',
396 425 renderer='rhodecode:templates/admin/settings/settings.mako')
397 426
398 427 config.add_route(
399 428 name='admin_settings_labs',
400 429 pattern='/settings/labs')
401 430 config.add_view(
402 431 AdminSettingsView,
403 432 attr='settings_labs',
404 433 route_name='admin_settings_labs', request_method='GET',
405 434 renderer='rhodecode:templates/admin/settings/settings.mako')
406 435
407 436 config.add_route(
408 437 name='admin_settings_labs_update',
409 438 pattern='/settings/labs/update')
410 439 config.add_view(
411 440 AdminSettingsView,
412 441 attr='settings_labs_update',
413 442 route_name='admin_settings_labs_update', request_method='POST',
414 443 renderer='rhodecode:templates/admin/settings/settings.mako')
415 444
416 445 # Automation EE feature
417 446 config.add_route(
418 447 'admin_settings_automation',
419 448 pattern=ADMIN_PREFIX + '/settings/automation')
420 449 config.add_view(
421 450 AdminSettingsView,
422 451 attr='settings_automation',
423 452 route_name='admin_settings_automation', request_method='GET',
424 453 renderer='rhodecode:templates/admin/settings/settings.mako')
425 454
426 455 # global permissions
427 456
428 457 config.add_route(
429 458 name='admin_permissions_application',
430 459 pattern='/permissions/application')
431 460 config.add_view(
432 461 AdminPermissionsView,
433 462 attr='permissions_application',
434 463 route_name='admin_permissions_application', request_method='GET',
435 464 renderer='rhodecode:templates/admin/permissions/permissions.mako')
436 465
437 466 config.add_route(
438 467 name='admin_permissions_application_update',
439 468 pattern='/permissions/application/update')
440 469 config.add_view(
441 470 AdminPermissionsView,
442 471 attr='permissions_application_update',
443 472 route_name='admin_permissions_application_update', request_method='POST',
444 473 renderer='rhodecode:templates/admin/permissions/permissions.mako')
445 474
446 475 config.add_route(
447 476 name='admin_permissions_global',
448 477 pattern='/permissions/global')
449 478 config.add_view(
450 479 AdminPermissionsView,
451 480 attr='permissions_global',
452 481 route_name='admin_permissions_global', request_method='GET',
453 482 renderer='rhodecode:templates/admin/permissions/permissions.mako')
454 483
455 484 config.add_route(
456 485 name='admin_permissions_global_update',
457 486 pattern='/permissions/global/update')
458 487 config.add_view(
459 488 AdminPermissionsView,
460 489 attr='permissions_global_update',
461 490 route_name='admin_permissions_global_update', request_method='POST',
462 491 renderer='rhodecode:templates/admin/permissions/permissions.mako')
463 492
464 493 config.add_route(
465 494 name='admin_permissions_object',
466 495 pattern='/permissions/object')
467 496 config.add_view(
468 497 AdminPermissionsView,
469 498 attr='permissions_objects',
470 499 route_name='admin_permissions_object', request_method='GET',
471 500 renderer='rhodecode:templates/admin/permissions/permissions.mako')
472 501
473 502 config.add_route(
474 503 name='admin_permissions_object_update',
475 504 pattern='/permissions/object/update')
476 505 config.add_view(
477 506 AdminPermissionsView,
478 507 attr='permissions_objects_update',
479 508 route_name='admin_permissions_object_update', request_method='POST',
480 509 renderer='rhodecode:templates/admin/permissions/permissions.mako')
481 510
482 511 # Branch perms EE feature
483 512 config.add_route(
484 513 name='admin_permissions_branch',
485 514 pattern='/permissions/branch')
486 515 config.add_view(
487 516 AdminPermissionsView,
488 517 attr='permissions_branch',
489 518 route_name='admin_permissions_branch', request_method='GET',
490 519 renderer='rhodecode:templates/admin/permissions/permissions.mako')
491 520
492 521 config.add_route(
493 522 name='admin_permissions_ips',
494 523 pattern='/permissions/ips')
495 524 config.add_view(
496 525 AdminPermissionsView,
497 526 attr='permissions_ips',
498 527 route_name='admin_permissions_ips', request_method='GET',
499 528 renderer='rhodecode:templates/admin/permissions/permissions.mako')
500 529
501 530 config.add_route(
502 531 name='admin_permissions_overview',
503 532 pattern='/permissions/overview')
504 533 config.add_view(
505 534 AdminPermissionsView,
506 535 attr='permissions_overview',
507 536 route_name='admin_permissions_overview', request_method='GET',
508 537 renderer='rhodecode:templates/admin/permissions/permissions.mako')
509 538
510 539 config.add_route(
511 540 name='admin_permissions_auth_token_access',
512 541 pattern='/permissions/auth_token_access')
513 542 config.add_view(
514 543 AdminPermissionsView,
515 544 attr='auth_token_access',
516 545 route_name='admin_permissions_auth_token_access', request_method='GET',
517 546 renderer='rhodecode:templates/admin/permissions/permissions.mako')
518 547
519 548 config.add_route(
520 549 name='admin_permissions_ssh_keys',
521 550 pattern='/permissions/ssh_keys')
522 551 config.add_view(
523 552 AdminPermissionsView,
524 553 attr='ssh_keys',
525 554 route_name='admin_permissions_ssh_keys', request_method='GET',
526 555 renderer='rhodecode:templates/admin/permissions/permissions.mako')
527 556
528 557 config.add_route(
529 558 name='admin_permissions_ssh_keys_data',
530 559 pattern='/permissions/ssh_keys/data')
531 560 config.add_view(
532 561 AdminPermissionsView,
533 562 attr='ssh_keys_data',
534 563 route_name='admin_permissions_ssh_keys_data', request_method='GET',
535 564 renderer='json_ext', xhr=True)
536 565
537 566 config.add_route(
538 567 name='admin_permissions_ssh_keys_update',
539 568 pattern='/permissions/ssh_keys/update')
540 569 config.add_view(
541 570 AdminPermissionsView,
542 571 attr='ssh_keys_update',
543 572 route_name='admin_permissions_ssh_keys_update', request_method='POST',
544 573 renderer='rhodecode:templates/admin/permissions/permissions.mako')
545 574
546 575 # users admin
547 576 config.add_route(
548 577 name='users',
549 578 pattern='/users')
550 579 config.add_view(
551 580 AdminUsersView,
552 581 attr='users_list',
553 582 route_name='users', request_method='GET',
554 583 renderer='rhodecode:templates/admin/users/users.mako')
555 584
556 585 config.add_route(
557 586 name='users_data',
558 587 pattern='/users_data')
559 588 config.add_view(
560 589 AdminUsersView,
561 590 attr='users_list_data',
562 591 # renderer defined below
563 592 route_name='users_data', request_method='GET',
564 593 renderer='json_ext', xhr=True)
565 594
566 595 config.add_route(
567 596 name='users_create',
568 597 pattern='/users/create')
569 598 config.add_view(
570 599 AdminUsersView,
571 600 attr='users_create',
572 601 route_name='users_create', request_method='POST',
573 602 renderer='rhodecode:templates/admin/users/user_add.mako')
574 603
575 604 config.add_route(
576 605 name='users_new',
577 606 pattern='/users/new')
578 607 config.add_view(
579 608 AdminUsersView,
580 609 attr='users_new',
581 610 route_name='users_new', request_method='GET',
582 611 renderer='rhodecode:templates/admin/users/user_add.mako')
583 612
584 613 # user management
585 614 config.add_route(
586 615 name='user_edit',
587 616 pattern='/users/{user_id:\d+}/edit',
588 617 user_route=True)
589 618 config.add_view(
590 619 UsersView,
591 620 attr='user_edit',
592 621 route_name='user_edit', request_method='GET',
593 622 renderer='rhodecode:templates/admin/users/user_edit.mako')
594 623
595 624 config.add_route(
596 625 name='user_edit_advanced',
597 626 pattern='/users/{user_id:\d+}/edit/advanced',
598 627 user_route=True)
599 628 config.add_view(
600 629 UsersView,
601 630 attr='user_edit_advanced',
602 631 route_name='user_edit_advanced', request_method='GET',
603 632 renderer='rhodecode:templates/admin/users/user_edit.mako')
604 633
605 634 config.add_route(
606 635 name='user_edit_global_perms',
607 636 pattern='/users/{user_id:\d+}/edit/global_permissions',
608 637 user_route=True)
609 638 config.add_view(
610 639 UsersView,
611 640 attr='user_edit_global_perms',
612 641 route_name='user_edit_global_perms', request_method='GET',
613 642 renderer='rhodecode:templates/admin/users/user_edit.mako')
614 643
615 644 config.add_route(
616 645 name='user_edit_global_perms_update',
617 646 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
618 647 user_route=True)
619 648 config.add_view(
620 649 UsersView,
621 650 attr='user_edit_global_perms_update',
622 651 route_name='user_edit_global_perms_update', request_method='POST',
623 652 renderer='rhodecode:templates/admin/users/user_edit.mako')
624 653
625 654 config.add_route(
626 655 name='user_update',
627 656 pattern='/users/{user_id:\d+}/update',
628 657 user_route=True)
629 658 config.add_view(
630 659 UsersView,
631 660 attr='user_update',
632 661 route_name='user_update', request_method='POST',
633 662 renderer='rhodecode:templates/admin/users/user_edit.mako')
634 663
635 664 config.add_route(
636 665 name='user_delete',
637 666 pattern='/users/{user_id:\d+}/delete',
638 667 user_route=True)
639 668 config.add_view(
640 669 UsersView,
641 670 attr='user_delete',
642 671 route_name='user_delete', request_method='POST',
643 672 renderer='rhodecode:templates/admin/users/user_edit.mako')
644 673
645 674 config.add_route(
646 675 name='user_enable_force_password_reset',
647 676 pattern='/users/{user_id:\d+}/password_reset_enable',
648 677 user_route=True)
649 678 config.add_view(
650 679 UsersView,
651 680 attr='user_enable_force_password_reset',
652 681 route_name='user_enable_force_password_reset', request_method='POST',
653 682 renderer='rhodecode:templates/admin/users/user_edit.mako')
654 683
655 684 config.add_route(
656 685 name='user_disable_force_password_reset',
657 686 pattern='/users/{user_id:\d+}/password_reset_disable',
658 687 user_route=True)
659 688 config.add_view(
660 689 UsersView,
661 690 attr='user_disable_force_password_reset',
662 691 route_name='user_disable_force_password_reset', request_method='POST',
663 692 renderer='rhodecode:templates/admin/users/user_edit.mako')
664 693
665 694 config.add_route(
666 695 name='user_create_personal_repo_group',
667 696 pattern='/users/{user_id:\d+}/create_repo_group',
668 697 user_route=True)
669 698 config.add_view(
670 699 UsersView,
671 700 attr='user_create_personal_repo_group',
672 701 route_name='user_create_personal_repo_group', request_method='POST',
673 702 renderer='rhodecode:templates/admin/users/user_edit.mako')
674 703
675 704 # user notice
676 705 config.add_route(
677 706 name='user_notice_dismiss',
678 707 pattern='/users/{user_id:\d+}/notice_dismiss',
679 708 user_route=True)
680 709 config.add_view(
681 710 UsersView,
682 711 attr='user_notice_dismiss',
683 712 route_name='user_notice_dismiss', request_method='POST',
684 713 renderer='json_ext', xhr=True)
685 714
686 715 # user auth tokens
687 716 config.add_route(
688 717 name='edit_user_auth_tokens',
689 718 pattern='/users/{user_id:\d+}/edit/auth_tokens',
690 719 user_route=True)
691 720 config.add_view(
692 721 UsersView,
693 722 attr='auth_tokens',
694 723 route_name='edit_user_auth_tokens', request_method='GET',
695 724 renderer='rhodecode:templates/admin/users/user_edit.mako')
696 725
697 726 config.add_route(
698 727 name='edit_user_auth_tokens_view',
699 728 pattern='/users/{user_id:\d+}/edit/auth_tokens/view',
700 729 user_route=True)
701 730 config.add_view(
702 731 UsersView,
703 732 attr='auth_tokens_view',
704 733 route_name='edit_user_auth_tokens_view', request_method='POST',
705 734 renderer='json_ext', xhr=True)
706 735
707 736 config.add_route(
708 737 name='edit_user_auth_tokens_add',
709 738 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
710 739 user_route=True)
711 740 config.add_view(
712 741 UsersView,
713 742 attr='auth_tokens_add',
714 743 route_name='edit_user_auth_tokens_add', request_method='POST')
715 744
716 745 config.add_route(
717 746 name='edit_user_auth_tokens_delete',
718 747 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
719 748 user_route=True)
720 749 config.add_view(
721 750 UsersView,
722 751 attr='auth_tokens_delete',
723 752 route_name='edit_user_auth_tokens_delete', request_method='POST')
724 753
725 754 # user ssh keys
726 755 config.add_route(
727 756 name='edit_user_ssh_keys',
728 757 pattern='/users/{user_id:\d+}/edit/ssh_keys',
729 758 user_route=True)
730 759 config.add_view(
731 760 UsersView,
732 761 attr='ssh_keys',
733 762 route_name='edit_user_ssh_keys', request_method='GET',
734 763 renderer='rhodecode:templates/admin/users/user_edit.mako')
735 764
736 765 config.add_route(
737 766 name='edit_user_ssh_keys_generate_keypair',
738 767 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
739 768 user_route=True)
740 769 config.add_view(
741 770 UsersView,
742 771 attr='ssh_keys_generate_keypair',
743 772 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
744 773 renderer='rhodecode:templates/admin/users/user_edit.mako')
745 774
746 775 config.add_route(
747 776 name='edit_user_ssh_keys_add',
748 777 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
749 778 user_route=True)
750 779 config.add_view(
751 780 UsersView,
752 781 attr='ssh_keys_add',
753 782 route_name='edit_user_ssh_keys_add', request_method='POST')
754 783
755 784 config.add_route(
756 785 name='edit_user_ssh_keys_delete',
757 786 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
758 787 user_route=True)
759 788 config.add_view(
760 789 UsersView,
761 790 attr='ssh_keys_delete',
762 791 route_name='edit_user_ssh_keys_delete', request_method='POST')
763 792
764 793 # user emails
765 794 config.add_route(
766 795 name='edit_user_emails',
767 796 pattern='/users/{user_id:\d+}/edit/emails',
768 797 user_route=True)
769 798 config.add_view(
770 799 UsersView,
771 800 attr='emails',
772 801 route_name='edit_user_emails', request_method='GET',
773 802 renderer='rhodecode:templates/admin/users/user_edit.mako')
774 803
775 804 config.add_route(
776 805 name='edit_user_emails_add',
777 806 pattern='/users/{user_id:\d+}/edit/emails/new',
778 807 user_route=True)
779 808 config.add_view(
780 809 UsersView,
781 810 attr='emails_add',
782 811 route_name='edit_user_emails_add', request_method='POST')
783 812
784 813 config.add_route(
785 814 name='edit_user_emails_delete',
786 815 pattern='/users/{user_id:\d+}/edit/emails/delete',
787 816 user_route=True)
788 817 config.add_view(
789 818 UsersView,
790 819 attr='emails_delete',
791 820 route_name='edit_user_emails_delete', request_method='POST')
792 821
793 822 # user IPs
794 823 config.add_route(
795 824 name='edit_user_ips',
796 825 pattern='/users/{user_id:\d+}/edit/ips',
797 826 user_route=True)
798 827 config.add_view(
799 828 UsersView,
800 829 attr='ips',
801 830 route_name='edit_user_ips', request_method='GET',
802 831 renderer='rhodecode:templates/admin/users/user_edit.mako')
803 832
804 833 config.add_route(
805 834 name='edit_user_ips_add',
806 835 pattern='/users/{user_id:\d+}/edit/ips/new',
807 836 user_route_with_default=True) # enabled for default user too
808 837 config.add_view(
809 838 UsersView,
810 839 attr='ips_add',
811 840 route_name='edit_user_ips_add', request_method='POST')
812 841
813 842 config.add_route(
814 843 name='edit_user_ips_delete',
815 844 pattern='/users/{user_id:\d+}/edit/ips/delete',
816 845 user_route_with_default=True) # enabled for default user too
817 846 config.add_view(
818 847 UsersView,
819 848 attr='ips_delete',
820 849 route_name='edit_user_ips_delete', request_method='POST')
821 850
822 851 # user perms
823 852 config.add_route(
824 853 name='edit_user_perms_summary',
825 854 pattern='/users/{user_id:\d+}/edit/permissions_summary',
826 855 user_route=True)
827 856 config.add_view(
828 857 UsersView,
829 858 attr='user_perms_summary',
830 859 route_name='edit_user_perms_summary', request_method='GET',
831 860 renderer='rhodecode:templates/admin/users/user_edit.mako')
832 861
833 862 config.add_route(
834 863 name='edit_user_perms_summary_json',
835 864 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
836 865 user_route=True)
837 866 config.add_view(
838 867 UsersView,
839 868 attr='user_perms_summary_json',
840 869 route_name='edit_user_perms_summary_json', request_method='GET',
841 870 renderer='json_ext')
842 871
843 872 # user user groups management
844 873 config.add_route(
845 874 name='edit_user_groups_management',
846 875 pattern='/users/{user_id:\d+}/edit/groups_management',
847 876 user_route=True)
848 877 config.add_view(
849 878 UsersView,
850 879 attr='groups_management',
851 880 route_name='edit_user_groups_management', request_method='GET',
852 881 renderer='rhodecode:templates/admin/users/user_edit.mako')
853 882
854 883 config.add_route(
855 884 name='edit_user_groups_management_updates',
856 885 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
857 886 user_route=True)
858 887 config.add_view(
859 888 UsersView,
860 889 attr='groups_management_updates',
861 890 route_name='edit_user_groups_management_updates', request_method='POST')
862 891
863 892 # user audit logs
864 893 config.add_route(
865 894 name='edit_user_audit_logs',
866 895 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
867 896 config.add_view(
868 897 UsersView,
869 898 attr='user_audit_logs',
870 899 route_name='edit_user_audit_logs', request_method='GET',
871 900 renderer='rhodecode:templates/admin/users/user_edit.mako')
872 901
873 902 config.add_route(
874 903 name='edit_user_audit_logs_download',
875 904 pattern='/users/{user_id:\d+}/edit/audit/download', user_route=True)
876 905 config.add_view(
877 906 UsersView,
878 907 attr='user_audit_logs_download',
879 908 route_name='edit_user_audit_logs_download', request_method='GET',
880 909 renderer='string')
881 910
882 911 # user caches
883 912 config.add_route(
884 913 name='edit_user_caches',
885 914 pattern='/users/{user_id:\d+}/edit/caches',
886 915 user_route=True)
887 916 config.add_view(
888 917 UsersView,
889 918 attr='user_caches',
890 919 route_name='edit_user_caches', request_method='GET',
891 920 renderer='rhodecode:templates/admin/users/user_edit.mako')
892 921
893 922 config.add_route(
894 923 name='edit_user_caches_update',
895 924 pattern='/users/{user_id:\d+}/edit/caches/update',
896 925 user_route=True)
897 926 config.add_view(
898 927 UsersView,
899 928 attr='user_caches_update',
900 929 route_name='edit_user_caches_update', request_method='POST')
901 930
902 931 # user-groups admin
903 932 config.add_route(
904 933 name='user_groups',
905 934 pattern='/user_groups')
906 935 config.add_view(
907 936 AdminUserGroupsView,
908 937 attr='user_groups_list',
909 938 route_name='user_groups', request_method='GET',
910 939 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
911 940
912 941 config.add_route(
913 942 name='user_groups_data',
914 943 pattern='/user_groups_data')
915 944 config.add_view(
916 945 AdminUserGroupsView,
917 946 attr='user_groups_list_data',
918 947 route_name='user_groups_data', request_method='GET',
919 948 renderer='json_ext', xhr=True)
920 949
921 950 config.add_route(
922 951 name='user_groups_new',
923 952 pattern='/user_groups/new')
924 953 config.add_view(
925 954 AdminUserGroupsView,
926 955 attr='user_groups_new',
927 956 route_name='user_groups_new', request_method='GET',
928 957 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
929 958
930 959 config.add_route(
931 960 name='user_groups_create',
932 961 pattern='/user_groups/create')
933 962 config.add_view(
934 963 AdminUserGroupsView,
935 964 attr='user_groups_create',
936 965 route_name='user_groups_create', request_method='POST',
937 966 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
938 967
939 968 # repos admin
940 969 config.add_route(
941 970 name='repos',
942 971 pattern='/repos')
943 972 config.add_view(
944 973 AdminReposView,
945 974 attr='repository_list',
946 975 route_name='repos', request_method='GET',
947 976 renderer='rhodecode:templates/admin/repos/repos.mako')
948 977
949 978 config.add_route(
950 979 name='repos_data',
951 980 pattern='/repos_data')
952 981 config.add_view(
953 982 AdminReposView,
954 983 attr='repository_list_data',
955 984 route_name='repos_data', request_method='GET',
956 985 renderer='json_ext', xhr=True)
957 986
958 987 config.add_route(
959 988 name='repo_new',
960 989 pattern='/repos/new')
961 990 config.add_view(
962 991 AdminReposView,
963 992 attr='repository_new',
964 993 route_name='repo_new', request_method='GET',
965 994 renderer='rhodecode:templates/admin/repos/repo_add.mako')
966 995
967 996 config.add_route(
968 997 name='repo_create',
969 998 pattern='/repos/create')
970 999 config.add_view(
971 1000 AdminReposView,
972 1001 attr='repository_create',
973 1002 route_name='repo_create', request_method='POST',
974 1003 renderer='rhodecode:templates/admin/repos/repos.mako')
975 1004
976 1005 # repo groups admin
977 1006 config.add_route(
978 1007 name='repo_groups',
979 1008 pattern='/repo_groups')
980 1009 config.add_view(
981 1010 AdminRepoGroupsView,
982 1011 attr='repo_group_list',
983 1012 route_name='repo_groups', request_method='GET',
984 1013 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
985 1014
986 1015 config.add_route(
987 1016 name='repo_groups_data',
988 1017 pattern='/repo_groups_data')
989 1018 config.add_view(
990 1019 AdminRepoGroupsView,
991 1020 attr='repo_group_list_data',
992 1021 route_name='repo_groups_data', request_method='GET',
993 1022 renderer='json_ext', xhr=True)
994 1023
995 1024 config.add_route(
996 1025 name='repo_group_new',
997 1026 pattern='/repo_group/new')
998 1027 config.add_view(
999 1028 AdminRepoGroupsView,
1000 1029 attr='repo_group_new',
1001 1030 route_name='repo_group_new', request_method='GET',
1002 1031 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
1003 1032
1004 1033 config.add_route(
1005 1034 name='repo_group_create',
1006 1035 pattern='/repo_group/create')
1007 1036 config.add_view(
1008 1037 AdminRepoGroupsView,
1009 1038 attr='repo_group_create',
1010 1039 route_name='repo_group_create', request_method='POST',
1011 1040 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
1012 1041
1013 1042
1014 1043 def includeme(config):
1015 1044 from rhodecode.apps._base.navigation import includeme as nav_includeme
1016 1045 from rhodecode.apps.admin.views.main_views import AdminMainView
1017 1046
1018 1047 # Create admin navigation registry and add it to the pyramid registry.
1019 1048 nav_includeme(config)
1020 1049
1021 1050 # main admin routes
1022 1051 config.add_route(
1023 1052 name='admin_home', pattern=ADMIN_PREFIX)
1024 1053 config.add_view(
1025 1054 AdminMainView,
1026 1055 attr='admin_main',
1027 1056 route_name='admin_home', request_method='GET',
1028 1057 renderer='rhodecode:templates/admin/main.mako')
1029 1058
1030 1059 # pr global redirect
1031 1060 config.add_route(
1032 1061 name='pull_requests_global_0', # backward compat
1033 1062 pattern=ADMIN_PREFIX + '/pull_requests/{pull_request_id:\d+}')
1034 1063 config.add_view(
1035 1064 AdminMainView,
1036 1065 attr='pull_requests',
1037 1066 route_name='pull_requests_global_0', request_method='GET')
1038 1067
1039 1068 config.add_route(
1040 1069 name='pull_requests_global_1', # backward compat
1041 1070 pattern=ADMIN_PREFIX + '/pull-requests/{pull_request_id:\d+}')
1042 1071 config.add_view(
1043 1072 AdminMainView,
1044 1073 attr='pull_requests',
1045 1074 route_name='pull_requests_global_1', request_method='GET')
1046 1075
1047 1076 config.add_route(
1048 1077 name='pull_requests_global',
1049 1078 pattern=ADMIN_PREFIX + '/pull-request/{pull_request_id:\d+}')
1050 1079 config.add_view(
1051 1080 AdminMainView,
1052 1081 attr='pull_requests',
1053 1082 route_name='pull_requests_global', request_method='GET')
1054 1083
1055 1084 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
@@ -1,204 +1,208 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 # -*- coding: utf-8 -*-
21 21
22 22 # Copyright (C) 2016-2020 RhodeCode GmbH
23 23 #
24 24 # This program is free software: you can redistribute it and/or modify
25 25 # it under the terms of the GNU Affero General Public License, version 3
26 26 # (only), as published by the Free Software Foundation.
27 27 #
28 28 # This program is distributed in the hope that it will be useful,
29 29 # but WITHOUT ANY WARRANTY; without even the implied warranty of
30 30 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 31 # GNU General Public License for more details.
32 32 #
33 33 # You should have received a copy of the GNU Affero General Public License
34 34 # along with this program. If not, see <http://www.gnu.org/licenses/>.
35 35 #
36 36 # This program is dual-licensed. If you wish to learn more about the
37 37 # RhodeCode Enterprise Edition, including its added features, Support services,
38 38 # and proprietary license terms, please see https://rhodecode.com/licenses/
39 39
40 40 import pytest
41 41
42 42 from rhodecode.model.db import User
43 43 from rhodecode.tests import TestController, assert_session_flash
44 44 from rhodecode.lib import helpers as h
45 45
46 46
47 47 def route_path(name, params=None, **kwargs):
48 48 import urllib
49 49 from rhodecode.apps._base import ADMIN_PREFIX
50 50
51 51 base_url = {
52 52 'my_account_edit': ADMIN_PREFIX + '/my_account/edit',
53 53 'my_account_update': ADMIN_PREFIX + '/my_account/update',
54 54 'my_account_pullrequests': ADMIN_PREFIX + '/my_account/pull_requests',
55 55 'my_account_pullrequests_data': ADMIN_PREFIX + '/my_account/pull_requests/data',
56 56 }[name].format(**kwargs)
57 57
58 58 if params:
59 59 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
60 60 return base_url
61 61
62 62
63 63 class TestMyAccountEdit(TestController):
64 64
65 65 def test_my_account_edit(self):
66 66 self.log_user()
67 67 response = self.app.get(route_path('my_account_edit'))
68 68
69 69 response.mustcontain('value="test_admin')
70 70
71 71 @pytest.mark.backends("git", "hg")
72 72 def test_my_account_my_pullrequests(self, pr_util):
73 73 self.log_user()
74 74 response = self.app.get(route_path('my_account_pullrequests'))
75 75 response.mustcontain('There are currently no open pull '
76 76 'requests requiring your participation.')
77 77
78 78 @pytest.mark.backends("git", "hg")
79 def test_my_account_my_pullrequests_data(self, pr_util, xhr_header):
79 @pytest.mark.parametrize('params, expected_title', [
80 ({'closed': 1}, 'Closed'),
81 ({'awaiting_my_review': 1}, 'Awaiting my review'),
82 ])
83 def test_my_account_my_pullrequests_data(self, pr_util, xhr_header, params, expected_title):
80 84 self.log_user()
81 85 response = self.app.get(route_path('my_account_pullrequests_data'),
82 86 extra_environ=xhr_header)
83 87 assert response.json == {
84 88 u'data': [], u'draw': None,
85 89 u'recordsFiltered': 0, u'recordsTotal': 0}
86 90
87 91 pr = pr_util.create_pull_request(title='TestMyAccountPR')
88 92 expected = {
89 93 'author_raw': 'RhodeCode Admin',
90 94 'name_raw': pr.pull_request_id
91 95 }
92 96 response = self.app.get(route_path('my_account_pullrequests_data'),
93 97 extra_environ=xhr_header)
94 98 assert response.json['recordsTotal'] == 1
95 99 assert response.json['data'][0]['author_raw'] == expected['author_raw']
96 100
97 101 assert response.json['data'][0]['author_raw'] == expected['author_raw']
98 102 assert response.json['data'][0]['name_raw'] == expected['name_raw']
99 103
100 104 @pytest.mark.parametrize(
101 105 "name, attrs", [
102 106 ('firstname', {'firstname': 'new_username'}),
103 107 ('lastname', {'lastname': 'new_username'}),
104 108 ('admin', {'admin': True}),
105 109 ('admin', {'admin': False}),
106 110 ('extern_type', {'extern_type': 'ldap'}),
107 111 ('extern_type', {'extern_type': None}),
108 112 # ('extern_name', {'extern_name': 'test'}),
109 113 # ('extern_name', {'extern_name': None}),
110 114 ('active', {'active': False}),
111 115 ('active', {'active': True}),
112 116 ('email', {'email': u'some@email.com'}),
113 117 ])
114 118 def test_my_account_update(self, name, attrs, user_util):
115 119 usr = user_util.create_user(password='qweqwe')
116 120 params = usr.get_api_data() # current user data
117 121 user_id = usr.user_id
118 122 self.log_user(
119 123 username=usr.username, password='qweqwe')
120 124
121 125 params.update({'password_confirmation': ''})
122 126 params.update({'new_password': ''})
123 127 params.update({'extern_type': u'rhodecode'})
124 128 params.update({'extern_name': u'rhodecode'})
125 129 params.update({'csrf_token': self.csrf_token})
126 130
127 131 params.update(attrs)
128 132 # my account page cannot set language param yet, only for admins
129 133 del params['language']
130 134 if name == 'email':
131 135 uem = user_util.create_additional_user_email(usr, attrs['email'])
132 136 email_before = User.get(user_id).email
133 137
134 138 response = self.app.post(route_path('my_account_update'), params)
135 139
136 140 assert_session_flash(
137 141 response, 'Your account was updated successfully')
138 142
139 143 del params['csrf_token']
140 144
141 145 updated_user = User.get(user_id)
142 146 updated_params = updated_user.get_api_data()
143 147 updated_params.update({'password_confirmation': ''})
144 148 updated_params.update({'new_password': ''})
145 149
146 150 params['last_login'] = updated_params['last_login']
147 151 params['last_activity'] = updated_params['last_activity']
148 152 # my account page cannot set language param yet, only for admins
149 153 # but we get this info from API anyway
150 154 params['language'] = updated_params['language']
151 155
152 156 if name == 'email':
153 157 params['emails'] = [attrs['email'], email_before]
154 158 if name == 'extern_type':
155 159 # cannot update this via form, expected value is original one
156 160 params['extern_type'] = "rhodecode"
157 161 if name == 'extern_name':
158 162 # cannot update this via form, expected value is original one
159 163 params['extern_name'] = str(user_id)
160 164 if name == 'active':
161 165 # my account cannot deactivate account
162 166 params['active'] = True
163 167 if name == 'admin':
164 168 # my account cannot make you an admin !
165 169 params['admin'] = False
166 170
167 171 assert params == updated_params
168 172
169 173 def test_my_account_update_err_email_not_exists_in_emails(self):
170 174 self.log_user()
171 175
172 176 new_email = 'test_regular@mail.com' # not in emails
173 177 params = {
174 178 'username': 'test_admin',
175 179 'new_password': 'test12',
176 180 'password_confirmation': 'test122',
177 181 'firstname': 'NewName',
178 182 'lastname': 'NewLastname',
179 183 'email': new_email,
180 184 'csrf_token': self.csrf_token,
181 185 }
182 186
183 187 response = self.app.post(route_path('my_account_update'),
184 188 params=params)
185 189
186 190 response.mustcontain('"test_regular@mail.com" is not one of test_admin@mail.com')
187 191
188 192 def test_my_account_update_bad_email_address(self):
189 193 self.log_user('test_regular2', 'test12')
190 194
191 195 new_email = 'newmail.pl'
192 196 params = {
193 197 'username': 'test_admin',
194 198 'new_password': 'test12',
195 199 'password_confirmation': 'test122',
196 200 'firstname': 'NewName',
197 201 'lastname': 'NewLastname',
198 202 'email': new_email,
199 203 'csrf_token': self.csrf_token,
200 204 }
201 205 response = self.app.post(route_path('my_account_update'),
202 206 params=params)
203 207
204 208 response.mustcontain('"newmail.pl" is not one of test_regular2@mail.com')
@@ -1,752 +1,783 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23 import string
24 24
25 25 import formencode
26 26 import formencode.htmlfill
27 27 import peppercorn
28 28 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
29 29
30 30 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 31 from rhodecode import forms
32 32 from rhodecode.lib import helpers as h
33 33 from rhodecode.lib import audit_logger
34 34 from rhodecode.lib.ext_json import json
35 35 from rhodecode.lib.auth import (
36 36 LoginRequired, NotAnonymous, CSRFRequired,
37 37 HasRepoPermissionAny, HasRepoGroupPermissionAny, AuthUser)
38 38 from rhodecode.lib.channelstream import (
39 39 channelstream_request, ChannelstreamException)
40 40 from rhodecode.lib.utils2 import safe_int, md5, str2bool
41 41 from rhodecode.model.auth_token import AuthTokenModel
42 42 from rhodecode.model.comment import CommentsModel
43 43 from rhodecode.model.db import (
44 44 IntegrityError, or_, in_filter_generator,
45 45 Repository, UserEmailMap, UserApiKeys, UserFollowing,
46 PullRequest, UserBookmark, RepoGroup)
46 PullRequest, UserBookmark, RepoGroup, ChangesetStatus)
47 47 from rhodecode.model.meta import Session
48 48 from rhodecode.model.pull_request import PullRequestModel
49 49 from rhodecode.model.user import UserModel
50 50 from rhodecode.model.user_group import UserGroupModel
51 51 from rhodecode.model.validation_schema.schemas import user_schema
52 52
53 53 log = logging.getLogger(__name__)
54 54
55 55
56 56 class MyAccountView(BaseAppView, DataGridAppView):
57 57 ALLOW_SCOPED_TOKENS = False
58 58 """
59 59 This view has alternative version inside EE, if modified please take a look
60 60 in there as well.
61 61 """
62 62
63 63 def load_default_context(self):
64 64 c = self._get_local_tmpl_context()
65 65 c.user = c.auth_user.get_instance()
66 66 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
67 67 return c
68 68
69 69 @LoginRequired()
70 70 @NotAnonymous()
71 71 def my_account_profile(self):
72 72 c = self.load_default_context()
73 73 c.active = 'profile'
74 74 c.extern_type = c.user.extern_type
75 75 return self._get_template_context(c)
76 76
77 77 @LoginRequired()
78 78 @NotAnonymous()
79 79 def my_account_edit(self):
80 80 c = self.load_default_context()
81 81 c.active = 'profile_edit'
82 82 c.extern_type = c.user.extern_type
83 83 c.extern_name = c.user.extern_name
84 84
85 85 schema = user_schema.UserProfileSchema().bind(
86 86 username=c.user.username, user_emails=c.user.emails)
87 87 appstruct = {
88 88 'username': c.user.username,
89 89 'email': c.user.email,
90 90 'firstname': c.user.firstname,
91 91 'lastname': c.user.lastname,
92 92 'description': c.user.description,
93 93 }
94 94 c.form = forms.RcForm(
95 95 schema, appstruct=appstruct,
96 96 action=h.route_path('my_account_update'),
97 97 buttons=(forms.buttons.save, forms.buttons.reset))
98 98
99 99 return self._get_template_context(c)
100 100
101 101 @LoginRequired()
102 102 @NotAnonymous()
103 103 @CSRFRequired()
104 104 def my_account_update(self):
105 105 _ = self.request.translate
106 106 c = self.load_default_context()
107 107 c.active = 'profile_edit'
108 108 c.perm_user = c.auth_user
109 109 c.extern_type = c.user.extern_type
110 110 c.extern_name = c.user.extern_name
111 111
112 112 schema = user_schema.UserProfileSchema().bind(
113 113 username=c.user.username, user_emails=c.user.emails)
114 114 form = forms.RcForm(
115 115 schema, buttons=(forms.buttons.save, forms.buttons.reset))
116 116
117 117 controls = self.request.POST.items()
118 118 try:
119 119 valid_data = form.validate(controls)
120 120 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
121 121 'new_password', 'password_confirmation']
122 122 if c.extern_type != "rhodecode":
123 123 # forbid updating username for external accounts
124 124 skip_attrs.append('username')
125 125 old_email = c.user.email
126 126 UserModel().update_user(
127 127 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
128 128 **valid_data)
129 129 if old_email != valid_data['email']:
130 130 old = UserEmailMap.query() \
131 131 .filter(UserEmailMap.user == c.user)\
132 132 .filter(UserEmailMap.email == valid_data['email'])\
133 133 .first()
134 134 old.email = old_email
135 135 h.flash(_('Your account was updated successfully'), category='success')
136 136 Session().commit()
137 137 except forms.ValidationFailure as e:
138 138 c.form = e
139 139 return self._get_template_context(c)
140 140 except Exception:
141 141 log.exception("Exception updating user")
142 142 h.flash(_('Error occurred during update of user'),
143 143 category='error')
144 144 raise HTTPFound(h.route_path('my_account_profile'))
145 145
146 146 @LoginRequired()
147 147 @NotAnonymous()
148 148 def my_account_password(self):
149 149 c = self.load_default_context()
150 150 c.active = 'password'
151 151 c.extern_type = c.user.extern_type
152 152
153 153 schema = user_schema.ChangePasswordSchema().bind(
154 154 username=c.user.username)
155 155
156 156 form = forms.Form(
157 157 schema,
158 158 action=h.route_path('my_account_password_update'),
159 159 buttons=(forms.buttons.save, forms.buttons.reset))
160 160
161 161 c.form = form
162 162 return self._get_template_context(c)
163 163
164 164 @LoginRequired()
165 165 @NotAnonymous()
166 166 @CSRFRequired()
167 167 def my_account_password_update(self):
168 168 _ = self.request.translate
169 169 c = self.load_default_context()
170 170 c.active = 'password'
171 171 c.extern_type = c.user.extern_type
172 172
173 173 schema = user_schema.ChangePasswordSchema().bind(
174 174 username=c.user.username)
175 175
176 176 form = forms.Form(
177 177 schema, buttons=(forms.buttons.save, forms.buttons.reset))
178 178
179 179 if c.extern_type != 'rhodecode':
180 180 raise HTTPFound(self.request.route_path('my_account_password'))
181 181
182 182 controls = self.request.POST.items()
183 183 try:
184 184 valid_data = form.validate(controls)
185 185 UserModel().update_user(c.user.user_id, **valid_data)
186 186 c.user.update_userdata(force_password_change=False)
187 187 Session().commit()
188 188 except forms.ValidationFailure as e:
189 189 c.form = e
190 190 return self._get_template_context(c)
191 191
192 192 except Exception:
193 193 log.exception("Exception updating password")
194 194 h.flash(_('Error occurred during update of user password'),
195 195 category='error')
196 196 else:
197 197 instance = c.auth_user.get_instance()
198 198 self.session.setdefault('rhodecode_user', {}).update(
199 199 {'password': md5(instance.password)})
200 200 self.session.save()
201 201 h.flash(_("Successfully updated password"), category='success')
202 202
203 203 raise HTTPFound(self.request.route_path('my_account_password'))
204 204
205 205 @LoginRequired()
206 206 @NotAnonymous()
207 207 def my_account_auth_tokens(self):
208 208 _ = self.request.translate
209 209
210 210 c = self.load_default_context()
211 211 c.active = 'auth_tokens'
212 212 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
213 213 c.role_values = [
214 214 (x, AuthTokenModel.cls._get_role_name(x))
215 215 for x in AuthTokenModel.cls.ROLES]
216 216 c.role_options = [(c.role_values, _("Role"))]
217 217 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
218 218 c.user.user_id, show_expired=True)
219 219 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
220 220 return self._get_template_context(c)
221 221
222 222 @LoginRequired()
223 223 @NotAnonymous()
224 224 @CSRFRequired()
225 225 def my_account_auth_tokens_view(self):
226 226 _ = self.request.translate
227 227 c = self.load_default_context()
228 228
229 229 auth_token_id = self.request.POST.get('auth_token_id')
230 230
231 231 if auth_token_id:
232 232 token = UserApiKeys.get_or_404(auth_token_id)
233 233 if token.user.user_id != c.user.user_id:
234 234 raise HTTPNotFound()
235 235
236 236 return {
237 237 'auth_token': token.api_key
238 238 }
239 239
240 240 def maybe_attach_token_scope(self, token):
241 241 # implemented in EE edition
242 242 pass
243 243
244 244 @LoginRequired()
245 245 @NotAnonymous()
246 246 @CSRFRequired()
247 247 def my_account_auth_tokens_add(self):
248 248 _ = self.request.translate
249 249 c = self.load_default_context()
250 250
251 251 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
252 252 description = self.request.POST.get('description')
253 253 role = self.request.POST.get('role')
254 254
255 255 token = UserModel().add_auth_token(
256 256 user=c.user.user_id,
257 257 lifetime_minutes=lifetime, role=role, description=description,
258 258 scope_callback=self.maybe_attach_token_scope)
259 259 token_data = token.get_api_data()
260 260
261 261 audit_logger.store_web(
262 262 'user.edit.token.add', action_data={
263 263 'data': {'token': token_data, 'user': 'self'}},
264 264 user=self._rhodecode_user, )
265 265 Session().commit()
266 266
267 267 h.flash(_("Auth token successfully created"), category='success')
268 268 return HTTPFound(h.route_path('my_account_auth_tokens'))
269 269
270 270 @LoginRequired()
271 271 @NotAnonymous()
272 272 @CSRFRequired()
273 273 def my_account_auth_tokens_delete(self):
274 274 _ = self.request.translate
275 275 c = self.load_default_context()
276 276
277 277 del_auth_token = self.request.POST.get('del_auth_token')
278 278
279 279 if del_auth_token:
280 280 token = UserApiKeys.get_or_404(del_auth_token)
281 281 token_data = token.get_api_data()
282 282
283 283 AuthTokenModel().delete(del_auth_token, c.user.user_id)
284 284 audit_logger.store_web(
285 285 'user.edit.token.delete', action_data={
286 286 'data': {'token': token_data, 'user': 'self'}},
287 287 user=self._rhodecode_user,)
288 288 Session().commit()
289 289 h.flash(_("Auth token successfully deleted"), category='success')
290 290
291 291 return HTTPFound(h.route_path('my_account_auth_tokens'))
292 292
293 293 @LoginRequired()
294 294 @NotAnonymous()
295 295 def my_account_emails(self):
296 296 _ = self.request.translate
297 297
298 298 c = self.load_default_context()
299 299 c.active = 'emails'
300 300
301 301 c.user_email_map = UserEmailMap.query()\
302 302 .filter(UserEmailMap.user == c.user).all()
303 303
304 304 schema = user_schema.AddEmailSchema().bind(
305 305 username=c.user.username, user_emails=c.user.emails)
306 306
307 307 form = forms.RcForm(schema,
308 308 action=h.route_path('my_account_emails_add'),
309 309 buttons=(forms.buttons.save, forms.buttons.reset))
310 310
311 311 c.form = form
312 312 return self._get_template_context(c)
313 313
314 314 @LoginRequired()
315 315 @NotAnonymous()
316 316 @CSRFRequired()
317 317 def my_account_emails_add(self):
318 318 _ = self.request.translate
319 319 c = self.load_default_context()
320 320 c.active = 'emails'
321 321
322 322 schema = user_schema.AddEmailSchema().bind(
323 323 username=c.user.username, user_emails=c.user.emails)
324 324
325 325 form = forms.RcForm(
326 326 schema, action=h.route_path('my_account_emails_add'),
327 327 buttons=(forms.buttons.save, forms.buttons.reset))
328 328
329 329 controls = self.request.POST.items()
330 330 try:
331 331 valid_data = form.validate(controls)
332 332 UserModel().add_extra_email(c.user.user_id, valid_data['email'])
333 333 audit_logger.store_web(
334 334 'user.edit.email.add', action_data={
335 335 'data': {'email': valid_data['email'], 'user': 'self'}},
336 336 user=self._rhodecode_user,)
337 337 Session().commit()
338 338 except formencode.Invalid as error:
339 339 h.flash(h.escape(error.error_dict['email']), category='error')
340 340 except forms.ValidationFailure as e:
341 341 c.user_email_map = UserEmailMap.query() \
342 342 .filter(UserEmailMap.user == c.user).all()
343 343 c.form = e
344 344 return self._get_template_context(c)
345 345 except Exception:
346 346 log.exception("Exception adding email")
347 347 h.flash(_('Error occurred during adding email'),
348 348 category='error')
349 349 else:
350 350 h.flash(_("Successfully added email"), category='success')
351 351
352 352 raise HTTPFound(self.request.route_path('my_account_emails'))
353 353
354 354 @LoginRequired()
355 355 @NotAnonymous()
356 356 @CSRFRequired()
357 357 def my_account_emails_delete(self):
358 358 _ = self.request.translate
359 359 c = self.load_default_context()
360 360
361 361 del_email_id = self.request.POST.get('del_email_id')
362 362 if del_email_id:
363 363 email = UserEmailMap.get_or_404(del_email_id).email
364 364 UserModel().delete_extra_email(c.user.user_id, del_email_id)
365 365 audit_logger.store_web(
366 366 'user.edit.email.delete', action_data={
367 367 'data': {'email': email, 'user': 'self'}},
368 368 user=self._rhodecode_user,)
369 369 Session().commit()
370 370 h.flash(_("Email successfully deleted"),
371 371 category='success')
372 372 return HTTPFound(h.route_path('my_account_emails'))
373 373
374 374 @LoginRequired()
375 375 @NotAnonymous()
376 376 @CSRFRequired()
377 377 def my_account_notifications_test_channelstream(self):
378 378 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
379 379 self._rhodecode_user.username, datetime.datetime.now())
380 380 payload = {
381 381 # 'channel': 'broadcast',
382 382 'type': 'message',
383 383 'timestamp': datetime.datetime.utcnow(),
384 384 'user': 'system',
385 385 'pm_users': [self._rhodecode_user.username],
386 386 'message': {
387 387 'message': message,
388 388 'level': 'info',
389 389 'topic': '/notifications'
390 390 }
391 391 }
392 392
393 393 registry = self.request.registry
394 394 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
395 395 channelstream_config = rhodecode_plugins.get('channelstream', {})
396 396
397 397 try:
398 398 channelstream_request(channelstream_config, [payload], '/message')
399 399 except ChannelstreamException as e:
400 400 log.exception('Failed to send channelstream data')
401 401 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
402 402 return {"response": 'Channelstream data sent. '
403 403 'You should see a new live message now.'}
404 404
405 405 def _load_my_repos_data(self, watched=False):
406 406
407 407 allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(AuthUser.repo_read_perms)
408 408
409 409 if watched:
410 410 # repos user watch
411 411 repo_list = Session().query(
412 412 Repository
413 413 ) \
414 414 .join(
415 415 (UserFollowing, UserFollowing.follows_repo_id == Repository.repo_id)
416 416 ) \
417 417 .filter(
418 418 UserFollowing.user_id == self._rhodecode_user.user_id
419 419 ) \
420 420 .filter(or_(
421 421 # generate multiple IN to fix limitation problems
422 422 *in_filter_generator(Repository.repo_id, allowed_ids))
423 423 ) \
424 424 .order_by(Repository.repo_name) \
425 425 .all()
426 426
427 427 else:
428 428 # repos user is owner of
429 429 repo_list = Session().query(
430 430 Repository
431 431 ) \
432 432 .filter(
433 433 Repository.user_id == self._rhodecode_user.user_id
434 434 ) \
435 435 .filter(or_(
436 436 # generate multiple IN to fix limitation problems
437 437 *in_filter_generator(Repository.repo_id, allowed_ids))
438 438 ) \
439 439 .order_by(Repository.repo_name) \
440 440 .all()
441 441
442 442 _render = self.request.get_partial_renderer(
443 443 'rhodecode:templates/data_table/_dt_elements.mako')
444 444
445 445 def repo_lnk(name, rtype, rstate, private, archived, fork_of):
446 446 return _render('repo_name', name, rtype, rstate, private, archived, fork_of,
447 447 short_name=False, admin=False)
448 448
449 449 repos_data = []
450 450 for repo in repo_list:
451 451 row = {
452 452 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
453 453 repo.private, repo.archived, repo.fork),
454 454 "name_raw": repo.repo_name.lower(),
455 455 }
456 456
457 457 repos_data.append(row)
458 458
459 459 # json used to render the grid
460 460 return json.dumps(repos_data)
461 461
462 462 @LoginRequired()
463 463 @NotAnonymous()
464 464 def my_account_repos(self):
465 465 c = self.load_default_context()
466 466 c.active = 'repos'
467 467
468 468 # json used to render the grid
469 469 c.data = self._load_my_repos_data()
470 470 return self._get_template_context(c)
471 471
472 472 @LoginRequired()
473 473 @NotAnonymous()
474 474 def my_account_watched(self):
475 475 c = self.load_default_context()
476 476 c.active = 'watched'
477 477
478 478 # json used to render the grid
479 479 c.data = self._load_my_repos_data(watched=True)
480 480 return self._get_template_context(c)
481 481
482 482 @LoginRequired()
483 483 @NotAnonymous()
484 484 def my_account_bookmarks(self):
485 485 c = self.load_default_context()
486 486 c.active = 'bookmarks'
487 487 c.bookmark_items = UserBookmark.get_bookmarks_for_user(
488 488 self._rhodecode_db_user.user_id, cache=False)
489 489 return self._get_template_context(c)
490 490
491 491 def _process_bookmark_entry(self, entry, user_id):
492 492 position = safe_int(entry.get('position'))
493 493 cur_position = safe_int(entry.get('cur_position'))
494 494 if position is None:
495 495 return
496 496
497 497 # check if this is an existing entry
498 498 is_new = False
499 499 db_entry = UserBookmark().get_by_position_for_user(cur_position, user_id)
500 500
501 501 if db_entry and str2bool(entry.get('remove')):
502 502 log.debug('Marked bookmark %s for deletion', db_entry)
503 503 Session().delete(db_entry)
504 504 return
505 505
506 506 if not db_entry:
507 507 # new
508 508 db_entry = UserBookmark()
509 509 is_new = True
510 510
511 511 should_save = False
512 512 default_redirect_url = ''
513 513
514 514 # save repo
515 515 if entry.get('bookmark_repo') and safe_int(entry.get('bookmark_repo')):
516 516 repo = Repository.get(entry['bookmark_repo'])
517 517 perm_check = HasRepoPermissionAny(
518 518 'repository.read', 'repository.write', 'repository.admin')
519 519 if repo and perm_check(repo_name=repo.repo_name):
520 520 db_entry.repository = repo
521 521 should_save = True
522 522 default_redirect_url = '${repo_url}'
523 523 # save repo group
524 524 elif entry.get('bookmark_repo_group') and safe_int(entry.get('bookmark_repo_group')):
525 525 repo_group = RepoGroup.get(entry['bookmark_repo_group'])
526 526 perm_check = HasRepoGroupPermissionAny(
527 527 'group.read', 'group.write', 'group.admin')
528 528
529 529 if repo_group and perm_check(group_name=repo_group.group_name):
530 530 db_entry.repository_group = repo_group
531 531 should_save = True
532 532 default_redirect_url = '${repo_group_url}'
533 533 # save generic info
534 534 elif entry.get('title') and entry.get('redirect_url'):
535 535 should_save = True
536 536
537 537 if should_save:
538 538 # mark user and position
539 539 db_entry.user_id = user_id
540 540 db_entry.position = position
541 541 db_entry.title = entry.get('title')
542 542 db_entry.redirect_url = entry.get('redirect_url') or default_redirect_url
543 543 log.debug('Saving bookmark %s, new:%s', db_entry, is_new)
544 544
545 545 Session().add(db_entry)
546 546
547 547 @LoginRequired()
548 548 @NotAnonymous()
549 549 @CSRFRequired()
550 550 def my_account_bookmarks_update(self):
551 551 _ = self.request.translate
552 552 c = self.load_default_context()
553 553 c.active = 'bookmarks'
554 554
555 555 controls = peppercorn.parse(self.request.POST.items())
556 556 user_id = c.user.user_id
557 557
558 558 # validate positions
559 559 positions = {}
560 560 for entry in controls.get('bookmarks', []):
561 561 position = safe_int(entry['position'])
562 562 if position is None:
563 563 continue
564 564
565 565 if position in positions:
566 566 h.flash(_("Position {} is defined twice. "
567 567 "Please correct this error.").format(position), category='error')
568 568 return HTTPFound(h.route_path('my_account_bookmarks'))
569 569
570 570 entry['position'] = position
571 571 entry['cur_position'] = safe_int(entry.get('cur_position'))
572 572 positions[position] = entry
573 573
574 574 try:
575 575 for entry in positions.values():
576 576 self._process_bookmark_entry(entry, user_id)
577 577
578 578 Session().commit()
579 579 h.flash(_("Update Bookmarks"), category='success')
580 580 except IntegrityError:
581 581 h.flash(_("Failed to update bookmarks. "
582 582 "Make sure an unique position is used."), category='error')
583 583
584 584 return HTTPFound(h.route_path('my_account_bookmarks'))
585 585
586 586 @LoginRequired()
587 587 @NotAnonymous()
588 588 def my_account_goto_bookmark(self):
589 589
590 590 bookmark_id = self.request.matchdict['bookmark_id']
591 591 user_bookmark = UserBookmark().query()\
592 592 .filter(UserBookmark.user_id == self.request.user.user_id) \
593 593 .filter(UserBookmark.position == bookmark_id).scalar()
594 594
595 595 redirect_url = h.route_path('my_account_bookmarks')
596 596 if not user_bookmark:
597 597 raise HTTPFound(redirect_url)
598 598
599 599 # repository set
600 600 if user_bookmark.repository:
601 601 repo_name = user_bookmark.repository.repo_name
602 602 base_redirect_url = h.route_path(
603 603 'repo_summary', repo_name=repo_name)
604 604 if user_bookmark.redirect_url and \
605 605 '${repo_url}' in user_bookmark.redirect_url:
606 606 redirect_url = string.Template(user_bookmark.redirect_url)\
607 607 .safe_substitute({'repo_url': base_redirect_url})
608 608 else:
609 609 redirect_url = base_redirect_url
610 610 # repository group set
611 611 elif user_bookmark.repository_group:
612 612 repo_group_name = user_bookmark.repository_group.group_name
613 613 base_redirect_url = h.route_path(
614 614 'repo_group_home', repo_group_name=repo_group_name)
615 615 if user_bookmark.redirect_url and \
616 616 '${repo_group_url}' in user_bookmark.redirect_url:
617 617 redirect_url = string.Template(user_bookmark.redirect_url)\
618 618 .safe_substitute({'repo_group_url': base_redirect_url})
619 619 else:
620 620 redirect_url = base_redirect_url
621 621 # custom URL set
622 622 elif user_bookmark.redirect_url:
623 623 server_url = h.route_url('home').rstrip('/')
624 624 redirect_url = string.Template(user_bookmark.redirect_url) \
625 625 .safe_substitute({'server_url': server_url})
626 626
627 627 log.debug('Redirecting bookmark %s to %s', user_bookmark, redirect_url)
628 628 raise HTTPFound(redirect_url)
629 629
630 630 @LoginRequired()
631 631 @NotAnonymous()
632 632 def my_account_perms(self):
633 633 c = self.load_default_context()
634 634 c.active = 'perms'
635 635
636 636 c.perm_user = c.auth_user
637 637 return self._get_template_context(c)
638 638
639 639 @LoginRequired()
640 640 @NotAnonymous()
641 641 def my_notifications(self):
642 642 c = self.load_default_context()
643 643 c.active = 'notifications'
644 644
645 645 return self._get_template_context(c)
646 646
647 647 @LoginRequired()
648 648 @NotAnonymous()
649 649 @CSRFRequired()
650 650 def my_notifications_toggle_visibility(self):
651 651 user = self._rhodecode_db_user
652 652 new_status = not user.user_data.get('notification_status', True)
653 653 user.update_userdata(notification_status=new_status)
654 654 Session().commit()
655 655 return user.user_data['notification_status']
656 656
657 def _get_pull_requests_list(self, statuses):
657 def _get_pull_requests_list(self, statuses, filter_type=None):
658 658 draw, start, limit = self._extract_chunk(self.request)
659 659 search_q, order_by, order_dir = self._extract_ordering(self.request)
660 660
661 661 _render = self.request.get_partial_renderer(
662 662 'rhodecode:templates/data_table/_dt_elements.mako')
663 663
664 if filter_type == 'awaiting_my_review':
665 pull_requests = PullRequestModel().get_im_participating_in_for_review(
666 user_id=self._rhodecode_user.user_id,
667 statuses=statuses, query=search_q,
668 offset=start, length=limit, order_by=order_by,
669 order_dir=order_dir)
670
671 pull_requests_total_count = PullRequestModel().count_im_participating_in_for_review(
672 user_id=self._rhodecode_user.user_id, statuses=statuses, query=search_q)
673 else:
664 674 pull_requests = PullRequestModel().get_im_participating_in(
665 675 user_id=self._rhodecode_user.user_id,
666 676 statuses=statuses, query=search_q,
667 677 offset=start, length=limit, order_by=order_by,
668 678 order_dir=order_dir)
669 679
670 680 pull_requests_total_count = PullRequestModel().count_im_participating_in(
671 681 user_id=self._rhodecode_user.user_id, statuses=statuses, query=search_q)
672 682
673 683 data = []
674 684 comments_model = CommentsModel()
675 685 for pr in pull_requests:
676 686 repo_id = pr.target_repo_id
677 687 comments_count = comments_model.get_all_comments(
678 688 repo_id, pull_request=pr, include_drafts=False, count_only=True)
679 689 owned = pr.user_id == self._rhodecode_user.user_id
680 690
691 review_statuses = pr.reviewers_statuses(user=self._rhodecode_db_user)
692 my_review_status = ChangesetStatus.STATUS_NOT_REVIEWED
693 if review_statuses and review_statuses[4]:
694 _review_obj, _user, _reasons, _mandatory, statuses = review_statuses
695 my_review_status = statuses[0][1].status
696
681 697 data.append({
682 698 'target_repo': _render('pullrequest_target_repo',
683 699 pr.target_repo.repo_name),
684 700 'name': _render('pullrequest_name',
685 701 pr.pull_request_id, pr.pull_request_state,
686 702 pr.work_in_progress, pr.target_repo.repo_name,
687 703 short=True),
688 704 'name_raw': pr.pull_request_id,
689 705 'status': _render('pullrequest_status',
690 706 pr.calculated_review_status()),
707 'my_status': _render('pullrequest_status',
708 my_review_status),
691 709 'title': _render('pullrequest_title', pr.title, pr.description),
692 710 'description': h.escape(pr.description),
693 711 'updated_on': _render('pullrequest_updated_on',
694 712 h.datetime_to_time(pr.updated_on),
695 713 pr.versions_count),
696 714 'updated_on_raw': h.datetime_to_time(pr.updated_on),
697 715 'created_on': _render('pullrequest_updated_on',
698 716 h.datetime_to_time(pr.created_on)),
699 717 'created_on_raw': h.datetime_to_time(pr.created_on),
700 718 'state': pr.pull_request_state,
701 719 'author': _render('pullrequest_author',
702 720 pr.author.full_contact, ),
703 721 'author_raw': pr.author.full_name,
704 722 'comments': _render('pullrequest_comments', comments_count),
705 723 'comments_raw': comments_count,
706 724 'closed': pr.is_closed(),
707 725 'owned': owned
708 726 })
709 727
710 728 # json used to render the grid
711 729 data = ({
712 730 'draw': draw,
713 731 'data': data,
714 732 'recordsTotal': pull_requests_total_count,
715 733 'recordsFiltered': pull_requests_total_count,
716 734 })
717 735 return data
718 736
719 737 @LoginRequired()
720 738 @NotAnonymous()
721 739 def my_account_pullrequests(self):
722 740 c = self.load_default_context()
723 741 c.active = 'pullrequests'
724 742 req_get = self.request.GET
725 743
726 c.closed = str2bool(req_get.get('pr_show_closed'))
744 c.closed = str2bool(req_get.get('closed'))
745 c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
746
747 c.selected_filter = 'all'
748 if c.closed:
749 c.selected_filter = 'all_closed'
750 if c.awaiting_my_review:
751 c.selected_filter = 'awaiting_my_review'
727 752
728 753 return self._get_template_context(c)
729 754
730 755 @LoginRequired()
731 756 @NotAnonymous()
732 757 def my_account_pullrequests_data(self):
733 758 self.load_default_context()
734 759 req_get = self.request.GET
760
761 awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
735 762 closed = str2bool(req_get.get('closed'))
736 763
737 764 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
738 765 if closed:
739 766 statuses += [PullRequest.STATUS_CLOSED]
740 767
741 data = self._get_pull_requests_list(statuses=statuses)
768 filter_type = \
769 'awaiting_my_review' if awaiting_my_review \
770 else None
771
772 data = self._get_pull_requests_list(statuses=statuses, filter_type=filter_type)
742 773 return data
743 774
744 775 @LoginRequired()
745 776 @NotAnonymous()
746 777 def my_account_user_group_membership(self):
747 778 c = self.load_default_context()
748 779 c.active = 'user_group_membership'
749 780 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
750 781 for group in self._rhodecode_db_user.group_member]
751 782 c.user_groups = json.dumps(groups)
752 783 return self._get_template_context(c)
@@ -1,84 +1,84 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 22 from rhodecode.model.db import Repository
23 23
24 24
25 25 def route_path(name, params=None, **kwargs):
26 26 import urllib
27 27
28 28 base_url = {
29 29 'pullrequest_show_all': '/{repo_name}/pull-request',
30 30 'pullrequest_show_all_data': '/{repo_name}/pull-request-data',
31 31 }[name].format(**kwargs)
32 32
33 33 if params:
34 34 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
35 35 return base_url
36 36
37 37
38 38 @pytest.mark.backends("git", "hg")
39 39 @pytest.mark.usefixtures('autologin_user', 'app')
40 40 class TestPullRequestList(object):
41 41
42 42 @pytest.mark.parametrize('params, expected_title', [
43 43 ({'source': 0, 'closed': 1}, 'Closed'),
44 ({'source': 0, 'my': 1}, 'Opened by me'),
44 ({'source': 0, 'my': 1}, 'Created by me'),
45 45 ({'source': 0, 'awaiting_review': 1}, 'Awaiting review'),
46 46 ({'source': 0, 'awaiting_my_review': 1}, 'Awaiting my review'),
47 47 ({'source': 1}, 'From this repo'),
48 48 ])
49 49 def test_showing_list_page(self, backend, pr_util, params, expected_title):
50 50 pull_request = pr_util.create_pull_request()
51 51
52 52 response = self.app.get(
53 53 route_path('pullrequest_show_all',
54 54 repo_name=pull_request.target_repo.repo_name,
55 55 params=params))
56 56
57 57 assert_response = response.assert_response()
58 58
59 59 element = assert_response.get_element('.title .active')
60 60 element_text = element.text_content()
61 61 assert expected_title == element_text
62 62
63 63 def test_showing_list_page_data(self, backend, pr_util, xhr_header):
64 64 pull_request = pr_util.create_pull_request()
65 65 response = self.app.get(
66 66 route_path('pullrequest_show_all_data',
67 67 repo_name=pull_request.target_repo.repo_name),
68 68 extra_environ=xhr_header)
69 69
70 70 assert response.json['recordsTotal'] == 1
71 71 assert response.json['data'][0]['description'] == 'Description'
72 72
73 73 def test_description_is_escaped_on_index_page(self, backend, pr_util, xhr_header):
74 74 xss_description = "<script>alert('Hi!')</script>"
75 75 pull_request = pr_util.create_pull_request(description=xss_description)
76 76
77 77 response = self.app.get(
78 78 route_path('pullrequest_show_all_data',
79 79 repo_name=pull_request.target_repo.repo_name),
80 80 extra_environ=xhr_header)
81 81
82 82 assert response.json['recordsTotal'] == 1
83 83 assert response.json['data'][0]['description'] == \
84 84 "&lt;script&gt;alert(&#39;Hi!&#39;)&lt;/script&gt;"
@@ -1,1861 +1,1868 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import collections
23 23
24 24 import formencode
25 25 import formencode.htmlfill
26 26 import peppercorn
27 27 from pyramid.httpexceptions import (
28 28 HTTPFound, HTTPNotFound, HTTPForbidden, HTTPBadRequest, HTTPConflict)
29 29
30 30 from pyramid.renderers import render
31 31
32 32 from rhodecode.apps._base import RepoAppView, DataGridAppView
33 33
34 34 from rhodecode.lib import helpers as h, diffs, codeblocks, channelstream
35 35 from rhodecode.lib.base import vcs_operation_context
36 36 from rhodecode.lib.diffs import load_cached_diff, cache_diff, diff_cache_exist
37 37 from rhodecode.lib.exceptions import CommentVersionMismatch
38 38 from rhodecode.lib.ext_json import json
39 39 from rhodecode.lib.auth import (
40 40 LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator,
41 41 NotAnonymous, CSRFRequired)
42 42 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode, safe_int, aslist
43 43 from rhodecode.lib.vcs.backends.base import (
44 44 EmptyCommit, UpdateFailureReason, unicode_to_reference)
45 45 from rhodecode.lib.vcs.exceptions import (
46 46 CommitDoesNotExistError, RepositoryRequirementError, EmptyRepositoryError)
47 47 from rhodecode.model.changeset_status import ChangesetStatusModel
48 48 from rhodecode.model.comment import CommentsModel
49 49 from rhodecode.model.db import (
50 50 func, false, or_, PullRequest, ChangesetComment, ChangesetStatus, Repository,
51 51 PullRequestReviewers)
52 52 from rhodecode.model.forms import PullRequestForm
53 53 from rhodecode.model.meta import Session
54 54 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
55 55 from rhodecode.model.scm import ScmModel
56 56
57 57 log = logging.getLogger(__name__)
58 58
59 59
60 60 class RepoPullRequestsView(RepoAppView, DataGridAppView):
61 61
62 62 def load_default_context(self):
63 63 c = self._get_local_tmpl_context(include_app_defaults=True)
64 64 c.REVIEW_STATUS_APPROVED = ChangesetStatus.STATUS_APPROVED
65 65 c.REVIEW_STATUS_REJECTED = ChangesetStatus.STATUS_REJECTED
66 66 # backward compat., we use for OLD PRs a plain renderer
67 67 c.renderer = 'plain'
68 68 return c
69 69
70 70 def _get_pull_requests_list(
71 71 self, repo_name, source, filter_type, opened_by, statuses):
72 72
73 73 draw, start, limit = self._extract_chunk(self.request)
74 74 search_q, order_by, order_dir = self._extract_ordering(self.request)
75 75 _render = self.request.get_partial_renderer(
76 76 'rhodecode:templates/data_table/_dt_elements.mako')
77 77
78 78 # pagination
79 79
80 80 if filter_type == 'awaiting_review':
81 81 pull_requests = PullRequestModel().get_awaiting_review(
82 repo_name, search_q=search_q, source=source, opened_by=opened_by,
83 statuses=statuses, offset=start, length=limit,
84 order_by=order_by, order_dir=order_dir)
82 repo_name,
83 search_q=search_q, statuses=statuses,
84 offset=start, length=limit, order_by=order_by, order_dir=order_dir)
85 85 pull_requests_total_count = PullRequestModel().count_awaiting_review(
86 repo_name, search_q=search_q, source=source, statuses=statuses,
87 opened_by=opened_by)
86 repo_name,
87 search_q=search_q, statuses=statuses)
88 88 elif filter_type == 'awaiting_my_review':
89 89 pull_requests = PullRequestModel().get_awaiting_my_review(
90 repo_name, search_q=search_q, source=source, opened_by=opened_by,
91 user_id=self._rhodecode_user.user_id, statuses=statuses,
92 offset=start, length=limit, order_by=order_by,
93 order_dir=order_dir)
90 repo_name, self._rhodecode_user.user_id,
91 search_q=search_q, statuses=statuses,
92 offset=start, length=limit, order_by=order_by, order_dir=order_dir)
94 93 pull_requests_total_count = PullRequestModel().count_awaiting_my_review(
95 repo_name, search_q=search_q, source=source, user_id=self._rhodecode_user.user_id,
96 statuses=statuses, opened_by=opened_by)
94 repo_name, self._rhodecode_user.user_id,
95 search_q=search_q, statuses=statuses)
97 96 else:
98 97 pull_requests = PullRequestModel().get_all(
99 98 repo_name, search_q=search_q, source=source, opened_by=opened_by,
100 99 statuses=statuses, offset=start, length=limit,
101 100 order_by=order_by, order_dir=order_dir)
102 101 pull_requests_total_count = PullRequestModel().count_all(
103 102 repo_name, search_q=search_q, source=source, statuses=statuses,
104 103 opened_by=opened_by)
105 104
106 105 data = []
107 106 comments_model = CommentsModel()
108 107 for pr in pull_requests:
109 108 comments_count = comments_model.get_all_comments(
110 109 self.db_repo.repo_id, pull_request=pr,
111 110 include_drafts=False, count_only=True)
112 111
112 review_statuses = pr.reviewers_statuses(user=self._rhodecode_db_user)
113 my_review_status = ChangesetStatus.STATUS_NOT_REVIEWED
114 if review_statuses and review_statuses[4]:
115 _review_obj, _user, _reasons, _mandatory, statuses = review_statuses
116 my_review_status = statuses[0][1].status
117
113 118 data.append({
114 119 'name': _render('pullrequest_name',
115 120 pr.pull_request_id, pr.pull_request_state,
116 121 pr.work_in_progress, pr.target_repo.repo_name,
117 122 short=True),
118 123 'name_raw': pr.pull_request_id,
119 124 'status': _render('pullrequest_status',
120 125 pr.calculated_review_status()),
126 'my_status': _render('pullrequest_status',
127 my_review_status),
121 128 'title': _render('pullrequest_title', pr.title, pr.description),
122 129 'description': h.escape(pr.description),
123 130 'updated_on': _render('pullrequest_updated_on',
124 131 h.datetime_to_time(pr.updated_on),
125 132 pr.versions_count),
126 133 'updated_on_raw': h.datetime_to_time(pr.updated_on),
127 134 'created_on': _render('pullrequest_updated_on',
128 135 h.datetime_to_time(pr.created_on)),
129 136 'created_on_raw': h.datetime_to_time(pr.created_on),
130 137 'state': pr.pull_request_state,
131 138 'author': _render('pullrequest_author',
132 139 pr.author.full_contact, ),
133 140 'author_raw': pr.author.full_name,
134 141 'comments': _render('pullrequest_comments', comments_count),
135 142 'comments_raw': comments_count,
136 143 'closed': pr.is_closed(),
137 144 })
138 145
139 146 data = ({
140 147 'draw': draw,
141 148 'data': data,
142 149 'recordsTotal': pull_requests_total_count,
143 150 'recordsFiltered': pull_requests_total_count,
144 151 })
145 152 return data
146 153
147 154 @LoginRequired()
148 155 @HasRepoPermissionAnyDecorator(
149 156 'repository.read', 'repository.write', 'repository.admin')
150 157 def pull_request_list(self):
151 158 c = self.load_default_context()
152 159
153 160 req_get = self.request.GET
154 161 c.source = str2bool(req_get.get('source'))
155 162 c.closed = str2bool(req_get.get('closed'))
156 163 c.my = str2bool(req_get.get('my'))
157 164 c.awaiting_review = str2bool(req_get.get('awaiting_review'))
158 165 c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
159 166
160 167 c.active = 'open'
161 168 if c.my:
162 169 c.active = 'my'
163 170 if c.closed:
164 171 c.active = 'closed'
165 172 if c.awaiting_review and not c.source:
166 173 c.active = 'awaiting'
167 174 if c.source and not c.awaiting_review:
168 175 c.active = 'source'
169 176 if c.awaiting_my_review:
170 177 c.active = 'awaiting_my'
171 178
172 179 return self._get_template_context(c)
173 180
174 181 @LoginRequired()
175 182 @HasRepoPermissionAnyDecorator(
176 183 'repository.read', 'repository.write', 'repository.admin')
177 184 def pull_request_list_data(self):
178 185 self.load_default_context()
179 186
180 187 # additional filters
181 188 req_get = self.request.GET
182 189 source = str2bool(req_get.get('source'))
183 190 closed = str2bool(req_get.get('closed'))
184 191 my = str2bool(req_get.get('my'))
185 192 awaiting_review = str2bool(req_get.get('awaiting_review'))
186 193 awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
187 194
188 195 filter_type = 'awaiting_review' if awaiting_review \
189 196 else 'awaiting_my_review' if awaiting_my_review \
190 197 else None
191 198
192 199 opened_by = None
193 200 if my:
194 201 opened_by = [self._rhodecode_user.user_id]
195 202
196 203 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
197 204 if closed:
198 205 statuses = [PullRequest.STATUS_CLOSED]
199 206
200 207 data = self._get_pull_requests_list(
201 208 repo_name=self.db_repo_name, source=source,
202 209 filter_type=filter_type, opened_by=opened_by, statuses=statuses)
203 210
204 211 return data
205 212
206 213 def _is_diff_cache_enabled(self, target_repo):
207 214 caching_enabled = self._get_general_setting(
208 215 target_repo, 'rhodecode_diff_cache')
209 216 log.debug('Diff caching enabled: %s', caching_enabled)
210 217 return caching_enabled
211 218
212 219 def _get_diffset(self, source_repo_name, source_repo,
213 220 ancestor_commit,
214 221 source_ref_id, target_ref_id,
215 222 target_commit, source_commit, diff_limit, file_limit,
216 223 fulldiff, hide_whitespace_changes, diff_context, use_ancestor=True):
217 224
218 225 target_commit_final = target_commit
219 226 source_commit_final = source_commit
220 227
221 228 if use_ancestor:
222 229 # we might want to not use it for versions
223 230 target_ref_id = ancestor_commit.raw_id
224 231 target_commit_final = ancestor_commit
225 232
226 233 vcs_diff = PullRequestModel().get_diff(
227 234 source_repo, source_ref_id, target_ref_id,
228 235 hide_whitespace_changes, diff_context)
229 236
230 237 diff_processor = diffs.DiffProcessor(
231 238 vcs_diff, format='newdiff', diff_limit=diff_limit,
232 239 file_limit=file_limit, show_full_diff=fulldiff)
233 240
234 241 _parsed = diff_processor.prepare()
235 242
236 243 diffset = codeblocks.DiffSet(
237 244 repo_name=self.db_repo_name,
238 245 source_repo_name=source_repo_name,
239 246 source_node_getter=codeblocks.diffset_node_getter(target_commit_final),
240 247 target_node_getter=codeblocks.diffset_node_getter(source_commit_final),
241 248 )
242 249 diffset = self.path_filter.render_patchset_filtered(
243 250 diffset, _parsed, target_ref_id, source_ref_id)
244 251
245 252 return diffset
246 253
247 254 def _get_range_diffset(self, source_scm, source_repo,
248 255 commit1, commit2, diff_limit, file_limit,
249 256 fulldiff, hide_whitespace_changes, diff_context):
250 257 vcs_diff = source_scm.get_diff(
251 258 commit1, commit2,
252 259 ignore_whitespace=hide_whitespace_changes,
253 260 context=diff_context)
254 261
255 262 diff_processor = diffs.DiffProcessor(
256 263 vcs_diff, format='newdiff', diff_limit=diff_limit,
257 264 file_limit=file_limit, show_full_diff=fulldiff)
258 265
259 266 _parsed = diff_processor.prepare()
260 267
261 268 diffset = codeblocks.DiffSet(
262 269 repo_name=source_repo.repo_name,
263 270 source_node_getter=codeblocks.diffset_node_getter(commit1),
264 271 target_node_getter=codeblocks.diffset_node_getter(commit2))
265 272
266 273 diffset = self.path_filter.render_patchset_filtered(
267 274 diffset, _parsed, commit1.raw_id, commit2.raw_id)
268 275
269 276 return diffset
270 277
271 278 def register_comments_vars(self, c, pull_request, versions, include_drafts=True):
272 279 comments_model = CommentsModel()
273 280
274 281 # GENERAL COMMENTS with versions #
275 282 q = comments_model._all_general_comments_of_pull_request(pull_request)
276 283 q = q.order_by(ChangesetComment.comment_id.asc())
277 284 if not include_drafts:
278 285 q = q.filter(ChangesetComment.draft == false())
279 286 general_comments = q
280 287
281 288 # pick comments we want to render at current version
282 289 c.comment_versions = comments_model.aggregate_comments(
283 290 general_comments, versions, c.at_version_num)
284 291
285 292 # INLINE COMMENTS with versions #
286 293 q = comments_model._all_inline_comments_of_pull_request(pull_request)
287 294 q = q.order_by(ChangesetComment.comment_id.asc())
288 295 if not include_drafts:
289 296 q = q.filter(ChangesetComment.draft == false())
290 297 inline_comments = q
291 298
292 299 c.inline_versions = comments_model.aggregate_comments(
293 300 inline_comments, versions, c.at_version_num, inline=True)
294 301
295 302 # Comments inline+general
296 303 if c.at_version:
297 304 c.inline_comments_flat = c.inline_versions[c.at_version_num]['display']
298 305 c.comments = c.comment_versions[c.at_version_num]['display']
299 306 else:
300 307 c.inline_comments_flat = c.inline_versions[c.at_version_num]['until']
301 308 c.comments = c.comment_versions[c.at_version_num]['until']
302 309
303 310 return general_comments, inline_comments
304 311
305 312 @LoginRequired()
306 313 @HasRepoPermissionAnyDecorator(
307 314 'repository.read', 'repository.write', 'repository.admin')
308 315 def pull_request_show(self):
309 316 _ = self.request.translate
310 317 c = self.load_default_context()
311 318
312 319 pull_request = PullRequest.get_or_404(
313 320 self.request.matchdict['pull_request_id'])
314 321 pull_request_id = pull_request.pull_request_id
315 322
316 323 c.state_progressing = pull_request.is_state_changing()
317 324 c.pr_broadcast_channel = channelstream.pr_channel(pull_request)
318 325
319 326 _new_state = {
320 327 'created': PullRequest.STATE_CREATED,
321 328 }.get(self.request.GET.get('force_state'))
322 329
323 330 if c.is_super_admin and _new_state:
324 331 with pull_request.set_state(PullRequest.STATE_UPDATING, final_state=_new_state):
325 332 h.flash(
326 333 _('Pull Request state was force changed to `{}`').format(_new_state),
327 334 category='success')
328 335 Session().commit()
329 336
330 337 raise HTTPFound(h.route_path(
331 338 'pullrequest_show', repo_name=self.db_repo_name,
332 339 pull_request_id=pull_request_id))
333 340
334 341 version = self.request.GET.get('version')
335 342 from_version = self.request.GET.get('from_version') or version
336 343 merge_checks = self.request.GET.get('merge_checks')
337 344 c.fulldiff = str2bool(self.request.GET.get('fulldiff'))
338 345 force_refresh = str2bool(self.request.GET.get('force_refresh'))
339 346 c.range_diff_on = self.request.GET.get('range-diff') == "1"
340 347
341 348 # fetch global flags of ignore ws or context lines
342 349 diff_context = diffs.get_diff_context(self.request)
343 350 hide_whitespace_changes = diffs.get_diff_whitespace_flag(self.request)
344 351
345 352 (pull_request_latest,
346 353 pull_request_at_ver,
347 354 pull_request_display_obj,
348 355 at_version) = PullRequestModel().get_pr_version(
349 356 pull_request_id, version=version)
350 357
351 358 pr_closed = pull_request_latest.is_closed()
352 359
353 360 if pr_closed and (version or from_version):
354 361 # not allow to browse versions for closed PR
355 362 raise HTTPFound(h.route_path(
356 363 'pullrequest_show', repo_name=self.db_repo_name,
357 364 pull_request_id=pull_request_id))
358 365
359 366 versions = pull_request_display_obj.versions()
360 367
361 368 c.commit_versions = PullRequestModel().pr_commits_versions(versions)
362 369
363 370 # used to store per-commit range diffs
364 371 c.changes = collections.OrderedDict()
365 372
366 373 c.at_version = at_version
367 374 c.at_version_num = (at_version
368 375 if at_version and at_version != PullRequest.LATEST_VER
369 376 else None)
370 377
371 378 c.at_version_index = ChangesetComment.get_index_from_version(
372 379 c.at_version_num, versions)
373 380
374 381 (prev_pull_request_latest,
375 382 prev_pull_request_at_ver,
376 383 prev_pull_request_display_obj,
377 384 prev_at_version) = PullRequestModel().get_pr_version(
378 385 pull_request_id, version=from_version)
379 386
380 387 c.from_version = prev_at_version
381 388 c.from_version_num = (prev_at_version
382 389 if prev_at_version and prev_at_version != PullRequest.LATEST_VER
383 390 else None)
384 391 c.from_version_index = ChangesetComment.get_index_from_version(
385 392 c.from_version_num, versions)
386 393
387 394 # define if we're in COMPARE mode or VIEW at version mode
388 395 compare = at_version != prev_at_version
389 396
390 397 # pull_requests repo_name we opened it against
391 398 # ie. target_repo must match
392 399 if self.db_repo_name != pull_request_at_ver.target_repo.repo_name:
393 400 log.warning('Mismatch between the current repo: %s, and target %s',
394 401 self.db_repo_name, pull_request_at_ver.target_repo.repo_name)
395 402 raise HTTPNotFound()
396 403
397 404 c.shadow_clone_url = PullRequestModel().get_shadow_clone_url(pull_request_at_ver)
398 405
399 406 c.pull_request = pull_request_display_obj
400 407 c.renderer = pull_request_at_ver.description_renderer or c.renderer
401 408 c.pull_request_latest = pull_request_latest
402 409
403 410 # inject latest version
404 411 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
405 412 c.versions = versions + [latest_ver]
406 413
407 414 if compare or (at_version and not at_version == PullRequest.LATEST_VER):
408 415 c.allowed_to_change_status = False
409 416 c.allowed_to_update = False
410 417 c.allowed_to_merge = False
411 418 c.allowed_to_delete = False
412 419 c.allowed_to_comment = False
413 420 c.allowed_to_close = False
414 421 else:
415 422 can_change_status = PullRequestModel().check_user_change_status(
416 423 pull_request_at_ver, self._rhodecode_user)
417 424 c.allowed_to_change_status = can_change_status and not pr_closed
418 425
419 426 c.allowed_to_update = PullRequestModel().check_user_update(
420 427 pull_request_latest, self._rhodecode_user) and not pr_closed
421 428 c.allowed_to_merge = PullRequestModel().check_user_merge(
422 429 pull_request_latest, self._rhodecode_user) and not pr_closed
423 430 c.allowed_to_delete = PullRequestModel().check_user_delete(
424 431 pull_request_latest, self._rhodecode_user) and not pr_closed
425 432 c.allowed_to_comment = not pr_closed
426 433 c.allowed_to_close = c.allowed_to_merge and not pr_closed
427 434
428 435 c.forbid_adding_reviewers = False
429 436
430 437 if pull_request_latest.reviewer_data and \
431 438 'rules' in pull_request_latest.reviewer_data:
432 439 rules = pull_request_latest.reviewer_data['rules'] or {}
433 440 try:
434 441 c.forbid_adding_reviewers = rules.get('forbid_adding_reviewers')
435 442 except Exception:
436 443 pass
437 444
438 445 # check merge capabilities
439 446 _merge_check = MergeCheck.validate(
440 447 pull_request_latest, auth_user=self._rhodecode_user,
441 448 translator=self.request.translate,
442 449 force_shadow_repo_refresh=force_refresh)
443 450
444 451 c.pr_merge_errors = _merge_check.error_details
445 452 c.pr_merge_possible = not _merge_check.failed
446 453 c.pr_merge_message = _merge_check.merge_msg
447 454 c.pr_merge_source_commit = _merge_check.source_commit
448 455 c.pr_merge_target_commit = _merge_check.target_commit
449 456
450 457 c.pr_merge_info = MergeCheck.get_merge_conditions(
451 458 pull_request_latest, translator=self.request.translate)
452 459
453 460 c.pull_request_review_status = _merge_check.review_status
454 461 if merge_checks:
455 462 self.request.override_renderer = \
456 463 'rhodecode:templates/pullrequests/pullrequest_merge_checks.mako'
457 464 return self._get_template_context(c)
458 465
459 466 c.reviewers_count = pull_request.reviewers_count
460 467 c.observers_count = pull_request.observers_count
461 468
462 469 # reviewers and statuses
463 470 c.pull_request_default_reviewers_data_json = json.dumps(pull_request.reviewer_data)
464 471 c.pull_request_set_reviewers_data_json = collections.OrderedDict({'reviewers': []})
465 472 c.pull_request_set_observers_data_json = collections.OrderedDict({'observers': []})
466 473
467 474 for review_obj, member, reasons, mandatory, status in pull_request_at_ver.reviewers_statuses():
468 475 member_reviewer = h.reviewer_as_json(
469 476 member, reasons=reasons, mandatory=mandatory,
470 477 role=review_obj.role,
471 478 user_group=review_obj.rule_user_group_data()
472 479 )
473 480
474 481 current_review_status = status[0][1].status if status else ChangesetStatus.STATUS_NOT_REVIEWED
475 482 member_reviewer['review_status'] = current_review_status
476 483 member_reviewer['review_status_label'] = h.commit_status_lbl(current_review_status)
477 484 member_reviewer['allowed_to_update'] = c.allowed_to_update
478 485 c.pull_request_set_reviewers_data_json['reviewers'].append(member_reviewer)
479 486
480 487 c.pull_request_set_reviewers_data_json = json.dumps(c.pull_request_set_reviewers_data_json)
481 488
482 489 for observer_obj, member in pull_request_at_ver.observers():
483 490 member_observer = h.reviewer_as_json(
484 491 member, reasons=[], mandatory=False,
485 492 role=observer_obj.role,
486 493 user_group=observer_obj.rule_user_group_data()
487 494 )
488 495 member_observer['allowed_to_update'] = c.allowed_to_update
489 496 c.pull_request_set_observers_data_json['observers'].append(member_observer)
490 497
491 498 c.pull_request_set_observers_data_json = json.dumps(c.pull_request_set_observers_data_json)
492 499
493 500 general_comments, inline_comments = \
494 501 self.register_comments_vars(c, pull_request_latest, versions)
495 502
496 503 # TODOs
497 504 c.unresolved_comments = CommentsModel() \
498 505 .get_pull_request_unresolved_todos(pull_request_latest)
499 506 c.resolved_comments = CommentsModel() \
500 507 .get_pull_request_resolved_todos(pull_request_latest)
501 508
502 509 # Drafts
503 510 c.draft_comments = CommentsModel().get_pull_request_drafts(
504 511 self._rhodecode_db_user.user_id,
505 512 pull_request_latest)
506 513
507 514 # if we use version, then do not show later comments
508 515 # than current version
509 516 display_inline_comments = collections.defaultdict(
510 517 lambda: collections.defaultdict(list))
511 518 for co in inline_comments:
512 519 if c.at_version_num:
513 520 # pick comments that are at least UPTO given version, so we
514 521 # don't render comments for higher version
515 522 should_render = co.pull_request_version_id and \
516 523 co.pull_request_version_id <= c.at_version_num
517 524 else:
518 525 # showing all, for 'latest'
519 526 should_render = True
520 527
521 528 if should_render:
522 529 display_inline_comments[co.f_path][co.line_no].append(co)
523 530
524 531 # load diff data into template context, if we use compare mode then
525 532 # diff is calculated based on changes between versions of PR
526 533
527 534 source_repo = pull_request_at_ver.source_repo
528 535 source_ref_id = pull_request_at_ver.source_ref_parts.commit_id
529 536
530 537 target_repo = pull_request_at_ver.target_repo
531 538 target_ref_id = pull_request_at_ver.target_ref_parts.commit_id
532 539
533 540 if compare:
534 541 # in compare switch the diff base to latest commit from prev version
535 542 target_ref_id = prev_pull_request_display_obj.revisions[0]
536 543
537 544 # despite opening commits for bookmarks/branches/tags, we always
538 545 # convert this to rev to prevent changes after bookmark or branch change
539 546 c.source_ref_type = 'rev'
540 547 c.source_ref = source_ref_id
541 548
542 549 c.target_ref_type = 'rev'
543 550 c.target_ref = target_ref_id
544 551
545 552 c.source_repo = source_repo
546 553 c.target_repo = target_repo
547 554
548 555 c.commit_ranges = []
549 556 source_commit = EmptyCommit()
550 557 target_commit = EmptyCommit()
551 558 c.missing_requirements = False
552 559
553 560 source_scm = source_repo.scm_instance()
554 561 target_scm = target_repo.scm_instance()
555 562
556 563 shadow_scm = None
557 564 try:
558 565 shadow_scm = pull_request_latest.get_shadow_repo()
559 566 except Exception:
560 567 log.debug('Failed to get shadow repo', exc_info=True)
561 568 # try first the existing source_repo, and then shadow
562 569 # repo if we can obtain one
563 570 commits_source_repo = source_scm
564 571 if shadow_scm:
565 572 commits_source_repo = shadow_scm
566 573
567 574 c.commits_source_repo = commits_source_repo
568 575 c.ancestor = None # set it to None, to hide it from PR view
569 576
570 577 # empty version means latest, so we keep this to prevent
571 578 # double caching
572 579 version_normalized = version or PullRequest.LATEST_VER
573 580 from_version_normalized = from_version or PullRequest.LATEST_VER
574 581
575 582 cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path(target_repo)
576 583 cache_file_path = diff_cache_exist(
577 584 cache_path, 'pull_request', pull_request_id, version_normalized,
578 585 from_version_normalized, source_ref_id, target_ref_id,
579 586 hide_whitespace_changes, diff_context, c.fulldiff)
580 587
581 588 caching_enabled = self._is_diff_cache_enabled(c.target_repo)
582 589 force_recache = self.get_recache_flag()
583 590
584 591 cached_diff = None
585 592 if caching_enabled:
586 593 cached_diff = load_cached_diff(cache_file_path)
587 594
588 595 has_proper_commit_cache = (
589 596 cached_diff and cached_diff.get('commits')
590 597 and len(cached_diff.get('commits', [])) == 5
591 598 and cached_diff.get('commits')[0]
592 599 and cached_diff.get('commits')[3])
593 600
594 601 if not force_recache and not c.range_diff_on and has_proper_commit_cache:
595 602 diff_commit_cache = \
596 603 (ancestor_commit, commit_cache, missing_requirements,
597 604 source_commit, target_commit) = cached_diff['commits']
598 605 else:
599 606 # NOTE(marcink): we reach potentially unreachable errors when a PR has
600 607 # merge errors resulting in potentially hidden commits in the shadow repo.
601 608 maybe_unreachable = _merge_check.MERGE_CHECK in _merge_check.error_details \
602 609 and _merge_check.merge_response
603 610 maybe_unreachable = maybe_unreachable \
604 611 and _merge_check.merge_response.metadata.get('unresolved_files')
605 612 log.debug("Using unreachable commits due to MERGE_CHECK in merge simulation")
606 613 diff_commit_cache = \
607 614 (ancestor_commit, commit_cache, missing_requirements,
608 615 source_commit, target_commit) = self.get_commits(
609 616 commits_source_repo,
610 617 pull_request_at_ver,
611 618 source_commit,
612 619 source_ref_id,
613 620 source_scm,
614 621 target_commit,
615 622 target_ref_id,
616 623 target_scm,
617 624 maybe_unreachable=maybe_unreachable)
618 625
619 626 # register our commit range
620 627 for comm in commit_cache.values():
621 628 c.commit_ranges.append(comm)
622 629
623 630 c.missing_requirements = missing_requirements
624 631 c.ancestor_commit = ancestor_commit
625 632 c.statuses = source_repo.statuses(
626 633 [x.raw_id for x in c.commit_ranges])
627 634
628 635 # auto collapse if we have more than limit
629 636 collapse_limit = diffs.DiffProcessor._collapse_commits_over
630 637 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
631 638 c.compare_mode = compare
632 639
633 640 # diff_limit is the old behavior, will cut off the whole diff
634 641 # if the limit is applied otherwise will just hide the
635 642 # big files from the front-end
636 643 diff_limit = c.visual.cut_off_limit_diff
637 644 file_limit = c.visual.cut_off_limit_file
638 645
639 646 c.missing_commits = False
640 647 if (c.missing_requirements
641 648 or isinstance(source_commit, EmptyCommit)
642 649 or source_commit == target_commit):
643 650
644 651 c.missing_commits = True
645 652 else:
646 653 c.inline_comments = display_inline_comments
647 654
648 655 use_ancestor = True
649 656 if from_version_normalized != version_normalized:
650 657 use_ancestor = False
651 658
652 659 has_proper_diff_cache = cached_diff and cached_diff.get('commits')
653 660 if not force_recache and has_proper_diff_cache:
654 661 c.diffset = cached_diff['diff']
655 662 else:
656 663 try:
657 664 c.diffset = self._get_diffset(
658 665 c.source_repo.repo_name, commits_source_repo,
659 666 c.ancestor_commit,
660 667 source_ref_id, target_ref_id,
661 668 target_commit, source_commit,
662 669 diff_limit, file_limit, c.fulldiff,
663 670 hide_whitespace_changes, diff_context,
664 671 use_ancestor=use_ancestor
665 672 )
666 673
667 674 # save cached diff
668 675 if caching_enabled:
669 676 cache_diff(cache_file_path, c.diffset, diff_commit_cache)
670 677 except CommitDoesNotExistError:
671 678 log.exception('Failed to generate diffset')
672 679 c.missing_commits = True
673 680
674 681 if not c.missing_commits:
675 682
676 683 c.limited_diff = c.diffset.limited_diff
677 684
678 685 # calculate removed files that are bound to comments
679 686 comment_deleted_files = [
680 687 fname for fname in display_inline_comments
681 688 if fname not in c.diffset.file_stats]
682 689
683 690 c.deleted_files_comments = collections.defaultdict(dict)
684 691 for fname, per_line_comments in display_inline_comments.items():
685 692 if fname in comment_deleted_files:
686 693 c.deleted_files_comments[fname]['stats'] = 0
687 694 c.deleted_files_comments[fname]['comments'] = list()
688 695 for lno, comments in per_line_comments.items():
689 696 c.deleted_files_comments[fname]['comments'].extend(comments)
690 697
691 698 # maybe calculate the range diff
692 699 if c.range_diff_on:
693 700 # TODO(marcink): set whitespace/context
694 701 context_lcl = 3
695 702 ign_whitespace_lcl = False
696 703
697 704 for commit in c.commit_ranges:
698 705 commit2 = commit
699 706 commit1 = commit.first_parent
700 707
701 708 range_diff_cache_file_path = diff_cache_exist(
702 709 cache_path, 'diff', commit.raw_id,
703 710 ign_whitespace_lcl, context_lcl, c.fulldiff)
704 711
705 712 cached_diff = None
706 713 if caching_enabled:
707 714 cached_diff = load_cached_diff(range_diff_cache_file_path)
708 715
709 716 has_proper_diff_cache = cached_diff and cached_diff.get('diff')
710 717 if not force_recache and has_proper_diff_cache:
711 718 diffset = cached_diff['diff']
712 719 else:
713 720 diffset = self._get_range_diffset(
714 721 commits_source_repo, source_repo,
715 722 commit1, commit2, diff_limit, file_limit,
716 723 c.fulldiff, ign_whitespace_lcl, context_lcl
717 724 )
718 725
719 726 # save cached diff
720 727 if caching_enabled:
721 728 cache_diff(range_diff_cache_file_path, diffset, None)
722 729
723 730 c.changes[commit.raw_id] = diffset
724 731
725 732 # this is a hack to properly display links, when creating PR, the
726 733 # compare view and others uses different notation, and
727 734 # compare_commits.mako renders links based on the target_repo.
728 735 # We need to swap that here to generate it properly on the html side
729 736 c.target_repo = c.source_repo
730 737
731 738 c.commit_statuses = ChangesetStatus.STATUSES
732 739
733 740 c.show_version_changes = not pr_closed
734 741 if c.show_version_changes:
735 742 cur_obj = pull_request_at_ver
736 743 prev_obj = prev_pull_request_at_ver
737 744
738 745 old_commit_ids = prev_obj.revisions
739 746 new_commit_ids = cur_obj.revisions
740 747 commit_changes = PullRequestModel()._calculate_commit_id_changes(
741 748 old_commit_ids, new_commit_ids)
742 749 c.commit_changes_summary = commit_changes
743 750
744 751 # calculate the diff for commits between versions
745 752 c.commit_changes = []
746 753
747 754 def mark(cs, fw):
748 755 return list(h.itertools.izip_longest([], cs, fillvalue=fw))
749 756
750 757 for c_type, raw_id in mark(commit_changes.added, 'a') \
751 758 + mark(commit_changes.removed, 'r') \
752 759 + mark(commit_changes.common, 'c'):
753 760
754 761 if raw_id in commit_cache:
755 762 commit = commit_cache[raw_id]
756 763 else:
757 764 try:
758 765 commit = commits_source_repo.get_commit(raw_id)
759 766 except CommitDoesNotExistError:
760 767 # in case we fail extracting still use "dummy" commit
761 768 # for display in commit diff
762 769 commit = h.AttributeDict(
763 770 {'raw_id': raw_id,
764 771 'message': 'EMPTY or MISSING COMMIT'})
765 772 c.commit_changes.append([c_type, commit])
766 773
767 774 # current user review statuses for each version
768 775 c.review_versions = {}
769 776 is_reviewer = PullRequestModel().is_user_reviewer(
770 777 pull_request, self._rhodecode_user)
771 778 if is_reviewer:
772 779 for co in general_comments:
773 780 if co.author.user_id == self._rhodecode_user.user_id:
774 781 status = co.status_change
775 782 if status:
776 783 _ver_pr = status[0].comment.pull_request_version_id
777 784 c.review_versions[_ver_pr] = status[0]
778 785
779 786 return self._get_template_context(c)
780 787
781 788 def get_commits(
782 789 self, commits_source_repo, pull_request_at_ver, source_commit,
783 790 source_ref_id, source_scm, target_commit, target_ref_id, target_scm,
784 791 maybe_unreachable=False):
785 792
786 793 commit_cache = collections.OrderedDict()
787 794 missing_requirements = False
788 795
789 796 try:
790 797 pre_load = ["author", "date", "message", "branch", "parents"]
791 798
792 799 pull_request_commits = pull_request_at_ver.revisions
793 800 log.debug('Loading %s commits from %s',
794 801 len(pull_request_commits), commits_source_repo)
795 802
796 803 for rev in pull_request_commits:
797 804 comm = commits_source_repo.get_commit(commit_id=rev, pre_load=pre_load,
798 805 maybe_unreachable=maybe_unreachable)
799 806 commit_cache[comm.raw_id] = comm
800 807
801 808 # Order here matters, we first need to get target, and then
802 809 # the source
803 810 target_commit = commits_source_repo.get_commit(
804 811 commit_id=safe_str(target_ref_id))
805 812
806 813 source_commit = commits_source_repo.get_commit(
807 814 commit_id=safe_str(source_ref_id), maybe_unreachable=True)
808 815 except CommitDoesNotExistError:
809 816 log.warning('Failed to get commit from `{}` repo'.format(
810 817 commits_source_repo), exc_info=True)
811 818 except RepositoryRequirementError:
812 819 log.warning('Failed to get all required data from repo', exc_info=True)
813 820 missing_requirements = True
814 821
815 822 pr_ancestor_id = pull_request_at_ver.common_ancestor_id
816 823
817 824 try:
818 825 ancestor_commit = source_scm.get_commit(pr_ancestor_id)
819 826 except Exception:
820 827 ancestor_commit = None
821 828
822 829 return ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit
823 830
824 831 def assure_not_empty_repo(self):
825 832 _ = self.request.translate
826 833
827 834 try:
828 835 self.db_repo.scm_instance().get_commit()
829 836 except EmptyRepositoryError:
830 837 h.flash(h.literal(_('There are no commits yet')),
831 838 category='warning')
832 839 raise HTTPFound(
833 840 h.route_path('repo_summary', repo_name=self.db_repo.repo_name))
834 841
835 842 @LoginRequired()
836 843 @NotAnonymous()
837 844 @HasRepoPermissionAnyDecorator(
838 845 'repository.read', 'repository.write', 'repository.admin')
839 846 def pull_request_new(self):
840 847 _ = self.request.translate
841 848 c = self.load_default_context()
842 849
843 850 self.assure_not_empty_repo()
844 851 source_repo = self.db_repo
845 852
846 853 commit_id = self.request.GET.get('commit')
847 854 branch_ref = self.request.GET.get('branch')
848 855 bookmark_ref = self.request.GET.get('bookmark')
849 856
850 857 try:
851 858 source_repo_data = PullRequestModel().generate_repo_data(
852 859 source_repo, commit_id=commit_id,
853 860 branch=branch_ref, bookmark=bookmark_ref,
854 861 translator=self.request.translate)
855 862 except CommitDoesNotExistError as e:
856 863 log.exception(e)
857 864 h.flash(_('Commit does not exist'), 'error')
858 865 raise HTTPFound(
859 866 h.route_path('pullrequest_new', repo_name=source_repo.repo_name))
860 867
861 868 default_target_repo = source_repo
862 869
863 870 if source_repo.parent and c.has_origin_repo_read_perm:
864 871 parent_vcs_obj = source_repo.parent.scm_instance()
865 872 if parent_vcs_obj and not parent_vcs_obj.is_empty():
866 873 # change default if we have a parent repo
867 874 default_target_repo = source_repo.parent
868 875
869 876 target_repo_data = PullRequestModel().generate_repo_data(
870 877 default_target_repo, translator=self.request.translate)
871 878
872 879 selected_source_ref = source_repo_data['refs']['selected_ref']
873 880 title_source_ref = ''
874 881 if selected_source_ref:
875 882 title_source_ref = selected_source_ref.split(':', 2)[1]
876 883 c.default_title = PullRequestModel().generate_pullrequest_title(
877 884 source=source_repo.repo_name,
878 885 source_ref=title_source_ref,
879 886 target=default_target_repo.repo_name
880 887 )
881 888
882 889 c.default_repo_data = {
883 890 'source_repo_name': source_repo.repo_name,
884 891 'source_refs_json': json.dumps(source_repo_data),
885 892 'target_repo_name': default_target_repo.repo_name,
886 893 'target_refs_json': json.dumps(target_repo_data),
887 894 }
888 895 c.default_source_ref = selected_source_ref
889 896
890 897 return self._get_template_context(c)
891 898
892 899 @LoginRequired()
893 900 @NotAnonymous()
894 901 @HasRepoPermissionAnyDecorator(
895 902 'repository.read', 'repository.write', 'repository.admin')
896 903 def pull_request_repo_refs(self):
897 904 self.load_default_context()
898 905 target_repo_name = self.request.matchdict['target_repo_name']
899 906 repo = Repository.get_by_repo_name(target_repo_name)
900 907 if not repo:
901 908 raise HTTPNotFound()
902 909
903 910 target_perm = HasRepoPermissionAny(
904 911 'repository.read', 'repository.write', 'repository.admin')(
905 912 target_repo_name)
906 913 if not target_perm:
907 914 raise HTTPNotFound()
908 915
909 916 return PullRequestModel().generate_repo_data(
910 917 repo, translator=self.request.translate)
911 918
912 919 @LoginRequired()
913 920 @NotAnonymous()
914 921 @HasRepoPermissionAnyDecorator(
915 922 'repository.read', 'repository.write', 'repository.admin')
916 923 def pullrequest_repo_targets(self):
917 924 _ = self.request.translate
918 925 filter_query = self.request.GET.get('query')
919 926
920 927 # get the parents
921 928 parent_target_repos = []
922 929 if self.db_repo.parent:
923 930 parents_query = Repository.query() \
924 931 .order_by(func.length(Repository.repo_name)) \
925 932 .filter(Repository.fork_id == self.db_repo.parent.repo_id)
926 933
927 934 if filter_query:
928 935 ilike_expression = u'%{}%'.format(safe_unicode(filter_query))
929 936 parents_query = parents_query.filter(
930 937 Repository.repo_name.ilike(ilike_expression))
931 938 parents = parents_query.limit(20).all()
932 939
933 940 for parent in parents:
934 941 parent_vcs_obj = parent.scm_instance()
935 942 if parent_vcs_obj and not parent_vcs_obj.is_empty():
936 943 parent_target_repos.append(parent)
937 944
938 945 # get other forks, and repo itself
939 946 query = Repository.query() \
940 947 .order_by(func.length(Repository.repo_name)) \
941 948 .filter(
942 949 or_(Repository.repo_id == self.db_repo.repo_id, # repo itself
943 950 Repository.fork_id == self.db_repo.repo_id) # forks of this repo
944 951 ) \
945 952 .filter(~Repository.repo_id.in_([x.repo_id for x in parent_target_repos]))
946 953
947 954 if filter_query:
948 955 ilike_expression = u'%{}%'.format(safe_unicode(filter_query))
949 956 query = query.filter(Repository.repo_name.ilike(ilike_expression))
950 957
951 958 limit = max(20 - len(parent_target_repos), 5) # not less then 5
952 959 target_repos = query.limit(limit).all()
953 960
954 961 all_target_repos = target_repos + parent_target_repos
955 962
956 963 repos = []
957 964 # This checks permissions to the repositories
958 965 for obj in ScmModel().get_repos(all_target_repos):
959 966 repos.append({
960 967 'id': obj['name'],
961 968 'text': obj['name'],
962 969 'type': 'repo',
963 970 'repo_id': obj['dbrepo']['repo_id'],
964 971 'repo_type': obj['dbrepo']['repo_type'],
965 972 'private': obj['dbrepo']['private'],
966 973
967 974 })
968 975
969 976 data = {
970 977 'more': False,
971 978 'results': [{
972 979 'text': _('Repositories'),
973 980 'children': repos
974 981 }] if repos else []
975 982 }
976 983 return data
977 984
978 985 @classmethod
979 986 def get_comment_ids(cls, post_data):
980 987 return filter(lambda e: e > 0, map(safe_int, aslist(post_data.get('comments'), ',')))
981 988
982 989 @LoginRequired()
983 990 @NotAnonymous()
984 991 @HasRepoPermissionAnyDecorator(
985 992 'repository.read', 'repository.write', 'repository.admin')
986 993 def pullrequest_comments(self):
987 994 self.load_default_context()
988 995
989 996 pull_request = PullRequest.get_or_404(
990 997 self.request.matchdict['pull_request_id'])
991 998 pull_request_id = pull_request.pull_request_id
992 999 version = self.request.GET.get('version')
993 1000
994 1001 _render = self.request.get_partial_renderer(
995 1002 'rhodecode:templates/base/sidebar.mako')
996 1003 c = _render.get_call_context()
997 1004
998 1005 (pull_request_latest,
999 1006 pull_request_at_ver,
1000 1007 pull_request_display_obj,
1001 1008 at_version) = PullRequestModel().get_pr_version(
1002 1009 pull_request_id, version=version)
1003 1010 versions = pull_request_display_obj.versions()
1004 1011 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
1005 1012 c.versions = versions + [latest_ver]
1006 1013
1007 1014 c.at_version = at_version
1008 1015 c.at_version_num = (at_version
1009 1016 if at_version and at_version != PullRequest.LATEST_VER
1010 1017 else None)
1011 1018
1012 1019 self.register_comments_vars(c, pull_request_latest, versions, include_drafts=False)
1013 1020 all_comments = c.inline_comments_flat + c.comments
1014 1021
1015 1022 existing_ids = self.get_comment_ids(self.request.POST)
1016 1023 return _render('comments_table', all_comments, len(all_comments),
1017 1024 existing_ids=existing_ids)
1018 1025
1019 1026 @LoginRequired()
1020 1027 @NotAnonymous()
1021 1028 @HasRepoPermissionAnyDecorator(
1022 1029 'repository.read', 'repository.write', 'repository.admin')
1023 1030 def pullrequest_todos(self):
1024 1031 self.load_default_context()
1025 1032
1026 1033 pull_request = PullRequest.get_or_404(
1027 1034 self.request.matchdict['pull_request_id'])
1028 1035 pull_request_id = pull_request.pull_request_id
1029 1036 version = self.request.GET.get('version')
1030 1037
1031 1038 _render = self.request.get_partial_renderer(
1032 1039 'rhodecode:templates/base/sidebar.mako')
1033 1040 c = _render.get_call_context()
1034 1041 (pull_request_latest,
1035 1042 pull_request_at_ver,
1036 1043 pull_request_display_obj,
1037 1044 at_version) = PullRequestModel().get_pr_version(
1038 1045 pull_request_id, version=version)
1039 1046 versions = pull_request_display_obj.versions()
1040 1047 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
1041 1048 c.versions = versions + [latest_ver]
1042 1049
1043 1050 c.at_version = at_version
1044 1051 c.at_version_num = (at_version
1045 1052 if at_version and at_version != PullRequest.LATEST_VER
1046 1053 else None)
1047 1054
1048 1055 c.unresolved_comments = CommentsModel() \
1049 1056 .get_pull_request_unresolved_todos(pull_request, include_drafts=False)
1050 1057 c.resolved_comments = CommentsModel() \
1051 1058 .get_pull_request_resolved_todos(pull_request, include_drafts=False)
1052 1059
1053 1060 all_comments = c.unresolved_comments + c.resolved_comments
1054 1061 existing_ids = self.get_comment_ids(self.request.POST)
1055 1062 return _render('comments_table', all_comments, len(c.unresolved_comments),
1056 1063 todo_comments=True, existing_ids=existing_ids)
1057 1064
1058 1065 @LoginRequired()
1059 1066 @NotAnonymous()
1060 1067 @HasRepoPermissionAnyDecorator(
1061 1068 'repository.read', 'repository.write', 'repository.admin')
1062 1069 def pullrequest_drafts(self):
1063 1070 self.load_default_context()
1064 1071
1065 1072 pull_request = PullRequest.get_or_404(
1066 1073 self.request.matchdict['pull_request_id'])
1067 1074 pull_request_id = pull_request.pull_request_id
1068 1075 version = self.request.GET.get('version')
1069 1076
1070 1077 _render = self.request.get_partial_renderer(
1071 1078 'rhodecode:templates/base/sidebar.mako')
1072 1079 c = _render.get_call_context()
1073 1080
1074 1081 (pull_request_latest,
1075 1082 pull_request_at_ver,
1076 1083 pull_request_display_obj,
1077 1084 at_version) = PullRequestModel().get_pr_version(
1078 1085 pull_request_id, version=version)
1079 1086 versions = pull_request_display_obj.versions()
1080 1087 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
1081 1088 c.versions = versions + [latest_ver]
1082 1089
1083 1090 c.at_version = at_version
1084 1091 c.at_version_num = (at_version
1085 1092 if at_version and at_version != PullRequest.LATEST_VER
1086 1093 else None)
1087 1094
1088 1095 c.draft_comments = CommentsModel() \
1089 1096 .get_pull_request_drafts(self._rhodecode_db_user.user_id, pull_request)
1090 1097
1091 1098 all_comments = c.draft_comments
1092 1099
1093 1100 existing_ids = self.get_comment_ids(self.request.POST)
1094 1101 return _render('comments_table', all_comments, len(all_comments),
1095 1102 existing_ids=existing_ids, draft_comments=True)
1096 1103
1097 1104 @LoginRequired()
1098 1105 @NotAnonymous()
1099 1106 @HasRepoPermissionAnyDecorator(
1100 1107 'repository.read', 'repository.write', 'repository.admin')
1101 1108 @CSRFRequired()
1102 1109 def pull_request_create(self):
1103 1110 _ = self.request.translate
1104 1111 self.assure_not_empty_repo()
1105 1112 self.load_default_context()
1106 1113
1107 1114 controls = peppercorn.parse(self.request.POST.items())
1108 1115
1109 1116 try:
1110 1117 form = PullRequestForm(
1111 1118 self.request.translate, self.db_repo.repo_id)()
1112 1119 _form = form.to_python(controls)
1113 1120 except formencode.Invalid as errors:
1114 1121 if errors.error_dict.get('revisions'):
1115 1122 msg = 'Revisions: %s' % errors.error_dict['revisions']
1116 1123 elif errors.error_dict.get('pullrequest_title'):
1117 1124 msg = errors.error_dict.get('pullrequest_title')
1118 1125 else:
1119 1126 msg = _('Error creating pull request: {}').format(errors)
1120 1127 log.exception(msg)
1121 1128 h.flash(msg, 'error')
1122 1129
1123 1130 # would rather just go back to form ...
1124 1131 raise HTTPFound(
1125 1132 h.route_path('pullrequest_new', repo_name=self.db_repo_name))
1126 1133
1127 1134 source_repo = _form['source_repo']
1128 1135 source_ref = _form['source_ref']
1129 1136 target_repo = _form['target_repo']
1130 1137 target_ref = _form['target_ref']
1131 1138 commit_ids = _form['revisions'][::-1]
1132 1139 common_ancestor_id = _form['common_ancestor']
1133 1140
1134 1141 # find the ancestor for this pr
1135 1142 source_db_repo = Repository.get_by_repo_name(_form['source_repo'])
1136 1143 target_db_repo = Repository.get_by_repo_name(_form['target_repo'])
1137 1144
1138 1145 if not (source_db_repo or target_db_repo):
1139 1146 h.flash(_('source_repo or target repo not found'), category='error')
1140 1147 raise HTTPFound(
1141 1148 h.route_path('pullrequest_new', repo_name=self.db_repo_name))
1142 1149
1143 1150 # re-check permissions again here
1144 1151 # source_repo we must have read permissions
1145 1152
1146 1153 source_perm = HasRepoPermissionAny(
1147 1154 'repository.read', 'repository.write', 'repository.admin')(
1148 1155 source_db_repo.repo_name)
1149 1156 if not source_perm:
1150 1157 msg = _('Not Enough permissions to source repo `{}`.'.format(
1151 1158 source_db_repo.repo_name))
1152 1159 h.flash(msg, category='error')
1153 1160 # copy the args back to redirect
1154 1161 org_query = self.request.GET.mixed()
1155 1162 raise HTTPFound(
1156 1163 h.route_path('pullrequest_new', repo_name=self.db_repo_name,
1157 1164 _query=org_query))
1158 1165
1159 1166 # target repo we must have read permissions, and also later on
1160 1167 # we want to check branch permissions here
1161 1168 target_perm = HasRepoPermissionAny(
1162 1169 'repository.read', 'repository.write', 'repository.admin')(
1163 1170 target_db_repo.repo_name)
1164 1171 if not target_perm:
1165 1172 msg = _('Not Enough permissions to target repo `{}`.'.format(
1166 1173 target_db_repo.repo_name))
1167 1174 h.flash(msg, category='error')
1168 1175 # copy the args back to redirect
1169 1176 org_query = self.request.GET.mixed()
1170 1177 raise HTTPFound(
1171 1178 h.route_path('pullrequest_new', repo_name=self.db_repo_name,
1172 1179 _query=org_query))
1173 1180
1174 1181 source_scm = source_db_repo.scm_instance()
1175 1182 target_scm = target_db_repo.scm_instance()
1176 1183
1177 1184 source_ref_obj = unicode_to_reference(source_ref)
1178 1185 target_ref_obj = unicode_to_reference(target_ref)
1179 1186
1180 1187 source_commit = source_scm.get_commit(source_ref_obj.commit_id)
1181 1188 target_commit = target_scm.get_commit(target_ref_obj.commit_id)
1182 1189
1183 1190 ancestor = source_scm.get_common_ancestor(
1184 1191 source_commit.raw_id, target_commit.raw_id, target_scm)
1185 1192
1186 1193 # recalculate target ref based on ancestor
1187 1194 target_ref = ':'.join((target_ref_obj.type, target_ref_obj.name, ancestor))
1188 1195
1189 1196 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
1190 1197 PullRequestModel().get_reviewer_functions()
1191 1198
1192 1199 # recalculate reviewers logic, to make sure we can validate this
1193 1200 reviewer_rules = get_default_reviewers_data(
1194 1201 self._rhodecode_db_user,
1195 1202 source_db_repo,
1196 1203 source_ref_obj,
1197 1204 target_db_repo,
1198 1205 target_ref_obj,
1199 1206 include_diff_info=False)
1200 1207
1201 1208 reviewers = validate_default_reviewers(_form['review_members'], reviewer_rules)
1202 1209 observers = validate_observers(_form['observer_members'], reviewer_rules)
1203 1210
1204 1211 pullrequest_title = _form['pullrequest_title']
1205 1212 title_source_ref = source_ref_obj.name
1206 1213 if not pullrequest_title:
1207 1214 pullrequest_title = PullRequestModel().generate_pullrequest_title(
1208 1215 source=source_repo,
1209 1216 source_ref=title_source_ref,
1210 1217 target=target_repo
1211 1218 )
1212 1219
1213 1220 description = _form['pullrequest_desc']
1214 1221 description_renderer = _form['description_renderer']
1215 1222
1216 1223 try:
1217 1224 pull_request = PullRequestModel().create(
1218 1225 created_by=self._rhodecode_user.user_id,
1219 1226 source_repo=source_repo,
1220 1227 source_ref=source_ref,
1221 1228 target_repo=target_repo,
1222 1229 target_ref=target_ref,
1223 1230 revisions=commit_ids,
1224 1231 common_ancestor_id=common_ancestor_id,
1225 1232 reviewers=reviewers,
1226 1233 observers=observers,
1227 1234 title=pullrequest_title,
1228 1235 description=description,
1229 1236 description_renderer=description_renderer,
1230 1237 reviewer_data=reviewer_rules,
1231 1238 auth_user=self._rhodecode_user
1232 1239 )
1233 1240 Session().commit()
1234 1241
1235 1242 h.flash(_('Successfully opened new pull request'),
1236 1243 category='success')
1237 1244 except Exception:
1238 1245 msg = _('Error occurred during creation of this pull request.')
1239 1246 log.exception(msg)
1240 1247 h.flash(msg, category='error')
1241 1248
1242 1249 # copy the args back to redirect
1243 1250 org_query = self.request.GET.mixed()
1244 1251 raise HTTPFound(
1245 1252 h.route_path('pullrequest_new', repo_name=self.db_repo_name,
1246 1253 _query=org_query))
1247 1254
1248 1255 raise HTTPFound(
1249 1256 h.route_path('pullrequest_show', repo_name=target_repo,
1250 1257 pull_request_id=pull_request.pull_request_id))
1251 1258
1252 1259 @LoginRequired()
1253 1260 @NotAnonymous()
1254 1261 @HasRepoPermissionAnyDecorator(
1255 1262 'repository.read', 'repository.write', 'repository.admin')
1256 1263 @CSRFRequired()
1257 1264 def pull_request_update(self):
1258 1265 pull_request = PullRequest.get_or_404(
1259 1266 self.request.matchdict['pull_request_id'])
1260 1267 _ = self.request.translate
1261 1268
1262 1269 c = self.load_default_context()
1263 1270 redirect_url = None
1264 1271
1265 1272 if pull_request.is_closed():
1266 1273 log.debug('update: forbidden because pull request is closed')
1267 1274 msg = _(u'Cannot update closed pull requests.')
1268 1275 h.flash(msg, category='error')
1269 1276 return {'response': True,
1270 1277 'redirect_url': redirect_url}
1271 1278
1272 1279 is_state_changing = pull_request.is_state_changing()
1273 1280 c.pr_broadcast_channel = channelstream.pr_channel(pull_request)
1274 1281
1275 1282 # only owner or admin can update it
1276 1283 allowed_to_update = PullRequestModel().check_user_update(
1277 1284 pull_request, self._rhodecode_user)
1278 1285
1279 1286 if allowed_to_update:
1280 1287 controls = peppercorn.parse(self.request.POST.items())
1281 1288 force_refresh = str2bool(self.request.POST.get('force_refresh'))
1282 1289
1283 1290 if 'review_members' in controls:
1284 1291 self._update_reviewers(
1285 1292 c,
1286 1293 pull_request, controls['review_members'],
1287 1294 pull_request.reviewer_data,
1288 1295 PullRequestReviewers.ROLE_REVIEWER)
1289 1296 elif 'observer_members' in controls:
1290 1297 self._update_reviewers(
1291 1298 c,
1292 1299 pull_request, controls['observer_members'],
1293 1300 pull_request.reviewer_data,
1294 1301 PullRequestReviewers.ROLE_OBSERVER)
1295 1302 elif str2bool(self.request.POST.get('update_commits', 'false')):
1296 1303 if is_state_changing:
1297 1304 log.debug('commits update: forbidden because pull request is in state %s',
1298 1305 pull_request.pull_request_state)
1299 1306 msg = _(u'Cannot update pull requests commits in state other than `{}`. '
1300 1307 u'Current state is: `{}`').format(
1301 1308 PullRequest.STATE_CREATED, pull_request.pull_request_state)
1302 1309 h.flash(msg, category='error')
1303 1310 return {'response': True,
1304 1311 'redirect_url': redirect_url}
1305 1312
1306 1313 self._update_commits(c, pull_request)
1307 1314 if force_refresh:
1308 1315 redirect_url = h.route_path(
1309 1316 'pullrequest_show', repo_name=self.db_repo_name,
1310 1317 pull_request_id=pull_request.pull_request_id,
1311 1318 _query={"force_refresh": 1})
1312 1319 elif str2bool(self.request.POST.get('edit_pull_request', 'false')):
1313 1320 self._edit_pull_request(pull_request)
1314 1321 else:
1315 1322 log.error('Unhandled update data.')
1316 1323 raise HTTPBadRequest()
1317 1324
1318 1325 return {'response': True,
1319 1326 'redirect_url': redirect_url}
1320 1327 raise HTTPForbidden()
1321 1328
1322 1329 def _edit_pull_request(self, pull_request):
1323 1330 """
1324 1331 Edit title and description
1325 1332 """
1326 1333 _ = self.request.translate
1327 1334
1328 1335 try:
1329 1336 PullRequestModel().edit(
1330 1337 pull_request,
1331 1338 self.request.POST.get('title'),
1332 1339 self.request.POST.get('description'),
1333 1340 self.request.POST.get('description_renderer'),
1334 1341 self._rhodecode_user)
1335 1342 except ValueError:
1336 1343 msg = _(u'Cannot update closed pull requests.')
1337 1344 h.flash(msg, category='error')
1338 1345 return
1339 1346 else:
1340 1347 Session().commit()
1341 1348
1342 1349 msg = _(u'Pull request title & description updated.')
1343 1350 h.flash(msg, category='success')
1344 1351 return
1345 1352
1346 1353 def _update_commits(self, c, pull_request):
1347 1354 _ = self.request.translate
1348 1355
1349 1356 with pull_request.set_state(PullRequest.STATE_UPDATING):
1350 1357 resp = PullRequestModel().update_commits(
1351 1358 pull_request, self._rhodecode_db_user)
1352 1359
1353 1360 if resp.executed:
1354 1361
1355 1362 if resp.target_changed and resp.source_changed:
1356 1363 changed = 'target and source repositories'
1357 1364 elif resp.target_changed and not resp.source_changed:
1358 1365 changed = 'target repository'
1359 1366 elif not resp.target_changed and resp.source_changed:
1360 1367 changed = 'source repository'
1361 1368 else:
1362 1369 changed = 'nothing'
1363 1370
1364 1371 msg = _(u'Pull request updated to "{source_commit_id}" with '
1365 1372 u'{count_added} added, {count_removed} removed commits. '
1366 1373 u'Source of changes: {change_source}.')
1367 1374 msg = msg.format(
1368 1375 source_commit_id=pull_request.source_ref_parts.commit_id,
1369 1376 count_added=len(resp.changes.added),
1370 1377 count_removed=len(resp.changes.removed),
1371 1378 change_source=changed)
1372 1379 h.flash(msg, category='success')
1373 1380 channelstream.pr_update_channelstream_push(
1374 1381 self.request, c.pr_broadcast_channel, self._rhodecode_user, msg)
1375 1382 else:
1376 1383 msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason]
1377 1384 warning_reasons = [
1378 1385 UpdateFailureReason.NO_CHANGE,
1379 1386 UpdateFailureReason.WRONG_REF_TYPE,
1380 1387 ]
1381 1388 category = 'warning' if resp.reason in warning_reasons else 'error'
1382 1389 h.flash(msg, category=category)
1383 1390
1384 1391 def _update_reviewers(self, c, pull_request, review_members, reviewer_rules, role):
1385 1392 _ = self.request.translate
1386 1393
1387 1394 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
1388 1395 PullRequestModel().get_reviewer_functions()
1389 1396
1390 1397 if role == PullRequestReviewers.ROLE_REVIEWER:
1391 1398 try:
1392 1399 reviewers = validate_default_reviewers(review_members, reviewer_rules)
1393 1400 except ValueError as e:
1394 1401 log.error('Reviewers Validation: {}'.format(e))
1395 1402 h.flash(e, category='error')
1396 1403 return
1397 1404
1398 1405 old_calculated_status = pull_request.calculated_review_status()
1399 1406 PullRequestModel().update_reviewers(
1400 1407 pull_request, reviewers, self._rhodecode_db_user)
1401 1408
1402 1409 Session().commit()
1403 1410
1404 1411 msg = _('Pull request reviewers updated.')
1405 1412 h.flash(msg, category='success')
1406 1413 channelstream.pr_update_channelstream_push(
1407 1414 self.request, c.pr_broadcast_channel, self._rhodecode_user, msg)
1408 1415
1409 1416 # trigger status changed if change in reviewers changes the status
1410 1417 calculated_status = pull_request.calculated_review_status()
1411 1418 if old_calculated_status != calculated_status:
1412 1419 PullRequestModel().trigger_pull_request_hook(
1413 1420 pull_request, self._rhodecode_user, 'review_status_change',
1414 1421 data={'status': calculated_status})
1415 1422
1416 1423 elif role == PullRequestReviewers.ROLE_OBSERVER:
1417 1424 try:
1418 1425 observers = validate_observers(review_members, reviewer_rules)
1419 1426 except ValueError as e:
1420 1427 log.error('Observers Validation: {}'.format(e))
1421 1428 h.flash(e, category='error')
1422 1429 return
1423 1430
1424 1431 PullRequestModel().update_observers(
1425 1432 pull_request, observers, self._rhodecode_db_user)
1426 1433
1427 1434 Session().commit()
1428 1435 msg = _('Pull request observers updated.')
1429 1436 h.flash(msg, category='success')
1430 1437 channelstream.pr_update_channelstream_push(
1431 1438 self.request, c.pr_broadcast_channel, self._rhodecode_user, msg)
1432 1439
1433 1440 @LoginRequired()
1434 1441 @NotAnonymous()
1435 1442 @HasRepoPermissionAnyDecorator(
1436 1443 'repository.read', 'repository.write', 'repository.admin')
1437 1444 @CSRFRequired()
1438 1445 def pull_request_merge(self):
1439 1446 """
1440 1447 Merge will perform a server-side merge of the specified
1441 1448 pull request, if the pull request is approved and mergeable.
1442 1449 After successful merging, the pull request is automatically
1443 1450 closed, with a relevant comment.
1444 1451 """
1445 1452 pull_request = PullRequest.get_or_404(
1446 1453 self.request.matchdict['pull_request_id'])
1447 1454 _ = self.request.translate
1448 1455
1449 1456 if pull_request.is_state_changing():
1450 1457 log.debug('show: forbidden because pull request is in state %s',
1451 1458 pull_request.pull_request_state)
1452 1459 msg = _(u'Cannot merge pull requests in state other than `{}`. '
1453 1460 u'Current state is: `{}`').format(PullRequest.STATE_CREATED,
1454 1461 pull_request.pull_request_state)
1455 1462 h.flash(msg, category='error')
1456 1463 raise HTTPFound(
1457 1464 h.route_path('pullrequest_show',
1458 1465 repo_name=pull_request.target_repo.repo_name,
1459 1466 pull_request_id=pull_request.pull_request_id))
1460 1467
1461 1468 self.load_default_context()
1462 1469
1463 1470 with pull_request.set_state(PullRequest.STATE_UPDATING):
1464 1471 check = MergeCheck.validate(
1465 1472 pull_request, auth_user=self._rhodecode_user,
1466 1473 translator=self.request.translate)
1467 1474 merge_possible = not check.failed
1468 1475
1469 1476 for err_type, error_msg in check.errors:
1470 1477 h.flash(error_msg, category=err_type)
1471 1478
1472 1479 if merge_possible:
1473 1480 log.debug("Pre-conditions checked, trying to merge.")
1474 1481 extras = vcs_operation_context(
1475 1482 self.request.environ, repo_name=pull_request.target_repo.repo_name,
1476 1483 username=self._rhodecode_db_user.username, action='push',
1477 1484 scm=pull_request.target_repo.repo_type)
1478 1485 with pull_request.set_state(PullRequest.STATE_UPDATING):
1479 1486 self._merge_pull_request(
1480 1487 pull_request, self._rhodecode_db_user, extras)
1481 1488 else:
1482 1489 log.debug("Pre-conditions failed, NOT merging.")
1483 1490
1484 1491 raise HTTPFound(
1485 1492 h.route_path('pullrequest_show',
1486 1493 repo_name=pull_request.target_repo.repo_name,
1487 1494 pull_request_id=pull_request.pull_request_id))
1488 1495
1489 1496 def _merge_pull_request(self, pull_request, user, extras):
1490 1497 _ = self.request.translate
1491 1498 merge_resp = PullRequestModel().merge_repo(pull_request, user, extras=extras)
1492 1499
1493 1500 if merge_resp.executed:
1494 1501 log.debug("The merge was successful, closing the pull request.")
1495 1502 PullRequestModel().close_pull_request(
1496 1503 pull_request.pull_request_id, user)
1497 1504 Session().commit()
1498 1505 msg = _('Pull request was successfully merged and closed.')
1499 1506 h.flash(msg, category='success')
1500 1507 else:
1501 1508 log.debug(
1502 1509 "The merge was not successful. Merge response: %s", merge_resp)
1503 1510 msg = merge_resp.merge_status_message
1504 1511 h.flash(msg, category='error')
1505 1512
1506 1513 @LoginRequired()
1507 1514 @NotAnonymous()
1508 1515 @HasRepoPermissionAnyDecorator(
1509 1516 'repository.read', 'repository.write', 'repository.admin')
1510 1517 @CSRFRequired()
1511 1518 def pull_request_delete(self):
1512 1519 _ = self.request.translate
1513 1520
1514 1521 pull_request = PullRequest.get_or_404(
1515 1522 self.request.matchdict['pull_request_id'])
1516 1523 self.load_default_context()
1517 1524
1518 1525 pr_closed = pull_request.is_closed()
1519 1526 allowed_to_delete = PullRequestModel().check_user_delete(
1520 1527 pull_request, self._rhodecode_user) and not pr_closed
1521 1528
1522 1529 # only owner can delete it !
1523 1530 if allowed_to_delete:
1524 1531 PullRequestModel().delete(pull_request, self._rhodecode_user)
1525 1532 Session().commit()
1526 1533 h.flash(_('Successfully deleted pull request'),
1527 1534 category='success')
1528 1535 raise HTTPFound(h.route_path('pullrequest_show_all',
1529 1536 repo_name=self.db_repo_name))
1530 1537
1531 1538 log.warning('user %s tried to delete pull request without access',
1532 1539 self._rhodecode_user)
1533 1540 raise HTTPNotFound()
1534 1541
1535 1542 def _pull_request_comments_create(self, pull_request, comments):
1536 1543 _ = self.request.translate
1537 1544 data = {}
1538 1545 if not comments:
1539 1546 return
1540 1547 pull_request_id = pull_request.pull_request_id
1541 1548
1542 1549 all_drafts = len([x for x in comments if str2bool(x['is_draft'])]) == len(comments)
1543 1550
1544 1551 for entry in comments:
1545 1552 c = self.load_default_context()
1546 1553 comment_type = entry['comment_type']
1547 1554 text = entry['text']
1548 1555 status = entry['status']
1549 1556 is_draft = str2bool(entry['is_draft'])
1550 1557 resolves_comment_id = entry['resolves_comment_id']
1551 1558 close_pull_request = entry['close_pull_request']
1552 1559 f_path = entry['f_path']
1553 1560 line_no = entry['line']
1554 1561 target_elem_id = 'file-{}'.format(h.safeid(h.safe_unicode(f_path)))
1555 1562
1556 1563 # the logic here should work like following, if we submit close
1557 1564 # pr comment, use `close_pull_request_with_comment` function
1558 1565 # else handle regular comment logic
1559 1566
1560 1567 if close_pull_request:
1561 1568 # only owner or admin or person with write permissions
1562 1569 allowed_to_close = PullRequestModel().check_user_update(
1563 1570 pull_request, self._rhodecode_user)
1564 1571 if not allowed_to_close:
1565 1572 log.debug('comment: forbidden because not allowed to close '
1566 1573 'pull request %s', pull_request_id)
1567 1574 raise HTTPForbidden()
1568 1575
1569 1576 # This also triggers `review_status_change`
1570 1577 comment, status = PullRequestModel().close_pull_request_with_comment(
1571 1578 pull_request, self._rhodecode_user, self.db_repo, message=text,
1572 1579 auth_user=self._rhodecode_user)
1573 1580 Session().flush()
1574 1581 is_inline = comment.is_inline
1575 1582
1576 1583 PullRequestModel().trigger_pull_request_hook(
1577 1584 pull_request, self._rhodecode_user, 'comment',
1578 1585 data={'comment': comment})
1579 1586
1580 1587 else:
1581 1588 # regular comment case, could be inline, or one with status.
1582 1589 # for that one we check also permissions
1583 1590 # Additionally ENSURE if somehow draft is sent we're then unable to change status
1584 1591 allowed_to_change_status = PullRequestModel().check_user_change_status(
1585 1592 pull_request, self._rhodecode_user) and not is_draft
1586 1593
1587 1594 if status and allowed_to_change_status:
1588 1595 message = (_('Status change %(transition_icon)s %(status)s')
1589 1596 % {'transition_icon': '>',
1590 1597 'status': ChangesetStatus.get_status_lbl(status)})
1591 1598 text = text or message
1592 1599
1593 1600 comment = CommentsModel().create(
1594 1601 text=text,
1595 1602 repo=self.db_repo.repo_id,
1596 1603 user=self._rhodecode_user.user_id,
1597 1604 pull_request=pull_request,
1598 1605 f_path=f_path,
1599 1606 line_no=line_no,
1600 1607 status_change=(ChangesetStatus.get_status_lbl(status)
1601 1608 if status and allowed_to_change_status else None),
1602 1609 status_change_type=(status
1603 1610 if status and allowed_to_change_status else None),
1604 1611 comment_type=comment_type,
1605 1612 is_draft=is_draft,
1606 1613 resolves_comment_id=resolves_comment_id,
1607 1614 auth_user=self._rhodecode_user,
1608 1615 send_email=not is_draft, # skip notification for draft comments
1609 1616 )
1610 1617 is_inline = comment.is_inline
1611 1618
1612 1619 if allowed_to_change_status:
1613 1620 # calculate old status before we change it
1614 1621 old_calculated_status = pull_request.calculated_review_status()
1615 1622
1616 1623 # get status if set !
1617 1624 if status:
1618 1625 ChangesetStatusModel().set_status(
1619 1626 self.db_repo.repo_id,
1620 1627 status,
1621 1628 self._rhodecode_user.user_id,
1622 1629 comment,
1623 1630 pull_request=pull_request
1624 1631 )
1625 1632
1626 1633 Session().flush()
1627 1634 # this is somehow required to get access to some relationship
1628 1635 # loaded on comment
1629 1636 Session().refresh(comment)
1630 1637
1631 1638 # skip notifications for drafts
1632 1639 if not is_draft:
1633 1640 PullRequestModel().trigger_pull_request_hook(
1634 1641 pull_request, self._rhodecode_user, 'comment',
1635 1642 data={'comment': comment})
1636 1643
1637 1644 # we now calculate the status of pull request, and based on that
1638 1645 # calculation we set the commits status
1639 1646 calculated_status = pull_request.calculated_review_status()
1640 1647 if old_calculated_status != calculated_status:
1641 1648 PullRequestModel().trigger_pull_request_hook(
1642 1649 pull_request, self._rhodecode_user, 'review_status_change',
1643 1650 data={'status': calculated_status})
1644 1651
1645 1652 comment_id = comment.comment_id
1646 1653 data[comment_id] = {
1647 1654 'target_id': target_elem_id
1648 1655 }
1649 1656 Session().flush()
1650 1657
1651 1658 c.co = comment
1652 1659 c.at_version_num = None
1653 1660 c.is_new = True
1654 1661 rendered_comment = render(
1655 1662 'rhodecode:templates/changeset/changeset_comment_block.mako',
1656 1663 self._get_template_context(c), self.request)
1657 1664
1658 1665 data[comment_id].update(comment.get_dict())
1659 1666 data[comment_id].update({'rendered_text': rendered_comment})
1660 1667
1661 1668 Session().commit()
1662 1669
1663 1670 # skip channelstream for draft comments
1664 1671 if not all_drafts:
1665 1672 comment_broadcast_channel = channelstream.comment_channel(
1666 1673 self.db_repo_name, pull_request_obj=pull_request)
1667 1674
1668 1675 comment_data = data
1669 1676 posted_comment_type = 'inline' if is_inline else 'general'
1670 1677 if len(data) == 1:
1671 1678 msg = _('posted {} new {} comment').format(len(data), posted_comment_type)
1672 1679 else:
1673 1680 msg = _('posted {} new {} comments').format(len(data), posted_comment_type)
1674 1681
1675 1682 channelstream.comment_channelstream_push(
1676 1683 self.request, comment_broadcast_channel, self._rhodecode_user, msg,
1677 1684 comment_data=comment_data)
1678 1685
1679 1686 return data
1680 1687
1681 1688 @LoginRequired()
1682 1689 @NotAnonymous()
1683 1690 @HasRepoPermissionAnyDecorator(
1684 1691 'repository.read', 'repository.write', 'repository.admin')
1685 1692 @CSRFRequired()
1686 1693 def pull_request_comment_create(self):
1687 1694 _ = self.request.translate
1688 1695
1689 1696 pull_request = PullRequest.get_or_404(self.request.matchdict['pull_request_id'])
1690 1697
1691 1698 if pull_request.is_closed():
1692 1699 log.debug('comment: forbidden because pull request is closed')
1693 1700 raise HTTPForbidden()
1694 1701
1695 1702 allowed_to_comment = PullRequestModel().check_user_comment(
1696 1703 pull_request, self._rhodecode_user)
1697 1704 if not allowed_to_comment:
1698 1705 log.debug('comment: forbidden because pull request is from forbidden repo')
1699 1706 raise HTTPForbidden()
1700 1707
1701 1708 comment_data = {
1702 1709 'comment_type': self.request.POST.get('comment_type'),
1703 1710 'text': self.request.POST.get('text'),
1704 1711 'status': self.request.POST.get('changeset_status', None),
1705 1712 'is_draft': self.request.POST.get('draft'),
1706 1713 'resolves_comment_id': self.request.POST.get('resolves_comment_id', None),
1707 1714 'close_pull_request': self.request.POST.get('close_pull_request'),
1708 1715 'f_path': self.request.POST.get('f_path'),
1709 1716 'line': self.request.POST.get('line'),
1710 1717 }
1711 1718 data = self._pull_request_comments_create(pull_request, [comment_data])
1712 1719
1713 1720 return data
1714 1721
1715 1722 @LoginRequired()
1716 1723 @NotAnonymous()
1717 1724 @HasRepoPermissionAnyDecorator(
1718 1725 'repository.read', 'repository.write', 'repository.admin')
1719 1726 @CSRFRequired()
1720 1727 def pull_request_comment_delete(self):
1721 1728 pull_request = PullRequest.get_or_404(
1722 1729 self.request.matchdict['pull_request_id'])
1723 1730
1724 1731 comment = ChangesetComment.get_or_404(
1725 1732 self.request.matchdict['comment_id'])
1726 1733 comment_id = comment.comment_id
1727 1734
1728 1735 if comment.immutable:
1729 1736 # don't allow deleting comments that are immutable
1730 1737 raise HTTPForbidden()
1731 1738
1732 1739 if pull_request.is_closed():
1733 1740 log.debug('comment: forbidden because pull request is closed')
1734 1741 raise HTTPForbidden()
1735 1742
1736 1743 if not comment:
1737 1744 log.debug('Comment with id:%s not found, skipping', comment_id)
1738 1745 # comment already deleted in another call probably
1739 1746 return True
1740 1747
1741 1748 if comment.pull_request.is_closed():
1742 1749 # don't allow deleting comments on closed pull request
1743 1750 raise HTTPForbidden()
1744 1751
1745 1752 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
1746 1753 super_admin = h.HasPermissionAny('hg.admin')()
1747 1754 comment_owner = comment.author.user_id == self._rhodecode_user.user_id
1748 1755 is_repo_comment = comment.repo.repo_name == self.db_repo_name
1749 1756 comment_repo_admin = is_repo_admin and is_repo_comment
1750 1757
1751 1758 if comment.draft and not comment_owner:
1752 1759 # We never allow to delete draft comments for other than owners
1753 1760 raise HTTPNotFound()
1754 1761
1755 1762 if super_admin or comment_owner or comment_repo_admin:
1756 1763 old_calculated_status = comment.pull_request.calculated_review_status()
1757 1764 CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user)
1758 1765 Session().commit()
1759 1766 calculated_status = comment.pull_request.calculated_review_status()
1760 1767 if old_calculated_status != calculated_status:
1761 1768 PullRequestModel().trigger_pull_request_hook(
1762 1769 comment.pull_request, self._rhodecode_user, 'review_status_change',
1763 1770 data={'status': calculated_status})
1764 1771 return True
1765 1772 else:
1766 1773 log.warning('No permissions for user %s to delete comment_id: %s',
1767 1774 self._rhodecode_db_user, comment_id)
1768 1775 raise HTTPNotFound()
1769 1776
1770 1777 @LoginRequired()
1771 1778 @NotAnonymous()
1772 1779 @HasRepoPermissionAnyDecorator(
1773 1780 'repository.read', 'repository.write', 'repository.admin')
1774 1781 @CSRFRequired()
1775 1782 def pull_request_comment_edit(self):
1776 1783 self.load_default_context()
1777 1784
1778 1785 pull_request = PullRequest.get_or_404(
1779 1786 self.request.matchdict['pull_request_id']
1780 1787 )
1781 1788 comment = ChangesetComment.get_or_404(
1782 1789 self.request.matchdict['comment_id']
1783 1790 )
1784 1791 comment_id = comment.comment_id
1785 1792
1786 1793 if comment.immutable:
1787 1794 # don't allow deleting comments that are immutable
1788 1795 raise HTTPForbidden()
1789 1796
1790 1797 if pull_request.is_closed():
1791 1798 log.debug('comment: forbidden because pull request is closed')
1792 1799 raise HTTPForbidden()
1793 1800
1794 1801 if comment.pull_request.is_closed():
1795 1802 # don't allow deleting comments on closed pull request
1796 1803 raise HTTPForbidden()
1797 1804
1798 1805 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
1799 1806 super_admin = h.HasPermissionAny('hg.admin')()
1800 1807 comment_owner = comment.author.user_id == self._rhodecode_user.user_id
1801 1808 is_repo_comment = comment.repo.repo_name == self.db_repo_name
1802 1809 comment_repo_admin = is_repo_admin and is_repo_comment
1803 1810
1804 1811 if super_admin or comment_owner or comment_repo_admin:
1805 1812 text = self.request.POST.get('text')
1806 1813 version = self.request.POST.get('version')
1807 1814 if text == comment.text:
1808 1815 log.warning(
1809 1816 'Comment(PR): '
1810 1817 'Trying to create new version '
1811 1818 'with the same comment body {}'.format(
1812 1819 comment_id,
1813 1820 )
1814 1821 )
1815 1822 raise HTTPNotFound()
1816 1823
1817 1824 if version.isdigit():
1818 1825 version = int(version)
1819 1826 else:
1820 1827 log.warning(
1821 1828 'Comment(PR): Wrong version type {} {} '
1822 1829 'for comment {}'.format(
1823 1830 version,
1824 1831 type(version),
1825 1832 comment_id,
1826 1833 )
1827 1834 )
1828 1835 raise HTTPNotFound()
1829 1836
1830 1837 try:
1831 1838 comment_history = CommentsModel().edit(
1832 1839 comment_id=comment_id,
1833 1840 text=text,
1834 1841 auth_user=self._rhodecode_user,
1835 1842 version=version,
1836 1843 )
1837 1844 except CommentVersionMismatch:
1838 1845 raise HTTPConflict()
1839 1846
1840 1847 if not comment_history:
1841 1848 raise HTTPNotFound()
1842 1849
1843 1850 Session().commit()
1844 1851 if not comment.draft:
1845 1852 PullRequestModel().trigger_pull_request_hook(
1846 1853 pull_request, self._rhodecode_user, 'comment_edit',
1847 1854 data={'comment': comment})
1848 1855
1849 1856 return {
1850 1857 'comment_history_id': comment_history.comment_history_id,
1851 1858 'comment_id': comment.comment_id,
1852 1859 'comment_version': comment_history.version,
1853 1860 'comment_author_username': comment_history.author.username,
1854 1861 'comment_author_gravatar': h.gravatar_url(comment_history.author.email, 16),
1855 1862 'comment_created_on': h.age_component(comment_history.created_on,
1856 1863 time_is_local=True),
1857 1864 }
1858 1865 else:
1859 1866 log.warning('No permissions for user %s to edit comment_id: %s',
1860 1867 self._rhodecode_db_user, comment_id)
1861 1868 raise HTTPNotFound()
This diff has been collapsed as it changes many lines, (635 lines changed) Show them Hide them
@@ -1,11666 +1,11671 b''
1 1 # Translations template for rhodecode-enterprise-ce.
2 2 # Copyright (C) 2021 RhodeCode GmbH
3 3 # This file is distributed under the same license as the rhodecode-enterprise-ce project.
4 4 # FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
5 5 #
6 6 #, fuzzy
7 7 msgid ""
8 8 msgstr ""
9 "Project-Id-Version: rhodecode-enterprise-ce 4.24.0\n"
9 "Project-Id-Version: rhodecode-enterprise-ce 4.25.0\n"
10 10 "Report-Msgid-Bugs-To: marcin@rhodecode.com\n"
11 "POT-Creation-Date: 2021-01-14 15:36+0000\n"
11 "POT-Creation-Date: 2021-04-05 19:29+0000\n"
12 12 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 14 "Language-Team: LANGUAGE <LL@li.org>\n"
15 15 "MIME-Version: 1.0\n"
16 16 "Content-Type: text/plain; charset=utf-8\n"
17 17 "Content-Transfer-Encoding: 8bit\n"
18 18 "Generated-By: Babel 1.3\n"
19 19
20 20 #: rhodecode/api/views/pull_request_api.py:646
21 21 #: rhodecode/api/views/repo_api.py:1686
22 22 msgid "posted a new {} comment"
23 23 msgstr ""
24 24
25 25 #: rhodecode/apps/admin/views/defaults.py:82
26 26 msgid "Default settings updated successfully"
27 27 msgstr ""
28 28
29 29 #: rhodecode/apps/admin/views/defaults.py:100
30 30 msgid "Error occurred during update of default values"
31 31 msgstr ""
32 32
33 33 #: rhodecode/apps/admin/views/exception_tracker.py:146
34 34 msgid "Removed {} Exceptions"
35 35 msgstr ""
36 36
37 37 #: rhodecode/apps/admin/views/exception_tracker.py:160
38 38 msgid "Removed Exception {}"
39 39 msgstr ""
40 40
41 41 #: rhodecode/apps/admin/views/permissions.py:114
42 42 msgid "Application permissions updated successfully"
43 43 msgstr ""
44 44
45 45 #: rhodecode/apps/admin/views/permissions.py:135
46 46 #: rhodecode/apps/admin/views/permissions.py:205
47 47 #: rhodecode/apps/admin/views/permissions.py:298
48 48 msgid "Error occurred during update of permissions"
49 49 msgstr ""
50 50
51 51 #: rhodecode/apps/admin/views/permissions.py:185
52 52 msgid "Object permissions updated successfully"
53 53 msgstr ""
54 54
55 55 #: rhodecode/apps/admin/views/permissions.py:278
56 56 msgid "Global permissions updated successfully"
57 57 msgstr ""
58 58
59 59 #: rhodecode/apps/admin/views/permissions.py:448
60 60 #: rhodecode/templates/admin/gists/gist_show.mako:50
61 61 #: rhodecode/templates/admin/integrations/list.mako:172
62 62 #: rhodecode/templates/admin/my_account/my_account_profile.mako:7
63 63 #: rhodecode/templates/base/issue_tracker_settings.mako:138
64 64 #: rhodecode/templates/changeset/changeset_file_comment.mako:233
65 65 #: rhodecode/templates/changeset/changeset_file_comment.mako:247
66 66 #: rhodecode/templates/changeset/changeset_file_comment.mako:256
67 67 #: rhodecode/templates/data_table/_dt_elements.mako:173
68 68 #: rhodecode/templates/data_table/_dt_elements.mako:251
69 69 #: rhodecode/templates/data_table/_dt_elements.mako:266
70 70 #: rhodecode/templates/data_table/_dt_elements.mako:267
71 71 #: rhodecode/templates/data_table/_dt_elements.mako:282
72 72 #: rhodecode/templates/debug_style/buttons.html:128
73 73 #: rhodecode/templates/files/files_add.mako:55
74 74 #: rhodecode/templates/files/files_edit.mako:57
75 75 #: rhodecode/templates/files/files_source.mako:39
76 76 #: rhodecode/templates/files/files_source.mako:52
77 77 #: rhodecode/templates/pullrequests/pullrequest_show.mako:81
78 78 #: rhodecode/templates/pullrequests/pullrequest_show.mako:609
79 79 #: rhodecode/templates/pullrequests/pullrequest_show.mako:675
80 80 #: rhodecode/templates/user_group/profile.mako:8
81 81 #: rhodecode/templates/users/user_profile.mako:8
82 82 msgid "Edit"
83 83 msgstr ""
84 84
85 85 #: rhodecode/apps/admin/views/permissions.py:473
86 86 msgid "Updated SSH keys file: {}"
87 87 msgstr ""
88 88
89 89 #: rhodecode/apps/admin/views/permissions.py:476
90 90 msgid "SSH key support is disabled in .ini file"
91 91 msgstr ""
92 92
93 93 #: rhodecode/apps/admin/views/repo_groups.py:330
94 94 #, python-format
95 95 msgid "Created repository group %s"
96 96 msgstr ""
97 97
98 98 #: rhodecode/apps/admin/views/repo_groups.py:348
99 99 #, python-format
100 100 msgid "Error occurred during creation of repository group %s"
101 101 msgstr ""
102 102
103 103 #: rhodecode/apps/admin/views/sessions.py:86
104 104 msgid "Cleaned up old sessions"
105 105 msgstr ""
106 106
107 107 #: rhodecode/apps/admin/views/sessions.py:91
108 108 msgid "Failed to cleanup up old sessions"
109 109 msgstr ""
110 110
111 111 #: rhodecode/apps/admin/views/settings.py:156
112 112 #: rhodecode/apps/admin/views/settings.py:291
113 113 #: rhodecode/apps/admin/views/settings.py:360
114 #: rhodecode/apps/admin/views/settings.py:663
114 #: rhodecode/apps/admin/views/settings.py:664
115 115 #: rhodecode/apps/repository/views/repo_settings_vcs.py:116
116 116 msgid "Some form inputs contain invalid data."
117 117 msgstr ""
118 118
119 119 #: rhodecode/apps/admin/views/settings.py:183
120 120 #: rhodecode/apps/admin/views/settings.py:327
121 121 msgid "Error occurred during updating application settings"
122 122 msgstr ""
123 123
124 124 #: rhodecode/apps/admin/views/settings.py:187
125 125 #: rhodecode/apps/repository/views/repo_settings_vcs.py:135
126 126 msgid "Updated VCS settings"
127 127 msgstr ""
128 128
129 129 #: rhodecode/apps/admin/views/settings.py:253
130 130 #, python-format
131 131 msgid "Repositories successfully rescanned added: %s ; removed: %s"
132 132 msgstr ""
133 133
134 134 #: rhodecode/apps/admin/views/settings.py:323
135 135 msgid "Updated application settings"
136 136 msgstr ""
137 137
138 #: rhodecode/apps/admin/views/settings.py:399
138 #: rhodecode/apps/admin/views/settings.py:400
139 139 msgid "Updated visualisation settings"
140 140 msgstr ""
141 141
142 #: rhodecode/apps/admin/views/settings.py:402
142 #: rhodecode/apps/admin/views/settings.py:403
143 143 msgid "Error occurred during updating visualisation settings"
144 144 msgstr ""
145 145
146 #: rhodecode/apps/admin/views/settings.py:464
146 #: rhodecode/apps/admin/views/settings.py:465
147 147 #: rhodecode/apps/repository/views/repo_settings_issue_trackers.py:115
148 148 msgid "Invalid issue tracker pattern: {}"
149 149 msgstr ""
150 150
151 #: rhodecode/apps/admin/views/settings.py:481
151 #: rhodecode/apps/admin/views/settings.py:482
152 152 #: rhodecode/apps/repository/views/repo_settings_issue_trackers.py:124
153 153 msgid "Updated issue tracker entries"
154 154 msgstr ""
155 155
156 #: rhodecode/apps/admin/views/settings.py:498
156 #: rhodecode/apps/admin/views/settings.py:499
157 157 #: rhodecode/apps/repository/views/repo_settings_issue_trackers.py:82
158 158 msgid "Removed issue tracker entry."
159 159 msgstr ""
160 160
161 #: rhodecode/apps/admin/views/settings.py:530
161 #: rhodecode/apps/admin/views/settings.py:531
162 162 msgid "Please enter email address"
163 163 msgstr ""
164 164
165 #: rhodecode/apps/admin/views/settings.py:546
165 #: rhodecode/apps/admin/views/settings.py:547
166 166 msgid "Send email task created"
167 167 msgstr ""
168 168
169 #: rhodecode/apps/admin/views/settings.py:587
169 #: rhodecode/apps/admin/views/settings.py:588
170 170 msgid "Added new hook"
171 171 msgstr ""
172 172
173 #: rhodecode/apps/admin/views/settings.py:602
173 #: rhodecode/apps/admin/views/settings.py:603
174 174 msgid "Updated hooks"
175 175 msgstr ""
176 176
177 #: rhodecode/apps/admin/views/settings.py:606
177 #: rhodecode/apps/admin/views/settings.py:607
178 178 msgid "Error occurred during hook creation"
179 179 msgstr ""
180 180
181 #: rhodecode/apps/admin/views/settings.py:687
181 #: rhodecode/apps/admin/views/settings.py:688
182 182 msgid "Error occurred during updating labs settings"
183 183 msgstr ""
184 184
185 #: rhodecode/apps/admin/views/settings.py:692
185 #: rhodecode/apps/admin/views/settings.py:693
186 186 msgid "Updated Labs settings"
187 187 msgstr ""
188 188
189 189 #: rhodecode/apps/admin/views/svn_config.py:43
190 190 msgid "Apache configuration for Subversion generated at `{}`."
191 191 msgstr ""
192 192
193 193 #: rhodecode/apps/admin/views/svn_config.py:51
194 194 msgid "Failed to generate the Apache configuration for Subversion."
195 195 msgstr ""
196 196
197 197 #: rhodecode/apps/admin/views/system_info.py:76
198 198 msgid "Note: please make sure this server can access `${url}` for the update link to work"
199 199 msgstr ""
200 200
201 201 #: rhodecode/apps/admin/views/system_info.py:87
202 202 msgid "Update info"
203 203 msgstr ""
204 204
205 205 #: rhodecode/apps/admin/views/system_info.py:89
206 206 msgid "Check for updates"
207 207 msgstr ""
208 208
209 209 #: rhodecode/apps/admin/views/system_info.py:94
210 210 msgid "RhodeCode Version"
211 211 msgstr ""
212 212
213 213 #: rhodecode/apps/admin/views/system_info.py:95
214 214 msgid "Latest version"
215 215 msgstr ""
216 216
217 217 #: rhodecode/apps/admin/views/system_info.py:96
218 218 msgid "RhodeCode Base URL"
219 219 msgstr ""
220 220
221 221 #: rhodecode/apps/admin/views/system_info.py:97
222 222 msgid "RhodeCode Server IP"
223 223 msgstr ""
224 224
225 225 #: rhodecode/apps/admin/views/system_info.py:98
226 226 msgid "RhodeCode Server ID"
227 227 msgstr ""
228 228
229 229 #: rhodecode/apps/admin/views/system_info.py:99
230 230 msgid "RhodeCode Configuration"
231 231 msgstr ""
232 232
233 233 #: rhodecode/apps/admin/views/system_info.py:100
234 234 msgid "RhodeCode Certificate"
235 235 msgstr ""
236 236
237 237 #: rhodecode/apps/admin/views/system_info.py:101
238 238 msgid "Workers"
239 239 msgstr ""
240 240
241 241 #: rhodecode/apps/admin/views/system_info.py:102
242 242 msgid "Worker Type"
243 243 msgstr ""
244 244
245 245 #: rhodecode/apps/admin/views/system_info.py:106
246 246 msgid "Database"
247 247 msgstr ""
248 248
249 249 #: rhodecode/apps/admin/views/system_info.py:107
250 250 msgid "Database version"
251 251 msgstr ""
252 252
253 253 #: rhodecode/apps/admin/views/system_info.py:111
254 254 msgid "Platform"
255 255 msgstr ""
256 256
257 257 #: rhodecode/apps/admin/views/system_info.py:112
258 258 msgid "Platform UUID"
259 259 msgstr ""
260 260
261 261 #: rhodecode/apps/admin/views/system_info.py:113
262 262 msgid "Lang"
263 263 msgstr ""
264 264
265 265 #: rhodecode/apps/admin/views/system_info.py:114
266 266 msgid "Python version"
267 267 msgstr ""
268 268
269 269 #: rhodecode/apps/admin/views/system_info.py:115
270 270 msgid "Python path"
271 271 msgstr ""
272 272
273 273 #: rhodecode/apps/admin/views/system_info.py:119
274 274 msgid "CPU"
275 275 msgstr ""
276 276
277 277 #: rhodecode/apps/admin/views/system_info.py:120
278 278 msgid "Load"
279 279 msgstr ""
280 280
281 281 #: rhodecode/apps/admin/views/system_info.py:121
282 282 msgid "Memory"
283 283 msgstr ""
284 284
285 285 #: rhodecode/apps/admin/views/system_info.py:122
286 286 msgid "Uptime"
287 287 msgstr ""
288 288
289 289 #: rhodecode/apps/admin/views/system_info.py:126
290 290 msgid "Ulimit"
291 291 msgstr ""
292 292
293 293 #: rhodecode/apps/admin/views/system_info.py:129
294 294 msgid "Storage location"
295 295 msgstr ""
296 296
297 297 #: rhodecode/apps/admin/views/system_info.py:130
298 298 msgid "Storage info"
299 299 msgstr ""
300 300
301 301 #: rhodecode/apps/admin/views/system_info.py:131
302 302 msgid "Storage inodes"
303 303 msgstr ""
304 304
305 305 #: rhodecode/apps/admin/views/system_info.py:133
306 306 msgid "Gist storage location"
307 307 msgstr ""
308 308
309 309 #: rhodecode/apps/admin/views/system_info.py:134
310 310 msgid "Gist storage info"
311 311 msgstr ""
312 312
313 313 #: rhodecode/apps/admin/views/system_info.py:136
314 314 msgid "Archive cache storage location"
315 315 msgstr ""
316 316
317 317 #: rhodecode/apps/admin/views/system_info.py:137
318 318 msgid "Archive cache info"
319 319 msgstr ""
320 320
321 321 #: rhodecode/apps/admin/views/system_info.py:139
322 322 msgid "Temp storage location"
323 323 msgstr ""
324 324
325 325 #: rhodecode/apps/admin/views/system_info.py:140
326 326 msgid "Temp storage info"
327 327 msgstr ""
328 328
329 329 #: rhodecode/apps/admin/views/system_info.py:142
330 330 msgid "Search info"
331 331 msgstr ""
332 332
333 333 #: rhodecode/apps/admin/views/system_info.py:143
334 334 msgid "Search location"
335 335 msgstr ""
336 336
337 337 #: rhodecode/apps/admin/views/system_info.py:147
338 338 msgid "VCS Backends"
339 339 msgstr ""
340 340
341 341 #: rhodecode/apps/admin/views/system_info.py:148
342 342 #: rhodecode/templates/admin/settings/settings_system.mako:32
343 343 msgid "VCS Server"
344 344 msgstr ""
345 345
346 346 #: rhodecode/apps/admin/views/system_info.py:149
347 347 msgid "GIT"
348 348 msgstr ""
349 349
350 350 #: rhodecode/apps/admin/views/system_info.py:150
351 351 msgid "HG"
352 352 msgstr ""
353 353
354 354 #: rhodecode/apps/admin/views/system_info.py:151
355 355 msgid "SVN"
356 356 msgstr ""
357 357
358 358 #: rhodecode/apps/admin/views/user_groups.py:224
359 359 #, python-format
360 360 msgid "Created user group %(user_group_link)s"
361 361 msgstr ""
362 362
363 363 #: rhodecode/apps/admin/views/user_groups.py:246
364 364 #, python-format
365 365 msgid "Error occurred during creation of user group %s"
366 366 msgstr ""
367 367
368 368 #: rhodecode/apps/admin/views/users.py:208
369 369 #, python-format
370 370 msgid "Created user %(user_link)s"
371 371 msgstr ""
372 372
373 373 #: rhodecode/apps/admin/views/users.py:229
374 374 #, python-format
375 375 msgid "Error occurred during creation of user %s"
376 376 msgstr ""
377 377
378 378 #: rhodecode/apps/admin/views/users.py:332
379 379 msgid "User updated successfully"
380 380 msgstr ""
381 381
382 382 #: rhodecode/apps/admin/views/users.py:350
383 383 #, python-format
384 384 msgid "Error occurred during update of user %s"
385 385 msgstr ""
386 386
387 387 #: rhodecode/apps/admin/views/users.py:378
388 388 #, python-format
389 389 msgid "Detached %s repositories"
390 390 msgstr ""
391 391
392 392 #: rhodecode/apps/admin/views/users.py:381
393 393 #, python-format
394 394 msgid "Deleted %s repositories"
395 395 msgstr ""
396 396
397 397 #: rhodecode/apps/admin/views/users.py:387
398 398 #, python-format
399 399 msgid "Detached %s repository groups"
400 400 msgstr ""
401 401
402 402 #: rhodecode/apps/admin/views/users.py:390
403 403 #, python-format
404 404 msgid "Deleted %s repository groups"
405 405 msgstr ""
406 406
407 407 #: rhodecode/apps/admin/views/users.py:396
408 408 #, python-format
409 409 msgid "Detached %s user groups"
410 410 msgstr ""
411 411
412 412 #: rhodecode/apps/admin/views/users.py:399
413 413 #, python-format
414 414 msgid "Deleted %s user groups"
415 415 msgstr ""
416 416
417 417 #: rhodecode/apps/admin/views/users.py:405
418 418 #, python-format
419 419 msgid "Detached %s pull requests"
420 420 msgstr ""
421 421
422 422 #: rhodecode/apps/admin/views/users.py:408
423 423 #, python-format
424 424 msgid "Deleted %s pull requests"
425 425 msgstr ""
426 426
427 427 #: rhodecode/apps/admin/views/users.py:414
428 428 #, python-format
429 429 msgid "Detached %s artifacts"
430 430 msgstr ""
431 431
432 432 #: rhodecode/apps/admin/views/users.py:417
433 433 #, python-format
434 434 msgid "Deleted %s artifacts"
435 435 msgstr ""
436 436
437 437 #: rhodecode/apps/admin/views/users.py:466
438 438 msgid "Successfully deleted user `{}`"
439 439 msgstr ""
440 440
441 441 #: rhodecode/apps/admin/views/users.py:473
442 442 msgid "An error occurred during deletion of user"
443 443 msgstr ""
444 444
445 445 #: rhodecode/apps/admin/views/users.py:536
446 446 msgid ""
447 447 "The user participates as reviewer in {} pull request and cannot be deleted. \n"
448 448 "You can set the user to \"{}\" instead of deleting it."
449 449 msgstr ""
450 450
451 451 #: rhodecode/apps/admin/views/users.py:542
452 452 msgid ""
453 453 "The user participates as reviewer in {} pull requests and cannot be deleted. \n"
454 454 "You can set the user to \"{}\" instead of deleting it."
455 455 msgstr ""
456 456
457 457 #: rhodecode/apps/admin/views/users.py:625
458 458 msgid "User global permissions updated successfully"
459 459 msgstr ""
460 460
461 461 #: rhodecode/apps/admin/views/users.py:643
462 462 #: rhodecode/apps/user_group/views/__init__.py:449
463 463 msgid "An error occurred during permissions saving"
464 464 msgstr ""
465 465
466 466 #: rhodecode/apps/admin/views/users.py:663
467 467 msgid "Force password change enabled for user"
468 468 msgstr ""
469 469
470 470 #: rhodecode/apps/admin/views/users.py:671
471 471 #: rhodecode/apps/admin/views/users.py:698
472 472 msgid "An error occurred during password reset for user"
473 473 msgstr ""
474 474
475 475 #: rhodecode/apps/admin/views/users.py:689
476 476 msgid "Force password change disabled for user"
477 477 msgstr ""
478 478
479 479 #: rhodecode/apps/admin/views/users.py:756
480 480 #, python-format
481 481 msgid "Linked repository group `%s` as personal"
482 482 msgstr ""
483 483
484 484 #: rhodecode/apps/admin/views/users.py:762
485 485 #, python-format
486 486 msgid "Created repository group `%s`"
487 487 msgstr ""
488 488
489 489 #: rhodecode/apps/admin/views/users.py:766
490 490 #, python-format
491 491 msgid "Repository group `%s` is already taken"
492 492 msgstr ""
493 493
494 494 #: rhodecode/apps/admin/views/users.py:771
495 495 msgid "An error occurred during repository group creation for user"
496 496 msgstr ""
497 497
498 498 #: rhodecode/apps/admin/views/users.py:791
499 499 #: rhodecode/apps/my_account/views/my_account.py:216
500 500 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:28
501 501 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:33
502 502 msgid "Role"
503 503 msgstr ""
504 504
505 505 #: rhodecode/apps/admin/views/users.py:844
506 506 #: rhodecode/apps/my_account/views/my_account.py:267
507 507 msgid "Auth token successfully created"
508 508 msgstr ""
509 509
510 510 #: rhodecode/apps/admin/views/users.py:871
511 511 #: rhodecode/apps/my_account/views/my_account.py:289
512 512 msgid "Auth token successfully deleted"
513 513 msgstr ""
514 514
515 515 #: rhodecode/apps/admin/views/users.py:939
516 516 #: rhodecode/apps/my_account/views/my_account_ssh_keys.py:106
517 517 msgid "Ssh Key successfully created"
518 518 msgstr ""
519 519
520 520 #: rhodecode/apps/admin/views/users.py:945
521 521 #: rhodecode/apps/admin/views/users.py:949
522 522 #: rhodecode/apps/my_account/views/my_account_ssh_keys.py:112
523 523 #: rhodecode/apps/my_account/views/my_account_ssh_keys.py:116
524 524 msgid "An error occurred during ssh key saving: {}"
525 525 msgstr ""
526 526
527 527 #: rhodecode/apps/admin/views/users.py:981
528 528 #: rhodecode/apps/my_account/views/my_account_ssh_keys.py:144
529 529 msgid "Ssh key successfully deleted"
530 530 msgstr ""
531 531
532 532 #: rhodecode/apps/admin/views/users.py:1022
533 533 #, python-format
534 534 msgid "Added new email address `%s` for user account"
535 535 msgstr ""
536 536
537 537 #: rhodecode/apps/admin/views/users.py:1028
538 538 msgid "Email `{}` is already registered for another user."
539 539 msgstr ""
540 540
541 541 #: rhodecode/apps/admin/views/users.py:1032
542 542 msgid "An error occurred during email saving"
543 543 msgstr ""
544 544
545 545 #: rhodecode/apps/admin/views/users.py:1057
546 546 msgid "Removed email address from user account"
547 547 msgstr ""
548 548
549 549 #: rhodecode/apps/admin/views/users.py:1098
550 550 #, python-format
551 551 msgid "An error occurred during ip saving:%s"
552 552 msgstr ""
553 553
554 554 #: rhodecode/apps/admin/views/users.py:1120
555 555 msgid "An error occurred during ip saving"
556 556 msgstr ""
557 557
558 558 #: rhodecode/apps/admin/views/users.py:1124
559 559 #, python-format
560 560 msgid "Added ips %s to user whitelist"
561 561 msgstr ""
562 562
563 563 #: rhodecode/apps/admin/views/users.py:1152
564 564 msgid "Removed ip address from user whitelist"
565 565 msgstr ""
566 566
567 567 #: rhodecode/apps/admin/views/users.py:1212
568 568 msgid "Groups successfully changed"
569 569 msgstr ""
570 570
571 571 #: rhodecode/apps/admin/views/users.py:1315
572 572 msgid "Deleted {} cache keys"
573 573 msgstr ""
574 574
575 575 #: rhodecode/apps/gist/views.py:56 rhodecode/model/auth_token.py:51
576 576 msgid "forever"
577 577 msgstr ""
578 578
579 579 #: rhodecode/apps/gist/views.py:57
580 580 msgid "5 minutes"
581 581 msgstr ""
582 582
583 583 #: rhodecode/apps/gist/views.py:58
584 584 msgid "1 hour"
585 585 msgstr ""
586 586
587 587 #: rhodecode/apps/gist/views.py:59
588 588 msgid "1 day"
589 589 msgstr ""
590 590
591 591 #: rhodecode/apps/gist/views.py:60
592 592 msgid "1 month"
593 593 msgstr ""
594 594
595 #: rhodecode/apps/gist/views.py:63 rhodecode/public/js/scripts.js:48529
595 #: rhodecode/apps/gist/views.py:63 rhodecode/public/js/scripts.js:48670
596 596 #: rhodecode/public/js/scripts.min.js:1
597 597 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:48
598 598 #: rhodecode/public/js/src/rhodecode.js:634
599 599 msgid "Lifetime"
600 600 msgstr ""
601 601
602 602 #: rhodecode/apps/gist/views.py:65
603 603 msgid "Requires registered account"
604 604 msgstr ""
605 605
606 606 #: rhodecode/apps/gist/views.py:66
607 607 msgid "Can be accessed by anonymous users"
608 608 msgstr ""
609 609
610 610 #: rhodecode/apps/gist/views.py:208
611 611 msgid "Error occurred during gist creation"
612 612 msgstr ""
613 613
614 614 #: rhodecode/apps/gist/views.py:230
615 615 #, python-format
616 616 msgid "Deleted gist %s"
617 617 msgstr ""
618 618
619 619 #: rhodecode/apps/gist/views.py:303
620 620 #: rhodecode/templates/admin/gists/gist_show.mako:76
621 621 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:50
622 622 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:55
623 623 #: rhodecode/templates/data_table/_dt_elements.mako:341
624 624 msgid "never"
625 625 msgstr ""
626 626
627 627 #: rhodecode/apps/gist/views.py:309
628 628 #, python-format
629 629 msgid "%(expiry)s - current value"
630 630 msgstr ""
631 631
632 632 #: rhodecode/apps/gist/views.py:351
633 633 msgid "Successfully updated gist content"
634 634 msgstr ""
635 635
636 636 #: rhodecode/apps/gist/views.py:356
637 637 msgid "Successfully updated gist data"
638 638 msgstr ""
639 639
640 640 #: rhodecode/apps/gist/views.py:359
641 641 msgid "Error occurred during update of gist {}: {}"
642 642 msgstr ""
643 643
644 644 #: rhodecode/apps/gist/views.py:363
645 645 #, python-format
646 646 msgid "Error occurred during update of gist %s"
647 647 msgstr ""
648 648
649 649 #: rhodecode/apps/home/views.py:442
650 650 #: rhodecode/apps/repository/views/repo_pull_requests.py:972
651 651 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:219
652 652 #: rhodecode/templates/admin/repos/repo_add.mako:15
653 653 #: rhodecode/templates/admin/repos/repo_add.mako:19
654 654 #: rhodecode/templates/admin/users/user_edit_advanced.mako:12
655 655 #: rhodecode/templates/base/base.mako:114
656 #: rhodecode/templates/base/base.mako:133
657 #: rhodecode/templates/base/base.mako:1191
656 #: rhodecode/templates/base/base.mako:134
657 #: rhodecode/templates/base/base.mako:1192
658 658 msgid "Repositories"
659 659 msgstr ""
660 660
661 661 #: rhodecode/apps/home/views.py:466
662 662 #: rhodecode/templates/admin/integrations/form.mako:17
663 663 #: rhodecode/templates/admin/integrations/list.mako:10
664 664 #: rhodecode/templates/admin/permissions/permissions_objects.mako:31
665 665 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:218
666 666 msgid "Repository Groups"
667 667 msgstr ""
668 668
669 669 #: rhodecode/apps/journal/views.py:129 rhodecode/apps/journal/views.py:175
670 670 msgid "public journal"
671 671 msgstr ""
672 672
673 673 #: rhodecode/apps/journal/views.py:133 rhodecode/apps/journal/views.py:179
674 674 msgid "journal"
675 675 msgstr ""
676 676
677 677 #: rhodecode/apps/login/views.py:290 rhodecode/apps/login/views.py:386
678 678 msgid "Bad captcha"
679 679 msgstr ""
680 680
681 681 #: rhodecode/apps/login/views.py:316
682 682 msgid "You have successfully registered with RhodeCode. You can log-in now."
683 683 msgstr ""
684 684
685 685 #: rhodecode/apps/login/views.py:320
686 686 msgid "Please use the {identity} button to log-in"
687 687 msgstr ""
688 688
689 689 #: rhodecode/apps/login/views.py:355
690 690 msgid "If such email exists, a password reset link was sent to it."
691 691 msgstr ""
692 692
693 693 #: rhodecode/apps/login/views.py:368
694 694 msgid "Password reset has been disabled."
695 695 msgstr ""
696 696
697 697 #: rhodecode/apps/login/views.py:455
698 698 msgid "Given reset token is invalid"
699 699 msgstr ""
700 700
701 701 #: rhodecode/apps/login/views.py:463
702 702 msgid "Your password reset was successful, a new password has been sent to your email"
703 703 msgstr ""
704 704
705 705 #: rhodecode/apps/my_account/views/my_account.py:135
706 706 msgid "Your account was updated successfully"
707 707 msgstr ""
708 708
709 709 #: rhodecode/apps/my_account/views/my_account.py:142
710 710 msgid "Error occurred during update of user"
711 711 msgstr ""
712 712
713 713 #: rhodecode/apps/my_account/views/my_account.py:194
714 714 msgid "Error occurred during update of user password"
715 715 msgstr ""
716 716
717 717 #: rhodecode/apps/my_account/views/my_account.py:201
718 718 msgid "Successfully updated password"
719 719 msgstr ""
720 720
721 721 #: rhodecode/apps/my_account/views/my_account.py:347
722 722 msgid "Error occurred during adding email"
723 723 msgstr ""
724 724
725 725 #: rhodecode/apps/my_account/views/my_account.py:350
726 726 msgid "Successfully added email"
727 727 msgstr ""
728 728
729 729 #: rhodecode/apps/my_account/views/my_account.py:370
730 730 msgid "Email successfully deleted"
731 731 msgstr ""
732 732
733 733 #: rhodecode/apps/my_account/views/my_account.py:566
734 734 msgid "Position {} is defined twice. Please correct this error."
735 735 msgstr ""
736 736
737 737 #: rhodecode/apps/my_account/views/my_account.py:579
738 738 msgid "Update Bookmarks"
739 739 msgstr ""
740 740
741 741 #: rhodecode/apps/my_account/views/my_account.py:581
742 742 msgid "Failed to update bookmarks. Make sure an unique position is used."
743 743 msgstr ""
744 744
745 745 #: rhodecode/apps/repo_group/views/repo_group_advanced.py:53
746 746 #: rhodecode/apps/repository/views/repo_settings_advanced.py:82
747 747 msgid "updated commit cache"
748 748 msgstr ""
749 749
750 750 #: rhodecode/apps/repo_group/views/repo_group_advanced.py:98
751 751 #, python-format
752 752 msgid "Removed repository group `%s`"
753 753 msgstr ""
754 754
755 755 #: rhodecode/apps/repo_group/views/repo_group_advanced.py:102
756 756 #, python-format
757 757 msgid "Error occurred during deletion of repository group %s"
758 758 msgstr ""
759 759
760 760 #: rhodecode/apps/repo_group/views/repo_group_permissions.py:69
761 761 #: rhodecode/apps/user_group/views/__init__.py:322
762 762 msgid "Cannot change permission for yourself as admin"
763 763 msgstr ""
764 764
765 765 #: rhodecode/apps/repo_group/views/repo_group_permissions.py:93
766 766 msgid "Repository Group permissions updated"
767 767 msgstr ""
768 768
769 769 #: rhodecode/apps/repo_group/views/repo_group_settings.py:168
770 770 msgid "Repository Group `{}` updated successfully"
771 771 msgstr ""
772 772
773 773 #: rhodecode/apps/repo_group/views/repo_group_settings.py:173
774 774 #, python-format
775 775 msgid "Error occurred during update of repository group %s"
776 776 msgstr ""
777 777
778 778 #: rhodecode/apps/repository/views/repo_caches.py:75
779 779 msgid "Cache invalidation successful"
780 780 msgstr ""
781 781
782 782 #: rhodecode/apps/repository/views/repo_caches.py:79
783 783 msgid "An error occurred during cache invalidation"
784 784 msgstr ""
785 785
786 786 #: rhodecode/apps/repository/views/repo_changelog.py:66
787 787 #: rhodecode/apps/repository/views/repo_compare.py:64
788 788 #: rhodecode/apps/repository/views/repo_pull_requests.py:830
789 789 msgid "There are no commits yet"
790 790 msgstr ""
791 791
792 792 #: rhodecode/apps/repository/views/repo_changelog.py:71
793 793 #: rhodecode/apps/repository/views/repo_files.py:205
794 794 msgid "No such commit exists for this repository"
795 795 msgstr ""
796 796
797 797 #: rhodecode/apps/repository/views/repo_checks.py:94
798 798 #, python-format
799 799 msgid "Created repository %s from %s"
800 800 msgstr ""
801 801
802 802 #: rhodecode/apps/repository/views/repo_checks.py:103
803 803 #, python-format
804 804 msgid "Forked repository %s as %s"
805 805 msgstr ""
806 806
807 807 #: rhodecode/apps/repository/views/repo_checks.py:106
808 808 #, python-format
809 809 msgid "Created repository %s"
810 810 msgstr ""
811 811
812 812 #: rhodecode/apps/repository/views/repo_commits.py:112
813 813 msgid "No such commit exists. Org exception: `{}`"
814 814 msgstr ""
815 815
816 816 #: rhodecode/apps/repository/views/repo_commits.py:388
817 817 #: rhodecode/apps/repository/views/repo_pull_requests.py:1588
818 818 #, python-format
819 819 msgid "Status change %(transition_icon)s %(status)s"
820 820 msgstr ""
821 821
822 822 #: rhodecode/apps/repository/views/repo_commits.py:426
823 823 msgid "Changing the status of a commit associated with a closed pull request is not allowed"
824 824 msgstr ""
825 825
826 826 #: rhodecode/apps/repository/views/repo_commits.py:472
827 827 #: rhodecode/apps/repository/views/repo_pull_requests.py:1671
828 828 msgid "posted {} new {} comment"
829 829 msgstr ""
830 830
831 831 #: rhodecode/apps/repository/views/repo_commits.py:474
832 832 #: rhodecode/apps/repository/views/repo_pull_requests.py:1673
833 833 msgid "posted {} new {} comments"
834 834 msgstr ""
835 835
836 836 #: rhodecode/apps/repository/views/repo_compare.py:99
837 837 msgid "Select commit"
838 838 msgstr ""
839 839
840 840 #: rhodecode/apps/repository/views/repo_compare.py:167
841 841 msgid "Could not find the source repo: `{}`"
842 842 msgstr ""
843 843
844 844 #: rhodecode/apps/repository/views/repo_compare.py:175
845 845 msgid "Could not find the target repo: `{}`"
846 846 msgstr ""
847 847
848 848 #: rhodecode/apps/repository/views/repo_compare.py:186
849 849 msgid "The comparison of two different kinds of remote repos is not available"
850 850 msgstr ""
851 851
852 852 #: rhodecode/apps/repository/views/repo_compare.py:219
853 853 msgid "Could not compare repos with different large file settings"
854 854 msgstr ""
855 855
856 856 #: rhodecode/apps/repository/views/repo_compare.py:265
857 857 #, python-format
858 858 msgid "Repositories unrelated. Cannot compare commit %(commit1)s from repository %(repo1)s with commit %(commit2)s from repository %(repo2)s."
859 859 msgstr ""
860 860
861 861 #: rhodecode/apps/repository/views/repo_feed.py:66
862 862 #, python-format
863 863 msgid "Changes on %s repository"
864 864 msgstr ""
865 865
866 866 #: rhodecode/apps/repository/views/repo_feed.py:67
867 867 #, python-format
868 868 msgid "%s %s feed"
869 869 msgstr ""
870 870
871 871 #: rhodecode/apps/repository/views/repo_files.py:96
872 872 #, python-format
873 873 msgid "This repository has been locked by %s on %s"
874 874 msgstr ""
875 875
876 876 #: rhodecode/apps/repository/views/repo_files.py:109
877 877 msgid "Cannot modify file. Given commit `{}` is not head of a branch."
878 878 msgstr ""
879 879
880 880 #: rhodecode/apps/repository/views/repo_files.py:127
881 881 msgid "Branch `{}` changes forbidden by rule {}."
882 882 msgstr ""
883 883
884 884 #: rhodecode/apps/repository/views/repo_files.py:175
885 885 msgid "Click here to add a new file."
886 886 msgstr ""
887 887
888 888 #: rhodecode/apps/repository/views/repo_files.py:180
889 889 #, python-format
890 890 msgid "There are no files yet. %s"
891 891 msgstr ""
892 892
893 893 #: rhodecode/apps/repository/views/repo_files.py:185
894 894 msgid "No such commit exists for this repository. Commit: {}"
895 895 msgstr ""
896 896
897 #: rhodecode/apps/repository/views/repo_files.py:358
897 #: rhodecode/apps/repository/views/repo_files.py:359
898 898 msgid "Downloads disabled"
899 899 msgstr ""
900 900
901 #: rhodecode/apps/repository/views/repo_files.py:364
901 #: rhodecode/apps/repository/views/repo_files.py:365
902 902 msgid "Unknown archive type for: `{}`"
903 903 msgstr ""
904 904
905 #: rhodecode/apps/repository/views/repo_files.py:370
905 #: rhodecode/apps/repository/views/repo_files.py:371
906 906 msgid "Unknown commit_id {}"
907 907 msgstr ""
908 908
909 #: rhodecode/apps/repository/views/repo_files.py:373
909 #: rhodecode/apps/repository/views/repo_files.py:374
910 910 msgid "Empty repository"
911 911 msgstr ""
912 912
913 #: rhodecode/apps/repository/views/repo_files.py:378
913 #: rhodecode/apps/repository/views/repo_files.py:384
914 914 msgid "No node at path {} for this repository"
915 915 msgstr ""
916 916
917 #: rhodecode/apps/repository/views/repo_files.py:429
917 #: rhodecode/apps/repository/views/repo_files.py:436
918 918 msgid "Unknown archive type"
919 919 msgstr ""
920 920
921 #: rhodecode/apps/repository/views/repo_files.py:986
921 #: rhodecode/apps/repository/views/repo_files.py:993
922 922 msgid "Changesets"
923 923 msgstr ""
924 924
925 #: rhodecode/apps/repository/views/repo_files.py:1007
926 #: rhodecode/apps/repository/views/repo_summary.py:243
927 #: rhodecode/model/pull_request.py:1910 rhodecode/model/scm.py:999
925 #: rhodecode/apps/repository/views/repo_files.py:1014
926 #: rhodecode/apps/repository/views/repo_summary.py:239
927 #: rhodecode/model/pull_request.py:1912 rhodecode/model/scm.py:999
928 928 #: rhodecode/templates/base/vcs_settings.mako:235
929 929 #: rhodecode/templates/summary/components.mako:10
930 930 msgid "Branches"
931 931 msgstr ""
932 932
933 #: rhodecode/apps/repository/views/repo_files.py:1011
933 #: rhodecode/apps/repository/views/repo_files.py:1018
934 934 #: rhodecode/model/scm.py:1016 rhodecode/templates/base/vcs_settings.mako:260
935 935 #: rhodecode/templates/summary/components.mako:34
936 936 msgid "Tags"
937 937 msgstr ""
938 938
939 #: rhodecode/apps/repository/views/repo_files.py:1155
940 #: rhodecode/apps/repository/views/repo_files.py:1181
939 #: rhodecode/apps/repository/views/repo_files.py:1162
940 #: rhodecode/apps/repository/views/repo_files.py:1188
941 941 msgid "Deleted file {} via RhodeCode Enterprise"
942 942 msgstr ""
943 943
944 #: rhodecode/apps/repository/views/repo_files.py:1202
944 #: rhodecode/apps/repository/views/repo_files.py:1209
945 945 msgid "Successfully deleted file `{}`"
946 946 msgstr ""
947 947
948 #: rhodecode/apps/repository/views/repo_files.py:1206
948 #: rhodecode/apps/repository/views/repo_files.py:1213
949 #: rhodecode/apps/repository/views/repo_files.py:1326
950 #: rhodecode/apps/repository/views/repo_files.py:1450
951 #: rhodecode/apps/repository/views/repo_files.py:1571
952 msgid "Error occurred during commit"
953 msgstr ""
954
955 #: rhodecode/apps/repository/views/repo_files.py:1243
956 #: rhodecode/apps/repository/views/repo_files.py:1272
957 msgid "Edited file {} via RhodeCode Enterprise"
958 msgstr ""
959
960 #: rhodecode/apps/repository/views/repo_files.py:1295
961 msgid "No changes detected on {}"
962 msgstr ""
963
949 964 #: rhodecode/apps/repository/views/repo_files.py:1319
950 #: rhodecode/apps/repository/views/repo_files.py:1443
951 #: rhodecode/apps/repository/views/repo_files.py:1564
952 msgid "Error occurred during commit"
953 msgstr ""
954
955 #: rhodecode/apps/repository/views/repo_files.py:1236
956 #: rhodecode/apps/repository/views/repo_files.py:1265
957 msgid "Edited file {} via RhodeCode Enterprise"
958 msgstr ""
959
960 #: rhodecode/apps/repository/views/repo_files.py:1288
961 msgid "No changes detected on {}"
962 msgstr ""
963
964 #: rhodecode/apps/repository/views/repo_files.py:1312
965 965 msgid "Successfully committed changes to file `{}`"
966 966 msgstr ""
967 967
968 #: rhodecode/apps/repository/views/repo_files.py:1348
969 #: rhodecode/apps/repository/views/repo_files.py:1387
968 #: rhodecode/apps/repository/views/repo_files.py:1355
969 #: rhodecode/apps/repository/views/repo_files.py:1394
970 970 msgid "Added file via RhodeCode Enterprise"
971 971 msgstr ""
972 972
973 #: rhodecode/apps/repository/views/repo_files.py:1403
973 #: rhodecode/apps/repository/views/repo_files.py:1410
974 974 msgid "No filename specified"
975 975 msgstr ""
976 976
977 #: rhodecode/apps/repository/views/repo_files.py:1428
977 #: rhodecode/apps/repository/views/repo_files.py:1435
978 978 msgid "Successfully committed new file `{}`"
979 979 msgstr ""
980 980
981 #: rhodecode/apps/repository/views/repo_files.py:1436
982 #: rhodecode/apps/repository/views/repo_files.py:1546
981 #: rhodecode/apps/repository/views/repo_files.py:1443
982 #: rhodecode/apps/repository/views/repo_files.py:1553
983 983 msgid "The location specified must be a relative path and must not contain .. in the path"
984 984 msgstr ""
985 985
986 #: rhodecode/apps/repository/views/repo_files.py:1491
986 #: rhodecode/apps/repository/views/repo_files.py:1498
987 987 msgid "Uploaded file via RhodeCode Enterprise"
988 988 msgstr ""
989 989
990 #: rhodecode/apps/repository/views/repo_files.py:1535
990 #: rhodecode/apps/repository/views/repo_files.py:1542
991 991 msgid "Successfully committed {} new files"
992 992 msgstr ""
993 993
994 #: rhodecode/apps/repository/views/repo_files.py:1537
994 #: rhodecode/apps/repository/views/repo_files.py:1544
995 995 msgid "Successfully committed 1 new file"
996 996 msgstr ""
997 997
998 998 #: rhodecode/apps/repository/views/repo_forks.py:140
999 999 msgid "Compare fork"
1000 1000 msgstr ""
1001 1001
1002 1002 #: rhodecode/apps/repository/views/repo_forks.py:239
1003 1003 #, python-format
1004 1004 msgid "An error occurred during repository forking %s"
1005 1005 msgstr ""
1006 1006
1007 1007 #: rhodecode/apps/repository/views/repo_permissions.py:53
1008 1008 msgid "Explicitly add user or user group with write or higher permission to modify their branch permissions."
1009 1009 msgstr ""
1010 1010
1011 1011 #: rhodecode/apps/repository/views/repo_permissions.py:85
1012 1012 msgid "Repository access permissions updated"
1013 1013 msgstr ""
1014 1014
1015 1015 #: rhodecode/apps/repository/views/repo_permissions.py:116
1016 1016 msgid "Repository `{}` private mode set successfully"
1017 1017 msgstr ""
1018 1018
1019 1019 #: rhodecode/apps/repository/views/repo_permissions.py:124
1020 1020 #: rhodecode/apps/repository/views/repo_settings.py:169
1021 1021 msgid "Error occurred during update of repository {}"
1022 1022 msgstr ""
1023 1023
1024 1024 #: rhodecode/apps/repository/views/repo_pull_requests.py:326
1025 1025 msgid "Pull Request state was force changed to `{}`"
1026 1026 msgstr ""
1027 1027
1028 1028 #: rhodecode/apps/repository/views/repo_pull_requests.py:857
1029 1029 msgid "Commit does not exist"
1030 1030 msgstr ""
1031 1031
1032 1032 #: rhodecode/apps/repository/views/repo_pull_requests.py:1119
1033 1033 msgid "Error creating pull request: {}"
1034 1034 msgstr ""
1035 1035
1036 1036 #: rhodecode/apps/repository/views/repo_pull_requests.py:1139
1037 1037 msgid "source_repo or target repo not found"
1038 1038 msgstr ""
1039 1039
1040 1040 #: rhodecode/apps/repository/views/repo_pull_requests.py:1150
1041 1041 msgid "Not Enough permissions to source repo `{}`."
1042 1042 msgstr ""
1043 1043
1044 1044 #: rhodecode/apps/repository/views/repo_pull_requests.py:1165
1045 1045 msgid "Not Enough permissions to target repo `{}`."
1046 1046 msgstr ""
1047 1047
1048 1048 #: rhodecode/apps/repository/views/repo_pull_requests.py:1235
1049 1049 msgid "Successfully opened new pull request"
1050 1050 msgstr ""
1051 1051
1052 1052 #: rhodecode/apps/repository/views/repo_pull_requests.py:1238
1053 1053 msgid "Error occurred during creation of this pull request."
1054 1054 msgstr ""
1055 1055
1056 1056 #: rhodecode/apps/repository/views/repo_pull_requests.py:1267
1057 1057 #: rhodecode/apps/repository/views/repo_pull_requests.py:1336
1058 1058 msgid "Cannot update closed pull requests."
1059 1059 msgstr ""
1060 1060
1061 1061 #: rhodecode/apps/repository/views/repo_pull_requests.py:1299
1062 1062 msgid "Cannot update pull requests commits in state other than `{}`. Current state is: `{}`"
1063 1063 msgstr ""
1064 1064
1065 1065 #: rhodecode/apps/repository/views/repo_pull_requests.py:1342
1066 1066 msgid "Pull request title & description updated."
1067 1067 msgstr ""
1068 1068
1069 1069 #: rhodecode/apps/repository/views/repo_pull_requests.py:1364
1070 1070 msgid "Pull request updated to \"{source_commit_id}\" with {count_added} added, {count_removed} removed commits. Source of changes: {change_source}."
1071 1071 msgstr ""
1072 1072
1073 1073 #: rhodecode/apps/repository/views/repo_pull_requests.py:1404
1074 1074 msgid "Pull request reviewers updated."
1075 1075 msgstr ""
1076 1076
1077 1077 #: rhodecode/apps/repository/views/repo_pull_requests.py:1428
1078 1078 msgid "Pull request observers updated."
1079 1079 msgstr ""
1080 1080
1081 1081 #: rhodecode/apps/repository/views/repo_pull_requests.py:1452
1082 1082 msgid "Cannot merge pull requests in state other than `{}`. Current state is: `{}`"
1083 1083 msgstr ""
1084 1084
1085 1085 #: rhodecode/apps/repository/views/repo_pull_requests.py:1498
1086 1086 msgid "Pull request was successfully merged and closed."
1087 1087 msgstr ""
1088 1088
1089 1089 #: rhodecode/apps/repository/views/repo_pull_requests.py:1526
1090 1090 msgid "Successfully deleted pull request"
1091 1091 msgstr ""
1092 1092
1093 1093 #: rhodecode/apps/repository/views/repo_settings.py:165
1094 1094 msgid "Repository `{}` updated successfully"
1095 1095 msgstr ""
1096 1096
1097 1097 #: rhodecode/apps/repository/views/repo_settings.py:199
1098 1098 msgid "Unlocked"
1099 1099 msgstr ""
1100 1100
1101 1101 #: rhodecode/apps/repository/views/repo_settings.py:204
1102 1102 msgid "Locked"
1103 1103 msgstr ""
1104 1104
1105 1105 #: rhodecode/apps/repository/views/repo_settings.py:206
1106 1106 #, python-format
1107 1107 msgid "Repository has been %s"
1108 1108 msgstr ""
1109 1109
1110 1110 #: rhodecode/apps/repository/views/repo_settings.py:210
1111 1111 #: rhodecode/apps/repository/views/repo_settings_advanced.py:287
1112 1112 msgid "An error occurred during unlocking"
1113 1113 msgstr ""
1114 1114
1115 1115 #: rhodecode/apps/repository/views/repo_settings.py:248
1116 1116 msgid "An error occurred during deletion of repository stats"
1117 1117 msgstr ""
1118 1118
1119 1119 #: rhodecode/apps/repository/views/repo_settings_advanced.py:108
1120 1120 #, python-format
1121 1121 msgid "Archived repository `%s`"
1122 1122 msgstr ""
1123 1123
1124 1124 #: rhodecode/apps/repository/views/repo_settings_advanced.py:113
1125 1125 #, python-format
1126 1126 msgid "An error occurred during archiving of `%s`"
1127 1127 msgstr ""
1128 1128
1129 1129 #: rhodecode/apps/repository/views/repo_settings_advanced.py:148
1130 1130 #, python-format
1131 1131 msgid "Detached %s forks"
1132 1132 msgstr ""
1133 1133
1134 1134 #: rhodecode/apps/repository/views/repo_settings_advanced.py:150
1135 1135 #, python-format
1136 1136 msgid "Deleted %s forks"
1137 1137 msgstr ""
1138 1138
1139 1139 #: rhodecode/apps/repository/views/repo_settings_advanced.py:159
1140 1140 #, python-format
1141 1141 msgid "Deleted repository `%s`"
1142 1142 msgstr ""
1143 1143
1144 1144 #: rhodecode/apps/repository/views/repo_settings_advanced.py:166
1145 1145 msgid "detach or delete"
1146 1146 msgstr ""
1147 1147
1148 1148 #: rhodecode/apps/repository/views/repo_settings_advanced.py:167
1149 1149 msgid "Cannot delete `{repo}` it still contains attached forks. Try using {delete_or_detach} option."
1150 1150 msgstr ""
1151 1151
1152 1152 #: rhodecode/apps/repository/views/repo_settings_advanced.py:182
1153 1153 msgid "Cannot delete `{repo}` it still contains {num} attached pull requests. Consider archiving the repository instead."
1154 1154 msgstr ""
1155 1155
1156 1156 #: rhodecode/apps/repository/views/repo_settings_advanced.py:191
1157 1157 #, python-format
1158 1158 msgid "An error occurred during deletion of `%s`"
1159 1159 msgstr ""
1160 1160
1161 1161 #: rhodecode/apps/repository/views/repo_settings_advanced.py:213
1162 1162 msgid "Updated repository visibility in public journal"
1163 1163 msgstr ""
1164 1164
1165 1165 #: rhodecode/apps/repository/views/repo_settings_advanced.py:217
1166 1166 msgid "An error occurred during setting this repository in public journal"
1167 1167 msgstr ""
1168 1168
1169 1169 #: rhodecode/apps/repository/views/repo_settings_advanced.py:250
1170 1170 msgid "Nothing"
1171 1171 msgstr ""
1172 1172
1173 1173 #: rhodecode/apps/repository/views/repo_settings_advanced.py:253
1174 1174 #, python-format
1175 1175 msgid "Marked repo %s as fork of %s"
1176 1176 msgstr ""
1177 1177
1178 1178 #: rhodecode/apps/repository/views/repo_settings_advanced.py:260
1179 1179 msgid "An error occurred during this operation"
1180 1180 msgstr ""
1181 1181
1182 1182 #: rhodecode/apps/repository/views/repo_settings_advanced.py:281
1183 1183 msgid "Locked repository"
1184 1184 msgstr ""
1185 1185
1186 1186 #: rhodecode/apps/repository/views/repo_settings_advanced.py:284
1187 1187 msgid "Unlocked repository"
1188 1188 msgstr ""
1189 1189
1190 1190 #: rhodecode/apps/repository/views/repo_settings_advanced.py:301
1191 1191 msgid "installed updated hooks into this repository"
1192 1192 msgstr ""
1193 1193
1194 1194 #: rhodecode/apps/repository/views/repo_settings_fields.py:79
1195 1195 msgid "An error occurred during creation of field"
1196 1196 msgstr ""
1197 1197
1198 1198 #: rhodecode/apps/repository/views/repo_settings_fields.py:98
1199 1199 msgid "An error occurred during removal of field"
1200 1200 msgstr ""
1201 1201
1202 1202 #: rhodecode/apps/repository/views/repo_settings_issue_trackers.py:77
1203 1203 msgid "Error occurred during deleting issue tracker entry"
1204 1204 msgstr ""
1205 1205
1206 1206 #: rhodecode/apps/repository/views/repo_settings_remote.py:58
1207 1207 msgid "Pulled from remote location"
1208 1208 msgstr ""
1209 1209
1210 1210 #: rhodecode/apps/repository/views/repo_settings_remote.py:61
1211 1211 msgid "An error occurred during pull from remote location"
1212 1212 msgstr ""
1213 1213
1214 1214 #: rhodecode/apps/repository/views/repo_settings_vcs.py:139
1215 1215 msgid "Error occurred during updating repository VCS settings"
1216 1216 msgstr ""
1217 1217
1218 #: rhodecode/apps/repository/views/repo_summary.py:222
1218 #: rhodecode/apps/repository/views/repo_summary.py:218
1219 1219 #: rhodecode/templates/admin/permissions/permissions.mako:42
1220 1220 #: rhodecode/templates/summary/components.mako:8
1221 1221 msgid "Branch"
1222 1222 msgstr ""
1223 1223
1224 #: rhodecode/apps/repository/views/repo_summary.py:223
1224 #: rhodecode/apps/repository/views/repo_summary.py:219
1225 1225 #: rhodecode/templates/summary/components.mako:32
1226 1226 msgid "Tag"
1227 1227 msgstr ""
1228 1228
1229 #: rhodecode/apps/repository/views/repo_summary.py:224
1229 #: rhodecode/apps/repository/views/repo_summary.py:220
1230 1230 #: rhodecode/templates/summary/components.mako:44
1231 1231 msgid "Bookmark"
1232 1232 msgstr ""
1233 1233
1234 #: rhodecode/apps/repository/views/repo_summary.py:244
1234 #: rhodecode/apps/repository/views/repo_summary.py:240
1235 1235 msgid "Closed branches"
1236 1236 msgstr ""
1237 1237
1238 1238 #: rhodecode/apps/ssh_support/events.py:29
1239 1239 msgid "RhodeCode SSH Key files changed."
1240 1240 msgstr ""
1241 1241
1242 1242 #: rhodecode/apps/svn_support/events.py:30
1243 1243 msgid "Configuration for Apache mad_dav_svn changed."
1244 1244 msgstr ""
1245 1245
1246 1246 #: rhodecode/apps/user_group/views/__init__.py:176
1247 1247 #, python-format
1248 1248 msgid "Updated user group %s"
1249 1249 msgstr ""
1250 1250
1251 1251 #: rhodecode/apps/user_group/views/__init__.py:212
1252 1252 #, python-format
1253 1253 msgid "Error occurred during update of user group %s"
1254 1254 msgstr ""
1255 1255
1256 1256 #: rhodecode/apps/user_group/views/__init__.py:235
1257 1257 msgid "Successfully deleted user group"
1258 1258 msgstr ""
1259 1259
1260 1260 #: rhodecode/apps/user_group/views/__init__.py:240
1261 1261 msgid "An error occurred during deletion of user group"
1262 1262 msgstr ""
1263 1263
1264 1264 #: rhodecode/apps/user_group/views/__init__.py:335
1265 1265 msgid "Target group cannot be the same"
1266 1266 msgstr ""
1267 1267
1268 1268 #: rhodecode/apps/user_group/views/__init__.py:350
1269 1269 msgid "User Group permissions updated"
1270 1270 msgstr ""
1271 1271
1272 1272 #: rhodecode/apps/user_group/views/__init__.py:429
1273 1273 msgid "User Group global permissions updated successfully"
1274 1274 msgstr ""
1275 1275
1276 1276 #: rhodecode/apps/user_group/views/__init__.py:505
1277 1277 msgid "User Group synchronization updated successfully"
1278 1278 msgstr ""
1279 1279
1280 1280 #: rhodecode/apps/user_group/views/__init__.py:509
1281 1281 msgid "An error occurred during synchronization update"
1282 1282 msgstr ""
1283 1283
1284 1284 #: rhodecode/authentication/routes.py:61
1285 1285 #: rhodecode/templates/admin/auth/plugin_settings.mako:14
1286 1286 msgid "Authentication Plugins"
1287 1287 msgstr ""
1288 1288
1289 1289 #: rhodecode/authentication/schema.py:36
1290 1290 msgid "Enable or disable this authentication plugin."
1291 1291 msgstr ""
1292 1292
1293 1293 #: rhodecode/authentication/schema.py:38 rhodecode/integrations/schema.py:32
1294 #: rhodecode/model/permission.py:110 rhodecode/model/permission.py:114
1295 #: rhodecode/model/permission.py:118 rhodecode/model/permission.py:122
1296 #: rhodecode/model/permission.py:126 rhodecode/model/permission.py:130
1294 #: rhodecode/model/permission.py:112 rhodecode/model/permission.py:116
1295 #: rhodecode/model/permission.py:120 rhodecode/model/permission.py:124
1296 #: rhodecode/model/permission.py:128 rhodecode/model/permission.py:132
1297 1297 #: rhodecode/model/validation_schema/schemas/integration_schema.py:195
1298 1298 #: rhodecode/templates/admin/auth/auth_settings.mako:64
1299 1299 #: rhodecode/templates/admin/integrations/list.mako:71
1300 1300 #: rhodecode/templates/admin/my_account/my_account_notifications.mako:23
1301 1301 msgid "Enabled"
1302 1302 msgstr ""
1303 1303
1304 1304 #: rhodecode/authentication/schema.py:44
1305 1305 msgid ""
1306 1306 "Amount of seconds to cache the authentication and permissions check response call for this plugin. \n"
1307 1307 "Useful for expensive calls like LDAP to improve the performance of the system (0 means disabled)."
1308 1308 msgstr ""
1309 1309
1310 1310 #: rhodecode/authentication/schema.py:49
1311 1311 msgid "Auth Cache TTL"
1312 1312 msgstr ""
1313 1313
1314 1314 #: rhodecode/authentication/views.py:92
1315 1315 msgid "Errors exist when saving plugin settings. Please check the form inputs."
1316 1316 msgstr ""
1317 1317
1318 1318 #: rhodecode/authentication/views.py:105 rhodecode/authentication/views.py:164
1319 1319 msgid "Auth settings updated successfully."
1320 1320 msgstr ""
1321 1321
1322 1322 #: rhodecode/authentication/views.py:167
1323 1323 msgid "Errors exist when saving plugin setting. Please check the form inputs."
1324 1324 msgstr ""
1325 1325
1326 1326 #: rhodecode/authentication/views.py:175
1327 1327 msgid "Error occurred during update of auth settings."
1328 1328 msgstr ""
1329 1329
1330 1330 #: rhodecode/authentication/plugins/auth_crowd.py:60
1331 1331 msgid "The FQDN or IP of the Atlassian CROWD Server"
1332 1332 msgstr ""
1333 1333
1334 1334 #: rhodecode/authentication/plugins/auth_crowd.py:62
1335 1335 msgid "Host"
1336 1336 msgstr ""
1337 1337
1338 1338 #: rhodecode/authentication/plugins/auth_crowd.py:67
1339 1339 msgid "The Port in use by the Atlassian CROWD Server"
1340 1340 msgstr ""
1341 1341
1342 1342 #: rhodecode/authentication/plugins/auth_crowd.py:69
1343 1343 #: rhodecode/authentication/plugins/auth_ldap.py:270
1344 1344 msgid "Port"
1345 1345 msgstr ""
1346 1346
1347 1347 #: rhodecode/authentication/plugins/auth_crowd.py:75
1348 1348 msgid "The Application Name to authenticate to CROWD"
1349 1349 msgstr ""
1350 1350
1351 1351 #: rhodecode/authentication/plugins/auth_crowd.py:77
1352 1352 msgid "Application Name"
1353 1353 msgstr ""
1354 1354
1355 1355 #: rhodecode/authentication/plugins/auth_crowd.py:82
1356 1356 msgid "The password to authenticate to CROWD"
1357 1357 msgstr ""
1358 1358
1359 1359 #: rhodecode/authentication/plugins/auth_crowd.py:84
1360 1360 msgid "Application Password"
1361 1361 msgstr ""
1362 1362
1363 1363 #: rhodecode/authentication/plugins/auth_crowd.py:89
1364 1364 msgid "A comma separated list of group names that identify users as RhodeCode Administrators"
1365 1365 msgstr ""
1366 1366
1367 1367 #: rhodecode/authentication/plugins/auth_crowd.py:93
1368 1368 msgid "Admin Groups"
1369 1369 msgstr ""
1370 1370
1371 1371 #: rhodecode/authentication/plugins/auth_crowd.py:217
1372 1372 msgid "CROWD"
1373 1373 msgstr ""
1374 1374
1375 1375 #: rhodecode/authentication/plugins/auth_headers.py:54
1376 1376 msgid "Header to extract the user from"
1377 1377 msgstr ""
1378 1378
1379 1379 #: rhodecode/authentication/plugins/auth_headers.py:56
1380 1380 msgid "Header"
1381 1381 msgstr ""
1382 1382
1383 1383 #: rhodecode/authentication/plugins/auth_headers.py:61
1384 1384 msgid "Header to extract the user from when main one fails"
1385 1385 msgstr ""
1386 1386
1387 1387 #: rhodecode/authentication/plugins/auth_headers.py:63
1388 1388 msgid "Fallback header"
1389 1389 msgstr ""
1390 1390
1391 1391 #: rhodecode/authentication/plugins/auth_headers.py:68
1392 1392 msgid "Perform cleaning of user, if passed user has @ in username then first part before @ is taken. If there's \\ in the username only the part after \\ is taken"
1393 1393 msgstr ""
1394 1394
1395 1395 #: rhodecode/authentication/plugins/auth_headers.py:73
1396 1396 msgid "Clean username"
1397 1397 msgstr ""
1398 1398
1399 1399 #: rhodecode/authentication/plugins/auth_headers.py:99
1400 1400 msgid "Headers"
1401 1401 msgstr ""
1402 1402
1403 1403 #: rhodecode/authentication/plugins/auth_jasig_cas.py:62
1404 1404 msgid "The url of the Jasig CAS REST service"
1405 1405 msgstr ""
1406 1406
1407 1407 #: rhodecode/authentication/plugins/auth_jasig_cas.py:64
1408 1408 msgid "URL"
1409 1409 msgstr ""
1410 1410
1411 1411 #: rhodecode/authentication/plugins/auth_jasig_cas.py:93
1412 1412 msgid "Jasig-CAS"
1413 1413 msgstr ""
1414 1414
1415 1415 #: rhodecode/authentication/plugins/auth_ldap.py:258
1416 1416 msgid ""
1417 1417 "Host[s] of the LDAP Server \n"
1418 1418 "(e.g., 192.168.2.154, or ldap-server.domain.com.\n"
1419 1419 " Multiple servers can be specified using commas"
1420 1420 msgstr ""
1421 1421
1422 1422 #: rhodecode/authentication/plugins/auth_ldap.py:262
1423 1423 msgid "LDAP Host"
1424 1424 msgstr ""
1425 1425
1426 1426 #: rhodecode/authentication/plugins/auth_ldap.py:267
1427 1427 msgid "Custom port that the LDAP server is listening on. Default value is: 389, use 636 for LDAPS (SSL)"
1428 1428 msgstr ""
1429 1429
1430 1430 #: rhodecode/authentication/plugins/auth_ldap.py:277
1431 1431 msgid "Timeout for LDAP connection"
1432 1432 msgstr ""
1433 1433
1434 1434 #: rhodecode/authentication/plugins/auth_ldap.py:279
1435 1435 msgid "Connection timeout"
1436 1436 msgstr ""
1437 1437
1438 1438 #: rhodecode/authentication/plugins/auth_ldap.py:286
1439 1439 msgid ""
1440 1440 "Optional user DN/account to connect to LDAP if authentication is required. \n"
1441 1441 "e.g., cn=admin,dc=mydomain,dc=com, or uid=root,cn=users,dc=mydomain,dc=com, or admin@mydomain.com"
1442 1442 msgstr ""
1443 1443
1444 1444 #: rhodecode/authentication/plugins/auth_ldap.py:291
1445 1445 msgid "Bind account"
1446 1446 msgstr ""
1447 1447
1448 1448 #: rhodecode/authentication/plugins/auth_ldap.py:296
1449 1449 msgid "Password to authenticate for given user DN."
1450 1450 msgstr ""
1451 1451
1452 1452 #: rhodecode/authentication/plugins/auth_ldap.py:299
1453 1453 msgid "Bind account password"
1454 1454 msgstr ""
1455 1455
1456 1456 #: rhodecode/authentication/plugins/auth_ldap.py:304
1457 1457 msgid "TLS Type"
1458 1458 msgstr ""
1459 1459
1460 1460 #: rhodecode/authentication/plugins/auth_ldap.py:305
1461 1461 msgid "Connection Security"
1462 1462 msgstr ""
1463 1463
1464 1464 #: rhodecode/authentication/plugins/auth_ldap.py:311
1465 1465 msgid ""
1466 1466 "Require Cert over TLS?. Self-signed and custom certificates can be used when\n"
1467 1467 " `RhodeCode Certificate` found in admin > settings > system info page is extended."
1468 1468 msgstr ""
1469 1469
1470 1470 #: rhodecode/authentication/plugins/auth_ldap.py:314
1471 1471 msgid "Certificate Checks"
1472 1472 msgstr ""
1473 1473
1474 1474 #: rhodecode/authentication/plugins/auth_ldap.py:320
1475 1475 msgid ""
1476 1476 "This specifies the PEM-format file path containing certificates for use in TLS connection.\n"
1477 1477 "If not specified `TLS Cert dir` will be used"
1478 1478 msgstr ""
1479 1479
1480 1480 #: rhodecode/authentication/plugins/auth_ldap.py:323
1481 1481 msgid "TLS Cert file"
1482 1482 msgstr ""
1483 1483
1484 1484 #: rhodecode/authentication/plugins/auth_ldap.py:329
1485 1485 msgid "This specifies the path of a directory that contains individual CA certificates in separate files."
1486 1486 msgstr ""
1487 1487
1488 1488 #: rhodecode/authentication/plugins/auth_ldap.py:331
1489 1489 msgid "TLS Cert dir"
1490 1490 msgstr ""
1491 1491
1492 1492 #: rhodecode/authentication/plugins/auth_ldap.py:336
1493 1493 msgid ""
1494 1494 "Base DN to search. Dynamic bind is supported. Add `$login` marker in it to be replaced with current user username \n"
1495 1495 "(e.g., dc=mydomain,dc=com, or ou=Users,dc=mydomain,dc=com)"
1496 1496 msgstr ""
1497 1497
1498 1498 #: rhodecode/authentication/plugins/auth_ldap.py:341
1499 1499 msgid "Base DN"
1500 1500 msgstr ""
1501 1501
1502 1502 #: rhodecode/authentication/plugins/auth_ldap.py:346
1503 1503 msgid ""
1504 1504 "Filter to narrow results \n"
1505 1505 "(e.g., (&(objectCategory=Person)(objectClass=user)), or \n"
1506 1506 "(memberof=cn=rc-login,ou=groups,ou=company,dc=mydomain,dc=com)))"
1507 1507 msgstr ""
1508 1508
1509 1509 #: rhodecode/authentication/plugins/auth_ldap.py:351
1510 1510 msgid "LDAP Search Filter"
1511 1511 msgstr ""
1512 1512
1513 1513 #: rhodecode/authentication/plugins/auth_ldap.py:357
1514 1514 msgid "How deep to search LDAP. If unsure set to SUBTREE"
1515 1515 msgstr ""
1516 1516
1517 1517 #: rhodecode/authentication/plugins/auth_ldap.py:358
1518 1518 msgid "LDAP Search Scope"
1519 1519 msgstr ""
1520 1520
1521 1521 #: rhodecode/authentication/plugins/auth_ldap.py:364
1522 1522 msgid "LDAP Attribute to map to user name (e.g., uid, or sAMAccountName)"
1523 1523 msgstr ""
1524 1524
1525 1525 #: rhodecode/authentication/plugins/auth_ldap.py:366
1526 1526 msgid "Login Attribute"
1527 1527 msgstr ""
1528 1528
1529 1529 #: rhodecode/authentication/plugins/auth_ldap.py:367
1530 1530 msgid "The LDAP Login attribute of the CN must be specified"
1531 1531 msgstr ""
1532 1532
1533 1533 #: rhodecode/authentication/plugins/auth_ldap.py:372
1534 1534 msgid ""
1535 1535 "LDAP Attribute to map to email address (e.g., mail).\n"
1536 1536 "Emails are a crucial part of RhodeCode. \n"
1537 1537 "If possible add a valid email attribute to ldap users."
1538 1538 msgstr ""
1539 1539
1540 1540 #: rhodecode/authentication/plugins/auth_ldap.py:377
1541 1541 msgid "Email Attribute"
1542 1542 msgstr ""
1543 1543
1544 1544 #: rhodecode/authentication/plugins/auth_ldap.py:382
1545 1545 msgid "LDAP Attribute to map to first name (e.g., givenName)"
1546 1546 msgstr ""
1547 1547
1548 1548 #: rhodecode/authentication/plugins/auth_ldap.py:385
1549 1549 msgid "First Name Attribute"
1550 1550 msgstr ""
1551 1551
1552 1552 #: rhodecode/authentication/plugins/auth_ldap.py:390
1553 1553 msgid "LDAP Attribute to map to last name (e.g., sn)"
1554 1554 msgstr ""
1555 1555
1556 1556 #: rhodecode/authentication/plugins/auth_ldap.py:393
1557 1557 msgid "Last Name Attribute"
1558 1558 msgstr ""
1559 1559
1560 1560 #: rhodecode/authentication/plugins/auth_ldap.py:425
1561 1561 msgid "LDAP"
1562 1562 msgstr ""
1563 1563
1564 1564 #: rhodecode/authentication/plugins/auth_pam.py:60
1565 1565 msgid "PAM service name to use for authentication."
1566 1566 msgstr ""
1567 1567
1568 1568 #: rhodecode/authentication/plugins/auth_pam.py:62
1569 1569 msgid "PAM service name"
1570 1570 msgstr ""
1571 1571
1572 1572 #: rhodecode/authentication/plugins/auth_pam.py:67
1573 1573 msgid "Regular expression for extracting user name/email etc. from Unix userinfo."
1574 1574 msgstr ""
1575 1575
1576 1576 #: rhodecode/authentication/plugins/auth_pam.py:70
1577 1577 msgid "Gecos Regex"
1578 1578 msgstr ""
1579 1579
1580 1580 #: rhodecode/authentication/plugins/auth_pam.py:99
1581 1581 msgid "PAM"
1582 1582 msgstr ""
1583 1583
1584 1584 #: rhodecode/authentication/plugins/auth_rhodecode.py:79
1585 1585 msgid "RhodeCode Internal"
1586 1586 msgstr ""
1587 1587
1588 1588 #: rhodecode/authentication/plugins/auth_rhodecode.py:200
1589 1589 msgid "Allowed user types for authentication using this plugin."
1590 1590 msgstr ""
1591 1591
1592 1592 #: rhodecode/authentication/plugins/auth_rhodecode.py:201
1593 1593 msgid "User restriction"
1594 1594 msgstr ""
1595 1595
1596 1596 #: rhodecode/authentication/plugins/auth_rhodecode.py:209
1597 1597 msgid "Allowed protocols for authentication using this plugin. VCS means GIT/HG/SVN. HTTP is web based login."
1598 1598 msgstr ""
1599 1599
1600 1600 #: rhodecode/authentication/plugins/auth_rhodecode.py:211
1601 1601 #: rhodecode/authentication/plugins/auth_token.py:173
1602 1602 msgid "Scope restriction"
1603 1603 msgstr ""
1604 1604
1605 1605 #: rhodecode/authentication/plugins/auth_token.py:77
1606 1606 msgid "Rhodecode Token"
1607 1607 msgstr ""
1608 1608
1609 1609 #: rhodecode/authentication/plugins/auth_token.py:172
1610 1610 msgid "Choose operation scope restriction when authenticating."
1611 1611 msgstr ""
1612 1612
1613 1613 #: rhodecode/events/pullrequest.py:78
1614 1614 msgid "pullrequest created"
1615 1615 msgstr ""
1616 1616
1617 1617 #: rhodecode/events/pullrequest.py:79
1618 1618 msgid "Event triggered after pull request was created"
1619 1619 msgstr ""
1620 1620
1621 1621 #: rhodecode/events/pullrequest.py:88
1622 1622 msgid "pullrequest closed"
1623 1623 msgstr ""
1624 1624
1625 1625 #: rhodecode/events/pullrequest.py:89
1626 1626 msgid "Event triggered after pull request was closed"
1627 1627 msgstr ""
1628 1628
1629 1629 #: rhodecode/events/pullrequest.py:98
1630 1630 msgid "pullrequest commits updated"
1631 1631 msgstr ""
1632 1632
1633 1633 #: rhodecode/events/pullrequest.py:99
1634 1634 msgid "Event triggered after pull requests was updated"
1635 1635 msgstr ""
1636 1636
1637 1637 #: rhodecode/events/pullrequest.py:108
1638 1638 msgid "pullrequest review changed"
1639 1639 msgstr ""
1640 1640
1641 1641 #: rhodecode/events/pullrequest.py:109
1642 1642 msgid "Event triggered after a review status of a pull requests has changed to other."
1643 1643 msgstr ""
1644 1644
1645 1645 #: rhodecode/events/pullrequest.py:123
1646 1646 msgid "pullrequest merged"
1647 1647 msgstr ""
1648 1648
1649 1649 #: rhodecode/events/pullrequest.py:124
1650 1650 msgid "Event triggered after a successful merge operation was executed on a pull request"
1651 1651 msgstr ""
1652 1652
1653 1653 #: rhodecode/events/pullrequest.py:134
1654 1654 msgid "pullrequest commented"
1655 1655 msgstr ""
1656 1656
1657 1657 #: rhodecode/events/pullrequest.py:135
1658 1658 msgid "Event triggered after a comment was made on a code in the pull request"
1659 1659 msgstr ""
1660 1660
1661 1661 #: rhodecode/events/pullrequest.py:173
1662 1662 msgid "pullrequest comment edited"
1663 1663 msgstr ""
1664 1664
1665 1665 #: rhodecode/events/pullrequest.py:174
1666 1666 msgid "Event triggered after a comment was edited on a code in the pull request"
1667 1667 msgstr ""
1668 1668
1669 1669 #: rhodecode/events/repo.py:191
1670 1670 msgid "repository commit comment"
1671 1671 msgstr ""
1672 1672
1673 1673 #: rhodecode/events/repo.py:192
1674 1674 msgid "Event triggered after a comment was made on commit inside a repository"
1675 1675 msgstr ""
1676 1676
1677 1677 #: rhodecode/events/repo.py:226
1678 1678 msgid "repository commit edit comment"
1679 1679 msgstr ""
1680 1680
1681 1681 #: rhodecode/events/repo.py:227
1682 1682 msgid "Event triggered after a comment was edited on commit inside a repository"
1683 1683 msgstr ""
1684 1684
1685 1685 #: rhodecode/events/repo.py:260
1686 1686 msgid "repository pre create"
1687 1687 msgstr ""
1688 1688
1689 1689 #: rhodecode/events/repo.py:261
1690 1690 msgid "Event triggered before repository is created"
1691 1691 msgstr ""
1692 1692
1693 1693 #: rhodecode/events/repo.py:270
1694 1694 msgid "repository created"
1695 1695 msgstr ""
1696 1696
1697 1697 #: rhodecode/events/repo.py:271
1698 1698 msgid "Event triggered after repository was created"
1699 1699 msgstr ""
1700 1700
1701 1701 #: rhodecode/events/repo.py:280
1702 1702 msgid "repository pre delete"
1703 1703 msgstr ""
1704 1704
1705 1705 #: rhodecode/events/repo.py:281
1706 1706 msgid "Event triggered before a repository is deleted"
1707 1707 msgstr ""
1708 1708
1709 1709 #: rhodecode/events/repo.py:290
1710 1710 msgid "repository deleted"
1711 1711 msgstr ""
1712 1712
1713 1713 #: rhodecode/events/repo.py:291
1714 1714 msgid "Event triggered after repository was deleted"
1715 1715 msgstr ""
1716 1716
1717 1717 #: rhodecode/events/repo.py:331
1718 1718 msgid "repository pre pull"
1719 1719 msgstr ""
1720 1720
1721 1721 #: rhodecode/events/repo.py:332
1722 1722 msgid "Event triggered before repository code is pulled"
1723 1723 msgstr ""
1724 1724
1725 1725 #: rhodecode/events/repo.py:341
1726 1726 msgid "repository pull"
1727 1727 msgstr ""
1728 1728
1729 1729 #: rhodecode/events/repo.py:342
1730 1730 msgid "Event triggered after repository code was pulled"
1731 1731 msgstr ""
1732 1732
1733 1733 #: rhodecode/events/repo.py:351
1734 1734 msgid "repository pre push"
1735 1735 msgstr ""
1736 1736
1737 1737 #: rhodecode/events/repo.py:352
1738 1738 msgid "Event triggered before the code is pushed to a repository"
1739 1739 msgstr ""
1740 1740
1741 1741 #: rhodecode/events/repo.py:364
1742 1742 msgid "repository push"
1743 1743 msgstr ""
1744 1744
1745 1745 #: rhodecode/events/repo.py:365
1746 1746 msgid "Event triggered after the code was pushed to a repository"
1747 1747 msgstr ""
1748 1748
1749 1749 #: rhodecode/events/repo_group.py:62
1750 1750 msgid "repository group created"
1751 1751 msgstr ""
1752 1752
1753 1753 #: rhodecode/events/repo_group.py:63
1754 1754 msgid "Event triggered after a repository group was created"
1755 1755 msgstr ""
1756 1756
1757 1757 #: rhodecode/events/repo_group.py:72
1758 1758 msgid "repository group deleted"
1759 1759 msgstr ""
1760 1760
1761 1761 #: rhodecode/events/repo_group.py:73
1762 1762 msgid "Event triggered after a repository group was deleted"
1763 1763 msgstr ""
1764 1764
1765 1765 #: rhodecode/events/repo_group.py:82
1766 1766 msgid "repository group update"
1767 1767 msgstr ""
1768 1768
1769 1769 #: rhodecode/events/repo_group.py:83
1770 1770 msgid "Event triggered after a repository group was updated"
1771 1771 msgstr ""
1772 1772
1773 1773 #: rhodecode/events/user.py:37
1774 1774 msgid "user registered"
1775 1775 msgstr ""
1776 1776
1777 1777 #: rhodecode/events/user.py:52
1778 1778 msgid "user pre create"
1779 1779 msgstr ""
1780 1780
1781 1781 #: rhodecode/events/user.py:66
1782 1782 msgid "user post create"
1783 1783 msgstr ""
1784 1784
1785 1785 #: rhodecode/events/user.py:80
1786 1786 msgid "user pre update"
1787 1787 msgstr ""
1788 1788
1789 1789 #: rhodecode/events/user.py:100
1790 1790 msgid "user permissions change"
1791 1791 msgstr ""
1792 1792
1793 1793 #: rhodecode/forms/__init__.py:35
1794 1794 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:103
1795 1795 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:73
1796 1796 #: rhodecode/templates/admin/permissions/permissions_application.mako:60
1797 1797 #: rhodecode/templates/admin/permissions/permissions_ips.mako:64
1798 1798 #: rhodecode/templates/admin/permissions/permissions_objects.mako:60
1799 1799 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:227
1800 1800 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:78
1801 1801 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:66
1802 1802 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:207
1803 1803 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:252
1804 1804 #: rhodecode/templates/admin/repos/repo_edit_vcs.mako:44
1805 1805 #: rhodecode/templates/admin/settings/settings_global.mako:141
1806 1806 #: rhodecode/templates/admin/settings/settings_issuetracker.mako:16
1807 1807 #: rhodecode/templates/admin/settings/settings_labs.mako:49
1808 1808 #: rhodecode/templates/admin/settings/settings_vcs.mako:14
1809 #: rhodecode/templates/admin/settings/settings_visual.mako:215
1809 #: rhodecode/templates/admin/settings/settings_visual.mako:218
1810 1810 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:217
1811 1811 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:107
1812 1812 #: rhodecode/templates/admin/users/user_edit_emails.mako:66
1813 1813 #: rhodecode/templates/admin/users/user_edit_ips.mako:76
1814 1814 #: rhodecode/templates/admin/users/user_edit_profile.mako:155
1815 1815 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:72
1816 1816 #: rhodecode/templates/base/default_perms_box.mako:102
1817 1817 msgid "Reset"
1818 1818 msgstr ""
1819 1819
1820 1820 #: rhodecode/forms/__init__.py:36 rhodecode/public/js/scripts.js:38315
1821 1821 #: rhodecode/public/js/scripts.min.js:1
1822 1822 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:29
1823 1823 #: rhodecode/public/js/src/rhodecode/utils/ajax.js:165
1824 1824 #: rhodecode/templates/admin/gists/gist_show.mako:59
1825 1825 #: rhodecode/templates/admin/integrations/list.mako:179
1826 1826 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:65
1827 1827 #: rhodecode/templates/admin/my_account/my_account_emails.mako:33
1828 1828 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:34
1829 1829 #: rhodecode/templates/admin/notifications/notifications_data.mako:22
1830 1830 #: rhodecode/templates/admin/notifications/notifications_show.mako:37
1831 1831 #: rhodecode/templates/admin/permissions/permissions_ips.mako:29
1832 1832 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:25
1833 1833 #: rhodecode/templates/admin/settings/settings_hooks.mako:46
1834 1834 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:69
1835 1835 #: rhodecode/templates/admin/users/user_edit_emails.mako:34
1836 1836 #: rhodecode/templates/admin/users/user_edit_ips.mako:40
1837 1837 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:35
1838 1838 #: rhodecode/templates/base/issue_tracker_settings.mako:147
1839 1839 #: rhodecode/templates/base/vcs_settings.mako:244
1840 1840 #: rhodecode/templates/base/vcs_settings.mako:269
1841 1841 #: rhodecode/templates/changeset/changeset_file_comment.mako:236
1842 1842 #: rhodecode/templates/changeset/changeset_file_comment.mako:250
1843 1843 #: rhodecode/templates/changeset/changeset_file_comment.mako:259
1844 #: rhodecode/templates/data_table/_dt_elements.mako:439
1844 #: rhodecode/templates/data_table/_dt_elements.mako:445
1845 1845 #: rhodecode/templates/debug_style/buttons.html:132
1846 1846 #: rhodecode/templates/files/files_source.mako:40
1847 1847 #: rhodecode/templates/files/files_source.mako:47
1848 1848 #: rhodecode/templates/files/files_source.mako:53
1849 1849 msgid "Delete"
1850 1850 msgstr ""
1851 1851
1852 1852 #: rhodecode/integrations/schema.py:30
1853 1853 #: rhodecode/model/validation_schema/schemas/integration_schema.py:193
1854 1854 msgid "Enable or disable this integration."
1855 1855 msgstr ""
1856 1856
1857 1857 #: rhodecode/integrations/schema.py:37
1858 1858 #: rhodecode/model/validation_schema/schemas/integration_schema.py:174
1859 1859 msgid "Short name for this integration."
1860 1860 msgstr ""
1861 1861
1862 1862 #: rhodecode/integrations/schema.py:39
1863 1863 #: rhodecode/model/validation_schema/schemas/integration_schema.py:176
1864 1864 msgid "Integration name"
1865 1865 msgstr ""
1866 1866
1867 1867 #: rhodecode/integrations/schema.py:51
1868 1868 msgid "Limit integrations to to work only on the direct children repositories of this repository group (no subgroups)"
1869 1869 msgstr ""
1870 1870
1871 1871 #: rhodecode/integrations/schema.py:55
1872 1872 msgid "Limit to childen repos only"
1873 1873 msgstr ""
1874 1874
1875 1875 #: rhodecode/integrations/schema.py:63
1876 1876 msgid "Limit integrations to to work only on root level repositories"
1877 1877 msgstr ""
1878 1878
1879 1879 #: rhodecode/integrations/schema.py:66
1880 1880 msgid "Root repositories only"
1881 1881 msgstr ""
1882 1882
1883 1883 #: rhodecode/integrations/views.py:147
1884 1884 msgid "{repo_name} repository"
1885 1885 msgstr ""
1886 1886
1887 1887 #: rhodecode/integrations/views.py:150
1888 1888 msgid "{repo_group_name} repo group"
1889 1889 msgstr ""
1890 1890
1891 1891 #: rhodecode/integrations/views.py:153
1892 1892 #: rhodecode/templates/admin/permissions/permissions.mako:36
1893 1893 msgid "Global"
1894 1894 msgstr ""
1895 1895
1896 1896 #: rhodecode/integrations/views.py:157
1897 1897 msgid "{name} integration"
1898 1898 msgstr ""
1899 1899
1900 1900 #: rhodecode/integrations/views.py:172
1901 1901 msgid "Integration {integration_name} deleted successfully."
1902 1902 msgstr ""
1903 1903
1904 1904 #: rhodecode/integrations/views.py:305
1905 1905 msgid "Errors exist when saving integration settings. Please check the form inputs."
1906 1906 msgstr ""
1907 1907
1908 1908 #: rhodecode/integrations/views.py:330
1909 1909 msgid "Integration {integration_name} updated successfully."
1910 1910 msgstr ""
1911 1911
1912 1912 #: rhodecode/integrations/types/email.py:157
1913 1913 msgid "Recipients"
1914 1914 msgstr ""
1915 1915
1916 1916 #: rhodecode/integrations/types/email.py:158
1917 1917 msgid "Email addresses to send push events to"
1918 1918 msgstr ""
1919 1919
1920 1920 #: rhodecode/integrations/types/email.py:163
1921 1921 #: rhodecode/integrations/types/email.py:164
1922 1922 msgid "Email address"
1923 1923 msgstr ""
1924 1924
1925 1925 #: rhodecode/integrations/types/email.py:175
1926 1926 #: rhodecode/templates/register.mako:97
1927 1927 #: rhodecode/templates/admin/my_account/my_account_profile.mako:78
1928 1928 #: rhodecode/templates/admin/users/user_add.mako:86
1929 1929 #: rhodecode/templates/admin/users/user_edit_profile.mako:65
1930 1930 #: rhodecode/templates/admin/users/users.mako:76
1931 1931 #: rhodecode/templates/email_templates/user_registration.mako:51
1932 1932 #: rhodecode/templates/users/user_profile.mako:69
1933 1933 msgid "Email"
1934 1934 msgstr ""
1935 1935
1936 1936 #: rhodecode/integrations/types/email.py:176
1937 1937 msgid "Send repo push summaries to a list of recipients via email"
1938 1938 msgstr ""
1939 1939
1940 1940 #: rhodecode/integrations/types/hipchat.py:63
1941 1941 msgid "Yellow"
1942 1942 msgstr ""
1943 1943
1944 1944 #: rhodecode/integrations/types/hipchat.py:64
1945 1945 msgid "Red"
1946 1946 msgstr ""
1947 1947
1948 1948 #: rhodecode/integrations/types/hipchat.py:65
1949 1949 msgid "Green"
1950 1950 msgstr ""
1951 1951
1952 1952 #: rhodecode/integrations/types/hipchat.py:66
1953 1953 msgid "Purple"
1954 1954 msgstr ""
1955 1955
1956 1956 #: rhodecode/integrations/types/hipchat.py:67
1957 1957 msgid "Gray"
1958 1958 msgstr ""
1959 1959
1960 1960 #: rhodecode/integrations/types/hipchat.py:72
1961 1961 msgid "Hipchat server URL"
1962 1962 msgstr ""
1963 1963
1964 1964 #: rhodecode/integrations/types/hipchat.py:73
1965 1965 msgid "Hipchat integration url."
1966 1966 msgstr ""
1967 1967
1968 1968 #: rhodecode/integrations/types/hipchat.py:83
1969 1969 msgid "Notify"
1970 1970 msgstr ""
1971 1971
1972 1972 #: rhodecode/integrations/types/hipchat.py:84
1973 1973 msgid "Make a notification to the users in room."
1974 1974 msgstr ""
1975 1975
1976 1976 #: rhodecode/integrations/types/hipchat.py:90
1977 1977 msgid "Color"
1978 1978 msgstr ""
1979 1979
1980 1980 #: rhodecode/integrations/types/hipchat.py:91
1981 1981 msgid "Background color of message."
1982 1982 msgstr ""
1983 1983
1984 1984 #: rhodecode/integrations/types/hipchat.py:102
1985 1985 msgid "Hipchat"
1986 1986 msgstr ""
1987 1987
1988 1988 #: rhodecode/integrations/types/hipchat.py:103
1989 1989 msgid "Send events such as repo pushes and pull requests to your hipchat channel."
1990 1990 msgstr ""
1991 1991
1992 1992 #: rhodecode/integrations/types/slack.py:72
1993 1993 msgid "Slack service URL"
1994 1994 msgstr ""
1995 1995
1996 1996 #: rhodecode/integrations/types/slack.py:73
1997 1997 msgid "This can be setup at the <a href=\"https://my.slack.com/services/new/incoming-webhook/\">slack app manager</a>"
1998 1998 msgstr ""
1999 1999
2000 2000 #: rhodecode/integrations/types/slack.py:86
2001 2001 #: rhodecode/integrations/types/webhook.py:81 rhodecode/templates/login.mako:44
2002 2002 #: rhodecode/templates/register.mako:50
2003 2003 #: rhodecode/templates/admin/admin_log_base.mako:7
2004 2004 #: rhodecode/templates/admin/my_account/my_account_profile.mako:38
2005 2005 #: rhodecode/templates/admin/my_account/my_account_profile_edit.mako:29
2006 2006 #: rhodecode/templates/admin/permissions/permissions_ssh_keys.mako:45
2007 2007 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:74
2008 2008 #: rhodecode/templates/admin/users/user_add.mako:35
2009 2009 #: rhodecode/templates/admin/users/user_edit_profile.mako:39
2010 2010 #: rhodecode/templates/admin/users/users.mako:74
2011 2011 #: rhodecode/templates/debug_style/login.html:36
2012 2012 #: rhodecode/templates/email_templates/user_registration.mako:43
2013 2013 #: rhodecode/templates/user_group/profile.mako:61
2014 2014 #: rhodecode/templates/users/user_profile.mako:29
2015 2015 msgid "Username"
2016 2016 msgstr ""
2017 2017
2018 2018 #: rhodecode/integrations/types/slack.py:87
2019 2019 msgid "Username to show notifications coming from."
2020 2020 msgstr ""
2021 2021
2022 2022 #: rhodecode/integrations/types/slack.py:96
2023 2023 msgid "Channel"
2024 2024 msgstr ""
2025 2025
2026 2026 #: rhodecode/integrations/types/slack.py:97
2027 2027 msgid "Channel to send notifications to."
2028 2028 msgstr ""
2029 2029
2030 2030 #: rhodecode/integrations/types/slack.py:106
2031 2031 msgid "Emoji"
2032 2032 msgstr ""
2033 2033
2034 2034 #: rhodecode/integrations/types/slack.py:107
2035 2035 msgid "Emoji to use eg. :studio_microphone:"
2036 2036 msgstr ""
2037 2037
2038 2038 #: rhodecode/integrations/types/slack.py:118
2039 2039 msgid "Slack"
2040 2040 msgstr ""
2041 2041
2042 2042 #: rhodecode/integrations/types/slack.py:119
2043 2043 msgid "Send events such as repo pushes and pull requests to your slack channel."
2044 2044 msgstr ""
2045 2045
2046 2046 #: rhodecode/integrations/types/webhook.py:49
2047 2047 msgid "Webhook URL"
2048 2048 msgstr ""
2049 2049
2050 2050 #: rhodecode/integrations/types/webhook.py:51
2051 2051 msgid "URL to which Webhook should submit data. If used some of the variables would trigger multiple calls, like ${branch} or ${commit_id}. Webhook will be called as many times as unique objects in data in such cases."
2052 2052 msgstr ""
2053 2053
2054 2054 #: rhodecode/integrations/types/webhook.py:70
2055 2055 msgid "Secret Token"
2056 2056 msgstr ""
2057 2057
2058 2058 #: rhodecode/integrations/types/webhook.py:71
2059 2059 msgid "Optional string used to validate received payloads. It will be sent together with event data in JSON"
2060 2060 msgstr ""
2061 2061
2062 2062 #: rhodecode/integrations/types/webhook.py:82
2063 2063 msgid "Optional username to authenticate the call."
2064 2064 msgstr ""
2065 2065
2066 2066 #: rhodecode/integrations/types/webhook.py:91 rhodecode/templates/login.mako:51
2067 2067 #: rhodecode/templates/register.mako:64
2068 2068 #: rhodecode/templates/admin/my_account/my_account.mako:31
2069 2069 #: rhodecode/templates/admin/users/user_add.mako:44
2070 2070 #: rhodecode/templates/debug_style/login.html:45
2071 2071 msgid "Password"
2072 2072 msgstr ""
2073 2073
2074 2074 #: rhodecode/integrations/types/webhook.py:92
2075 2075 msgid "Optional password to authenticate the call."
2076 2076 msgstr ""
2077 2077
2078 2078 #: rhodecode/integrations/types/webhook.py:102
2079 2079 msgid "Custom Header Key"
2080 2080 msgstr ""
2081 2081
2082 2082 #: rhodecode/integrations/types/webhook.py:103
2083 2083 msgid "Custom Header name to be set when calling endpoint."
2084 2084 msgstr ""
2085 2085
2086 2086 #: rhodecode/integrations/types/webhook.py:112
2087 2087 msgid "Custom Header Value"
2088 2088 msgstr ""
2089 2089
2090 2090 #: rhodecode/integrations/types/webhook.py:113
2091 2091 msgid "Custom Header value to be set when calling endpoint."
2092 2092 msgstr ""
2093 2093
2094 2094 #: rhodecode/integrations/types/webhook.py:122
2095 2095 msgid "Call Method"
2096 2096 msgstr ""
2097 2097
2098 2098 #: rhodecode/integrations/types/webhook.py:123
2099 2099 msgid "Select a HTTP method to use when calling the Webhook."
2100 2100 msgstr ""
2101 2101
2102 2102 #: rhodecode/integrations/types/webhook.py:135
2103 2103 msgid "Webhook"
2104 2104 msgstr ""
2105 2105
2106 2106 #: rhodecode/integrations/types/webhook.py:136
2107 2107 msgid "send JSON data to a url endpoint"
2108 2108 msgstr ""
2109 2109
2110 2110 #: rhodecode/lib/action_parser.py:93
2111 2111 msgid "[deleted] repository"
2112 2112 msgstr ""
2113 2113
2114 2114 #: rhodecode/lib/action_parser.py:96 rhodecode/lib/action_parser.py:114
2115 2115 msgid "[created] repository"
2116 2116 msgstr ""
2117 2117
2118 2118 #: rhodecode/lib/action_parser.py:99
2119 2119 msgid "[created] repository as fork"
2120 2120 msgstr ""
2121 2121
2122 2122 #: rhodecode/lib/action_parser.py:102 rhodecode/lib/action_parser.py:117
2123 2123 msgid "[forked] repository"
2124 2124 msgstr ""
2125 2125
2126 2126 #: rhodecode/lib/action_parser.py:105 rhodecode/lib/action_parser.py:120
2127 2127 msgid "[updated] repository"
2128 2128 msgstr ""
2129 2129
2130 2130 #: rhodecode/lib/action_parser.py:108
2131 2131 msgid "[downloaded] archive from repository"
2132 2132 msgstr ""
2133 2133
2134 2134 #: rhodecode/lib/action_parser.py:111
2135 2135 msgid "[delete] repository"
2136 2136 msgstr ""
2137 2137
2138 2138 #: rhodecode/lib/action_parser.py:123
2139 2139 msgid "[created] user"
2140 2140 msgstr ""
2141 2141
2142 2142 #: rhodecode/lib/action_parser.py:126
2143 2143 msgid "[updated] user"
2144 2144 msgstr ""
2145 2145
2146 2146 #: rhodecode/lib/action_parser.py:129
2147 2147 msgid "[created] user group"
2148 2148 msgstr ""
2149 2149
2150 2150 #: rhodecode/lib/action_parser.py:132
2151 2151 msgid "[updated] user group"
2152 2152 msgstr ""
2153 2153
2154 2154 #: rhodecode/lib/action_parser.py:135
2155 2155 msgid "[commented] on commit in repository"
2156 2156 msgstr ""
2157 2157
2158 2158 #: rhodecode/lib/action_parser.py:138
2159 2159 msgid "[commented] on pull request for"
2160 2160 msgstr ""
2161 2161
2162 2162 #: rhodecode/lib/action_parser.py:141
2163 2163 msgid "[closed] pull request for"
2164 2164 msgstr ""
2165 2165
2166 2166 #: rhodecode/lib/action_parser.py:144
2167 2167 msgid "[merged] pull request for"
2168 2168 msgstr ""
2169 2169
2170 2170 #: rhodecode/lib/action_parser.py:147
2171 2171 msgid "[pushed] into"
2172 2172 msgstr ""
2173 2173
2174 2174 #: rhodecode/lib/action_parser.py:150
2175 2175 msgid "[committed via RhodeCode] into repository"
2176 2176 msgstr ""
2177 2177
2178 2178 #: rhodecode/lib/action_parser.py:153
2179 2179 msgid "[pulled from remote] into repository"
2180 2180 msgstr ""
2181 2181
2182 2182 #: rhodecode/lib/action_parser.py:156
2183 2183 msgid "[pulled] from"
2184 2184 msgstr ""
2185 2185
2186 2186 #: rhodecode/lib/action_parser.py:159
2187 2187 msgid "[started following] repository"
2188 2188 msgstr ""
2189 2189
2190 2190 #: rhodecode/lib/action_parser.py:162
2191 2191 msgid "[stopped following] repository"
2192 2192 msgstr ""
2193 2193
2194 2194 #: rhodecode/lib/action_parser.py:172
2195 2195 #, python-format
2196 2196 msgid "fork name %s"
2197 2197 msgstr ""
2198 2198
2199 2199 #: rhodecode/lib/action_parser.py:191
2200 2200 #, python-format
2201 2201 msgid "Pull request #%s"
2202 2202 msgstr ""
2203 2203
2204 2204 #: rhodecode/lib/action_parser.py:226
2205 2205 #, python-format
2206 2206 msgid "Show all combined commits %s->%s"
2207 2207 msgstr ""
2208 2208
2209 2209 #: rhodecode/lib/action_parser.py:231
2210 2210 msgid "compare view"
2211 2211 msgstr ""
2212 2212
2213 2213 #: rhodecode/lib/action_parser.py:238
2214 2214 #, python-format
2215 2215 msgid " and %(num)s more commits"
2216 2216 msgstr ""
2217 2217
2218 2218 #: rhodecode/lib/action_parser.py:293
2219 2219 #, python-format
2220 2220 msgid "Deleted branch: %s"
2221 2221 msgstr ""
2222 2222
2223 2223 #: rhodecode/lib/action_parser.py:296
2224 2224 #, python-format
2225 2225 msgid "Created tag: %s"
2226 2226 msgstr ""
2227 2227
2228 2228 #: rhodecode/lib/action_parser.py:309
2229 2229 msgid "Commit not found"
2230 2230 msgstr ""
2231 2231
2232 #: rhodecode/lib/auth.py:1769
2232 #: rhodecode/lib/auth.py:1771
2233 2233 msgid "IP {} not allowed"
2234 2234 msgstr ""
2235 2235
2236 #: rhodecode/lib/auth.py:1861
2236 #: rhodecode/lib/auth.py:1863
2237 2237 msgid "You need to be a registered user to perform this action"
2238 2238 msgstr ""
2239 2239
2240 #: rhodecode/lib/auth.py:1905
2240 #: rhodecode/lib/auth.py:1907
2241 2241 msgid "You need to be signed in to view this page"
2242 2242 msgstr ""
2243 2243
2244 2244 #: rhodecode/lib/channelstream.py:318
2245 2245 msgid " Reload page to load changes"
2246 2246 msgstr ""
2247 2247
2248 2248 #: rhodecode/lib/diffs.py:903
2249 2249 msgid "Click to select line"
2250 2250 msgstr ""
2251 2251
2252 #: rhodecode/lib/helpers.py:1878
2252 #: rhodecode/lib/helpers.py:1883
2253 2253 msgid ""
2254 2254 "Example filter terms:\n"
2255 2255 " repository:vcs\n"
2256 2256 " username:marcin\n"
2257 2257 " username:(NOT marcin)\n"
2258 2258 " action:*push*\n"
2259 2259 " ip:127.0.0.1\n"
2260 2260 " date:20120101\n"
2261 2261 " date:[20120101100000 TO 20120102]\n"
2262 2262 "\n"
2263 2263 "Actions: {actions}\n"
2264 2264 "\n"
2265 2265 "Generate wildcards using '*' character:\n"
2266 2266 " \"repository:vcs*\" - search everything starting with 'vcs'\n"
2267 2267 " \"repository:*vcs*\" - search for repository containing 'vcs'\n"
2268 2268 "\n"
2269 2269 "Optional AND / OR operators in queries\n"
2270 2270 " \"repository:vcs OR repository:test\"\n"
2271 2271 " \"username:test AND repository:test*\"\n"
2272 2272 msgstr ""
2273 2273
2274 #: rhodecode/lib/helpers.py:1902
2274 #: rhodecode/lib/helpers.py:1907
2275 2275 #, python-format
2276 2276 msgid "%s repository is not mapped to db perhaps it was created or renamed from the filesystem please run the application again in order to rescan repositories"
2277 2277 msgstr ""
2278 2278
2279 2279 #: rhodecode/lib/utils2.py:542
2280 2280 msgid "in ${ago}"
2281 2281 msgstr ""
2282 2282
2283 2283 #: rhodecode/lib/utils2.py:548
2284 2284 msgid "${ago} ago"
2285 2285 msgstr ""
2286 2286
2287 2287 #: rhodecode/lib/utils2.py:557
2288 2288 msgid "${val}, ${detail}"
2289 2289 msgstr ""
2290 2290
2291 2291 #: rhodecode/lib/utils2.py:559
2292 2292 msgid "${val}, ${detail} ago"
2293 2293 msgstr ""
2294 2294
2295 2295 #: rhodecode/lib/utils2.py:561
2296 2296 msgid "in ${val}, ${detail}"
2297 2297 msgstr ""
2298 2298
2299 2299 #: rhodecode/lib/utils2.py:563
2300 2300 msgid "${val} and ${detail}"
2301 2301 msgstr ""
2302 2302
2303 2303 #: rhodecode/lib/utils2.py:565
2304 2304 msgid "${val} and ${detail} ago"
2305 2305 msgstr ""
2306 2306
2307 2307 #: rhodecode/lib/utils2.py:567
2308 2308 msgid "in ${val} and ${detail}"
2309 2309 msgstr ""
2310 2310
2311 2311 #: rhodecode/lib/utils2.py:571 rhodecode/public/js/scripts.js:22612
2312 2312 #: rhodecode/public/js/scripts.min.js:1
2313 2313 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:145
2314 2314 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:174
2315 2315 msgid "just now"
2316 2316 msgstr ""
2317 2317
2318 2318 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:568
2319 2319 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:581
2320 2320 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:598
2321 2321 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:681
2322 2322 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:656
2323 2323 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:658
2324 2324 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:681
2325 2325 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:682
2326 2326 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:699
2327 2327 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:716
2328 2328 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:732
2329 2329 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:735
2330 2330 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:737
2331 2331 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:737
2332 2332 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:764
2333 2333 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:774
2334 2334 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:815
2335 2335 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:815
2336 2336 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:816
2337 2337 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:816
2338 2338 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:822
2339 2339 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:944
2340 2340 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:969
2341 2341 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2587
2342 2342 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2653
2343 2343 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2739
2344 2344 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2772
2345 2345 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2772
2346 2346 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2987
2347 2347 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3073
2348 2348 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3079
2349 2349 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3110
2350 2350 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2275
2351 2351 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2267
2352 2352 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2266
2353 2353 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2270
2354 2354 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2270
2355 2355 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2321
2356 2356 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2322
2357 2357 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2522 rhodecode/model/db.py:3132
2358 2358 msgid "Repository no access"
2359 2359 msgstr ""
2360 2360
2361 2361 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:569
2362 2362 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:582
2363 2363 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:599
2364 2364 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:682
2365 2365 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:657
2366 2366 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:659
2367 2367 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:682
2368 2368 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:683
2369 2369 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:700
2370 2370 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:717
2371 2371 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:733
2372 2372 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:736
2373 2373 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:738
2374 2374 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:738
2375 2375 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:765
2376 2376 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:775
2377 2377 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:816
2378 2378 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:816
2379 2379 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:817
2380 2380 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:817
2381 2381 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:823
2382 2382 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:945
2383 2383 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:970
2384 2384 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2588
2385 2385 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2654
2386 2386 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2740
2387 2387 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2773
2388 2388 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2773
2389 2389 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2988
2390 2390 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3074
2391 2391 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3080
2392 2392 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3111
2393 2393 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2276
2394 2394 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2268
2395 2395 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2267
2396 2396 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2271
2397 2397 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2271
2398 2398 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2322
2399 2399 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2323
2400 2400 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2523 rhodecode/model/db.py:3133
2401 2401 msgid "Repository read access"
2402 2402 msgstr ""
2403 2403
2404 2404 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:570
2405 2405 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:583
2406 2406 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:600
2407 2407 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:683
2408 2408 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:658
2409 2409 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:660
2410 2410 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:683
2411 2411 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:684
2412 2412 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:701
2413 2413 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:718
2414 2414 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:734
2415 2415 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:737
2416 2416 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:739
2417 2417 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:739
2418 2418 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:766
2419 2419 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:776
2420 2420 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:817
2421 2421 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:817
2422 2422 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:818
2423 2423 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:818
2424 2424 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:824
2425 2425 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:946
2426 2426 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:971
2427 2427 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2589
2428 2428 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2655
2429 2429 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2741
2430 2430 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2774
2431 2431 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2774
2432 2432 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2989
2433 2433 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3075
2434 2434 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3081
2435 2435 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3112
2436 2436 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2277
2437 2437 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2269
2438 2438 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2268
2439 2439 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2272
2440 2440 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2272
2441 2441 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2323
2442 2442 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2324
2443 2443 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2524 rhodecode/model/db.py:3134
2444 2444 msgid "Repository write access"
2445 2445 msgstr ""
2446 2446
2447 2447 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:571
2448 2448 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:584
2449 2449 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:601
2450 2450 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:684
2451 2451 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:659
2452 2452 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:661
2453 2453 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:684
2454 2454 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:685
2455 2455 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:702
2456 2456 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:719
2457 2457 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:735
2458 2458 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:738
2459 2459 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:740
2460 2460 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:740
2461 2461 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:767
2462 2462 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:777
2463 2463 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:818
2464 2464 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:818
2465 2465 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:819
2466 2466 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:819
2467 2467 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:825
2468 2468 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:947
2469 2469 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:972
2470 2470 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2590
2471 2471 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2656
2472 2472 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2742
2473 2473 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2775
2474 2474 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2775
2475 2475 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2990
2476 2476 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3076
2477 2477 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3082
2478 2478 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3113
2479 2479 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2278
2480 2480 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2270
2481 2481 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2269
2482 2482 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2273
2483 2483 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2273
2484 2484 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2324
2485 2485 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2325
2486 2486 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2525 rhodecode/model/db.py:3135
2487 2487 msgid "Repository admin access"
2488 2488 msgstr ""
2489 2489
2490 2490 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:573
2491 2491 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:586
2492 2492 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:603
2493 2493 msgid "Repositories Group no access"
2494 2494 msgstr ""
2495 2495
2496 2496 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:574
2497 2497 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:587
2498 2498 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:604
2499 2499 msgid "Repositories Group read access"
2500 2500 msgstr ""
2501 2501
2502 2502 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:575
2503 2503 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:588
2504 2504 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:605
2505 2505 msgid "Repositories Group write access"
2506 2506 msgstr ""
2507 2507
2508 2508 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:576
2509 2509 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:589
2510 2510 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:606
2511 2511 msgid "Repositories Group admin access"
2512 2512 msgstr ""
2513 2513
2514 2514 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:578
2515 2515 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:591
2516 2516 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:608
2517 2517 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:691
2518 2518 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:654
2519 2519 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:656
2520 2520 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:679
2521 2521 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:680
2522 2522 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:697
2523 2523 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:714
2524 2524 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:730
2525 2525 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:733
2526 2526 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:735
2527 2527 msgid "RhodeCode Administrator"
2528 2528 msgstr ""
2529 2529
2530 2530 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:579
2531 2531 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:592
2532 2532 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:609
2533 2533 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:692
2534 2534 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:677
2535 2535 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:679
2536 2536 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:702
2537 2537 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:703
2538 2538 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:720
2539 2539 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:737
2540 2540 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:753
2541 2541 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:756
2542 2542 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:758
2543 2543 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:758
2544 2544 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:785
2545 2545 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:795
2546 2546 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:836
2547 2547 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:836
2548 2548 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:837
2549 2549 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:837
2550 2550 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:843
2551 2551 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:965
2552 2552 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:990
2553 2553 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2608
2554 2554 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2679
2555 2555 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2765
2556 2556 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2798
2557 2557 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2798
2558 2558 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3013
2559 2559 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3099
2560 2560 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3105
2561 2561 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3136
2562 2562 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2296
2563 2563 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2288
2564 2564 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2287
2565 2565 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2291
2566 2566 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2291
2567 2567 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2342
2568 2568 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2343
2569 2569 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2543 rhodecode/model/db.py:3158
2570 2570 msgid "Repository creation disabled"
2571 2571 msgstr ""
2572 2572
2573 2573 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:580
2574 2574 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:593
2575 2575 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:610
2576 2576 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:693
2577 2577 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:678
2578 2578 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:680
2579 2579 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:703
2580 2580 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:704
2581 2581 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:721
2582 2582 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:738
2583 2583 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:754
2584 2584 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:757
2585 2585 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:759
2586 2586 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:759
2587 2587 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:786
2588 2588 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:796
2589 2589 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:837
2590 2590 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:837
2591 2591 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:838
2592 2592 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:838
2593 2593 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:844
2594 2594 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:966
2595 2595 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:991
2596 2596 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2609
2597 2597 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2680
2598 2598 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2766
2599 2599 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2799
2600 2600 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2799
2601 2601 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3014
2602 2602 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3100
2603 2603 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3106
2604 2604 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3137
2605 2605 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2297
2606 2606 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2289
2607 2607 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2288
2608 2608 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2292
2609 2609 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2292
2610 2610 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2343
2611 2611 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2344
2612 2612 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2544 rhodecode/model/db.py:3159
2613 2613 msgid "Repository creation enabled"
2614 2614 msgstr ""
2615 2615
2616 2616 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:581
2617 2617 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:594
2618 2618 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:611
2619 2619 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:694
2620 2620 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:680
2621 2621 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:682
2622 2622 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:705
2623 2623 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:706
2624 2624 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:723
2625 2625 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:740
2626 2626 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:758
2627 2627 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:761
2628 2628 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:763
2629 2629 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:763
2630 2630 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:790
2631 2631 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:800
2632 2632 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:841
2633 2633 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:841
2634 2634 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:842
2635 2635 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:842
2636 2636 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:848
2637 2637 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:970
2638 2638 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:995
2639 2639 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2613
2640 2640 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2684
2641 2641 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2770
2642 2642 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2803
2643 2643 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2803
2644 2644 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3018
2645 2645 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3104
2646 2646 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3110
2647 2647 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3141
2648 2648 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2301
2649 2649 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2293
2650 2650 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2292
2651 2651 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2296
2652 2652 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2296
2653 2653 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2347
2654 2654 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2348
2655 2655 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2548 rhodecode/model/db.py:3163
2656 2656 msgid "Repository forking disabled"
2657 2657 msgstr ""
2658 2658
2659 2659 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:582
2660 2660 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:595
2661 2661 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:612
2662 2662 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:695
2663 2663 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:681
2664 2664 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:683
2665 2665 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:706
2666 2666 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:707
2667 2667 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:724
2668 2668 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:741
2669 2669 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:759
2670 2670 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:762
2671 2671 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:764
2672 2672 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:764
2673 2673 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:791
2674 2674 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:801
2675 2675 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:842
2676 2676 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:842
2677 2677 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:843
2678 2678 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:843
2679 2679 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:849
2680 2680 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:971
2681 2681 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:996
2682 2682 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2614
2683 2683 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2685
2684 2684 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2771
2685 2685 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2804
2686 2686 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2804
2687 2687 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3019
2688 2688 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3105
2689 2689 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3111
2690 2690 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3142
2691 2691 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2302
2692 2692 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2294
2693 2693 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2293
2694 2694 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2297
2695 2695 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2297
2696 2696 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2348
2697 2697 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2349
2698 2698 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2549 rhodecode/model/db.py:3164
2699 2699 msgid "Repository forking enabled"
2700 2700 msgstr ""
2701 2701
2702 2702 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:583
2703 2703 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:596
2704 2704 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:613
2705 2705 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:696
2706 2706 msgid "Register disabled"
2707 2707 msgstr ""
2708 2708
2709 2709 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:584
2710 2710 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:597
2711 2711 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:614
2712 2712 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:697
2713 2713 msgid "Register new user with RhodeCode with manual activation"
2714 2714 msgstr ""
2715 2715
2716 2716 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:587
2717 2717 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:600
2718 2718 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:617
2719 2719 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:700
2720 2720 msgid "Register new user with RhodeCode with auto activation"
2721 2721 msgstr ""
2722 2722
2723 2723 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:843
2724 2724 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:864
2725 2725 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:873
2726 2726 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:956
2727 2727 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:994
2728 2728 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:996
2729 2729 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:1019
2730 2730 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:1020
2731 2731 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:1037
2732 2732 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:1054
2733 2733 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:1073
2734 2734 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:1076
2735 2735 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:1087
2736 2736 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:1090
2737 2737 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:1120
2738 2738 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:1134
2739 2739 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:1175
2740 2740 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:1175
2741 2741 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:1184
2742 2742 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:1184
2743 2743 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:1194
2744 2744 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:1316
2745 2745 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:1341
2746 2746 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:3295
2747 2747 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:3378
2748 2748 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:3491
2749 2749 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:3524
2750 2750 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:3525
2751 2751 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3757
2752 2752 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3843
2753 2753 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3890
2754 2754 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3935
2755 2755 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2915
2756 2756 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2907
2757 2757 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2907
2758 2758 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2910
2759 2759 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2910
2760 2760 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:3011
2761 2761 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:3012
2762 2762 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:3230 rhodecode/model/db.py:3997
2763 #: rhodecode/public/js/scripts.js:42595 rhodecode/public/js/scripts.min.js:1
2763 #: rhodecode/public/js/scripts.js:42733 rhodecode/public/js/scripts.min.js:1
2764 2764 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:72
2765 2765 #: rhodecode/public/js/src/rhodecode/pullrequests.js:396
2766 2766 msgid "Not Reviewed"
2767 2767 msgstr ""
2768 2768
2769 2769 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:844
2770 2770 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:865
2771 2771 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:874
2772 2772 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:957
2773 2773 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:995
2774 2774 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:997
2775 2775 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:1020
2776 2776 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:1021
2777 2777 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:1038
2778 2778 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:1055
2779 2779 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:1074
2780 2780 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:1077
2781 2781 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:1088
2782 2782 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:1091
2783 2783 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:1121
2784 2784 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:1135
2785 2785 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:1176
2786 2786 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:1176
2787 2787 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:1185
2788 2788 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:1185
2789 2789 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:1195
2790 2790 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:1317
2791 2791 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:1342
2792 2792 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:3296
2793 2793 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:3379
2794 2794 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:3492
2795 2795 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:3525
2796 2796 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:3526
2797 2797 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3758
2798 2798 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3844
2799 2799 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3891
2800 2800 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3936
2801 2801 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2916
2802 2802 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2908
2803 2803 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2908
2804 2804 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2911
2805 2805 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2911
2806 2806 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:3012
2807 2807 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:3013
2808 2808 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:3231 rhodecode/model/db.py:3998
2809 2809 msgid "Approved"
2810 2810 msgstr ""
2811 2811
2812 2812 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:845
2813 2813 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:866
2814 2814 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:875
2815 2815 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:958
2816 2816 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:996
2817 2817 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:998
2818 2818 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:1021
2819 2819 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:1022
2820 2820 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:1039
2821 2821 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:1056
2822 2822 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:1075
2823 2823 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:1078
2824 2824 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:1089
2825 2825 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:1092
2826 2826 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:1122
2827 2827 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:1136
2828 2828 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:1177
2829 2829 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:1177
2830 2830 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:1186
2831 2831 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:1186
2832 2832 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:1196
2833 2833 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:1318
2834 2834 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:1343
2835 2835 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:3297
2836 2836 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:3380
2837 2837 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:3493
2838 2838 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:3526
2839 2839 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:3527
2840 2840 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3759
2841 2841 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3845
2842 2842 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3892
2843 2843 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3937
2844 2844 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2917
2845 2845 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2909
2846 2846 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2909
2847 2847 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2912
2848 2848 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2912
2849 2849 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:3013
2850 2850 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:3014
2851 2851 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:3232 rhodecode/model/db.py:3999
2852 2852 msgid "Rejected"
2853 2853 msgstr ""
2854 2854
2855 2855 #: rhodecode/lib/dbmigrate/schema/db_1_4_0.py:846
2856 2856 #: rhodecode/lib/dbmigrate/schema/db_1_5_0.py:867
2857 2857 #: rhodecode/lib/dbmigrate/schema/db_1_5_2.py:876
2858 2858 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:959
2859 2859 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:997
2860 2860 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:999
2861 2861 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:1022
2862 2862 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:1023
2863 2863 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:1040
2864 2864 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:1057
2865 2865 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:1076
2866 2866 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:1079
2867 2867 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:1090
2868 2868 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:1093
2869 2869 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:1123
2870 2870 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:1137
2871 2871 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:1178
2872 2872 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:1178
2873 2873 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:1187
2874 2874 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:1187
2875 2875 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:1197
2876 2876 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:1319
2877 2877 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:1344
2878 2878 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:3298
2879 2879 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:3381
2880 2880 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:3494
2881 2881 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:3527
2882 2882 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:3528
2883 2883 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3760
2884 2884 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3846
2885 2885 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3893
2886 2886 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3938
2887 2887 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2918
2888 2888 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2910
2889 2889 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2910
2890 2890 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2913
2891 2891 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2913
2892 2892 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:3014
2893 2893 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:3015
2894 2894 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:3233 rhodecode/model/db.py:4000
2895 2895 msgid "Under Review"
2896 2896 msgstr ""
2897 2897
2898 2898 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:686
2899 2899 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:661
2900 2900 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:663
2901 2901 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:686
2902 2902 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:687
2903 2903 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:704
2904 2904 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:721
2905 2905 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:737
2906 2906 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:740
2907 2907 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:742
2908 2908 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:742
2909 2909 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:769
2910 2910 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:779
2911 2911 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:820
2912 2912 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:820
2913 2913 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:821
2914 2914 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:821
2915 2915 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:827
2916 2916 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:949
2917 2917 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:974
2918 2918 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2592
2919 2919 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2658
2920 2920 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2744
2921 2921 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2777
2922 2922 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2777
2923 2923 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2992
2924 2924 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3078
2925 2925 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3084
2926 2926 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3115
2927 2927 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2280
2928 2928 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2272
2929 2929 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2271
2930 2930 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2275
2931 2931 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2275
2932 2932 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2326
2933 2933 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2327
2934 2934 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2527 rhodecode/model/db.py:3137
2935 2935 msgid "Repository group no access"
2936 2936 msgstr ""
2937 2937
2938 2938 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:687
2939 2939 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:662
2940 2940 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:664
2941 2941 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:687
2942 2942 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:688
2943 2943 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:705
2944 2944 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:722
2945 2945 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:738
2946 2946 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:741
2947 2947 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:743
2948 2948 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:743
2949 2949 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:770
2950 2950 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:780
2951 2951 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:821
2952 2952 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:821
2953 2953 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:822
2954 2954 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:822
2955 2955 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:828
2956 2956 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:950
2957 2957 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:975
2958 2958 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2593
2959 2959 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2659
2960 2960 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2745
2961 2961 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2778
2962 2962 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2778
2963 2963 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2993
2964 2964 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3079
2965 2965 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3085
2966 2966 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3116
2967 2967 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2281
2968 2968 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2273
2969 2969 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2272
2970 2970 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2276
2971 2971 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2276
2972 2972 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2327
2973 2973 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2328
2974 2974 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2528 rhodecode/model/db.py:3138
2975 2975 msgid "Repository group read access"
2976 2976 msgstr ""
2977 2977
2978 2978 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:688
2979 2979 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:663
2980 2980 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:665
2981 2981 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:688
2982 2982 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:689
2983 2983 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:706
2984 2984 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:723
2985 2985 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:739
2986 2986 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:742
2987 2987 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:744
2988 2988 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:744
2989 2989 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:771
2990 2990 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:781
2991 2991 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:822
2992 2992 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:822
2993 2993 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:823
2994 2994 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:823
2995 2995 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:829
2996 2996 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:951
2997 2997 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:976
2998 2998 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2594
2999 2999 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2660
3000 3000 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2746
3001 3001 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2779
3002 3002 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2779
3003 3003 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2994
3004 3004 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3080
3005 3005 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3086
3006 3006 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3117
3007 3007 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2282
3008 3008 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2274
3009 3009 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2273
3010 3010 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2277
3011 3011 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2277
3012 3012 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2328
3013 3013 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2329
3014 3014 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2529 rhodecode/model/db.py:3139
3015 3015 msgid "Repository group write access"
3016 3016 msgstr ""
3017 3017
3018 3018 #: rhodecode/lib/dbmigrate/schema/db_1_6_0.py:689
3019 3019 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:664
3020 3020 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:666
3021 3021 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:689
3022 3022 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:690
3023 3023 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:707
3024 3024 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:724
3025 3025 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:740
3026 3026 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:743
3027 3027 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:745
3028 3028 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:745
3029 3029 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:772
3030 3030 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:782
3031 3031 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:823
3032 3032 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:823
3033 3033 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:824
3034 3034 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:824
3035 3035 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:830
3036 3036 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:952
3037 3037 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:977
3038 3038 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2595
3039 3039 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2661
3040 3040 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2747
3041 3041 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2780
3042 3042 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2780
3043 3043 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2995
3044 3044 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3081
3045 3045 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3087
3046 3046 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3118
3047 3047 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2283
3048 3048 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2275
3049 3049 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2274
3050 3050 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2278
3051 3051 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2278
3052 3052 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2329
3053 3053 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2330
3054 3054 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2530 rhodecode/model/db.py:3140
3055 3055 msgid "Repository group admin access"
3056 3056 msgstr ""
3057 3057
3058 3058 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:666
3059 3059 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:668
3060 3060 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:691
3061 3061 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:692
3062 3062 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:709
3063 3063 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:726
3064 3064 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:742
3065 3065 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:745
3066 3066 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:747
3067 3067 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:747
3068 3068 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:774
3069 3069 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:784
3070 3070 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:825
3071 3071 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:825
3072 3072 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:826
3073 3073 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:826
3074 3074 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:832
3075 3075 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:954
3076 3076 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:979
3077 3077 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2597
3078 3078 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2663
3079 3079 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2749
3080 3080 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2782
3081 3081 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2782
3082 3082 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2997
3083 3083 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3083
3084 3084 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3089
3085 3085 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3120
3086 3086 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2285
3087 3087 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2277
3088 3088 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2276
3089 3089 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2280
3090 3090 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2280
3091 3091 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2331
3092 3092 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2332
3093 3093 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2532 rhodecode/model/db.py:3142
3094 3094 msgid "User group no access"
3095 3095 msgstr ""
3096 3096
3097 3097 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:667
3098 3098 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:669
3099 3099 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:692
3100 3100 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:693
3101 3101 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:710
3102 3102 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:727
3103 3103 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:743
3104 3104 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:746
3105 3105 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:748
3106 3106 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:748
3107 3107 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:775
3108 3108 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:785
3109 3109 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:826
3110 3110 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:826
3111 3111 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:827
3112 3112 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:827
3113 3113 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:833
3114 3114 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:955
3115 3115 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:980
3116 3116 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2598
3117 3117 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2664
3118 3118 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2750
3119 3119 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2783
3120 3120 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2783
3121 3121 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2998
3122 3122 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3084
3123 3123 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3090
3124 3124 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3121
3125 3125 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2286
3126 3126 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2278
3127 3127 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2277
3128 3128 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2281
3129 3129 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2281
3130 3130 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2332
3131 3131 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2333
3132 3132 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2533 rhodecode/model/db.py:3143
3133 3133 msgid "User group read access"
3134 3134 msgstr ""
3135 3135
3136 3136 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:668
3137 3137 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:670
3138 3138 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:693
3139 3139 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:694
3140 3140 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:711
3141 3141 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:728
3142 3142 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:744
3143 3143 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:747
3144 3144 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:749
3145 3145 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:749
3146 3146 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:776
3147 3147 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:786
3148 3148 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:827
3149 3149 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:827
3150 3150 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:828
3151 3151 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:828
3152 3152 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:834
3153 3153 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:956
3154 3154 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:981
3155 3155 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2599
3156 3156 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2665
3157 3157 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2751
3158 3158 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2784
3159 3159 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2784
3160 3160 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2999
3161 3161 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3085
3162 3162 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3091
3163 3163 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3122
3164 3164 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2287
3165 3165 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2279
3166 3166 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2278
3167 3167 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2282
3168 3168 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2282
3169 3169 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2333
3170 3170 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2334
3171 3171 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2534 rhodecode/model/db.py:3144
3172 3172 msgid "User group write access"
3173 3173 msgstr ""
3174 3174
3175 3175 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:669
3176 3176 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:671
3177 3177 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:694
3178 3178 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:695
3179 3179 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:712
3180 3180 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:729
3181 3181 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:745
3182 3182 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:748
3183 3183 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:750
3184 3184 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:750
3185 3185 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:777
3186 3186 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:787
3187 3187 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:828
3188 3188 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:828
3189 3189 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:829
3190 3190 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:829
3191 3191 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:835
3192 3192 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:957
3193 3193 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:982
3194 3194 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2600
3195 3195 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2666
3196 3196 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2752
3197 3197 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2785
3198 3198 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2785
3199 3199 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3000
3200 3200 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3086
3201 3201 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3092
3202 3202 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3123
3203 3203 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2288
3204 3204 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2280
3205 3205 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2279
3206 3206 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2283
3207 3207 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2283
3208 3208 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2334
3209 3209 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2335
3210 3210 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2535 rhodecode/model/db.py:3145
3211 3211 msgid "User group admin access"
3212 3212 msgstr ""
3213 3213
3214 3214 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:671
3215 3215 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:673
3216 3216 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:696
3217 3217 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:697
3218 3218 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:714
3219 3219 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:731
3220 3220 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:747
3221 3221 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:750
3222 3222 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:752
3223 3223 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:752
3224 3224 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:779
3225 3225 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:789
3226 3226 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:830
3227 3227 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:830
3228 3228 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:831
3229 3229 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:831
3230 3230 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:837
3231 3231 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:959
3232 3232 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:984
3233 3233 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2602
3234 3234 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2673
3235 3235 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2759
3236 3236 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2792
3237 3237 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2792
3238 3238 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3007
3239 3239 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3093
3240 3240 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3099
3241 3241 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3130
3242 3242 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2290
3243 3243 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2282
3244 3244 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2281
3245 3245 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2285
3246 3246 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2285
3247 3247 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2336
3248 3248 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2337
3249 3249 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2537 rhodecode/model/db.py:3152
3250 3250 msgid "Repository Group creation disabled"
3251 3251 msgstr ""
3252 3252
3253 3253 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:672
3254 3254 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:674
3255 3255 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:697
3256 3256 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:698
3257 3257 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:715
3258 3258 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:732
3259 3259 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:748
3260 3260 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:751
3261 3261 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:753
3262 3262 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:753
3263 3263 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:780
3264 3264 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:790
3265 3265 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:831
3266 3266 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:831
3267 3267 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:832
3268 3268 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:832
3269 3269 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:838
3270 3270 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:960
3271 3271 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:985
3272 3272 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2603
3273 3273 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2674
3274 3274 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2760
3275 3275 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2793
3276 3276 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2793
3277 3277 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3008
3278 3278 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3094
3279 3279 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3100
3280 3280 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3131
3281 3281 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2291
3282 3282 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2283
3283 3283 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2282
3284 3284 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2286
3285 3285 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2286
3286 3286 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2337
3287 3287 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2338
3288 3288 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2538 rhodecode/model/db.py:3153
3289 3289 msgid "Repository Group creation enabled"
3290 3290 msgstr ""
3291 3291
3292 3292 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:674
3293 3293 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:676
3294 3294 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:699
3295 3295 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:700
3296 3296 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:717
3297 3297 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:734
3298 3298 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:750
3299 3299 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:753
3300 3300 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:755
3301 3301 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:755
3302 3302 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:782
3303 3303 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:792
3304 3304 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:833
3305 3305 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:833
3306 3306 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:834
3307 3307 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:834
3308 3308 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:840
3309 3309 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:962
3310 3310 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:987
3311 3311 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2605
3312 3312 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2676
3313 3313 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2762
3314 3314 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2795
3315 3315 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2795
3316 3316 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3010
3317 3317 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3096
3318 3318 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3102
3319 3319 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3133
3320 3320 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2293
3321 3321 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2285
3322 3322 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2284
3323 3323 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2288
3324 3324 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2288
3325 3325 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2339
3326 3326 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2340
3327 3327 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2540 rhodecode/model/db.py:3155
3328 3328 msgid "User Group creation disabled"
3329 3329 msgstr ""
3330 3330
3331 3331 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:675
3332 3332 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:677
3333 3333 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:700
3334 3334 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:701
3335 3335 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:718
3336 3336 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:735
3337 3337 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:751
3338 3338 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:754
3339 3339 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:756
3340 3340 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:756
3341 3341 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:783
3342 3342 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:793
3343 3343 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:834
3344 3344 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:834
3345 3345 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:835
3346 3346 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:835
3347 3347 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:841
3348 3348 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:963
3349 3349 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:988
3350 3350 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2606
3351 3351 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2677
3352 3352 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2763
3353 3353 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2796
3354 3354 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2796
3355 3355 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3011
3356 3356 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3097
3357 3357 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3103
3358 3358 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3134
3359 3359 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2294
3360 3360 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2286
3361 3361 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2285
3362 3362 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2289
3363 3363 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2289
3364 3364 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2340
3365 3365 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2341
3366 3366 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2541 rhodecode/model/db.py:3156
3367 3367 msgid "User Group creation enabled"
3368 3368 msgstr ""
3369 3369
3370 3370 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:683
3371 3371 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:685
3372 3372 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:708
3373 3373 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:709
3374 3374 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:726
3375 3375 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:743
3376 3376 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:761
3377 3377 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:764
3378 3378 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:766
3379 3379 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:766
3380 3380 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:793
3381 3381 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:803
3382 3382 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:844
3383 3383 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:844
3384 3384 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:845
3385 3385 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:845
3386 3386 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:851
3387 3387 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:973
3388 3388 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:998
3389 3389 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2616
3390 3390 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2687
3391 3391 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2773
3392 3392 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2806
3393 3393 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2806
3394 3394 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3021
3395 3395 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3107
3396 3396 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3113
3397 3397 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3144
3398 3398 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2304
3399 3399 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2296
3400 3400 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2295
3401 3401 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2299
3402 3402 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2299
3403 3403 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2350
3404 3404 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2351
3405 3405 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2551 rhodecode/model/db.py:3166
3406 3406 msgid "Registration disabled"
3407 3407 msgstr ""
3408 3408
3409 3409 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:684
3410 3410 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:686
3411 3411 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:709
3412 3412 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:710
3413 3413 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:727
3414 3414 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:744
3415 3415 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:762
3416 3416 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:765
3417 3417 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:767
3418 3418 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:767
3419 3419 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:794
3420 3420 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:804
3421 3421 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:845
3422 3422 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:845
3423 3423 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:846
3424 3424 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:846
3425 3425 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:852
3426 3426 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:974
3427 3427 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:999
3428 3428 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2617
3429 3429 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2688
3430 3430 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2774
3431 3431 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2807
3432 3432 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2807
3433 3433 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3022
3434 3434 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3108
3435 3435 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3114
3436 3436 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3145
3437 3437 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2305
3438 3438 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2297
3439 3439 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2296
3440 3440 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2300
3441 3441 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2300
3442 3442 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2351
3443 3443 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2352
3444 3444 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2552 rhodecode/model/db.py:3167
3445 3445 msgid "User Registration with manual account activation"
3446 3446 msgstr ""
3447 3447
3448 3448 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:685
3449 3449 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:687
3450 3450 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:710
3451 3451 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:711
3452 3452 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:728
3453 3453 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:745
3454 3454 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:763
3455 3455 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:766
3456 3456 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:768
3457 3457 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:768
3458 3458 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:795
3459 3459 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:805
3460 3460 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:846
3461 3461 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:846
3462 3462 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:847
3463 3463 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:847
3464 3464 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:853
3465 3465 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:975
3466 3466 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:1000
3467 3467 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2618
3468 3468 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2689
3469 3469 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2775
3470 3470 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2808
3471 3471 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2808
3472 3472 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3023
3473 3473 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3109
3474 3474 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3115
3475 3475 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3146
3476 3476 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2306
3477 3477 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2298
3478 3478 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2297
3479 3479 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2301
3480 3480 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2301
3481 3481 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2352
3482 3482 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2353
3483 3483 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2553 rhodecode/model/db.py:3168
3484 3484 msgid "User Registration with automatic account activation"
3485 3485 msgstr ""
3486 3486
3487 3487 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:687
3488 3488 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:689
3489 3489 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:712
3490 3490 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:713
3491 3491 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:730
3492 3492 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:747
3493 3493 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:765
3494 3494 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:768
3495 3495 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:770
3496 3496 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:770
3497 3497 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:797
3498 3498 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:807
3499 3499 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:848
3500 3500 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:848
3501 3501 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:849
3502 3502 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:849
3503 3503 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:855
3504 3504 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:977
3505 3505 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:1002
3506 3506 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2624
3507 3507 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2695
3508 3508 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2781
3509 3509 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2814
3510 3510 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2814
3511 3511 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3029
3512 3512 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3115
3513 3513 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3121
3514 3514 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3152
3515 3515 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2308
3516 3516 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2300
3517 3517 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2299
3518 3518 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2303
3519 3519 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2303
3520 3520 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2358
3521 3521 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2359
3522 3522 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2559 rhodecode/model/db.py:3174
3523 #: rhodecode/model/permission.py:105
3523 #: rhodecode/model/permission.py:107
3524 3524 msgid "Manual activation of external account"
3525 3525 msgstr ""
3526 3526
3527 3527 #: rhodecode/lib/dbmigrate/schema/db_1_7_0.py:688
3528 3528 #: rhodecode/lib/dbmigrate/schema/db_1_8_0.py:690
3529 3529 #: rhodecode/lib/dbmigrate/schema/db_2_0_0.py:713
3530 3530 #: rhodecode/lib/dbmigrate/schema/db_2_0_1.py:714
3531 3531 #: rhodecode/lib/dbmigrate/schema/db_2_0_2.py:731
3532 3532 #: rhodecode/lib/dbmigrate/schema/db_2_1_0.py:748
3533 3533 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:766
3534 3534 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:769
3535 3535 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:771
3536 3536 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:771
3537 3537 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:798
3538 3538 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:808
3539 3539 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:849
3540 3540 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:849
3541 3541 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:850
3542 3542 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:850
3543 3543 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:856
3544 3544 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:978
3545 3545 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:1003
3546 3546 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2625
3547 3547 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2696
3548 3548 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2782
3549 3549 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2815
3550 3550 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2815
3551 3551 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3030
3552 3552 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3116
3553 3553 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3122
3554 3554 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3153
3555 3555 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2309
3556 3556 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2301
3557 3557 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2300
3558 3558 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2304
3559 3559 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2304
3560 3560 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2359
3561 3561 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2360
3562 3562 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2560 rhodecode/model/db.py:3175
3563 #: rhodecode/model/permission.py:106
3563 #: rhodecode/model/permission.py:108
3564 3564 msgid "Automatic activation of external account"
3565 3565 msgstr ""
3566 3566
3567 3567 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:755
3568 3568 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:758
3569 3569 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:760
3570 3570 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:760
3571 3571 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:787
3572 3572 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:797
3573 3573 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:838
3574 3574 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:838
3575 3575 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:839
3576 3576 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:839
3577 3577 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:845
3578 3578 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:967
3579 3579 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:992
3580 3580 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2610
3581 3581 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2681
3582 3582 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2767
3583 3583 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2800
3584 3584 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2800
3585 3585 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3015
3586 3586 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3101
3587 3587 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3107
3588 3588 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3138
3589 3589 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2298
3590 3590 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2290
3591 3591 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2289
3592 3592 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2293
3593 3593 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2293
3594 3594 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2344
3595 3595 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2345
3596 3596 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2545 rhodecode/model/db.py:3160
3597 3597 msgid "Repository creation enabled with write permission to a repository group"
3598 3598 msgstr ""
3599 3599
3600 3600 #: rhodecode/lib/dbmigrate/schema/db_2_2_0.py:756
3601 3601 #: rhodecode/lib/dbmigrate/schema/db_2_2_3.py:759
3602 3602 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_0.py:761
3603 3603 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:761
3604 3604 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:788
3605 3605 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:798
3606 3606 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:839
3607 3607 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:839
3608 3608 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:840
3609 3609 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:840
3610 3610 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:846
3611 3611 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:968
3612 3612 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:993
3613 3613 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2611
3614 3614 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2682
3615 3615 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2768
3616 3616 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2801
3617 3617 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2801
3618 3618 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3016
3619 3619 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3102
3620 3620 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3108
3621 3621 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3139
3622 3622 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2299
3623 3623 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2291
3624 3624 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2290
3625 3625 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2294
3626 3626 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2294
3627 3627 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2345
3628 3628 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2346
3629 3629 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2546 rhodecode/model/db.py:3161
3630 3630 msgid "Repository creation disabled with write permission to a repository group"
3631 3631 msgstr ""
3632 3632
3633 3633 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_1.py:735
3634 3634 #: rhodecode/lib/dbmigrate/schema/db_2_3_0_2.py:762
3635 3635 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:772
3636 3636 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:813
3637 3637 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:813
3638 3638 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:814
3639 3639 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:814
3640 3640 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:820
3641 3641 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:942
3642 3642 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:967
3643 3643 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2585
3644 3644 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2651
3645 3645 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2737
3646 3646 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2770
3647 3647 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2770
3648 3648 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2985
3649 3649 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3071
3650 3650 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3077
3651 3651 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3108
3652 3652 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2273
3653 3653 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2265
3654 3654 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2264
3655 3655 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2268
3656 3656 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2268
3657 3657 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2319
3658 3658 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2320
3659 3659 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2520 rhodecode/model/db.py:3130
3660 3660 msgid "RhodeCode Super Administrator"
3661 3661 msgstr ""
3662 3662
3663 3663 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:810
3664 3664 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:851
3665 3665 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:851
3666 3666 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:852
3667 3667 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:852
3668 3668 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:858
3669 3669 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:980
3670 3670 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:1005
3671 3671 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2627
3672 3672 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2698
3673 3673 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2784
3674 3674 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2817
3675 3675 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2817
3676 3676 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3032
3677 3677 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3118
3678 3678 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3124
3679 3679 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3155
3680 3680 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2311
3681 3681 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2303
3682 3682 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2302
3683 3683 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2306
3684 3684 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2306
3685 3685 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2361
3686 3686 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2362
3687 3687 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2562 rhodecode/model/db.py:3177
3688 3688 msgid "Inherit object permissions from default user disabled"
3689 3689 msgstr ""
3690 3690
3691 3691 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_0.py:811
3692 3692 #: rhodecode/lib/dbmigrate/schema/db_3_0_0_1.py:852
3693 3693 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_0.py:852
3694 3694 #: rhodecode/lib/dbmigrate/schema/db_3_1_0_1.py:853
3695 3695 #: rhodecode/lib/dbmigrate/schema/db_3_2_0_0.py:853
3696 3696 #: rhodecode/lib/dbmigrate/schema/db_3_3_0_0.py:859
3697 3697 #: rhodecode/lib/dbmigrate/schema/db_3_5_0_0.py:981
3698 3698 #: rhodecode/lib/dbmigrate/schema/db_3_7_0_0.py:1006
3699 3699 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2628
3700 3700 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2699
3701 3701 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2785
3702 3702 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2818
3703 3703 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2818
3704 3704 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3033
3705 3705 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3119
3706 3706 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3125
3707 3707 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3156
3708 3708 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2312
3709 3709 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2304
3710 3710 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2303
3711 3711 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2307
3712 3712 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2307
3713 3713 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2362
3714 3714 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2363
3715 3715 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2563 rhodecode/model/db.py:3178
3716 3716 msgid "Inherit object permissions from default user enabled"
3717 3717 msgstr ""
3718 3718
3719 3719 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:1103
3720 3720 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:1107
3721 3721 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:1118
3722 3722 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:1120
3723 3723 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:1120
3724 3724 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:1137
3725 3725 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:1189
3726 3726 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:1195
3727 3727 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1202
3728 3728 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:910
3729 3729 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:911
3730 3730 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:910
3731 3731 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:912
3732 3732 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:912
3733 3733 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:955
3734 3734 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:956
3735 3735 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:1050 rhodecode/model/db.py:1224
3736 3736 msgid "all"
3737 3737 msgstr ""
3738 3738
3739 3739 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:1104
3740 3740 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:1108
3741 3741 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:1119
3742 3742 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:1121
3743 3743 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:1121
3744 3744 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:1138
3745 3745 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:1190
3746 3746 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:1196
3747 3747 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1203
3748 3748 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:911
3749 3749 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:912
3750 3750 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:911
3751 3751 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:913
3752 3752 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:913
3753 3753 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:956
3754 3754 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:957
3755 3755 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:1051 rhodecode/model/db.py:1225
3756 3756 msgid "http/web interface"
3757 3757 msgstr ""
3758 3758
3759 3759 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:1105
3760 3760 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:1109
3761 3761 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:1120
3762 3762 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:1122
3763 3763 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:1122
3764 3764 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:1139
3765 3765 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:1191
3766 3766 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:1197
3767 3767 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1204
3768 3768 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:912
3769 3769 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:913
3770 3770 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:912
3771 3771 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:914
3772 3772 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:914
3773 3773 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:957
3774 3774 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:958
3775 3775 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:1052 rhodecode/model/db.py:1226
3776 3776 msgid "vcs (git/hg/svn protocol)"
3777 3777 msgstr ""
3778 3778
3779 3779 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:1106
3780 3780 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:1110
3781 3781 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:1121
3782 3782 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:1123
3783 3783 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:1123
3784 3784 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:1140
3785 3785 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:1192
3786 3786 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:1198
3787 3787 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1205
3788 3788 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:913
3789 3789 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:914
3790 3790 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:913
3791 3791 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:915
3792 3792 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:915
3793 3793 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:958
3794 3794 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:959
3795 3795 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:1053 rhodecode/model/db.py:1227
3796 3796 msgid "api calls"
3797 3797 msgstr ""
3798 3798
3799 3799 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:1107
3800 3800 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:1111
3801 3801 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:1122
3802 3802 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:1124
3803 3803 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:1124
3804 3804 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:1141
3805 3805 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:1193
3806 3806 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:1199
3807 3807 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1206
3808 3808 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:914
3809 3809 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:915
3810 3810 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:914
3811 3811 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:916
3812 3812 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:916
3813 3813 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:959
3814 3814 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:960
3815 3815 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:1054 rhodecode/model/db.py:1228
3816 3816 msgid "feed access"
3817 3817 msgstr ""
3818 3818
3819 3819 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2348
3820 3820 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2414
3821 3821 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2488
3822 3822 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2511
3823 3823 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2511
3824 3824 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:2638
3825 3825 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:2729
3826 3826 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:2735
3827 3827 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:2766
3828 3828 #: rhodecode/lib/dbmigrate/schema/db_4_3_0_0.py:2051
3829 3829 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_0.py:2043
3830 3830 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_1.py:2042
3831 3831 #: rhodecode/lib/dbmigrate/schema/db_4_4_0_2.py:2046
3832 3832 #: rhodecode/lib/dbmigrate/schema/db_4_5_0_0.py:2046
3833 3833 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2090
3834 3834 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2091
3835 3835 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2284 rhodecode/model/db.py:2788
3836 3836 msgid "No parent"
3837 3837 msgstr ""
3838 3838
3839 3839 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2620
3840 3840 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2691
3841 3841 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2777
3842 3842 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2810
3843 3843 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2810
3844 3844 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3025
3845 3845 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3111
3846 3846 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3117
3847 3847 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3148
3848 3848 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2354
3849 3849 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2355
3850 3850 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2555 rhodecode/model/db.py:3170
3851 3851 msgid "Password reset enabled"
3852 3852 msgstr ""
3853 3853
3854 3854 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2621
3855 3855 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2692
3856 3856 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2778
3857 3857 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2811
3858 3858 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2811
3859 3859 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3026
3860 3860 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3112
3861 3861 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3118
3862 3862 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3149
3863 3863 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2355
3864 3864 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2356
3865 3865 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2556 rhodecode/model/db.py:3171
3866 3866 msgid "Password reset hidden"
3867 3867 msgstr ""
3868 3868
3869 3869 #: rhodecode/lib/dbmigrate/schema/db_4_11_0_0.py:2622
3870 3870 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2693
3871 3871 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2779
3872 3872 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2812
3873 3873 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2812
3874 3874 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3027
3875 3875 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3113
3876 3876 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3119
3877 3877 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3150
3878 3878 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_0.py:2356
3879 3879 #: rhodecode/lib/dbmigrate/schema/db_4_7_0_1.py:2357
3880 3880 #: rhodecode/lib/dbmigrate/schema/db_4_9_0_0.py:2557 rhodecode/model/db.py:3172
3881 3881 msgid "Password reset disabled"
3882 3882 msgstr ""
3883 3883
3884 3884 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2668
3885 3885 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2754
3886 3886 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2787
3887 3887 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2787
3888 3888 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3002
3889 3889 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3088
3890 3890 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3094
3891 3891 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3125
3892 3892 #: rhodecode/model/db.py:3147
3893 3893 msgid "Branch no permissions"
3894 3894 msgstr ""
3895 3895
3896 3896 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2669
3897 3897 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2755
3898 3898 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2788
3899 3899 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2788
3900 3900 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3003
3901 3901 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3089
3902 3902 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3095
3903 3903 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3126
3904 3904 #: rhodecode/model/db.py:3148
3905 3905 msgid "Branch access by web merge"
3906 3906 msgstr ""
3907 3907
3908 3908 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2670
3909 3909 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2756
3910 3910 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2789
3911 3911 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2789
3912 3912 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3004
3913 3913 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3090
3914 3914 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3096
3915 3915 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3127
3916 3916 #: rhodecode/model/db.py:3149
3917 3917 msgid "Branch access by push"
3918 3918 msgstr ""
3919 3919
3920 3920 #: rhodecode/lib/dbmigrate/schema/db_4_13_0_0.py:2671
3921 3921 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_0.py:2757
3922 3922 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_1.py:2790
3923 3923 #: rhodecode/lib/dbmigrate/schema/db_4_16_0_2.py:2790
3924 3924 #: rhodecode/lib/dbmigrate/schema/db_4_18_0_1.py:3005
3925 3925 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:3091
3926 3926 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:3097
3927 3927 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:3128
3928 3928 #: rhodecode/model/db.py:3150
3929 3929 msgid "Branch access by push with force"
3930 3930 msgstr ""
3931 3931
3932 3932 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_0.py:1194
3933 3933 #: rhodecode/lib/dbmigrate/schema/db_4_19_0_2.py:1200
3934 3934 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1207
3935 3935 #: rhodecode/model/db.py:1229
3936 3936 msgid "artifacts downloads"
3937 3937 msgstr ""
3938 3938
3939 3939 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1213
3940 3940 #: rhodecode/model/db.py:1235
3941 3941 msgid "Token for all actions."
3942 3942 msgstr ""
3943 3943
3944 3944 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1214
3945 3945 #: rhodecode/model/db.py:1236
3946 3946 msgid "Token to access RhodeCode pages via web interface without login using `api_access_controllers_whitelist` functionality."
3947 3947 msgstr ""
3948 3948
3949 3949 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1216
3950 3950 #: rhodecode/model/db.py:1238
3951 3951 msgid "Token to interact over git/hg/svn protocols. Requires auth_token authentication plugin to be active. <br/>Such Token should be used then instead of a password to interact with a repository, and additionally can be limited to single repository using repo scope."
3952 3952 msgstr ""
3953 3953
3954 3954 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1221
3955 3955 #: rhodecode/model/db.py:1243
3956 3956 msgid "Token limited to api calls."
3957 3957 msgstr ""
3958 3958
3959 3959 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1222
3960 3960 #: rhodecode/model/db.py:1244
3961 3961 msgid "Token to read RSS/ATOM feed."
3962 3962 msgstr ""
3963 3963
3964 3964 #: rhodecode/lib/dbmigrate/schema/db_4_20_0_0.py:1223
3965 3965 #: rhodecode/model/db.py:1245
3966 3966 msgid "Token for artifacts downloads."
3967 3967 msgstr ""
3968 3968
3969 3969 #: rhodecode/lib/index/whoosh.py:189
3970 3970 msgid "Index Type"
3971 3971 msgstr ""
3972 3972
3973 3973 #: rhodecode/lib/index/whoosh.py:192
3974 3974 msgid "File Index"
3975 3975 msgstr ""
3976 3976
3977 3977 #: rhodecode/lib/index/whoosh.py:193 rhodecode/lib/index/whoosh.py:199
3978 3978 msgid "Indexed documents"
3979 3979 msgstr ""
3980 3980
3981 3981 #: rhodecode/lib/index/whoosh.py:194 rhodecode/lib/index/whoosh.py:200
3982 3982 msgid "Last update"
3983 3983 msgstr ""
3984 3984
3985 3985 #: rhodecode/lib/index/whoosh.py:198
3986 3986 msgid "Commit index"
3987 3987 msgstr ""
3988 3988
3989 #: rhodecode/lib/vcs/backends/base.py:186
3989 #: rhodecode/lib/vcs/backends/base.py:190
3990 3990 msgid "This pull request can be automatically merged."
3991 3991 msgstr ""
3992 3992
3993 #: rhodecode/lib/vcs/backends/base.py:188
3993 #: rhodecode/lib/vcs/backends/base.py:192
3994 3994 msgid "This pull request cannot be merged because of an unhandled exception. {exception}"
3995 3995 msgstr ""
3996 3996
3997 #: rhodecode/lib/vcs/backends/base.py:191
3997 #: rhodecode/lib/vcs/backends/base.py:195
3998 3998 msgid "This pull request cannot be merged because of merge conflicts. {unresolved_files}"
3999 3999 msgstr ""
4000 4000
4001 #: rhodecode/lib/vcs/backends/base.py:193
4001 #: rhodecode/lib/vcs/backends/base.py:197
4002 4002 msgid "This pull request could not be merged because push to target:`{target}@{merge_commit}` failed."
4003 4003 msgstr ""
4004 4004
4005 #: rhodecode/lib/vcs/backends/base.py:196
4005 #: rhodecode/lib/vcs/backends/base.py:200
4006 4006 msgid "This pull request cannot be merged because the target `{target_ref.name}` is not a head."
4007 4007 msgstr ""
4008 4008
4009 #: rhodecode/lib/vcs/backends/base.py:199
4009 #: rhodecode/lib/vcs/backends/base.py:203
4010 4010 msgid "This pull request cannot be merged because the source contains more branches than the target."
4011 4011 msgstr ""
4012 4012
4013 #: rhodecode/lib/vcs/backends/base.py:202
4013 #: rhodecode/lib/vcs/backends/base.py:206
4014 4014 msgid "This pull request cannot be merged because the target `{target_ref.name}` has multiple heads: `{heads}`."
4015 4015 msgstr ""
4016 4016
4017 #: rhodecode/lib/vcs/backends/base.py:205
4018 msgid "This pull request cannot be merged because the target repository is locked by {locked_by}."
4019 msgstr ""
4020
4021 4017 #: rhodecode/lib/vcs/backends/base.py:209
4018 msgid "This pull request cannot be merged because the target repository is locked by {locked_by}."
4019 msgstr ""
4020
4021 #: rhodecode/lib/vcs/backends/base.py:213
4022 4022 msgid "This pull request cannot be merged because the target reference `{target_ref.name}` is missing."
4023 4023 msgstr ""
4024 4024
4025 #: rhodecode/lib/vcs/backends/base.py:212
4025 #: rhodecode/lib/vcs/backends/base.py:216
4026 4026 msgid "This pull request cannot be merged because the source reference `{source_ref.name}` is missing."
4027 4027 msgstr ""
4028 4028
4029 #: rhodecode/lib/vcs/backends/base.py:215
4029 #: rhodecode/lib/vcs/backends/base.py:219
4030 4030 msgid "This pull request cannot be merged because of conflicts related to sub repositories."
4031 4031 msgstr ""
4032 4032
4033 #: rhodecode/lib/vcs/backends/base.py:220
4033 #: rhodecode/lib/vcs/backends/base.py:224
4034 4034 msgid "This pull request cannot be merged because the target or the source reference is missing."
4035 4035 msgstr ""
4036 4036
4037 4037 #: rhodecode/model/auth_token.py:53
4038 4038 msgid "5 minutes {end_date}"
4039 4039 msgstr ""
4040 4040
4041 4041 #: rhodecode/model/auth_token.py:55
4042 4042 msgid "1 hour {end_date}"
4043 4043 msgstr ""
4044 4044
4045 4045 #: rhodecode/model/auth_token.py:57
4046 4046 msgid "1 day {end_date}"
4047 4047 msgstr ""
4048 4048
4049 4049 #: rhodecode/model/auth_token.py:59
4050 4050 msgid "1 month {end_date}"
4051 4051 msgstr ""
4052 4052
4053 4053 #: rhodecode/model/forms.py:88
4054 4054 msgid "Please enter a login"
4055 4055 msgstr ""
4056 4056
4057 4057 #: rhodecode/model/forms.py:89
4058 4058 #, python-format
4059 4059 msgid "Enter a value %(min)i characters long or more"
4060 4060 msgstr ""
4061 4061
4062 4062 #: rhodecode/model/forms.py:99
4063 4063 msgid "Please enter a password"
4064 4064 msgstr ""
4065 4065
4066 4066 #: rhodecode/model/forms.py:100
4067 4067 #, python-format
4068 4068 msgid "Enter %(min)i characters or more"
4069 4069 msgstr ""
4070 4070
4071 4071 #: rhodecode/model/notification.py:263
4072 4072 #, python-format
4073 4073 msgid "%(user)s commented on commit %(date_or_age)s"
4074 4074 msgstr ""
4075 4075
4076 4076 #: rhodecode/model/notification.py:264
4077 4077 #, python-format
4078 4078 msgid "%(user)s commented on commit at %(date_or_age)s"
4079 4079 msgstr ""
4080 4080
4081 4081 #: rhodecode/model/notification.py:267
4082 4082 #, python-format
4083 4083 msgid "%(user)s sent message %(date_or_age)s"
4084 4084 msgstr ""
4085 4085
4086 4086 #: rhodecode/model/notification.py:268
4087 4087 #, python-format
4088 4088 msgid "%(user)s sent message at %(date_or_age)s"
4089 4089 msgstr ""
4090 4090
4091 4091 #: rhodecode/model/notification.py:271
4092 4092 #, python-format
4093 4093 msgid "%(user)s mentioned you %(date_or_age)s"
4094 4094 msgstr ""
4095 4095
4096 4096 #: rhodecode/model/notification.py:272
4097 4097 #, python-format
4098 4098 msgid "%(user)s mentioned you at %(date_or_age)s"
4099 4099 msgstr ""
4100 4100
4101 4101 #: rhodecode/model/notification.py:275
4102 4102 #, python-format
4103 4103 msgid "%(user)s registered in RhodeCode %(date_or_age)s"
4104 4104 msgstr ""
4105 4105
4106 4106 #: rhodecode/model/notification.py:276
4107 4107 #, python-format
4108 4108 msgid "%(user)s registered in RhodeCode at %(date_or_age)s"
4109 4109 msgstr ""
4110 4110
4111 4111 #: rhodecode/model/notification.py:279
4112 4112 #, python-format
4113 4113 msgid "%(user)s opened new pull request %(date_or_age)s"
4114 4114 msgstr ""
4115 4115
4116 4116 #: rhodecode/model/notification.py:280
4117 4117 #, python-format
4118 4118 msgid "%(user)s opened new pull request at %(date_or_age)s"
4119 4119 msgstr ""
4120 4120
4121 4121 #: rhodecode/model/notification.py:283
4122 4122 #, python-format
4123 4123 msgid "%(user)s updated pull request %(date_or_age)s"
4124 4124 msgstr ""
4125 4125
4126 4126 #: rhodecode/model/notification.py:284
4127 4127 #, python-format
4128 4128 msgid "%(user)s updated pull request at %(date_or_age)s"
4129 4129 msgstr ""
4130 4130
4131 4131 #: rhodecode/model/notification.py:287
4132 4132 #, python-format
4133 4133 msgid "%(user)s commented on pull request %(date_or_age)s"
4134 4134 msgstr ""
4135 4135
4136 4136 #: rhodecode/model/notification.py:288
4137 4137 #, python-format
4138 4138 msgid "%(user)s commented on pull request at %(date_or_age)s"
4139 4139 msgstr ""
4140 4140
4141 #: rhodecode/model/permission.py:71 rhodecode/model/permission.py:77
4142 #: rhodecode/model/permission.py:83
4141 #: rhodecode/model/permission.py:73 rhodecode/model/permission.py:79
4142 #: rhodecode/model/permission.py:85
4143 4143 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:11
4144 4144 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:217
4145 4145 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:11
4146 4146 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:15
4147 4147 msgid "None"
4148 4148 msgstr ""
4149 4149
4150 #: rhodecode/model/permission.py:72 rhodecode/model/permission.py:78
4151 #: rhodecode/model/permission.py:84
4150 #: rhodecode/model/permission.py:74 rhodecode/model/permission.py:80
4151 #: rhodecode/model/permission.py:86
4152 4152 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:12
4153 4153 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:12
4154 4154 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:16
4155 4155 msgid "Read"
4156 4156 msgstr ""
4157 4157
4158 #: rhodecode/model/permission.py:73 rhodecode/model/permission.py:79
4159 #: rhodecode/model/permission.py:85
4158 #: rhodecode/model/permission.py:75 rhodecode/model/permission.py:81
4159 #: rhodecode/model/permission.py:87
4160 4160 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:13
4161 4161 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:13
4162 4162 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:17
4163 4163 #: rhodecode/templates/changeset/changeset_file_comment.mako:392
4164 4164 #: rhodecode/templates/changeset/changeset_file_comment.mako:443
4165 #: rhodecode/templates/data_table/_dt_elements.mako:453
4165 #: rhodecode/templates/data_table/_dt_elements.mako:460
4166 4166 msgid "Write"
4167 4167 msgstr ""
4168 4168
4169 #: rhodecode/model/permission.py:74 rhodecode/model/permission.py:80
4170 #: rhodecode/model/permission.py:86
4169 #: rhodecode/model/permission.py:76 rhodecode/model/permission.py:82
4170 #: rhodecode/model/permission.py:88
4171 4171 #: rhodecode/templates/admin/auth/plugin_settings.mako:12
4172 4172 #: rhodecode/templates/admin/defaults/defaults.mako:12
4173 4173 #: rhodecode/templates/admin/integrations/base.mako:21
4174 4174 #: rhodecode/templates/admin/integrations/form.mako:15
4175 4175 #: rhodecode/templates/admin/integrations/form.mako:28
4176 4176 #: rhodecode/templates/admin/integrations/global.mako:12
4177 4177 #: rhodecode/templates/admin/integrations/list.mako:8
4178 4178 #: rhodecode/templates/admin/integrations/list.mako:14
4179 4179 #: rhodecode/templates/admin/integrations/new.mako:11
4180 4180 #: rhodecode/templates/admin/permissions/permissions.mako:12
4181 4181 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:12
4182 4182 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:14
4183 4183 #: rhodecode/templates/admin/repos/repo_add.mako:13
4184 4184 #: rhodecode/templates/admin/repos/repo_add.mako:17
4185 4185 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:14
4186 4186 #: rhodecode/templates/admin/settings/settings.mako:12
4187 4187 #: rhodecode/templates/admin/user_groups/user_group_add.mako:11
4188 4188 #: rhodecode/templates/admin/user_groups/user_group_edit.mako:12
4189 4189 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:18
4190 4190 #: rhodecode/templates/admin/users/user_add.mako:11
4191 4191 #: rhodecode/templates/admin/users/user_edit.mako:12
4192 #: rhodecode/templates/base/base.mako:838
4192 #: rhodecode/templates/base/base.mako:839
4193 4193 msgid "Admin"
4194 4194 msgstr ""
4195 4195
4196 #: rhodecode/model/permission.py:89
4197 msgid "Protected/No Access"
4198 msgstr ""
4199
4200 #: rhodecode/model/permission.py:90
4201 msgid "Web merge"
4202 msgstr ""
4203
4204 4196 #: rhodecode/model/permission.py:91
4205 msgid "Push"
4197 msgid "Protected/No Access"
4206 4198 msgstr ""
4207 4199
4208 4200 #: rhodecode/model/permission.py:92
4201 msgid "Web merge"
4202 msgstr ""
4203
4204 #: rhodecode/model/permission.py:93
4205 msgid "Push"
4206 msgstr ""
4207
4208 #: rhodecode/model/permission.py:94
4209 4209 msgid "Force Push"
4210 4210 msgstr ""
4211 4211
4212 #: rhodecode/model/permission.py:95 rhodecode/model/permission.py:109
4213 #: rhodecode/model/permission.py:113 rhodecode/model/permission.py:117
4214 #: rhodecode/model/permission.py:121 rhodecode/model/permission.py:125
4215 #: rhodecode/model/permission.py:129
4212 #: rhodecode/model/permission.py:97 rhodecode/model/permission.py:111
4213 #: rhodecode/model/permission.py:115 rhodecode/model/permission.py:119
4214 #: rhodecode/model/permission.py:123 rhodecode/model/permission.py:127
4215 #: rhodecode/model/permission.py:131
4216 4216 #: rhodecode/templates/admin/my_account/my_account_notifications.mako:27
4217 4217 msgid "Disabled"
4218 4218 msgstr ""
4219 4219
4220 #: rhodecode/model/permission.py:96
4220 #: rhodecode/model/permission.py:98
4221 4221 msgid "Allowed with manual account activation"
4222 4222 msgstr ""
4223 4223
4224 #: rhodecode/model/permission.py:97
4224 #: rhodecode/model/permission.py:99
4225 4225 msgid "Allowed with automatic account activation"
4226 4226 msgstr ""
4227 4227
4228 #: rhodecode/model/permission.py:100
4229 msgid "Allow password recovery"
4230 msgstr ""
4231
4232 #: rhodecode/model/permission.py:101
4233 msgid "Hide password recovery link"
4234 msgstr ""
4235
4236 4228 #: rhodecode/model/permission.py:102
4229 msgid "Allow password recovery"
4230 msgstr ""
4231
4232 #: rhodecode/model/permission.py:103
4233 msgid "Hide password recovery link"
4234 msgstr ""
4235
4236 #: rhodecode/model/permission.py:104
4237 4237 msgid "Disable password recovery"
4238 4238 msgstr ""
4239 4239
4240 4240 #: rhodecode/model/pull_request.py:245
4241 4241 msgid "Pull request update successful."
4242 4242 msgstr ""
4243 4243
4244 4244 #: rhodecode/model/pull_request.py:247
4245 4245 msgid "Pull request update failed because of an unknown error."
4246 4246 msgstr ""
4247 4247
4248 4248 #: rhodecode/model/pull_request.py:249
4249 4249 msgid "No update needed because the source and target have not changed."
4250 4250 msgstr ""
4251 4251
4252 4252 #: rhodecode/model/pull_request.py:251
4253 4253 msgid "Pull request cannot be updated because the reference type is not supported for an update. Only Branch, Tag or Bookmark is allowed."
4254 4254 msgstr ""
4255 4255
4256 4256 #: rhodecode/model/pull_request.py:254
4257 4257 msgid "This pull request cannot be updated because the target reference is missing."
4258 4258 msgstr ""
4259 4259
4260 4260 #: rhodecode/model/pull_request.py:257
4261 4261 msgid "This pull request cannot be updated because the source reference is missing."
4262 4262 msgstr ""
4263 4263
4264 #: rhodecode/model/pull_request.py:1688
4264 #: rhodecode/model/pull_request.py:1690
4265 4265 msgid "Server-side pull request merging is disabled."
4266 4266 msgstr ""
4267 4267
4268 #: rhodecode/model/pull_request.py:1691
4268 #: rhodecode/model/pull_request.py:1693
4269 4269 msgid "This pull request is closed."
4270 4270 msgstr ""
4271 4271
4272 #: rhodecode/model/pull_request.py:1705
4272 #: rhodecode/model/pull_request.py:1707
4273 4273 msgid "Pull request merging is not supported."
4274 4274 msgstr ""
4275 4275
4276 #: rhodecode/model/pull_request.py:1722
4276 #: rhodecode/model/pull_request.py:1724
4277 4277 msgid "Target repository large files support is disabled."
4278 4278 msgstr ""
4279 4279
4280 #: rhodecode/model/pull_request.py:1725
4280 #: rhodecode/model/pull_request.py:1727
4281 4281 msgid "Source repository large files support is disabled."
4282 4282 msgstr ""
4283 4283
4284 #: rhodecode/model/pull_request.py:1909 rhodecode/model/scm.py:1008
4284 #: rhodecode/model/pull_request.py:1911 rhodecode/model/scm.py:1008
4285 4285 #: rhodecode/templates/admin/my_account/my_account.mako:32
4286 #: rhodecode/templates/base/base.mako:638
4286 #: rhodecode/templates/base/base.mako:639
4287 4287 #: rhodecode/templates/summary/components.mako:46
4288 4288 msgid "Bookmarks"
4289 4289 msgstr ""
4290 4290
4291 #: rhodecode/model/pull_request.py:1914
4291 #: rhodecode/model/pull_request.py:1916
4292 4292 msgid "Commit IDs"
4293 4293 msgstr ""
4294 4294
4295 #: rhodecode/model/pull_request.py:1917
4295 #: rhodecode/model/pull_request.py:1919
4296 4296 #: rhodecode/templates/summary/components.mako:22
4297 4297 msgid "Closed Branches"
4298 4298 msgstr ""
4299 4299
4300 #: rhodecode/model/pull_request.py:2103
4300 #: rhodecode/model/pull_request.py:2105
4301 4301 msgid "WIP marker in title prevents from accidental merge."
4302 4302 msgstr ""
4303 4303
4304 #: rhodecode/model/pull_request.py:2113
4304 #: rhodecode/model/pull_request.py:2115
4305 4305 msgid "User `{}` not allowed to perform merge."
4306 4306 msgstr ""
4307 4307
4308 #: rhodecode/model/pull_request.py:2131
4308 #: rhodecode/model/pull_request.py:2133
4309 4309 msgid "Target branch `{}` changes rejected by rule {}."
4310 4310 msgstr ""
4311 4311
4312 #: rhodecode/model/pull_request.py:2146
4312 #: rhodecode/model/pull_request.py:2148
4313 4313 msgid "Pull request reviewer approval is pending."
4314 4314 msgstr ""
4315 4315
4316 #: rhodecode/model/pull_request.py:2160
4316 #: rhodecode/model/pull_request.py:2162
4317 4317 msgid "Cannot merge, {} TODO still not resolved."
4318 4318 msgstr ""
4319 4319
4320 #: rhodecode/model/pull_request.py:2163
4320 #: rhodecode/model/pull_request.py:2165
4321 4321 msgid "Cannot merge, {} TODOs still not resolved."
4322 4322 msgstr ""
4323 4323
4324 #: rhodecode/model/pull_request.py:2218
4324 #: rhodecode/model/pull_request.py:2220
4325 4325 msgid "Merge strategy: rebase"
4326 4326 msgstr ""
4327 4327
4328 #: rhodecode/model/pull_request.py:2223
4328 #: rhodecode/model/pull_request.py:2225
4329 4329 msgid "Merge strategy: explicit merge commit"
4330 4330 msgstr ""
4331 4331
4332 #: rhodecode/model/pull_request.py:2231
4333 msgid "Source branch will be closed before the merge."
4334 msgstr ""
4335
4336 4332 #: rhodecode/model/pull_request.py:2233
4333 msgid "Source branch will be closed before the merge."
4334 msgstr ""
4335
4336 #: rhodecode/model/pull_request.py:2235
4337 4337 msgid "Source branch will be deleted after the merge."
4338 4338 msgstr ""
4339 4339
4340 4340 #: rhodecode/model/validators.py:93 rhodecode/model/validators.py:94
4341 4341 msgid "Value cannot be an empty list"
4342 4342 msgstr ""
4343 4343
4344 4344 #: rhodecode/model/validators.py:140
4345 4345 msgid "Pattern already exists"
4346 4346 msgstr ""
4347 4347
4348 4348 #: rhodecode/model/validators.py:161
4349 4349 #, python-format
4350 4350 msgid "Username \"%(username)s\" already exists"
4351 4351 msgstr ""
4352 4352
4353 4353 #: rhodecode/model/validators.py:163
4354 4354 #, python-format
4355 4355 msgid "Username \"%(username)s\" is forbidden"
4356 4356 msgstr ""
4357 4357
4358 4358 #: rhodecode/model/validators.py:165
4359 4359 #: rhodecode/model/validation_schema/schemas/user_schema.py:71
4360 4360 msgid "Username may only contain alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character or underscore"
4361 4361 msgstr ""
4362 4362
4363 4363 #: rhodecode/model/validators.py:196
4364 4364 #, python-format
4365 4365 msgid "Username %(username)s is not valid"
4366 4366 msgstr ""
4367 4367
4368 4368 #: rhodecode/model/validators.py:197
4369 4369 #, python-format
4370 4370 msgid "Username %(username)s is disabled"
4371 4371 msgstr ""
4372 4372
4373 4373 #: rhodecode/model/validators.py:222
4374 4374 msgid "Invalid user group name"
4375 4375 msgstr ""
4376 4376
4377 4377 #: rhodecode/model/validators.py:223
4378 4378 #, python-format
4379 4379 msgid "User group `%(usergroup)s` already exists"
4380 4380 msgstr ""
4381 4381
4382 4382 #: rhodecode/model/validators.py:225
4383 4383 msgid "user group name may only contain alphanumeric characters underscores, periods or dashes and must begin with alphanumeric character"
4384 4384 msgstr ""
4385 4385
4386 4386 #: rhodecode/model/validators.py:265
4387 4387 msgid "Cannot assign this group as parent"
4388 4388 msgstr ""
4389 4389
4390 4390 #: rhodecode/model/validators.py:266
4391 4391 #, python-format
4392 4392 msgid "Group \"%(group_name)s\" already exists"
4393 4393 msgstr ""
4394 4394
4395 4395 #: rhodecode/model/validators.py:267
4396 4396 #, python-format
4397 4397 msgid "Repository with name \"%(group_name)s\" already exists"
4398 4398 msgstr ""
4399 4399
4400 4400 #: rhodecode/model/validators.py:269
4401 4401 msgid "no permission to store repository groupin this location"
4402 4402 msgstr ""
4403 4403
4404 4404 #: rhodecode/model/validators.py:271
4405 4405 msgid "no permission to store repository group in root location"
4406 4406 msgstr ""
4407 4407
4408 4408 #: rhodecode/model/validators.py:388
4409 4409 msgid "Invalid characters (non-ascii) in password"
4410 4410 msgstr ""
4411 4411
4412 4412 #: rhodecode/model/validators.py:407
4413 4413 msgid "Passwords do not match"
4414 4414 msgstr ""
4415 4415
4416 4416 #: rhodecode/model/validators.py:427
4417 4417 msgid "invalid password"
4418 4418 msgstr ""
4419 4419
4420 4420 #: rhodecode/model/validators.py:428
4421 4421 msgid "invalid user name"
4422 4422 msgstr ""
4423 4423
4424 4424 #: rhodecode/model/validators.py:429
4425 4425 msgid "Your account is disabled"
4426 4426 msgstr ""
4427 4427
4428 4428 #: rhodecode/model/validators.py:465
4429 4429 #, python-format
4430 4430 msgid "Repository name %(repo)s is disallowed"
4431 4431 msgstr ""
4432 4432
4433 4433 #: rhodecode/model/validators.py:467
4434 4434 #, python-format
4435 4435 msgid "Repository with name %(repo)s already exists"
4436 4436 msgstr ""
4437 4437
4438 4438 #: rhodecode/model/validators.py:469
4439 4439 #, python-format
4440 4440 msgid "Repository group with name \"%(repo)s\" already exists"
4441 4441 msgstr ""
4442 4442
4443 4443 #: rhodecode/model/validators.py:472
4444 4444 #, python-format
4445 4445 msgid "Repository with name %(repo)s exists in group \"%(group)s\""
4446 4446 msgstr ""
4447 4447
4448 4448 #: rhodecode/model/validators.py:474
4449 4449 #, python-format
4450 4450 msgid "Repository group with name \"%(repo)s\" exists in group \"%(group)s\""
4451 4451 msgstr ""
4452 4452
4453 4453 #: rhodecode/model/validators.py:567
4454 4454 #: rhodecode/model/validation_schema/schemas/repo_schema.py:236
4455 4455 msgid "Repository name cannot end with .git"
4456 4456 msgstr ""
4457 4457
4458 4458 #: rhodecode/model/validators.py:627
4459 4459 #, python-format
4460 4460 msgid "invalid clone url for %(rtype)s repository"
4461 4461 msgstr ""
4462 4462
4463 4463 #: rhodecode/model/validators.py:628
4464 4464 #, python-format
4465 4465 msgid "Invalid clone url, provide a valid clone url starting with one of %(allowed_prefixes)s"
4466 4466 msgstr ""
4467 4467
4468 4468 #: rhodecode/model/validators.py:660
4469 4469 msgid "Fork have to be the same type as parent"
4470 4470 msgstr ""
4471 4471
4472 4472 #: rhodecode/model/validators.py:677
4473 4473 msgid "You do not have the permission to create repositories in this group."
4474 4474 msgstr ""
4475 4475
4476 4476 #: rhodecode/model/validators.py:680
4477 4477 #: rhodecode/model/validation_schema/schemas/repo_schema.py:136
4478 4478 msgid "You do not have the permission to store repositories in the root location."
4479 4479 msgstr ""
4480 4480
4481 4481 #: rhodecode/model/validators.py:740
4482 4482 msgid "This username or user group name is not valid"
4483 4483 msgstr ""
4484 4484
4485 4485 #: rhodecode/model/validators.py:853
4486 4486 msgid "This is not a valid path"
4487 4487 msgstr ""
4488 4488
4489 4489 #: rhodecode/model/validators.py:871
4490 4490 #: rhodecode/model/validation_schema/schemas/user_schema.py:144
4491 4491 #: rhodecode/model/validation_schema/schemas/user_schema.py:148
4492 4492 msgid "This e-mail address is already taken"
4493 4493 msgstr ""
4494 4494
4495 4495 #: rhodecode/model/validators.py:893
4496 4496 #, python-format
4497 4497 msgid "e-mail \"%(email)s\" does not exist."
4498 4498 msgstr ""
4499 4499
4500 4500 #: rhodecode/model/validators.py:914
4501 4501 #, python-format
4502 4502 msgid "Revisions %(revs)s are already part of pull request or have set status"
4503 4503 msgstr ""
4504 4504
4505 4505 #: rhodecode/model/validators.py:947
4506 4506 #: rhodecode/model/validation_schema/validators.py:41
4507 4507 #: rhodecode/model/validation_schema/validators.py:54
4508 4508 msgid "Please enter a valid IPv4 or IpV6 address"
4509 4509 msgstr ""
4510 4510
4511 4511 #: rhodecode/model/validators.py:948
4512 4512 #, python-format
4513 4513 msgid "The network size (bits) must be within the range of 0-32 (not %(bits)r)"
4514 4514 msgstr ""
4515 4515
4516 4516 #: rhodecode/model/validators.py:976
4517 4517 msgid "Key name can only consist of letters, underscore, dash or numbers"
4518 4518 msgstr ""
4519 4519
4520 4520 #: rhodecode/model/validators.py:993
4521 4521 #, python-format
4522 4522 msgid "Plugins %(loaded)s and %(next_to_load)s both export the same name"
4523 4523 msgstr ""
4524 4524
4525 4525 #: rhodecode/model/validators.py:996
4526 4526 #, python-format
4527 4527 msgid "The plugin \"%(plugin_id)s\" is missing an includeme function."
4528 4528 msgstr ""
4529 4529
4530 4530 #: rhodecode/model/validators.py:999
4531 4531 #, python-format
4532 4532 msgid "Can not load plugin \"%(plugin_id)s\""
4533 4533 msgstr ""
4534 4534
4535 4535 #: rhodecode/model/validators.py:1001
4536 4536 #, python-format
4537 4537 msgid "No plugin available with ID \"%(plugin_id)s\""
4538 4538 msgstr ""
4539 4539
4540 4540 #: rhodecode/model/validators.py:1069
4541 4541 msgid "Url must start with http or /"
4542 4542 msgstr ""
4543 4543
4544 4544 #: rhodecode/model/validation_schema/validators.py:62
4545 4545 msgid "Invalid glob pattern"
4546 4546 msgstr ""
4547 4547
4548 4548 #: rhodecode/model/validation_schema/validators.py:71
4549 4549 msgid "Name must start with a letter or number. Got `{}`"
4550 4550 msgstr ""
4551 4551
4552 4552 #: rhodecode/model/validation_schema/validators.py:142
4553 4553 msgid "invalid clone url for {repo_type} repository"
4554 4554 msgstr ""
4555 4555
4556 4556 #: rhodecode/model/validation_schema/validators.py:151
4557 4557 msgid "Please enter a valid json object"
4558 4558 msgstr ""
4559 4559
4560 4560 #: rhodecode/model/validation_schema/validators.py:159
4561 4561 msgid "Please enter a valid json object: `{}`"
4562 4562 msgstr ""
4563 4563
4564 4564 #: rhodecode/model/validation_schema/schemas/comment_schema.py:42
4565 4565 #: rhodecode/model/validation_schema/schemas/gist_schema.py:89
4566 4566 msgid "Gist with name {} already exists"
4567 4567 msgstr ""
4568 4568
4569 4569 #: rhodecode/model/validation_schema/schemas/comment_schema.py:48
4570 4570 #: rhodecode/model/validation_schema/schemas/gist_schema.py:95
4571 4571 msgid "Filename {} cannot be inside a directory"
4572 4572 msgstr ""
4573 4573
4574 4574 #: rhodecode/model/validation_schema/schemas/gist_schema.py:132
4575 4575 msgid "Duplicated value for filename found: `{}`"
4576 4576 msgstr ""
4577 4577
4578 4578 #: rhodecode/model/validation_schema/schemas/integration_schema.py:36
4579 4579 msgid "Pick a scope:"
4580 4580 msgstr ""
4581 4581
4582 4582 #: rhodecode/model/validation_schema/schemas/integration_schema.py:39
4583 4583 msgid "Global (all repositories)"
4584 4584 msgstr ""
4585 4585
4586 4586 #: rhodecode/model/validation_schema/schemas/integration_schema.py:40
4587 4587 msgid "Top level repositories only"
4588 4588 msgstr ""
4589 4589
4590 4590 #: rhodecode/model/validation_schema/schemas/integration_schema.py:79
4591 4591 msgid "Only repo admins can create integrations"
4592 4592 msgstr ""
4593 4593
4594 4594 #: rhodecode/model/validation_schema/schemas/integration_schema.py:86
4595 4595 msgid "Only repogroup admins can create integrations"
4596 4596 msgstr ""
4597 4597
4598 4598 #: rhodecode/model/validation_schema/schemas/integration_schema.py:91
4599 4599 msgid "Only superadmins can create global integrations"
4600 4600 msgstr ""
4601 4601
4602 4602 #: rhodecode/model/validation_schema/schemas/integration_schema.py:181
4603 4603 msgid "Scope of the integration. Recursive means the integration runs on all repos of that group and children recursively."
4604 4604 msgstr ""
4605 4605
4606 4606 #: rhodecode/model/validation_schema/schemas/integration_schema.py:184
4607 4607 msgid "Integration scope"
4608 4608 msgstr ""
4609 4609
4610 4610 #: rhodecode/model/validation_schema/schemas/integration_schema.py:214
4611 4611 msgid "General integration options"
4612 4612 msgstr ""
4613 4613
4614 4614 #: rhodecode/model/validation_schema/schemas/integration_schema.py:217
4615 4615 msgid "{integration_type} settings"
4616 4616 msgstr ""
4617 4617
4618 4618 #: rhodecode/model/validation_schema/schemas/repo_group_schema.py:52
4619 4619 msgid "Parent repository group `{}` does not exist"
4620 4620 msgstr ""
4621 4621
4622 4622 #: rhodecode/model/validation_schema/schemas/repo_group_schema.py:56
4623 4623 msgid "You do not have the permissions to store repository groups inside repository group `{}`"
4624 4624 msgstr ""
4625 4625
4626 4626 #: rhodecode/model/validation_schema/schemas/repo_group_schema.py:59
4627 4627 msgid "You do not have the permission to store repository groups in the root location."
4628 4628 msgstr ""
4629 4629
4630 4630 #: rhodecode/model/validation_schema/schemas/repo_group_schema.py:125
4631 4631 msgid "Repo group owner with id `{}` does not exists"
4632 4632 msgstr ""
4633 4633
4634 4634 #: rhodecode/model/validation_schema/schemas/repo_group_schema.py:143
4635 4635 #: rhodecode/model/validation_schema/schemas/repo_schema.py:221
4636 4636 msgid "Repository with name `{}` already exists"
4637 4637 msgstr ""
4638 4638
4639 4639 #: rhodecode/model/validation_schema/schemas/repo_group_schema.py:148
4640 4640 #: rhodecode/model/validation_schema/schemas/repo_schema.py:226
4641 4641 msgid "Repository group with name `{}` already exists"
4642 4642 msgstr ""
4643 4643
4644 4644 #: rhodecode/model/validation_schema/schemas/repo_schema.py:60
4645 4645 msgid "Repo owner with id `{}` does not exists"
4646 4646 msgstr ""
4647 4647
4648 4648 #: rhodecode/model/validation_schema/schemas/repo_schema.py:102
4649 4649 msgid "Fork with id `{}` does not exists"
4650 4650 msgstr ""
4651 4651
4652 4652 #: rhodecode/model/validation_schema/schemas/repo_schema.py:105
4653 4653 msgid "Cannot set fork of parameter of this repository to itself"
4654 4654 msgstr ""
4655 4655
4656 4656 #: rhodecode/model/validation_schema/schemas/repo_schema.py:130
4657 4657 #: rhodecode/model/validation_schema/schemas/repo_schema.py:134
4658 4658 msgid "Repository group `{}` does not exist"
4659 4659 msgstr ""
4660 4660
4661 4661 #: rhodecode/model/validation_schema/schemas/user_group_schema.py:32
4662 4662 msgid "Allowed in name are letters, numbers, and `-`, `_`, `.` Name must start with a letter or number. Got `{}`"
4663 4663 msgstr ""
4664 4664
4665 4665 #: rhodecode/model/validation_schema/schemas/user_group_schema.py:48
4666 4666 msgid "User group owner with id `{}` does not exists"
4667 4667 msgstr ""
4668 4668
4669 4669 #: rhodecode/model/validation_schema/schemas/user_schema.py:39
4670 4670 msgid "Password is incorrect"
4671 4671 msgstr ""
4672 4672
4673 4673 #: rhodecode/model/validation_schema/schemas/user_schema.py:62
4674 4674 msgid "New password must be different to old password"
4675 4675 msgstr ""
4676 4676
4677 4677 #: rhodecode/model/validation_schema/schemas/user_schema.py:183
4678 4678 msgid "Additional emails can be specified at <a href=\"{}\">extra emails</a> page."
4679 4679 msgstr ""
4680 4680
4681 4681 #: rhodecode/public/js/scripts.js:19
4682 4682 msgid ": , "
4683 4683 msgstr ""
4684 4684
4685 4685 #: rhodecode/public/js/scripts.js:20822 rhodecode/public/js/scripts.min.js:1
4686 4686 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:66
4687 4687 #: rhodecode/public/js/src/plugins/jquery.autocomplete.js:87
4688 4688 msgid "No results"
4689 4689 msgstr ""
4690 4690
4691 4691 #: rhodecode/public/js/scripts.js:22547 rhodecode/public/js/scripts.min.js:1
4692 4692 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:185
4693 4693 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:109
4694 4694 msgid "{0} year"
4695 4695 msgstr ""
4696 4696
4697 4697 #: rhodecode/public/js/scripts.js:22548 rhodecode/public/js/scripts.min.js:1
4698 4698 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:171
4699 4699 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:110
4700 4700 msgid "{0} month"
4701 4701 msgstr ""
4702 4702
4703 4703 #: rhodecode/public/js/scripts.js:22549 rhodecode/public/js/scripts.min.js:1
4704 4704 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:166
4705 4705 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:111
4706 4706 msgid "{0} day"
4707 4707 msgstr ""
4708 4708
4709 4709 #: rhodecode/public/js/scripts.js:22550 rhodecode/public/js/scripts.min.js:1
4710 4710 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:168
4711 4711 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:112
4712 4712 msgid "{0} hour"
4713 4713 msgstr ""
4714 4714
4715 4715 #: rhodecode/public/js/scripts.js:22551 rhodecode/public/js/scripts.min.js:1
4716 4716 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:170
4717 4717 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:113
4718 4718 msgid "{0} min"
4719 4719 msgstr ""
4720 4720
4721 4721 #: rhodecode/public/js/scripts.js:22552 rhodecode/public/js/scripts.min.js:1
4722 4722 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:180
4723 4723 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:114
4724 4724 msgid "{0} sec"
4725 4725 msgstr ""
4726 4726
4727 4727 #: rhodecode/public/js/scripts.js:22572 rhodecode/public/js/scripts.min.js:1
4728 4728 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:142
4729 4729 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:134
4730 4730 msgid "in {0}"
4731 4731 msgstr ""
4732 4732
4733 4733 #: rhodecode/public/js/scripts.js:22580 rhodecode/public/js/scripts.min.js:1
4734 4734 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:159
4735 4735 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:142
4736 4736 msgid "{0} ago"
4737 4737 msgstr ""
4738 4738
4739 4739 #: rhodecode/public/js/scripts.js:22592 rhodecode/public/js/scripts.min.js:1
4740 4740 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:187
4741 4741 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:154
4742 4742 msgid "{0}, {1} ago"
4743 4743 msgstr ""
4744 4744
4745 4745 #: rhodecode/public/js/scripts.js:22594 rhodecode/public/js/scripts.min.js:1
4746 4746 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:144
4747 4747 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:156
4748 4748 msgid "in {0}, {1}"
4749 4749 msgstr ""
4750 4750
4751 4751 #: rhodecode/public/js/scripts.js:22598 rhodecode/public/js/scripts.min.js:1
4752 4752 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:160
4753 4753 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:160
4754 4754 msgid "{0} and {1}"
4755 4755 msgstr ""
4756 4756
4757 4757 #: rhodecode/public/js/scripts.js:22600 rhodecode/public/js/scripts.min.js:1
4758 4758 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:161
4759 4759 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:162
4760 4760 msgid "{0} and {1} ago"
4761 4761 msgstr ""
4762 4762
4763 4763 #: rhodecode/public/js/scripts.js:22602 rhodecode/public/js/scripts.min.js:1
4764 4764 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:143
4765 4765 #: rhodecode/public/js/src/plugins/jquery.timeago-extension.js:164
4766 4766 msgid "in {0} and {1}"
4767 4767 msgstr ""
4768 4768
4769 4769 #: rhodecode/public/js/scripts.js:37600 rhodecode/public/js/scripts.min.js:1
4770 4770 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:52
4771 4771 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:4
4772 4772 msgid "Loading more results..."
4773 4773 msgstr ""
4774 4774
4775 4775 #: rhodecode/public/js/scripts.js:37603 rhodecode/public/js/scripts.min.js:1
4776 4776 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:85
4777 4777 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:7
4778 4778 msgid "Searching..."
4779 4779 msgstr ""
4780 4780
4781 4781 #: rhodecode/public/js/scripts.js:37606 rhodecode/public/js/scripts.min.js:1
4782 4782 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:59
4783 4783 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:10
4784 4784 msgid "No matches found"
4785 4785 msgstr ""
4786 4786
4787 4787 #: rhodecode/public/js/scripts.js:37609 rhodecode/public/js/scripts.min.js:1
4788 4788 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:51
4789 4789 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:13
4790 4790 msgid "Loading failed"
4791 4791 msgstr ""
4792 4792
4793 4793 #: rhodecode/public/js/scripts.js:37613 rhodecode/public/js/scripts.min.js:1
4794 4794 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:74
4795 4795 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:17
4796 4796 msgid "One result is available, press enter to select it."
4797 4797 msgstr ""
4798 4798
4799 4799 #: rhodecode/public/js/scripts.js:37615 rhodecode/public/js/scripts.min.js:1
4800 4800 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:179
4801 4801 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:19
4802 4802 msgid "{0} results are available, use up and down arrow keys to navigate."
4803 4803 msgstr ""
4804 4804
4805 4805 #: rhodecode/public/js/scripts.js:37620 rhodecode/public/js/scripts.min.js:1
4806 4806 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:79
4807 4807 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:24
4808 4808 msgid "Please enter {0} or more character"
4809 4809 msgstr ""
4810 4810
4811 4811 #: rhodecode/public/js/scripts.js:37622 rhodecode/public/js/scripts.min.js:1
4812 4812 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:80
4813 4813 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:26
4814 4814 msgid "Please enter {0} or more characters"
4815 4815 msgstr ""
4816 4816
4817 4817 #: rhodecode/public/js/scripts.js:37627 rhodecode/public/js/scripts.min.js:1
4818 4818 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:77
4819 4819 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:31
4820 4820 msgid "Please delete {0} character"
4821 4821 msgstr ""
4822 4822
4823 4823 #: rhodecode/public/js/scripts.js:37629 rhodecode/public/js/scripts.min.js:1
4824 4824 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:78
4825 4825 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:33
4826 4826 msgid "Please delete {0} characters"
4827 4827 msgstr ""
4828 4828
4829 4829 #: rhodecode/public/js/scripts.js:37633 rhodecode/public/js/scripts.min.js:1
4830 4830 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:132
4831 4831 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:37
4832 4832 msgid "You can only select {0} item"
4833 4833 msgstr ""
4834 4834
4835 4835 #: rhodecode/public/js/scripts.js:37635 rhodecode/public/js/scripts.min.js:1
4836 4836 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:133
4837 4837 #: rhodecode/public/js/rhodecode/i18n/select2/translations.js:39
4838 4838 msgid "You can only select {0} items"
4839 4839 msgstr ""
4840 4840
4841 4841 #: rhodecode/public/js/scripts.js:38289 rhodecode/public/js/scripts.min.js:1
4842 4842 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:9
4843 4843 #: rhodecode/public/js/src/rhodecode/utils/ajax.js:139
4844 4844 msgid "Ajax Request Error"
4845 4845 msgstr ""
4846 4846
4847 4847 #: rhodecode/public/js/scripts.js:38691 rhodecode/public/js/scripts.min.js:1
4848 4848 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:151
4849 4849 #: rhodecode/public/js/src/rhodecode/changelog.js:35
4850 4850 msgid "showing {0} out of {1} commit"
4851 4851 msgstr ""
4852 4852
4853 4853 #: rhodecode/public/js/scripts.js:38693 rhodecode/public/js/scripts.min.js:1
4854 4854 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:152
4855 4855 #: rhodecode/public/js/src/rhodecode/changelog.js:37
4856 4856 msgid "showing {0} out of {1} commits"
4857 4857 msgstr ""
4858 4858
4859 4859 #: rhodecode/public/js/scripts.js:39237 rhodecode/public/js/scripts.min.js:1
4860 4860 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:88
4861 4861 #: rhodecode/public/js/src/rhodecode/codemirror.js:368
4862 4862 msgid "Set status to Approved"
4863 4863 msgstr ""
4864 4864
4865 4865 #: rhodecode/public/js/scripts.js:39257 rhodecode/public/js/scripts.min.js:1
4866 4866 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:89
4867 4867 #: rhodecode/public/js/src/rhodecode/codemirror.js:388
4868 4868 msgid "Set status to Rejected"
4869 4869 msgstr ""
4870 4870
4871 4871 #: rhodecode/public/js/scripts.js:39276 rhodecode/public/js/scripts.min.js:1
4872 4872 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:112
4873 4873 #: rhodecode/public/js/src/rhodecode/codemirror.js:407
4874 4874 msgid "TODO comment"
4875 4875 msgstr ""
4876 4876
4877 4877 #: rhodecode/public/js/scripts.js:39296 rhodecode/public/js/scripts.min.js:1
4878 4878 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:73
4879 4879 #: rhodecode/public/js/src/rhodecode/codemirror.js:427
4880 4880 msgid "Note Comment"
4881 4881 msgstr ""
4882 4882
4883 #: rhodecode/public/js/scripts.js:39599 rhodecode/public/js/scripts.js:39987
4883 #: rhodecode/public/js/scripts.js:39599 rhodecode/public/js/scripts.js:39985
4884 4884 #: rhodecode/public/js/scripts.min.js:1
4885 4885 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:103
4886 4886 #: rhodecode/public/js/src/rhodecode/codemirror.js:730
4887 #: rhodecode/public/js/src/rhodecode/comments.js:267
4887 #: rhodecode/public/js/src/rhodecode/comments.js:265
4888 4888 msgid "Status Review"
4889 4889 msgstr ""
4890 4890
4891 #: rhodecode/public/js/scripts.js:39614 rhodecode/public/js/scripts.js:40004
4891 #: rhodecode/public/js/scripts.js:39614 rhodecode/public/js/scripts.js:40002
4892 4892 #: rhodecode/public/js/scripts.min.js:1
4893 4893 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:24
4894 4894 #: rhodecode/public/js/src/rhodecode/codemirror.js:745
4895 #: rhodecode/public/js/src/rhodecode/comments.js:284
4895 #: rhodecode/public/js/src/rhodecode/comments.js:282
4896 4896 msgid "Comment text will be set automatically based on currently selected status ({0}) ..."
4897 4897 msgstr ""
4898 4898
4899 #: rhodecode/public/js/scripts.js:39695 rhodecode/public/js/scripts.js:40213
4900 #: rhodecode/public/js/scripts.js:41745 rhodecode/public/js/scripts.min.js:1
4899 #: rhodecode/public/js/scripts.js:39695 rhodecode/public/js/scripts.js:40211
4900 #: rhodecode/public/js/scripts.js:41883 rhodecode/public/js/scripts.min.js:1
4901 4901 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:49
4902 4902 #: rhodecode/public/js/src/rhodecode/codemirror.js:826
4903 #: rhodecode/public/js/src/rhodecode/comments.js:493
4903 #: rhodecode/public/js/src/rhodecode/comments.js:491
4904 4904 #: rhodecode/public/js/src/rhodecode/files.js:499
4905 4905 #: rhodecode/templates/files/files_browser_tree.mako:57
4906 4906 msgid "Loading ..."
4907 4907 msgstr ""
4908 4908
4909 #: rhodecode/public/js/scripts.js:39860 rhodecode/public/js/scripts.min.js:1
4909 #: rhodecode/public/js/scripts.js:39859 rhodecode/public/js/scripts.min.js:1
4910 4910 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:123
4911 #: rhodecode/public/js/src/rhodecode/comments.js:140
4911 #: rhodecode/public/js/src/rhodecode/comments.js:139
4912 4912 msgid "Update Comment"
4913 4913 msgstr ""
4914 4914
4915 #: rhodecode/public/js/scripts.js:39884 rhodecode/public/js/scripts.min.js:1
4915 #: rhodecode/public/js/scripts.js:39883 rhodecode/public/js/scripts.min.js:1
4916 4916 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:150
4917 #: rhodecode/public/js/src/rhodecode/comments.js:164
4917 #: rhodecode/public/js/src/rhodecode/comments.js:163
4918 4918 msgid "resolve comment"
4919 4919 msgstr ""
4920 4920
4921 #: rhodecode/public/js/scripts.js:40155 rhodecode/public/js/scripts.min.js:1
4922 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:83
4923 #: rhodecode/public/js/src/rhodecode/comments.js:435
4924 msgid "Saving Draft..."
4925 msgstr ""
4926
4921 4927 #: rhodecode/public/js/scripts.js:40157 rhodecode/public/js/scripts.min.js:1
4922 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:83
4928 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:108
4923 4929 #: rhodecode/public/js/src/rhodecode/comments.js:437
4924 msgid "Saving Draft..."
4925 msgstr ""
4926
4927 #: rhodecode/public/js/scripts.js:40159 rhodecode/public/js/scripts.min.js:1
4928 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:108
4929 #: rhodecode/public/js/src/rhodecode/comments.js:439
4930 4930 msgid "Submitting..."
4931 4931 msgstr ""
4932 4932
4933 #: rhodecode/public/js/scripts.js:40481 rhodecode/public/js/scripts.min.js:1
4933 #: rhodecode/public/js/scripts.js:40479 rhodecode/public/js/scripts.min.js:1
4934 4934 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:131
4935 #: rhodecode/public/js/src/rhodecode/comments.js:761
4935 #: rhodecode/public/js/src/rhodecode/comments.js:759
4936 4936 msgid "Yes, delete comment #{0}!"
4937 4937 msgstr ""
4938 4938
4939 #: rhodecode/public/js/scripts.js:40526 rhodecode/public/js/scripts.min.js:1
4939 #: rhodecode/public/js/scripts.js:40524 rhodecode/public/js/scripts.min.js:1
4940 4940 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:106
4941 #: rhodecode/public/js/src/rhodecode/comments.js:806
4941 #: rhodecode/public/js/src/rhodecode/comments.js:804
4942 4942 msgid "Submit {0} draft comment."
4943 4943 msgstr ""
4944 4944
4945 #: rhodecode/public/js/scripts.js:40529 rhodecode/public/js/scripts.min.js:1
4945 #: rhodecode/public/js/scripts.js:40527 rhodecode/public/js/scripts.js:41225
4946 #: rhodecode/public/js/scripts.min.js:1
4946 4947 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:130
4947 #: rhodecode/public/js/src/rhodecode/comments.js:809
4948 #: rhodecode/public/js/src/rhodecode/comments.js:807
4949 #: rhodecode/public/js/src/rhodecode/comments.js:1505
4948 4950 msgid "Yes"
4949 4951 msgstr ""
4950 4952
4951 #: rhodecode/public/js/scripts.js:40621 rhodecode/public/js/scripts.min.js:1
4953 #: rhodecode/public/js/scripts.js:40619 rhodecode/public/js/scripts.min.js:1
4952 4954 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:47
4953 #: rhodecode/public/js/src/rhodecode/comments.js:901
4955 #: rhodecode/public/js/src/rhodecode/comments.js:899
4954 4956 msgid "Leave a resolution comment, or click resolve button to resolve TODO comment #{0}"
4955 4957 msgstr ""
4956 4958
4957 #: rhodecode/public/js/scripts.js:40825 rhodecode/public/js/scripts.min.js:1
4959 #: rhodecode/public/js/scripts.js:40823 rhodecode/public/js/scripts.min.js:1
4958 4960 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:23
4959 #: rhodecode/public/js/src/rhodecode/comments.js:1105
4961 #: rhodecode/public/js/src/rhodecode/comments.js:1103
4960 4962 msgid "Comment body was not changed."
4961 4963 msgstr ""
4962 4964
4963 #: rhodecode/public/js/scripts.js:41071 rhodecode/public/js/scripts.min.js:1
4965 #: rhodecode/public/js/scripts.js:41074 rhodecode/public/js/scripts.min.js:1
4964 4966 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:44
4965 #: rhodecode/public/js/src/rhodecode/comments.js:1351
4967 #: rhodecode/public/js/src/rhodecode/comments.js:1354
4966 4968 msgid "Leave a comment on file {0} line {1}."
4967 4969 msgstr ""
4968 4970
4969 #: rhodecode/public/js/scripts.js:41212 rhodecode/public/js/scripts.min.js:1
4971 #: rhodecode/public/js/scripts.js:41214 rhodecode/public/js/scripts.js:41267
4972 #: rhodecode/public/js/scripts.min.js:1
4970 4973 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:113
4971 #: rhodecode/public/js/src/rhodecode/comments.js:1492
4974 #: rhodecode/public/js/src/rhodecode/comments.js:1494
4975 #: rhodecode/public/js/src/rhodecode/comments.js:1547
4972 4976 msgid "TODO from comment {0} was fixed."
4973 4977 msgstr ""
4974 4978
4975 #: rhodecode/public/js/scripts.js:41494 rhodecode/public/js/scripts.min.js:1
4979 #: rhodecode/public/js/scripts.js:41632 rhodecode/public/js/scripts.min.js:1
4976 4980 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:154
4977 4981 #: rhodecode/public/js/src/rhodecode/files.js:248
4978 4982 msgid "truncated result"
4979 4983 msgstr ""
4980 4984
4981 #: rhodecode/public/js/scripts.js:41496 rhodecode/public/js/scripts.min.js:1
4985 #: rhodecode/public/js/scripts.js:41634 rhodecode/public/js/scripts.min.js:1
4982 4986 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:155
4983 4987 #: rhodecode/public/js/src/rhodecode/files.js:250
4984 4988 msgid "truncated results"
4985 4989 msgstr ""
4986 4990
4987 #: rhodecode/public/js/scripts.js:41505 rhodecode/public/js/scripts.min.js:1
4991 #: rhodecode/public/js/scripts.js:41643 rhodecode/public/js/scripts.min.js:1
4988 4992 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:60
4989 4993 #: rhodecode/public/js/src/rhodecode/files.js:259
4990 4994 msgid "No matching files"
4991 4995 msgstr ""
4992 4996
4993 #: rhodecode/public/js/scripts.js:41563 rhodecode/public/js/scripts.min.js:1
4997 #: rhodecode/public/js/scripts.js:41701 rhodecode/public/js/scripts.min.js:1
4994 4998 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:86
4995 4999 #: rhodecode/public/js/src/rhodecode/files.js:317
4996 5000 msgid "Selection link"
4997 5001 msgstr ""
4998 5002
4999 #: rhodecode/public/js/scripts.js:41660 rhodecode/public/js/scripts.min.js:1
5003 #: rhodecode/public/js/scripts.js:41798 rhodecode/public/js/scripts.min.js:1
5000 5004 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:10
5001 5005 #: rhodecode/public/js/src/rhodecode/files.js:414
5002 5006 msgid "All Authors"
5003 5007 msgstr ""
5004 5008
5005 #: rhodecode/public/js/scripts.js:41810 rhodecode/public/js/scripts.js:41813
5009 #: rhodecode/public/js/scripts.js:41948 rhodecode/public/js/scripts.js:41951
5006 5010 #: rhodecode/public/js/scripts.min.js:1
5007 5011 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:38
5008 5012 #: rhodecode/public/js/src/rhodecode/files.js:564
5009 5013 #: rhodecode/public/js/src/rhodecode/files.js:567
5010 5014 msgid "File `{0}` has a newer version available, or has been removed. Click {1} to see the latest version."
5011 5015 msgstr ""
5012 5016
5013 #: rhodecode/public/js/scripts.js:41816 rhodecode/public/js/scripts.min.js:1
5017 #: rhodecode/public/js/scripts.js:41954 rhodecode/public/js/scripts.min.js:1
5014 5018 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:117
5015 5019 #: rhodecode/public/js/src/rhodecode/files.js:570
5016 5020 msgid "There is an existing path `{0}` at this commit."
5017 5021 msgstr ""
5018 5022
5019 #: rhodecode/public/js/scripts.js:41819 rhodecode/public/js/scripts.min.js:1
5023 #: rhodecode/public/js/scripts.js:41957 rhodecode/public/js/scripts.min.js:1
5020 5024 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:116
5021 5025 #: rhodecode/public/js/src/rhodecode/files.js:573
5022 5026 msgid "There is a later version of file tree available. Click {0} to create a file at the latest tree."
5023 5027 msgstr ""
5024 5028
5025 #: rhodecode/public/js/scripts.js:41873 rhodecode/public/js/scripts.min.js:1
5029 #: rhodecode/public/js/scripts.js:42011 rhodecode/public/js/scripts.min.js:1
5026 5030 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:105
5027 5031 #: rhodecode/public/js/src/rhodecode/followers.js:26
5028 5032 msgid "Stopped watching this repository"
5029 5033 msgstr ""
5030 5034
5031 #: rhodecode/public/js/scripts.js:41874 rhodecode/public/js/scripts.min.js:1
5035 #: rhodecode/public/js/scripts.js:42012 rhodecode/public/js/scripts.min.js:1
5032 5036 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:129
5033 5037 #: rhodecode/public/js/src/rhodecode/followers.js:27
5034 #: rhodecode/templates/base/base.mako:310
5038 #: rhodecode/templates/base/base.mako:311
5035 5039 msgid "Watch"
5036 5040 msgstr ""
5037 5041
5038 #: rhodecode/public/js/scripts.js:41877 rhodecode/public/js/scripts.min.js:1
5042 #: rhodecode/public/js/scripts.js:42015 rhodecode/public/js/scripts.min.js:1
5039 5043 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:102
5040 5044 #: rhodecode/public/js/src/rhodecode/followers.js:30
5041 5045 msgid "Started watching this repository"
5042 5046 msgstr ""
5043 5047
5044 #: rhodecode/public/js/scripts.js:41878 rhodecode/public/js/scripts.min.js:1
5048 #: rhodecode/public/js/scripts.js:42016 rhodecode/public/js/scripts.min.js:1
5045 5049 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:122
5046 5050 #: rhodecode/public/js/src/rhodecode/followers.js:31
5047 #: rhodecode/templates/base/base.mako:308
5051 #: rhodecode/templates/base/base.mako:309
5048 5052 msgid "Unwatch"
5049 5053 msgstr ""
5050 5054
5051 #: rhodecode/public/js/scripts.js:42384 rhodecode/public/js/scripts.min.js:1
5055 #: rhodecode/public/js/scripts.js:42522 rhodecode/public/js/scripts.min.js:1
5052 5056 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:12
5053 5057 #: rhodecode/public/js/src/rhodecode/pullrequests.js:185
5054 5058 msgid "All reviewers must vote."
5055 5059 msgstr ""
5056 5060
5057 #: rhodecode/public/js/scripts.js:42408 rhodecode/public/js/scripts.min.js:1
5061 #: rhodecode/public/js/scripts.js:42546 rhodecode/public/js/scripts.min.js:1
5058 5062 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:54
5059 5063 #: rhodecode/public/js/src/rhodecode/pullrequests.js:209
5060 5064 msgid "No additional review rules set."
5061 5065 msgstr ""
5062 5066
5063 #: rhodecode/public/js/scripts.js:42454 rhodecode/public/js/scripts.min.js:1
5067 #: rhodecode/public/js/scripts.js:42592 rhodecode/public/js/scripts.min.js:1
5064 5068 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:50
5065 5069 #: rhodecode/public/js/src/rhodecode/pullrequests.js:255
5066 5070 msgid "Loading diff ..."
5067 5071 msgstr ""
5068 5072
5069 #: rhodecode/public/js/scripts.js:42507 rhodecode/public/js/scripts.min.js:1
5073 #: rhodecode/public/js/scripts.js:42645 rhodecode/public/js/scripts.min.js:1
5070 5074 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:115
5071 5075 #: rhodecode/public/js/src/rhodecode/pullrequests.js:308
5072 5076 msgid "There are no commits to merge."
5073 5077 msgstr ""
5074 5078
5075 #: rhodecode/public/js/scripts.js:42579 rhodecode/public/js/scripts.min.js:1
5079 #: rhodecode/public/js/scripts.js:42717 rhodecode/public/js/scripts.min.js:1
5076 5080 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:128
5077 5081 #: rhodecode/public/js/src/rhodecode/pullrequests.js:380
5078 5082 msgid "User `{0}` not allowed to be a reviewer"
5079 5083 msgstr ""
5080 5084
5081 #: rhodecode/public/js/scripts.js:42585 rhodecode/public/js/scripts.min.js:1
5085 #: rhodecode/public/js/scripts.js:42723 rhodecode/public/js/scripts.min.js:1
5082 5086 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:127
5083 5087 #: rhodecode/public/js/src/rhodecode/pullrequests.js:386
5084 5088 msgid "User `{0}` already in reviewers/observers"
5085 5089 msgstr ""
5086 5090
5087 #: rhodecode/public/js/scripts.js:42699 rhodecode/public/js/scripts.min.js:1
5091 #: rhodecode/public/js/scripts.js:42838 rhodecode/public/js/scripts.min.js:1
5088 5092 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:135
5089 5093 #: rhodecode/public/js/src/rhodecode/pullrequests.js:501
5090 5094 msgid "added manually by \"{0}\""
5091 5095 msgstr ""
5092 5096
5093 #: rhodecode/public/js/scripts.js:42704 rhodecode/public/js/scripts.min.js:1
5097 #: rhodecode/public/js/scripts.js:42843 rhodecode/public/js/scripts.min.js:1
5094 5098 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:147
5095 5099 #: rhodecode/public/js/src/rhodecode/pullrequests.js:506
5096 5100 msgid "member of \"{0}\""
5097 5101 msgstr ""
5098 5102
5099 #: rhodecode/public/js/scripts.js:42937 rhodecode/public/js/scripts.min.js:1
5103 #: rhodecode/public/js/scripts.js:43076 rhodecode/public/js/scripts.min.js:1
5100 5104 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:125
5101 5105 #: rhodecode/public/js/src/rhodecode/pullrequests.js:739
5102 5106 msgid "Updating..."
5103 5107 msgstr ""
5104 5108
5105 #: rhodecode/public/js/scripts.js:42947 rhodecode/public/js/scripts.min.js:1
5109 #: rhodecode/public/js/scripts.js:43086 rhodecode/public/js/scripts.min.js:1
5106 5110 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:40
5107 5111 #: rhodecode/public/js/src/rhodecode/pullrequests.js:749
5108 5112 msgid "Force updating..."
5109 5113 msgstr ""
5110 5114
5111 #: rhodecode/public/js/scripts.js:47812 rhodecode/public/js/scripts.min.js:1
5115 #: rhodecode/public/js/scripts.js:47953 rhodecode/public/js/scripts.min.js:1
5112 5116 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:98
5113 5117 #: rhodecode/public/js/src/rhodecode/users.js:54
5114 5118 msgid "Show this authentication token?"
5115 5119 msgstr ""
5116 5120
5117 #: rhodecode/public/js/scripts.js:47814 rhodecode/public/js/scripts.min.js:1
5121 #: rhodecode/public/js/scripts.js:47955 rhodecode/public/js/scripts.min.js:1
5118 5122 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:90
5119 5123 #: rhodecode/public/js/src/rhodecode/users.js:56
5120 5124 msgid "Show"
5121 5125 msgstr ""
5122 5126
5123 #: rhodecode/public/js/scripts.js:47850 rhodecode/public/js/scripts.min.js:1
5127 #: rhodecode/public/js/scripts.js:47991 rhodecode/public/js/scripts.min.js:1
5124 5128 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:16
5125 5129 #: rhodecode/public/js/src/rhodecode/users.js:92
5126 5130 msgid "Authentication Token"
5127 5131 msgstr ""
5128 5132
5129 #: rhodecode/public/js/scripts.js:48039 rhodecode/public/js/scripts.min.js:1
5133 #: rhodecode/public/js/scripts.js:48180 rhodecode/public/js/scripts.min.js:1
5130 5134 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:139
5131 5135 #: rhodecode/public/js/src/rhodecode.js:144
5132 5136 msgid "file"
5133 5137 msgstr ""
5134 5138
5135 #: rhodecode/public/js/scripts.js:48183 rhodecode/public/js/scripts.min.js:1
5139 #: rhodecode/public/js/scripts.js:48324 rhodecode/public/js/scripts.min.js:1
5136 5140 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:53
5137 5141 #: rhodecode/public/js/src/rhodecode.js:288
5138 5142 msgid "Loading..."
5139 5143 msgstr ""
5140 5144
5141 #: rhodecode/public/js/scripts.js:48565 rhodecode/public/js/scripts.min.js:1
5145 #: rhodecode/public/js/scripts.js:48706 rhodecode/public/js/scripts.min.js:1
5142 5146 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:136
5143 5147 #: rhodecode/public/js/src/rhodecode.js:670
5144 5148 msgid "date not in future"
5145 5149 msgstr ""
5146 5150
5147 #: rhodecode/public/js/scripts.js:48573 rhodecode/public/js/scripts.min.js:1
5151 #: rhodecode/public/js/scripts.js:48714 rhodecode/public/js/scripts.min.js:1
5148 5152 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:100
5149 5153 #: rhodecode/public/js/src/rhodecode.js:678
5150 5154 msgid "Specified expiration date"
5151 5155 msgstr ""
5152 5156
5153 5157 #: rhodecode/public/js/scripts.min.js:1
5154 5158 msgid "action"
5155 5159 msgstr ""
5156 5160
5157 5161 #: rhodecode/public/js/scripts.min.js:1
5158 5162 msgid "target"
5159 5163 msgstr ""
5160 5164
5161 5165 #: rhodecode/public/js/scripts.min.js:1
5162 5166 msgid "text"
5163 5167 msgstr ""
5164 5168
5165 5169 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:2
5166 5170 msgid "(from usergroup {0})"
5167 5171 msgstr ""
5168 5172
5169 5173 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:3
5170 5174 msgid "<strong>, and {0} file</strong> changed."
5171 5175 msgstr ""
5172 5176
5173 5177 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:4
5174 5178 msgid "<strong>, and {0} files</strong> changed."
5175 5179 msgstr ""
5176 5180
5177 5181 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:5
5178 5182 msgid "<strong>{0} file</strong> changed, "
5179 5183 msgstr ""
5180 5184
5181 5185 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:6
5182 5186 msgid "<strong>{0} files</strong> changed, "
5183 5187 msgstr ""
5184 5188
5185 5189 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:7
5186 5190 msgid "Add another comment"
5187 5191 msgstr ""
5188 5192
5189 5193 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:8
5190 5194 msgid "Adding new reviewers is forbidden."
5191 5195 msgstr ""
5192 5196
5193 5197 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:11
5194 5198 msgid "All individual reviewers must vote."
5195 5199 msgstr ""
5196 5200
5197 5201 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:13
5198 5202 msgid "Are you sure to close this pull request without merging?"
5199 5203 msgstr ""
5200 5204
5201 5205 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:14
5202 5206 msgid "At least {0} reviewer must vote."
5203 5207 msgstr ""
5204 5208
5205 5209 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:15
5206 5210 msgid "At least {0} reviewers must vote."
5207 5211 msgstr ""
5208 5212
5209 5213 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:17
5210 5214 msgid "Author is not allowed to be a reviewer."
5211 5215 msgstr ""
5212 5216
5213 5217 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:18
5214 5218 msgid "Changed files"
5215 5219 msgstr ""
5216 5220
5217 5221 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:19
5218 5222 #: rhodecode/public/js/src/i18n_messages.js:5
5219 5223 #: rhodecode/templates/pullrequests/pullrequest_show.mako:610
5220 5224 #: rhodecode/templates/pullrequests/pullrequest_show.mako:613
5221 5225 #: rhodecode/templates/pullrequests/pullrequest_show.mako:676
5222 5226 msgid "Close"
5223 5227 msgstr ""
5224 5228
5225 5229 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:20
5226 5230 #: rhodecode/templates/codeblocks/diffs.mako:119
5227 5231 msgid "Collapse all files"
5228 5232 msgstr ""
5229 5233
5230 5234 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:21
5231 5235 msgid "Collapse {0} commit"
5232 5236 msgstr ""
5233 5237
5234 5238 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:22
5235 5239 msgid "Collapse {0} commits"
5236 5240 msgstr ""
5237 5241
5238 5242 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:25
5239 5243 msgid "Commit Authors are not allowed to be a reviewer."
5240 5244 msgstr ""
5241 5245
5242 5246 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:26
5243 5247 msgid "Compare summary: <strong>{0} commit</strong>"
5244 5248 msgstr ""
5245 5249
5246 5250 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:27
5247 5251 msgid "Compare summary: <strong>{0} commits</strong>"
5248 5252 msgstr ""
5249 5253
5250 5254 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:28
5251 5255 msgid "Context file: "
5252 5256 msgstr ""
5253 5257
5254 5258 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:30
5255 5259 msgid "Delete this comment?"
5256 5260 msgstr ""
5257 5261
5258 5262 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:31
5259 5263 msgid "Diff to Commit "
5260 5264 msgstr ""
5261 5265
5262 5266 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:32
5263 5267 msgid "Error during search operation"
5264 5268 msgstr ""
5265 5269
5266 5270 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:33
5267 5271 #: rhodecode/templates/codeblocks/diffs.mako:117
5268 5272 msgid "Expand all files"
5269 5273 msgstr ""
5270 5274
5271 5275 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:34
5272 5276 msgid "Expand {0} commit"
5273 5277 msgstr ""
5274 5278
5275 5279 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:35
5276 5280 msgid "Expand {0} commits"
5277 5281 msgstr ""
5278 5282
5279 5283 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:36
5280 5284 msgid "Fetching repository state failed. Error code: {0} {1}. Try <a href=\"{2}\">refreshing</a> this page."
5281 5285 msgstr ""
5282 5286
5283 5287 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:37
5284 5288 msgid "Fetching repository state failed. Error code: {0} {1}. Try refreshing this page."
5285 5289 msgstr ""
5286 5290
5287 5291 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:39
5288 5292 msgid "Follow"
5289 5293 msgstr ""
5290 5294
5291 5295 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:41
5292 5296 #: rhodecode/templates/codeblocks/diffs.mako:988
5293 5297 msgid "Hide full context diff"
5294 5298 msgstr ""
5295 5299
5296 5300 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:42
5297 5301 #: rhodecode/templates/codeblocks/diffs.mako:980
5298 5302 msgid "Hide whitespace changes"
5299 5303 msgstr ""
5300 5304
5301 5305 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:43
5302 5306 #: rhodecode/public/js/src/i18n_messages.js:4
5303 5307 msgid "Invite reviewers to this discussion"
5304 5308 msgstr ""
5305 5309
5306 5310 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:45
5307 5311 msgid "Leave a comment on line {0}."
5308 5312 msgstr ""
5309 5313
5310 5314 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:46
5311 5315 msgid "Leave a comment, or click resolve button to resolve TODO comment #{0}"
5312 5316 msgstr ""
5313 5317
5314 5318 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:55
5315 5319 msgid "No bookmarks available yet."
5316 5320 msgstr ""
5317 5321
5318 5322 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:56
5319 5323 msgid "No branches available yet."
5320 5324 msgstr ""
5321 5325
5322 5326 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:57
5323 5327 msgid "No forks available yet."
5324 5328 msgstr ""
5325 5329
5326 5330 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:58
5327 5331 msgid "No gists available yet."
5328 5332 msgstr ""
5329 5333
5330 5334 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:61
5331 5335 msgid "No pull requests available yet."
5332 5336 msgstr ""
5333 5337
5334 5338 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:62
5335 5339 msgid "No repositories available yet."
5336 5340 msgstr ""
5337 5341
5338 5342 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:63
5339 5343 msgid "No repositories present."
5340 5344 msgstr ""
5341 5345
5342 5346 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:64
5343 5347 msgid "No repository groups available yet."
5344 5348 msgstr ""
5345 5349
5346 5350 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:65
5347 5351 msgid "No repository groups present."
5348 5352 msgstr ""
5349 5353
5350 5354 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:67
5351 5355 msgid "No review rules set."
5352 5356 msgstr ""
5353 5357
5354 5358 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:68
5355 5359 msgid "No ssh keys available yet."
5356 5360 msgstr ""
5357 5361
5358 5362 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:69
5359 5363 msgid "No tags available yet."
5360 5364 msgstr ""
5361 5365
5362 5366 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:70
5363 5367 msgid "No user groups available yet."
5364 5368 msgstr ""
5365 5369
5366 5370 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:71
5367 5371 msgid "No users available yet."
5368 5372 msgstr ""
5369 5373
5370 5374 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:75
5371 5375 #: rhodecode/templates/commits/changelog.mako:78
5372 5376 msgid "Open new pull request"
5373 5377 msgstr ""
5374 5378
5375 5379 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:76
5376 5380 msgid "Open new pull request for selected commit"
5377 5381 msgstr ""
5378 5382
5379 5383 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:81
5380 5384 msgid "Please wait creating pull request..."
5381 5385 msgstr ""
5382 5386
5383 5387 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:82
5384 5388 msgid "Reviewers picked from source code changes."
5385 5389 msgstr ""
5386 5390
5387 5391 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:84
5388 5392 msgid "Saving..."
5389 5393 msgstr ""
5390 5394
5391 5395 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:87
5392 5396 #: rhodecode/public/js/src/i18n_messages.js:6
5393 5397 #: rhodecode/templates/admin/settings/settings_email.mako:50
5394 5398 msgid "Send"
5395 5399 msgstr ""
5396 5400
5397 5401 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:91
5398 5402 msgid "Show at Commit "
5399 5403 msgstr ""
5400 5404
5401 5405 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:92
5402 5406 msgid "Show commit range {0} ... {1}"
5403 5407 msgstr ""
5404 5408
5405 5409 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:93
5406 5410 msgid "Show commit range {0}<i class=\"icon-angle-right\"></i>{1}"
5407 5411 msgstr ""
5408 5412
5409 5413 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:94
5410 5414 #: rhodecode/templates/codeblocks/diffs.mako:990
5411 5415 msgid "Show full context diff"
5412 5416 msgstr ""
5413 5417
5414 5418 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:95
5415 5419 #: rhodecode/templates/admin/settings/settings_exceptions_browse.mako:40
5416 5420 msgid "Show more"
5417 5421 msgstr ""
5418 5422
5419 5423 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:96
5420 5424 msgid "Show selected commit __S"
5421 5425 msgstr ""
5422 5426
5423 5427 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:97
5424 5428 msgid "Show selected commits __S ... __E"
5425 5429 msgstr ""
5426 5430
5427 5431 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:99
5428 5432 #: rhodecode/templates/codeblocks/diffs.mako:978
5429 5433 msgid "Show whitespace changes"
5430 5434 msgstr ""
5431 5435
5432 5436 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:101
5433 5437 msgid "Start following this repository"
5434 5438 msgstr ""
5435 5439
5436 5440 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:104
5437 5441 msgid "Stop following this repository"
5438 5442 msgstr ""
5439 5443
5440 5444 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:107
5441 5445 msgid "Submit {0} draft comments."
5442 5446 msgstr ""
5443 5447
5444 5448 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:109
5445 5449 msgid "Switch target repository with the source."
5446 5450 msgstr ""
5447 5451
5448 5452 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:110
5449 5453 #: rhodecode/public/js/src/i18n_messages.js:7
5450 5454 msgid "Switch to chat"
5451 5455 msgstr ""
5452 5456
5453 5457 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:111
5454 5458 #: rhodecode/public/js/src/i18n_messages.js:8
5455 5459 msgid "Switch to comment"
5456 5460 msgstr ""
5457 5461
5458 5462 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:114
5459 5463 msgid "There are currently no open pull requests requiring your participation."
5460 5464 msgstr ""
5461 5465
5462 5466 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:118
5463 5467 msgid "This pull requests will consist of <strong>{0} commit</strong>."
5464 5468 msgstr ""
5465 5469
5466 5470 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:119
5467 5471 msgid "This pull requests will consist of <strong>{0} commits</strong>."
5468 5472 msgstr ""
5469 5473
5470 5474 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:120
5471 5475 msgid "Toggle Wide Mode diff"
5472 5476 msgstr ""
5473 5477
5474 5478 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:121
5475 5479 msgid "Unfollow"
5476 5480 msgstr ""
5477 5481
5478 5482 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:124
5479 5483 msgid "Updated Comment"
5480 5484 msgstr ""
5481 5485
5482 5486 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:126
5483 5487 msgid "User `{0}` already in reviewers"
5484 5488 msgstr ""
5485 5489
5486 5490 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:134
5487 5491 #: rhodecode/templates/admin/auth/auth_settings.mako:69
5488 5492 msgid "activated"
5489 5493 msgstr ""
5490 5494
5491 5495 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:137
5492 5496 msgid "disabled"
5493 5497 msgstr ""
5494 5498
5495 5499 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:138
5496 5500 msgid "enabled"
5497 5501 msgstr ""
5498 5502
5499 5503 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:140
5500 5504 msgid "files"
5501 5505 msgstr ""
5502 5506
5503 5507 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:141
5504 5508 msgid "go to numeric commit"
5505 5509 msgstr ""
5506 5510
5507 5511 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:146
5508 5512 #: rhodecode/templates/index_base.mako:27
5509 5513 #: rhodecode/templates/pullrequests/pullrequest.mako:154
5510 5514 #: rhodecode/templates/pullrequests/pullrequest.mako:178
5511 5515 msgid "loading..."
5512 5516 msgstr ""
5513 5517
5514 5518 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:148
5515 5519 msgid "no commits"
5516 5520 msgstr ""
5517 5521
5518 5522 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:149
5519 5523 #: rhodecode/templates/admin/auth/auth_settings.mako:69
5520 5524 msgid "not active"
5521 5525 msgstr ""
5522 5526
5523 5527 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:153
5524 5528 msgid "specify commit"
5525 5529 msgstr ""
5526 5530
5527 5531 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:156
5528 5532 msgid "{0} ({1} inactive) of {2} user groups ({3} inactive)"
5529 5533 msgstr ""
5530 5534
5531 5535 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:157
5532 5536 msgid "{0} ({1} inactive) of {2} users ({3} inactive)"
5533 5537 msgstr ""
5534 5538
5535 5539 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:158
5536 5540 msgid "{0} active out of {1} users"
5537 5541 msgstr ""
5538 5542
5539 5543 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:162
5540 5544 msgid "{0} bookmark"
5541 5545 msgstr ""
5542 5546
5543 5547 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:163
5544 5548 msgid "{0} bookmarks"
5545 5549 msgstr ""
5546 5550
5547 5551 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:164
5548 5552 msgid "{0} branch"
5549 5553 msgstr ""
5550 5554
5551 5555 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:165
5552 5556 msgid "{0} branches"
5553 5557 msgstr ""
5554 5558
5555 5559 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:167
5556 5560 msgid "{0} days"
5557 5561 msgstr ""
5558 5562
5559 5563 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:169
5560 5564 msgid "{0} hours"
5561 5565 msgstr ""
5562 5566
5563 5567 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:172
5564 5568 msgid "{0} months"
5565 5569 msgstr ""
5566 5570
5567 5571 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:173
5568 5572 msgid "{0} of {1} repositories"
5569 5573 msgstr ""
5570 5574
5571 5575 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:174
5572 5576 msgid "{0} of {1} repository groups"
5573 5577 msgstr ""
5574 5578
5575 5579 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:175
5576 5580 msgid "{0} out of {1} ssh keys"
5577 5581 msgstr ""
5578 5582
5579 5583 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:176
5580 5584 msgid "{0} out of {1} users"
5581 5585 msgstr ""
5582 5586
5583 5587 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:177
5584 5588 msgid "{0} repositories"
5585 5589 msgstr ""
5586 5590
5587 5591 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:178
5588 5592 msgid "{0} repository groups"
5589 5593 msgstr ""
5590 5594
5591 5595 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:181
5592 5596 msgid "{0} tag"
5593 5597 msgstr ""
5594 5598
5595 5599 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:182
5596 5600 msgid "{0} tags"
5597 5601 msgstr ""
5598 5602
5599 5603 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:183
5600 5604 msgid "{0} user groups ({1} inactive)"
5601 5605 msgstr ""
5602 5606
5603 5607 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:184
5604 5608 msgid "{0} users ({1} inactive)"
5605 5609 msgstr ""
5606 5610
5607 5611 #: rhodecode/public/js/rhodecode/i18n/js_translations.js:186
5608 5612 msgid "{0} years"
5609 5613 msgstr ""
5610 5614
5611 5615 #: rhodecode/public/js/src/math_jax/extensions/HelpDialog.js:19
5612 5616 msgid "HelpDialog"
5613 5617 msgstr ""
5614 5618
5615 5619 #: rhodecode/public/js/src/math_jax/jax/output/HTML-CSS/autoload/mglyph.js:19
5616 5620 msgid "MathML"
5617 5621 msgstr ""
5618 5622
5619 5623 #: rhodecode/templates/index.mako:5
5620 5624 msgid "Dashboard"
5621 5625 msgstr ""
5622 5626
5623 5627 #: rhodecode/templates/index_base.mako:21
5624 5628 msgid "No repositories or repositories groups exists here."
5625 5629 msgstr ""
5626 5630
5627 5631 #: rhodecode/templates/index_base.mako:80
5628 5632 #: rhodecode/templates/index_base.mako:167
5629 5633 #: rhodecode/templates/admin/gists/gist_index.mako:107
5630 5634 #: rhodecode/templates/admin/integrations/list.mako:72
5631 5635 #: rhodecode/templates/admin/my_account/my_account_repos.mako:28
5632 5636 #: rhodecode/templates/admin/my_account/my_account_user_group_membership.mako:39
5633 5637 #: rhodecode/templates/admin/my_account/my_account_watched.mako:28
5634 5638 #: rhodecode/templates/admin/repo_groups/repo_groups.mako:74
5635 5639 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:16
5636 5640 #: rhodecode/templates/admin/repos/repos.mako:76
5637 5641 #: rhodecode/templates/admin/user_groups/user_groups.mako:76
5638 5642 #: rhodecode/templates/admin/users/user_edit_groups.mako:57
5639 5643 #: rhodecode/templates/base/perms_summary.mako:173
5640 5644 #: rhodecode/templates/base/perms_summary.mako:247
5641 5645 #: rhodecode/templates/bookmarks/bookmarks.mako:69
5642 5646 #: rhodecode/templates/branches/branches.mako:68
5643 5647 #: rhodecode/templates/files/files_browser_tree.mako:16
5644 5648 #: rhodecode/templates/tags/tags.mako:69
5645 5649 msgid "Name"
5646 5650 msgstr ""
5647 5651
5648 5652 #: rhodecode/templates/index_base.mako:89
5649 5653 #: rhodecode/templates/index_base.mako:176
5650 5654 #: rhodecode/templates/admin/gists/gist_index.mako:109
5651 5655 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:27
5652 5656 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:87
5653 5657 #: rhodecode/templates/admin/my_account/my_account_profile.mako:68
5654 5658 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:10
5655 5659 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:58
5656 5660 #: rhodecode/templates/admin/my_account/my_account_user_group_membership.mako:44
5657 5661 #: rhodecode/templates/admin/permissions/permissions_ssh_keys.mako:49
5658 5662 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:58
5659 5663 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:55
5660 5664 #: rhodecode/templates/admin/repo_groups/repo_groups.mako:77
5661 5665 #: rhodecode/templates/admin/repos/repo_add_base.mako:83
5662 5666 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:30
5663 5667 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:170
5664 5668 #: rhodecode/templates/admin/repos/repos.mako:84
5665 5669 #: rhodecode/templates/admin/user_groups/user_group_add.mako:42
5666 5670 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:46
5667 5671 #: rhodecode/templates/admin/user_groups/user_groups.mako:78
5668 5672 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:32
5669 5673 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:91
5670 5674 #: rhodecode/templates/admin/users/user_edit_groups.mako:62
5671 5675 #: rhodecode/templates/admin/users/user_edit_ips.mako:17
5672 5676 #: rhodecode/templates/admin/users/user_edit_profile.mako:74
5673 5677 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:15
5674 5678 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:57
5675 5679 #: rhodecode/templates/base/issue_tracker_settings.mako:79
5676 5680 #: rhodecode/templates/compare/compare_commits.mako:22
5677 5681 #: rhodecode/templates/email_templates/pull_request_review.mako:49
5678 5682 #: rhodecode/templates/email_templates/pull_request_review.mako:134
5679 5683 #: rhodecode/templates/email_templates/pull_request_update.mako:45
5680 5684 #: rhodecode/templates/email_templates/pull_request_update.mako:139
5681 5685 #: rhodecode/templates/forks/fork.mako:56
5682 5686 #: rhodecode/templates/forks/forks.mako:62
5683 5687 #: rhodecode/templates/pullrequests/pullrequest.mako:104
5684 5688 #: rhodecode/templates/pullrequests/pullrequest_show.mako:398
5685 5689 #: rhodecode/templates/summary/components.mako:159
5686 5690 #: rhodecode/templates/user_group/profile.mako:25
5687 5691 #: rhodecode/templates/users/user_profile.mako:59
5688 5692 msgid "Description"
5689 5693 msgstr ""
5690 5694
5691 5695 #: rhodecode/templates/index_base.mako:96
5692 5696 #: rhodecode/templates/index_base.mako:183
5693 5697 #: rhodecode/templates/admin/repo_groups/repo_groups.mako:80
5694 5698 #: rhodecode/templates/admin/repos/repos.mako:91
5695 5699 msgid "Last Change"
5696 5700 msgstr ""
5697 5701
5698 5702 #: rhodecode/templates/index_base.mako:109
5699 5703 #: rhodecode/templates/index_base.mako:196
5700 5704 #: rhodecode/templates/admin/my_account/my_account_user_group_membership.mako:50
5701 5705 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:8
5702 5706 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:37
5703 5707 #: rhodecode/templates/admin/repo_groups/repo_groups.mako:84
5704 5708 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:6
5705 5709 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:152
5706 5710 #: rhodecode/templates/admin/repos/repos.mako:104
5707 5711 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:6
5708 5712 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:28
5709 5713 #: rhodecode/templates/admin/user_groups/user_groups.mako:86
5710 5714 #: rhodecode/templates/admin/users/user_edit_groups.mako:68
5711 5715 #: rhodecode/templates/forks/forks.mako:58
5712 5716 #: rhodecode/templates/summary/components.mako:148
5713 5717 #: rhodecode/templates/user_group/profile.mako:35
5714 5718 msgid "Owner"
5715 5719 msgstr ""
5716 5720
5717 5721 #: rhodecode/templates/index_base.mako:190
5718 5722 #: rhodecode/templates/admin/repos/repos.mako:98
5719 5723 #: rhodecode/templates/bookmarks/bookmarks.mako:76
5720 5724 #: rhodecode/templates/branches/branches.mako:75
5721 5725 #: rhodecode/templates/compare/compare_commits.mako:20
5722 5726 #: rhodecode/templates/email_templates/commit_comment.mako:60
5723 5727 #: rhodecode/templates/email_templates/commit_comment.mako:114
5724 5728 #: rhodecode/templates/email_templates/commit_comment.mako:141
5725 5729 #: rhodecode/templates/files/file_authors_box.mako:28
5726 5730 #: rhodecode/templates/pullrequests/pullrequest_show.mako:396
5727 5731 #: rhodecode/templates/search/search_commit.mako:9
5728 5732 #: rhodecode/templates/summary/components.mako:117
5729 5733 #: rhodecode/templates/summary/components.mako:125
5730 5734 #: rhodecode/templates/tags/tags.mako:76
5731 5735 msgid "Commit"
5732 5736 msgstr ""
5733 5737
5734 5738 #: rhodecode/templates/index_repo_group.mako:5
5735 5739 #, python-format
5736 5740 msgid "%s Repository group dashboard"
5737 5741 msgstr ""
5738 5742
5739 5743 #: rhodecode/templates/index_repo_group.mako:13
5740 #: rhodecode/templates/base/base.mako:810
5741 5744 #: rhodecode/templates/base/base.mako:811
5745 #: rhodecode/templates/base/base.mako:812
5742 5746 msgid "Home"
5743 5747 msgstr ""
5744 5748
5745 5749 #: rhodecode/templates/login.mako:5 rhodecode/templates/login.mako:91
5746 5750 #: rhodecode/templates/debug_style/login.html:60
5747 5751 msgid "Sign In"
5748 5752 msgstr ""
5749 5753
5750 5754 #: rhodecode/templates/login.mako:39
5751 5755 msgid "Sign In using username/password"
5752 5756 msgstr ""
5753 5757
5754 5758 #: rhodecode/templates/login.mako:53
5755 5759 msgid "Forgot your password?"
5756 5760 msgstr ""
5757 5761
5758 5762 #: rhodecode/templates/login.mako:66
5759 5763 msgid "Remember my indefinitely"
5760 5764 msgstr ""
5761 5765
5762 5766 #: rhodecode/templates/login.mako:68
5763 5767 msgid "Remember me for {}"
5764 5768 msgstr ""
5765 5769
5766 5770 #: rhodecode/templates/login.mako:74
5767 5771 msgid "Create a new account."
5768 5772 msgstr ""
5769 5773
5770 5774 #: rhodecode/templates/login.mako:81
5771 5775 msgid "Password reset is disabled."
5772 5776 msgstr ""
5773 5777
5774 5778 #: rhodecode/templates/login.mako:82
5775 5779 msgid "Please contact "
5776 5780 msgstr ""
5777 5781
5778 5782 #: rhodecode/templates/login.mako:84 rhodecode/templates/password_reset.mako:40
5779 5783 #: rhodecode/templates/base/base.mako:63
5780 5784 msgid "Support"
5781 5785 msgstr ""
5782 5786
5783 5787 #: rhodecode/templates/login.mako:85 rhodecode/templates/password_reset.mako:41
5784 5788 msgid "or"
5785 5789 msgstr ""
5786 5790
5787 5791 #: rhodecode/templates/login.mako:87 rhodecode/templates/password_reset.mako:43
5788 5792 msgid "an administrator if you need help."
5789 5793 msgstr ""
5790 5794
5791 5795 #: rhodecode/templates/login.mako:91
5792 5796 msgid "Sign in to {}"
5793 5797 msgstr ""
5794 5798
5795 5799 #: rhodecode/templates/password_reset.mako:5
5796 5800 msgid "Reset Password"
5797 5801 msgstr ""
5798 5802
5799 5803 #: rhodecode/templates/password_reset.mako:38
5800 5804 msgid "Password reset is disabled. Please contact "
5801 5805 msgstr ""
5802 5806
5803 5807 #: rhodecode/templates/password_reset.mako:50
5804 5808 msgid "Reset your Password"
5805 5809 msgstr ""
5806 5810
5807 5811 #: rhodecode/templates/password_reset.mako:51
5808 5812 msgid "Go to the login page to sign in."
5809 5813 msgstr ""
5810 5814
5811 5815 #: rhodecode/templates/password_reset.mako:55
5812 5816 msgid "Email Address"
5813 5817 msgstr ""
5814 5818
5815 5819 #: rhodecode/templates/password_reset.mako:61
5816 5820 msgid "Password reset link will be sent to matching email address"
5817 5821 msgstr ""
5818 5822
5819 5823 #: rhodecode/templates/password_reset.mako:65
5820 5824 #: rhodecode/templates/register.mako:106
5821 5825 msgid "Captcha"
5822 5826 msgstr ""
5823 5827
5824 5828 #: rhodecode/templates/password_reset.mako:76
5825 5829 msgid "Send password reset email"
5826 5830 msgstr ""
5827 5831
5828 5832 #: rhodecode/templates/register.mako:5
5829 5833 msgid "Create an Account"
5830 5834 msgstr ""
5831 5835
5832 5836 #: rhodecode/templates/register.mako:40
5833 5837 msgid "Create an account linked with {}"
5834 5838 msgstr ""
5835 5839
5836 5840 #: rhodecode/templates/register.mako:42
5837 5841 msgid "Create an account"
5838 5842 msgstr ""
5839 5843
5840 5844 #: rhodecode/templates/register.mako:45
5841 5845 msgid "Go to the login page to sign in with an existing account."
5842 5846 msgstr ""
5843 5847
5844 5848 #: rhodecode/templates/register.mako:71
5845 5849 msgid "Re-enter password"
5846 5850 msgstr ""
5847 5851
5848 5852 #: rhodecode/templates/register.mako:83
5849 5853 #: rhodecode/templates/admin/my_account/my_account_profile.mako:48
5850 5854 #: rhodecode/templates/admin/my_account/my_account_profile_edit.mako:38
5851 5855 #: rhodecode/templates/admin/users/user_add.mako:68
5852 5856 #: rhodecode/templates/admin/users/user_edit_profile.mako:47
5853 5857 #: rhodecode/templates/admin/users/users.mako:78
5854 5858 msgid "First Name"
5855 5859 msgstr ""
5856 5860
5857 5861 #: rhodecode/templates/register.mako:90
5858 5862 #: rhodecode/templates/admin/my_account/my_account_profile.mako:58
5859 5863 #: rhodecode/templates/admin/my_account/my_account_profile_edit.mako:47
5860 5864 #: rhodecode/templates/admin/users/user_add.mako:77
5861 5865 #: rhodecode/templates/admin/users/user_edit_profile.mako:56
5862 5866 #: rhodecode/templates/admin/users/users.mako:80
5863 5867 msgid "Last Name"
5864 5868 msgstr ""
5865 5869
5866 5870 #: rhodecode/templates/register.mako:118
5867 5871 msgid "Account activation requires admin approval."
5868 5872 msgstr ""
5869 5873
5870 5874 #: rhodecode/templates/register.mako:125
5871 5875 msgid "Create Account"
5872 5876 msgstr ""
5873 5877
5874 5878 #: rhodecode/templates/register.mako:125
5875 5879 msgid "Create Account in {}"
5876 5880 msgstr ""
5877 5881
5878 5882 #: rhodecode/templates/admin/admin_audit_log_entry.mako:6
5879 5883 msgid "Admin audit log entry"
5880 5884 msgstr ""
5881 5885
5882 5886 #: rhodecode/templates/admin/admin_audit_log_entry.mako:26
5883 5887 msgid "Audit long entry"
5884 5888 msgstr ""
5885 5889
5886 5890 #: rhodecode/templates/admin/admin_audit_log_entry.mako:34
5887 5891 #: rhodecode/templates/users/user.mako:4
5888 5892 msgid "User"
5889 5893 msgstr ""
5890 5894
5891 5895 #: rhodecode/templates/admin/admin_audit_log_entry.mako:46
5892 5896 #: rhodecode/templates/admin/admin_log_base.mako:11
5893 5897 #: rhodecode/templates/bookmarks/bookmarks.mako:71
5894 5898 #: rhodecode/templates/branches/branches.mako:70
5895 5899 #: rhodecode/templates/tags/tags.mako:71
5896 5900 msgid "Date"
5897 5901 msgstr ""
5898 5902
5899 5903 #: rhodecode/templates/admin/admin_audit_log_entry.mako:54
5900 5904 #: rhodecode/templates/admin/admin_log_base.mako:12
5901 5905 msgid "IP"
5902 5906 msgstr ""
5903 5907
5904 5908 #: rhodecode/templates/admin/admin_audit_log_entry.mako:63
5905 5909 #: rhodecode/templates/admin/admin_log_base.mako:8
5906 5910 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:31
5907 5911 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:13
5908 5912 #: rhodecode/templates/admin/permissions/permissions_ssh_keys.mako:55
5909 5913 #: rhodecode/templates/admin/repo_groups/repo_groups.mako:86
5910 5914 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:13
5911 5915 #: rhodecode/templates/admin/repos/repos.mako:116
5912 5916 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:75
5913 5917 #: rhodecode/templates/admin/user_groups/user_groups.mako:88
5914 5918 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:36
5915 5919 #: rhodecode/templates/admin/users/user_edit_groups.mako:76
5916 5920 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:18
5917 5921 #: rhodecode/templates/admin/users/users.mako:91
5918 5922 #: rhodecode/templates/forks/forks.mako:69
5919 5923 msgid "Action"
5920 5924 msgstr ""
5921 5925
5922 5926 #: rhodecode/templates/admin/admin_audit_log_entry.mako:81
5923 5927 #: rhodecode/templates/admin/admin_log_base.mako:9
5924 5928 msgid "Action Data"
5925 5929 msgstr ""
5926 5930
5927 5931 #: rhodecode/templates/admin/admin_audit_log_entry.mako:89
5928 5932 #: rhodecode/templates/admin/admin_log_base.mako:47
5929 5933 msgid "data not available for v1 entries type"
5930 5934 msgstr ""
5931 5935
5932 5936 #: rhodecode/templates/admin/admin_audit_log_entry.mako:95
5933 5937 #: rhodecode/templates/admin/admin_log_base.mako:10
5934 5938 #: rhodecode/templates/admin/defaults/defaults.mako:32
5935 5939 #: rhodecode/templates/admin/permissions/permissions_objects.mako:16
5936 #: rhodecode/templates/base/base.mako:658
5937 #: rhodecode/templates/base/base.mako:660
5938 #: rhodecode/templates/base/base.mako:662
5940 #: rhodecode/templates/base/base.mako:659
5941 #: rhodecode/templates/base/base.mako:661
5942 #: rhodecode/templates/base/base.mako:663
5939 5943 #: rhodecode/templates/search/search_commit.mako:8
5940 5944 #: rhodecode/templates/search/search_path.mako:7
5941 5945 msgid "Repository"
5942 5946 msgstr ""
5943 5947
5944 5948 #: rhodecode/templates/admin/admin_audit_logs.mako:5
5945 5949 #: rhodecode/templates/base/base.mako:113
5946 5950 msgid "Admin audit logs"
5947 5951 msgstr ""
5948 5952
5949 5953 #: rhodecode/templates/admin/admin_audit_logs.mako:25
5950 5954 msgid "filter..."
5951 5955 msgstr ""
5952 5956
5953 5957 #: rhodecode/templates/admin/admin_audit_logs.mako:26
5954 5958 #: rhodecode/templates/admin/repos/repo_edit_audit.mako:15
5955 5959 #: rhodecode/templates/admin/users/user_edit_audit.mako:18
5956 5960 #: rhodecode/templates/journal/journal.mako:13
5957 5961 msgid "filter"
5958 5962 msgstr ""
5959 5963
5960 5964 #: rhodecode/templates/admin/admin_audit_logs.mako:27
5961 5965 #: rhodecode/templates/admin/repos/repo_edit.mako:91
5962 5966 #: rhodecode/templates/admin/users/user_edit.mako:48
5963 5967 msgid "Audit logs"
5964 5968 msgstr ""
5965 5969
5966 5970 #: rhodecode/templates/admin/admin_audit_logs.mako:29
5967 5971 #: rhodecode/templates/admin/repos/repo_edit_audit.mako:18
5968 5972 #: rhodecode/templates/admin/users/user_edit_audit.mako:21
5969 5973 #: rhodecode/templates/journal/journal.mako:16
5970 5974 msgid "Example Queries"
5971 5975 msgstr ""
5972 5976
5973 5977 #: rhodecode/templates/admin/admin_log_base.mako:6
5974 5978 msgid "Uid"
5975 5979 msgstr ""
5976 5980
5977 5981 #: rhodecode/templates/admin/admin_log_base.mako:42
5978 5982 msgid "toggle"
5979 5983 msgstr ""
5980 5984
5981 5985 #: rhodecode/templates/admin/admin_log_base.mako:68
5982 5986 msgid "No actions yet"
5983 5987 msgstr ""
5984 5988
5985 5989 #: rhodecode/templates/admin/main.mako:5
5986 5990 #: rhodecode/templates/admin/settings/settings.mako:5
5987 5991 msgid "Settings administration"
5988 5992 msgstr ""
5989 5993
5990 5994 #: rhodecode/templates/admin/main.mako:26
5991 5995 msgid "Administration area"
5992 5996 msgstr ""
5993 5997
5994 5998 #: rhodecode/templates/admin/main.mako:30
5995 5999 msgid "Repositories under administration"
5996 6000 msgstr ""
5997 6001
5998 6002 #: rhodecode/templates/admin/main.mako:34
5999 6003 #: rhodecode/templates/admin/repos/repo_add.mako:22
6000 6004 #: rhodecode/templates/admin/repos/repos.mako:31
6001 6005 msgid "Add Repository"
6002 6006 msgstr ""
6003 6007
6004 6008 #: rhodecode/templates/admin/main.mako:39
6005 6009 msgid "Repository groups under administration"
6006 6010 msgstr ""
6007 6011
6008 6012 #: rhodecode/templates/admin/main.mako:43
6009 6013 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:16
6010 6014 #: rhodecode/templates/admin/repo_groups/repo_groups.mako:31
6011 6015 msgid "Add Repository Group"
6012 6016 msgstr ""
6013 6017
6014 6018 #: rhodecode/templates/admin/main.mako:48
6015 6019 msgid "User groups under administration"
6016 6020 msgstr ""
6017 6021
6018 6022 #: rhodecode/templates/admin/main.mako:52
6019 6023 #: rhodecode/templates/admin/user_groups/user_group_add.mako:15
6020 6024 #: rhodecode/templates/admin/user_groups/user_groups.mako:31
6021 6025 msgid "Add User Group"
6022 6026 msgstr ""
6023 6027
6024 6028 #: rhodecode/templates/admin/auth/auth_settings.mako:5
6025 6029 #: rhodecode/templates/admin/auth/plugin_settings.mako:5
6026 6030 msgid "Authentication Settings"
6027 6031 msgstr ""
6028 6032
6029 6033 #: rhodecode/templates/admin/auth/auth_settings.mako:42
6030 6034 msgid "Enabled and Available Plugins"
6031 6035 msgstr ""
6032 6036
6033 6037 #: rhodecode/templates/admin/auth/auth_settings.mako:48
6034 6038 msgid "Ordered Activated Plugins"
6035 6039 msgstr ""
6036 6040
6037 6041 #: rhodecode/templates/admin/auth/auth_settings.mako:53
6038 6042 msgid ""
6039 6043 "List of plugins, separated by commas.\n"
6040 6044 "The order of the plugins is also the order in which RhodeCode Enterprise will try to authenticate a user."
6041 6045 msgstr ""
6042 6046
6043 6047 #: rhodecode/templates/admin/auth/auth_settings.mako:60
6044 6048 msgid "Activate"
6045 6049 msgstr ""
6046 6050
6047 6051 #: rhodecode/templates/admin/auth/auth_settings.mako:61
6048 6052 msgid "Plugin Name"
6049 6053 msgstr ""
6050 6054
6051 6055 #: rhodecode/templates/admin/auth/auth_settings.mako:62
6052 6056 #: rhodecode/templates/base/base.mako:64
6053 6057 msgid "Documentation"
6054 6058 msgstr ""
6055 6059
6056 6060 #: rhodecode/templates/admin/auth/auth_settings.mako:63
6057 6061 msgid "Plugin ID"
6058 6062 msgstr ""
6059 6063
6060 6064 #: rhodecode/templates/admin/auth/auth_settings.mako:85
6061 6065 #: rhodecode/templates/admin/auth/plugin_settings.mako:96
6062 6066 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:63
6063 6067 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:109
6064 6068 #: rhodecode/templates/admin/permissions/permissions_application.mako:59
6065 6069 #: rhodecode/templates/admin/permissions/permissions_objects.mako:59
6066 6070 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:226
6067 6071 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:77
6068 6072 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:66
6069 6073 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:84
6070 6074 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:206
6071 6075 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:251
6072 6076 #: rhodecode/templates/admin/settings/settings_hooks.mako:63
6073 6077 #: rhodecode/templates/admin/settings/settings_issuetracker.mako:15
6074 6078 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:216
6075 6079 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:106
6076 6080 #: rhodecode/templates/admin/users/user_edit_groups.mako:30
6077 6081 #: rhodecode/templates/admin/users/user_edit_profile.mako:154
6078 6082 #: rhodecode/templates/base/default_perms_box.mako:101
6079 6083 msgid "Save"
6080 6084 msgstr ""
6081 6085
6082 6086 #: rhodecode/templates/admin/auth/plugin_settings.mako:46
6083 6087 msgid "Plugin"
6084 6088 msgstr ""
6085 6089
6086 6090 #: rhodecode/templates/admin/defaults/defaults.mako:5
6087 6091 #: rhodecode/templates/admin/defaults/defaults.mako:14
6088 6092 msgid "Repositories defaults"
6089 6093 msgstr ""
6090 6094
6091 6095 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:3
6092 6096 msgid "Default Settings For New Repositories"
6093 6097 msgstr ""
6094 6098
6095 6099 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:14
6096 6100 #: rhodecode/templates/admin/gists/gist_index.mako:105
6097 6101 #: rhodecode/templates/admin/integrations/list.mako:73
6098 6102 #: rhodecode/templates/admin/repos/repo_add_base.mako:58
6099 6103 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:12
6100 6104 msgid "Type"
6101 6105 msgstr ""
6102 6106
6103 6107 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:23
6104 6108 #: rhodecode/templates/admin/repos/repo_add_base.mako:113
6105 6109 msgid "Private Repository"
6106 6110 msgstr ""
6107 6111
6108 6112 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:27
6109 6113 #: rhodecode/templates/admin/repos/repo_add_base.mako:117
6110 6114 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:199
6111 6115 #: rhodecode/templates/forks/fork.mako:92
6112 6116 msgid "Private repositories are only visible to people explicitly added as collaborators."
6113 6117 msgstr ""
6114 6118
6115 6119 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:34
6116 6120 msgid "Enable Statistics"
6117 6121 msgstr ""
6118 6122
6119 6123 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:38
6120 6124 msgid "Enable a statistics window on the repository summary page."
6121 6125 msgstr ""
6122 6126
6123 6127 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:44
6124 6128 msgid "Enable Downloads"
6125 6129 msgstr ""
6126 6130
6127 6131 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:48
6128 6132 msgid "Enable the download option on the repository summary page."
6129 6133 msgstr ""
6130 6134
6131 6135 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:54
6132 6136 msgid "Enable Locking"
6133 6137 msgstr ""
6134 6138
6135 6139 #: rhodecode/templates/admin/defaults/defaults_repositories.mako:58
6136 6140 msgid "Enable automatic repository locking. Pulling from a repository will lock it, and it is unlocked by pushing back by the same user."
6137 6141 msgstr ""
6138 6142
6139 6143 #: rhodecode/templates/admin/gists/gist_edit.mako:5
6140 6144 msgid "Edit Gist"
6141 6145 msgstr ""
6142 6146
6143 6147 #: rhodecode/templates/admin/gists/gist_edit.mako:33
6144 6148 #: rhodecode/templates/admin/gists/gist_new.mako:34
6145 6149 msgid "Gist lifetime"
6146 6150 msgstr ""
6147 6151
6148 6152 #: rhodecode/templates/admin/gists/gist_edit.mako:36
6149 6153 msgid "Gist access level"
6150 6154 msgstr ""
6151 6155
6152 6156 #: rhodecode/templates/admin/gists/gist_edit.mako:40
6153 6157 #: rhodecode/templates/admin/gists/gist_new.mako:40
6154 6158 msgid "Gist description ..."
6155 6159 msgstr ""
6156 6160
6157 6161 #: rhodecode/templates/admin/gists/gist_edit.mako:54
6158 6162 #: rhodecode/templates/admin/gists/gist_new.mako:48
6159 6163 #: rhodecode/templates/files/files_add.mako:66
6160 6164 #: rhodecode/templates/files/files_edit.mako:68
6161 6165 msgid "plain"
6162 6166 msgstr ""
6163 6167
6164 6168 #: rhodecode/templates/admin/gists/gist_edit.mako:99
6165 6169 msgid "Update Gist"
6166 6170 msgstr ""
6167 6171
6168 6172 #: rhodecode/templates/admin/gists/gist_edit.mako:100
6169 6173 #: rhodecode/templates/base/issue_tracker_settings.mako:151
6170 6174 #: rhodecode/templates/pullrequests/pullrequest_show.mako:82
6171 6175 msgid "Cancel"
6172 6176 msgstr ""
6173 6177
6174 6178 #: rhodecode/templates/admin/gists/gist_edit.mako:123
6175 6179 #, python-format
6176 6180 msgid "Gist was updated since you started editing. Copy your changes and click %(here)s to reload the new version."
6177 6181 msgstr ""
6178 6182
6179 6183 #: rhodecode/templates/admin/gists/gist_index.mako:6
6180 6184 msgid "Private Gists for user {}"
6181 6185 msgstr ""
6182 6186
6183 6187 #: rhodecode/templates/admin/gists/gist_index.mako:8
6184 6188 msgid "Public Gists for user {}"
6185 6189 msgstr ""
6186 6190
6187 6191 #: rhodecode/templates/admin/gists/gist_index.mako:10
6188 6192 msgid "Public Gists"
6189 6193 msgstr ""
6190 6194
6191 6195 #: rhodecode/templates/admin/gists/gist_index.mako:30
6192 6196 msgid "All gists"
6193 6197 msgstr ""
6194 6198
6195 6199 #: rhodecode/templates/admin/gists/gist_index.mako:32
6196 6200 msgid "All public"
6197 6201 msgstr ""
6198 6202
6199 6203 #: rhodecode/templates/admin/gists/gist_index.mako:34
6200 6204 msgid "My gists"
6201 6205 msgstr ""
6202 6206
6203 6207 #: rhodecode/templates/admin/gists/gist_index.mako:35
6204 6208 msgid "My private"
6205 6209 msgstr ""
6206 6210
6207 6211 #: rhodecode/templates/admin/gists/gist_index.mako:36
6208 6212 msgid "My public"
6209 6213 msgstr ""
6210 6214
6211 6215 #: rhodecode/templates/admin/gists/gist_index.mako:43
6212 6216 msgid "Create New Gist"
6213 6217 msgstr ""
6214 6218
6215 6219 #: rhodecode/templates/admin/gists/gist_index.mako:54
6216 6220 #: rhodecode/templates/admin/my_account/my_account_pullrequests.mako:23
6217 6221 #: rhodecode/templates/admin/my_account/my_account_repos.mako:7
6218 6222 #: rhodecode/templates/admin/my_account/my_account_watched.mako:7
6219 6223 #: rhodecode/templates/admin/permissions/permissions_ssh_keys.mako:11
6220 6224 #: rhodecode/templates/admin/repo_groups/repo_groups.mako:25
6221 6225 #: rhodecode/templates/admin/repos/repos.mako:25
6222 6226 #: rhodecode/templates/admin/user_groups/user_groups.mako:25
6223 6227 #: rhodecode/templates/admin/users/users.mako:26
6224 6228 #: rhodecode/templates/bookmarks/bookmarks.mako:39
6225 6229 #: rhodecode/templates/branches/branches.mako:39
6226 6230 #: rhodecode/templates/journal/journal.mako:12
6227 6231 #: rhodecode/templates/pullrequests/pullrequests.mako:53
6228 6232 #: rhodecode/templates/tags/tags.mako:39
6229 6233 msgid "quick filter..."
6230 6234 msgstr ""
6231 6235
6232 6236 #: rhodecode/templates/admin/gists/gist_index.mako:103
6233 6237 #: rhodecode/templates/admin/my_account/my_account_pullrequests.mako:87
6234 6238 #: rhodecode/templates/bookmarks/bookmarks.mako:73
6235 6239 #: rhodecode/templates/branches/branches.mako:72
6236 6240 #: rhodecode/templates/commits/changelog.mako:138
6237 6241 #: rhodecode/templates/compare/compare_commits.mako:19
6238 6242 #: rhodecode/templates/files/files_browser_tree.mako:20
6239 6243 #: rhodecode/templates/pullrequests/pullrequest_show.mako:395
6240 6244 #: rhodecode/templates/pullrequests/pullrequests.mako:100
6241 6245 #: rhodecode/templates/search/search_commit.mako:18
6242 6246 #: rhodecode/templates/summary/summary_commits.mako:11
6243 6247 #: rhodecode/templates/tags/tags.mako:73
6244 6248 msgid "Author"
6245 6249 msgstr ""
6246 6250
6247 6251 #: rhodecode/templates/admin/gists/gist_index.mako:111
6248 6252 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:11
6249 6253 #: rhodecode/templates/admin/permissions/permissions_ssh_keys.mako:51
6250 6254 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:9
6251 6255 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:7
6252 6256 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:7
6253 6257 #: rhodecode/templates/admin/users/user_edit_advanced.mako:6
6254 6258 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:16
6255 6259 #: rhodecode/templates/pullrequests/pullrequest_show.mako:58
6256 6260 msgid "Created on"
6257 6261 msgstr ""
6258 6262
6259 6263 #: rhodecode/templates/admin/gists/gist_index.mako:113
6260 6264 msgid "Expires"
6261 6265 msgstr ""
6262 6266
6263 6267 #: rhodecode/templates/admin/gists/gist_new.mako:5
6264 #: rhodecode/templates/base/base.mako:577
6268 #: rhodecode/templates/base/base.mako:578
6265 6269 msgid "New Gist"
6266 6270 msgstr ""
6267 6271
6268 6272 #: rhodecode/templates/admin/gists/gist_new.mako:31
6269 6273 msgid "Gist id"
6270 6274 msgstr ""
6271 6275
6272 6276 #: rhodecode/templates/admin/gists/gist_new.mako:32
6273 6277 msgid "Auto generated"
6274 6278 msgstr ""
6275 6279
6276 6280 #: rhodecode/templates/admin/gists/gist_new.mako:37
6277 6281 msgid "Private Gist access level"
6278 6282 msgstr ""
6279 6283
6280 6284 #: rhodecode/templates/admin/gists/gist_new.mako:47
6281 6285 msgid "name gist file..."
6282 6286 msgstr ""
6283 6287
6284 6288 #: rhodecode/templates/admin/gists/gist_new.mako:61
6285 6289 msgid "Create Gist"
6286 6290 msgstr ""
6287 6291
6288 6292 #: rhodecode/templates/admin/gists/gist_new.mako:69
6289 6293 #: rhodecode/templates/admin/gists/gist_new.mako:70
6290 6294 #: rhodecode/templates/data_table/_dt_elements.mako:352
6291 6295 msgid "Private Gist"
6292 6296 msgstr ""
6293 6297
6294 6298 #: rhodecode/templates/admin/gists/gist_new.mako:70
6295 6299 msgid "Private Gists are not listed and only accessible through their secret url."
6296 6300 msgstr ""
6297 6301
6298 6302 #: rhodecode/templates/admin/gists/gist_new.mako:73
6299 6303 #: rhodecode/templates/admin/gists/gist_new.mako:74
6300 6304 #: rhodecode/templates/data_table/_dt_elements.mako:350
6301 6305 msgid "Public Gist"
6302 6306 msgstr ""
6303 6307
6304 6308 #: rhodecode/templates/admin/gists/gist_new.mako:74
6305 6309 msgid "Public Gists are accessible to anyone and listed in Gists page."
6306 6310 msgstr ""
6307 6311
6308 6312 #: rhodecode/templates/admin/gists/gist_show.mako:14
6309 6313 #: rhodecode/templates/admin/gists/gist_show.mako:21
6310 6314 msgid "Gist"
6311 6315 msgstr ""
6312 6316
6313 6317 #: rhodecode/templates/admin/gists/gist_show.mako:41
6314 6318 msgid "Copy the url"
6315 6319 msgstr ""
6316 6320
6317 6321 #: rhodecode/templates/admin/gists/gist_show.mako:47
6318 6322 #: rhodecode/templates/files/files_source.mako:116
6319 6323 msgid "Copy content"
6320 6324 msgstr ""
6321 6325
6322 6326 #: rhodecode/templates/admin/gists/gist_show.mako:52
6323 6327 msgid "Show as Raw"
6324 6328 msgstr ""
6325 6329
6326 6330 #: rhodecode/templates/admin/gists/gist_show.mako:73
6327 6331 msgid "created"
6328 6332 msgstr ""
6329 6333
6330 6334 #: rhodecode/templates/admin/gists/gist_show.mako:74
6331 6335 msgid "expires"
6332 6336 msgstr ""
6333 6337
6334 6338 #: rhodecode/templates/admin/gists/gist_show.mako:95
6335 6339 #: rhodecode/templates/files/files_delete.mako:57
6336 6340 #: rhodecode/templates/files/files_source.mako:138
6337 6341 msgid "Show as raw"
6338 6342 msgstr ""
6339 6343
6340 6344 #: rhodecode/templates/admin/integrations/base.mako:14
6341 6345 msgid "Integrations Settings"
6342 6346 msgstr ""
6343 6347
6344 6348 #: rhodecode/templates/admin/integrations/base.mako:23
6345 6349 #: rhodecode/templates/admin/integrations/form.mako:8
6346 6350 #: rhodecode/templates/admin/integrations/form.mako:21
6347 6351 #: rhodecode/templates/admin/integrations/form.mako:32
6348 6352 #: rhodecode/templates/admin/integrations/global.mako:14
6349 6353 #: rhodecode/templates/admin/integrations/list.mako:21
6350 6354 #: rhodecode/templates/admin/integrations/list.mako:25
6351 6355 #: rhodecode/templates/admin/integrations/list.mako:29
6352 6356 #: rhodecode/templates/admin/integrations/list.mako:36
6353 6357 #: rhodecode/templates/admin/integrations/new.mako:15
6354 6358 #: rhodecode/templates/admin/repo_groups/repo_group_edit.mako:33
6355 6359 #: rhodecode/templates/admin/repos/repo_edit.mako:74
6356 #: rhodecode/templates/base/base.mako:120
6360 #: rhodecode/templates/base/base.mako:121
6357 6361 msgid "Integrations"
6358 6362 msgstr ""
6359 6363
6360 6364 #: rhodecode/templates/admin/integrations/form.mako:30
6361 6365 #: rhodecode/templates/admin/integrations/list.mako:16
6362 6366 #: rhodecode/templates/admin/integrations/new.mako:7
6363 6367 #: rhodecode/templates/admin/integrations/new.mako:9
6364 6368 #: rhodecode/templates/admin/integrations/new.mako:13
6365 6369 #: rhodecode/templates/admin/repo_groups/repo_group_edit.mako:30
6366 6370 #: rhodecode/templates/admin/repos/repo_edit.mako:15
6367 6371 #: rhodecode/templates/admin/repos/repo_edit.mako:42
6368 6372 #: rhodecode/templates/admin/settings/settings.mako:14
6369 6373 #: rhodecode/templates/admin/user_groups/user_group_edit.mako:34
6370 6374 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:9
6371 #: rhodecode/templates/base/base.mako:122
6375 #: rhodecode/templates/base/base.mako:123
6372 6376 msgid "Settings"
6373 6377 msgstr ""
6374 6378
6375 6379 #: rhodecode/templates/admin/integrations/form.mako:60
6376 6380 #, python-format
6377 6381 msgid "Create New %(integration_type)s Integration"
6378 6382 msgstr ""
6379 6383
6380 6384 #: rhodecode/templates/admin/integrations/global.mako:5
6381 6385 msgid "Integrations administration"
6382 6386 msgstr ""
6383 6387
6384 6388 #: rhodecode/templates/admin/integrations/list.mako:44
6385 6389 msgid "Current Integrations for Repository: {repo_name}"
6386 6390 msgstr ""
6387 6391
6388 6392 #: rhodecode/templates/admin/integrations/list.mako:46
6389 6393 msgid "Repository Group Integrations: {}"
6390 6394 msgstr ""
6391 6395
6392 6396 #: rhodecode/templates/admin/integrations/list.mako:48
6393 6397 msgid "Current Integrations"
6394 6398 msgstr ""
6395 6399
6396 6400 #: rhodecode/templates/admin/integrations/list.mako:65
6397 6401 #: rhodecode/templates/admin/integrations/new.mako:18
6398 6402 msgid "Create new integration"
6399 6403 msgstr ""
6400 6404
6401 6405 #: rhodecode/templates/admin/integrations/list.mako:74
6402 6406 msgid "Scope"
6403 6407 msgstr ""
6404 6408
6405 6409 #: rhodecode/templates/admin/integrations/list.mako:75
6406 6410 #: rhodecode/templates/compare/compare_diff.mako:84
6407 6411 msgid "Actions"
6408 6412 msgstr ""
6409 6413
6410 6414 #: rhodecode/templates/admin/integrations/list.mako:85
6411 6415 msgid "No {type} integrations for repo {repo} exist yet."
6412 6416 msgstr ""
6413 6417
6414 6418 #: rhodecode/templates/admin/integrations/list.mako:87
6415 6419 msgid "No {type} integrations for repogroup {repogroup} exist yet."
6416 6420 msgstr ""
6417 6421
6418 6422 #: rhodecode/templates/admin/integrations/list.mako:89
6419 6423 msgid "No {type} integrations exist yet."
6420 6424 msgstr ""
6421 6425
6422 6426 #: rhodecode/templates/admin/integrations/list.mako:103
6423 6427 msgid "Create one"
6424 6428 msgstr ""
6425 6429
6426 6430 #: rhodecode/templates/admin/integrations/list.mako:132
6427 6431 #: rhodecode/templates/hovercards/hovercard_pull_request.mako:19
6428 6432 msgid "repo"
6429 6433 msgstr ""
6430 6434
6431 6435 #: rhodecode/templates/admin/integrations/list.mako:136
6432 6436 msgid "repogroup"
6433 6437 msgstr ""
6434 6438
6435 6439 #: rhodecode/templates/admin/integrations/list.mako:138
6436 6440 msgid "child repos only"
6437 6441 msgstr ""
6438 6442
6439 6443 #: rhodecode/templates/admin/integrations/list.mako:140
6440 6444 msgid "cascade to all"
6441 6445 msgstr ""
6442 6446
6443 6447 #: rhodecode/templates/admin/integrations/list.mako:145
6444 6448 msgid "top level repos only"
6445 6449 msgstr ""
6446 6450
6447 6451 #: rhodecode/templates/admin/integrations/list.mako:147
6448 6452 msgid "global"
6449 6453 msgstr ""
6450 6454
6451 6455 #: rhodecode/templates/admin/integrations/list.mako:153
6452 6456 msgid "unknown integration"
6453 6457 msgstr ""
6454 6458
6455 6459 #: rhodecode/templates/admin/integrations/new.mako:23
6456 6460 msgid "Create New Integration for repository: {repo_name}"
6457 6461 msgstr ""
6458 6462
6459 6463 #: rhodecode/templates/admin/integrations/new.mako:25
6460 6464 msgid "Create New Integration for repository group: {repo_group_name}"
6461 6465 msgstr ""
6462 6466
6463 6467 #: rhodecode/templates/admin/integrations/new.mako:27
6464 6468 msgid "Create New Global Integration"
6465 6469 msgstr ""
6466 6470
6467 6471 #: rhodecode/templates/admin/integrations/new.mako:55
6468 6472 msgid "No description available"
6469 6473 msgstr ""
6470 6474
6471 6475 #: rhodecode/templates/admin/my_account/my_account.mako:5
6472 #: rhodecode/templates/base/base.mako:622
6476 #: rhodecode/templates/base/base.mako:623
6473 6477 msgid "My account"
6474 6478 msgstr ""
6475 6479
6476 6480 #: rhodecode/templates/admin/my_account/my_account.mako:12
6477 6481 msgid "My Account"
6478 6482 msgstr ""
6479 6483
6480 6484 #: rhodecode/templates/admin/my_account/my_account.mako:29
6481 6485 #: rhodecode/templates/email_templates/user_registration.mako:55
6482 6486 msgid "Profile"
6483 6487 msgstr ""
6484 6488
6485 6489 #: rhodecode/templates/admin/my_account/my_account.mako:30
6486 6490 #: rhodecode/templates/admin/users/user_edit.mako:45
6487 6491 #: rhodecode/templates/debug_style/index.html:55
6488 6492 msgid "Emails"
6489 6493 msgstr ""
6490 6494
6491 6495 #: rhodecode/templates/admin/my_account/my_account.mako:33
6492 6496 msgid "Auth Tokens"
6493 6497 msgstr ""
6494 6498
6495 6499 #: rhodecode/templates/admin/my_account/my_account.mako:34
6496 6500 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:3
6497 6501 #: rhodecode/templates/admin/permissions/permissions.mako:51
6498 6502 #: rhodecode/templates/admin/permissions/permissions_ssh_keys.mako:4
6499 6503 #: rhodecode/templates/admin/users/user_edit.mako:41
6500 6504 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:7
6501 6505 msgid "SSH Keys"
6502 6506 msgstr ""
6503 6507
6504 6508 #: rhodecode/templates/admin/my_account/my_account.mako:35
6505 6509 #: rhodecode/templates/admin/my_account/my_account_user_group_membership.mako:5
6506 6510 msgid "User Group Membership"
6507 6511 msgstr ""
6508 6512
6509 6513 #: rhodecode/templates/admin/my_account/my_account.mako:40
6510 6514 msgid "External Identities"
6511 6515 msgstr ""
6512 6516
6513 6517 #: rhodecode/templates/admin/my_account/my_account.mako:43
6514 6518 msgid "Owned Repositories"
6515 6519 msgstr ""
6516 6520
6517 6521 #: rhodecode/templates/admin/my_account/my_account.mako:44
6518 6522 msgid "Watched Repositories"
6519 6523 msgstr ""
6520 6524
6521 6525 #: rhodecode/templates/admin/my_account/my_account.mako:45
6522 6526 #: rhodecode/templates/admin/notifications/notifications_show_all.mako:42
6523 #: rhodecode/templates/base/base.mako:385
6524 #: rhodecode/templates/base/base.mako:626
6527 #: rhodecode/templates/base/base.mako:386
6528 #: rhodecode/templates/base/base.mako:627
6525 6529 msgid "Pull Requests"
6526 6530 msgstr ""
6527 6531
6528 6532 #: rhodecode/templates/admin/my_account/my_account.mako:46
6529 6533 #: rhodecode/templates/admin/permissions/permissions.mako:14
6530 6534 #: rhodecode/templates/admin/user_groups/user_group_edit.mako:35
6531 #: rhodecode/templates/base/base.mako:118
6535 #: rhodecode/templates/base/base.mako:119
6532 6536 msgid "Permissions"
6533 6537 msgstr ""
6534 6538
6535 6539 #: rhodecode/templates/admin/my_account/my_account.mako:47
6536 6540 msgid "Live Notifications"
6537 6541 msgstr ""
6538 6542
6539 6543 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:9
6540 6544 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:13
6541 6545 msgid "Authentication Tokens"
6542 6546 msgstr ""
6543 6547
6544 6548 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:14
6545 6549 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:19
6546 6550 msgid "Available roles"
6547 6551 msgstr ""
6548 6552
6549 6553 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:26
6550 6554 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:31
6551 6555 msgid "Token"
6552 6556 msgstr ""
6553 6557
6554 6558 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:29
6555 6559 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:34
6556 6560 msgid "Repository Scope"
6557 6561 msgstr ""
6558 6562
6559 6563 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:30
6560 6564 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:35
6561 6565 msgid "Expiration"
6562 6566 msgstr ""
6563 6567
6564 6568 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:72
6565 6569 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:76
6566 6570 msgid "No additional auth tokens specified"
6567 6571 msgstr ""
6568 6572
6569 6573 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:84
6570 6574 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:88
6571 6575 msgid "New authentication token"
6572 6576 msgstr ""
6573 6577
6574 6578 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:98
6575 6579 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:102
6576 6580 msgid "Repository scope works only with tokens with VCS type."
6577 6581 msgstr ""
6578 6582
6579 6583 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:102
6580 6584 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:72
6581 6585 #: rhodecode/templates/admin/permissions/permissions_ips.mako:63
6582 6586 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:65
6583 6587 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:106
6584 6588 #: rhodecode/templates/admin/users/user_edit_emails.mako:65
6585 6589 #: rhodecode/templates/admin/users/user_edit_ips.mako:75
6586 6590 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:71
6587 6591 msgid "Add"
6588 6592 msgstr ""
6589 6593
6590 6594 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:135
6591 6595 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:140
6592 6596 msgid "Select or enter expiration date"
6593 6597 msgstr ""
6594 6598
6595 6599 #: rhodecode/templates/admin/my_account/my_account_auth_tokens.mako:170
6596 6600 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:175
6597 6601 msgid "repository scope"
6598 6602 msgstr ""
6599 6603
6600 6604 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:7
6601 6605 msgid "Position"
6602 6606 msgstr ""
6603 6607
6604 6608 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:17
6605 6609 msgid "Bookmark title (max 30 characters, optional)"
6606 6610 msgstr ""
6607 6611
6608 6612 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:24
6609 6613 msgid "Clear"
6610 6614 msgstr ""
6611 6615
6612 6616 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:30
6613 6617 msgid "Server URL is available as ${server_url} variable. E.g. Redirect url: ${server_url}/_admin/exception_tracker"
6614 6618 msgstr ""
6615 6619
6616 6620 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:34
6617 6621 msgid "Redirect URL"
6618 6622 msgstr ""
6619 6623
6620 6624 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:44
6621 6625 msgid "Repository template"
6622 6626 msgstr ""
6623 6627
6624 6628 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:50
6625 6629 msgid "Repository group template"
6626 6630 msgstr ""
6627 6631
6628 6632 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:56
6629 6633 msgid "Template Repository or Repository group"
6630 6634 msgstr ""
6631 6635
6632 6636 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:66
6633 6637 msgid "Available as ${repo_url} e.g. Redirect url: ${repo_url}/changelog"
6634 6638 msgstr ""
6635 6639
6636 6640 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:68
6637 6641 msgid "Available as ${repo_group_url} e.g. Redirect url: ${repo_group_url}"
6638 6642 msgstr ""
6639 6643
6640 6644 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:70
6641 6645 msgid "Available as full url variables in redirect url. i.e: ${repo_url}, ${repo_group_url}."
6642 6646 msgstr ""
6643 6647
6644 6648 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:80
6645 6649 msgid "Your Bookmarks"
6646 6650 msgstr ""
6647 6651
6648 6652 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:85
6649 6653 msgid "Store upto 10 bookmark links to favorite repositories, external issue tracker or CI server. "
6650 6654 msgstr ""
6651 6655
6652 6656 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:87
6653 6657 msgid "Bookmarks are accessible from your username dropdown or by keyboard shortcut `g 0-9`"
6654 6658 msgstr ""
6655 6659
6656 6660 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:141
6657 6661 msgid "repository"
6658 6662 msgstr ""
6659 6663
6660 6664 #: rhodecode/templates/admin/my_account/my_account_bookmarks.mako:192
6661 6665 msgid "repository group"
6662 6666 msgstr ""
6663 6667
6664 6668 #: rhodecode/templates/admin/my_account/my_account_emails.mako:5
6665 6669 msgid "Account Emails"
6666 6670 msgstr ""
6667 6671
6668 6672 #: rhodecode/templates/admin/my_account/my_account_emails.mako:17
6669 6673 #: rhodecode/templates/admin/users/user_edit_emails.mako:19
6670 6674 msgid "Primary"
6671 6675 msgstr ""
6672 6676
6673 6677 #: rhodecode/templates/admin/my_account/my_account_emails.mako:43
6674 6678 #: rhodecode/templates/admin/users/user_edit_emails.mako:44
6675 6679 msgid "No additional emails specified"
6676 6680 msgstr ""
6677 6681
6678 6682 #: rhodecode/templates/admin/my_account/my_account_emails.mako:52
6679 6683 #: rhodecode/templates/admin/my_account/my_account_password.mako:6
6680 6684 msgid "Your user account details are managed by an external source. Details cannot be managed here."
6681 6685 msgstr ""
6682 6686
6683 6687 #: rhodecode/templates/admin/my_account/my_account_emails.mako:53
6684 6688 #: rhodecode/templates/admin/my_account/my_account_password.mako:9
6685 6689 msgid "Source type"
6686 6690 msgstr ""
6687 6691
6688 6692 #: rhodecode/templates/admin/my_account/my_account_notifications.mako:4
6689 6693 msgid "Your Live Notification Settings"
6690 6694 msgstr ""
6691 6695
6692 6696 #: rhodecode/templates/admin/my_account/my_account_notifications.mako:16
6693 6697 msgid "Notifications Status"
6694 6698 msgstr ""
6695 6699
6696 6700 #: rhodecode/templates/admin/my_account/my_account_notifications.mako:40
6697 6701 msgid "Test Notifications"
6698 6702 msgstr ""
6699 6703
6700 6704 #: rhodecode/templates/admin/my_account/my_account_password.mako:3
6701 6705 msgid "Change Your Account Password"
6702 6706 msgstr ""
6703 6707
6704 6708 #: rhodecode/templates/admin/my_account/my_account_password.mako:7
6705 6709 #: rhodecode/templates/admin/my_account/my_account_profile.mako:18
6706 6710 #: rhodecode/templates/admin/my_account/my_account_profile_edit.mako:19
6707 6711 msgid "For VCS access please generate"
6708 6712 msgstr ""
6709 6713
6710 6714 #: rhodecode/templates/admin/my_account/my_account_profile.mako:6
6711 6715 #: rhodecode/templates/admin/my_account/my_account_profile_edit.mako:4
6712 6716 msgid "My Profile"
6713 6717 msgstr ""
6714 6718
6715 6719 #: rhodecode/templates/admin/my_account/my_account_profile.mako:15
6716 6720 #: rhodecode/templates/admin/my_account/my_account_profile_edit.mako:16
6717 6721 #: rhodecode/templates/admin/users/user_edit_profile.mako:19
6718 6722 #, python-format
6719 6723 msgid "This user was created from external source (%s). Editing some of the settings is limited."
6720 6724 msgstr ""
6721 6725
6722 6726 #: rhodecode/templates/admin/my_account/my_account_profile.mako:24
6723 6727 #: rhodecode/templates/admin/my_account/my_account_profile_edit.mako:60
6724 6728 #: rhodecode/templates/admin/users/user_edit_profile.mako:26
6725 6729 #: rhodecode/templates/users/user_profile.mako:15
6726 6730 msgid "Photo"
6727 6731 msgstr ""
6728 6732
6729 6733 #: rhodecode/templates/admin/my_account/my_account_profile.mako:82
6730 6734 #: rhodecode/templates/users/user_profile.mako:73
6731 6735 msgid "Missing email, please update your user email address."
6732 6736 msgstr ""
6733 6737
6734 6738 #: rhodecode/templates/admin/my_account/my_account_profile_edit.mako:65
6735 6739 msgid "Change your avatar at"
6736 6740 msgstr ""
6737 6741
6738 6742 #: rhodecode/templates/admin/my_account/my_account_pullrequests.mako:13
6739 6743 #: rhodecode/templates/admin/notifications/notifications_show_all.mako:40
6740 6744 msgid "All"
6741 6745 msgstr ""
6742 6746
6743 6747 #: rhodecode/templates/admin/my_account/my_account_pullrequests.mako:14
6744 6748 msgid "All + Closed"
6745 6749 msgstr ""
6746 6750
6747 6751 #: rhodecode/templates/admin/my_account/my_account_pullrequests.mako:33
6748 6752 msgid "Pull Requests You Participate In"
6749 6753 msgstr ""
6750 6754
6751 6755 #: rhodecode/templates/admin/my_account/my_account_pullrequests.mako:75
6752 6756 #: rhodecode/templates/pullrequests/pullrequests.mako:96
6753 6757 msgid "Id"
6754 6758 msgstr ""
6755 6759
6756 6760 #: rhodecode/templates/admin/my_account/my_account_pullrequests.mako:81
6757 6761 #: rhodecode/templates/admin/settings/settings_global.mako:9
6758 6762 #: rhodecode/templates/email_templates/pull_request_review.mako:47
6759 6763 #: rhodecode/templates/email_templates/pull_request_update.mako:43
6760 6764 #: rhodecode/templates/pullrequests/pullrequest.mako:91
6761 6765 #: rhodecode/templates/pullrequests/pullrequests.mako:98
6762 6766 msgid "Title"
6763 6767 msgstr ""
6764 6768
6765 6769 #: rhodecode/templates/admin/my_account/my_account_pullrequests.mako:99
6766 6770 #: rhodecode/templates/pullrequests/pullrequests.mako:104
6767 6771 msgid "Last Update"
6768 6772 msgstr ""
6769 6773
6770 6774 #: rhodecode/templates/admin/my_account/my_account_pullrequests.mako:105
6771 6775 msgid "Target Repo"
6772 6776 msgstr ""
6773 6777
6774 6778 #: rhodecode/templates/admin/my_account/my_account_repos.mako:3
6775 6779 msgid "Repositories You Own"
6776 6780 msgstr ""
6777 6781
6778 6782 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:9
6779 6783 #: rhodecode/templates/admin/permissions/permissions_ssh_keys.mako:47
6780 6784 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:14
6781 6785 msgid "Fingerprint"
6782 6786 msgstr ""
6783 6787
6784 6788 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:12
6785 6789 #: rhodecode/templates/admin/permissions/permissions_ssh_keys.mako:53
6786 6790 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:17
6787 6791 msgid "Accessed on"
6788 6792 msgstr ""
6789 6793
6790 6794 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:16
6791 6795 msgid "SSH Keys usage is currently disabled, please ask your administrator to enable them."
6792 6796 msgstr ""
6793 6797
6794 6798 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:41
6795 6799 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:42
6796 6800 msgid "No additional ssh keys specified"
6797 6801 msgstr ""
6798 6802
6799 6803 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:55
6800 6804 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:54
6801 6805 msgid "New ssh key"
6802 6806 msgstr ""
6803 6807
6804 6808 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:60
6805 6809 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:59
6806 6810 msgid "Generate random RSA key"
6807 6811 msgstr ""
6808 6812
6809 6813 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:67
6810 6814 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:66
6811 6815 msgid "Public key, begins with 'ssh-rsa', 'ssh-dss', 'ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', or 'ecdsa-sha2-nistp521'"
6812 6816 msgstr ""
6813 6817
6814 6818 #: rhodecode/templates/admin/my_account/my_account_ssh_keys.mako:76
6815 6819 msgid "Click add to use this generated SSH key"
6816 6820 msgstr ""
6817 6821
6818 6822 #: rhodecode/templates/admin/my_account/my_account_user_group_membership.mako:49
6819 6823 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:51
6820 6824 #: rhodecode/templates/admin/user_groups/user_group_add.mako:51
6821 6825 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:55
6822 6826 #: rhodecode/templates/admin/user_groups/user_groups.mako:84
6823 6827 #: rhodecode/templates/admin/users/user_add.mako:97
6824 6828 #: rhodecode/templates/admin/users/user_edit_groups.mako:67
6825 6829 #: rhodecode/templates/admin/users/user_edit_profile.mako:110
6826 6830 #: rhodecode/templates/admin/users/users.mako:85
6827 6831 #: rhodecode/templates/user_group/profile.mako:44
6828 6832 msgid "Active"
6829 6833 msgstr ""
6830 6834
6831 6835 #: rhodecode/templates/admin/my_account/my_account_watched.mako:3
6832 6836 msgid "Your Watched Repositories"
6833 6837 msgstr ""
6834 6838
6835 6839 #: rhodecode/templates/admin/notifications/notifications_data.mako:5
6836 6840 msgid "My notifications"
6837 6841 msgstr ""
6838 6842
6839 6843 #: rhodecode/templates/admin/notifications/notifications_data.mako:26
6840 6844 msgid "Mark as read"
6841 6845 msgstr ""
6842 6846
6843 6847 #: rhodecode/templates/admin/notifications/notifications_data.mako:42
6844 6848 msgid "No notifications here yet"
6845 6849 msgstr ""
6846 6850
6847 6851 #: rhodecode/templates/admin/notifications/notifications_show.mako:5
6848 6852 #: rhodecode/templates/admin/notifications/notifications_show.mako:14
6849 6853 #: rhodecode/templates/debug_style/email_plain_rendered.mako:5
6850 6854 #: rhodecode/templates/debug_style/email_plain_rendered.mako:14
6851 6855 msgid "Show notification"
6852 6856 msgstr ""
6853 6857
6854 6858 #: rhodecode/templates/admin/notifications/notifications_show.mako:12
6855 6859 #: rhodecode/templates/admin/notifications/notifications_show_all.mako:5
6856 6860 #: rhodecode/templates/admin/notifications/notifications_show_all.mako:20
6857 6861 #: rhodecode/templates/debug_style/email_plain_rendered.mako:12
6858 6862 msgid "My Notifications"
6859 6863 msgstr ""
6860 6864
6861 6865 #: rhodecode/templates/admin/notifications/notifications_show.mako:42
6862 6866 #: rhodecode/templates/debug_style/email_plain_rendered.mako:38
6863 6867 msgid "Subject"
6864 6868 msgstr ""
6865 6869
6866 6870 #: rhodecode/templates/admin/notifications/notifications_show_all.mako:25
6867 6871 #: rhodecode/templates/admin/notifications/notifications_show_all.mako:29
6868 6872 msgid "Mark all as read"
6869 6873 msgstr ""
6870 6874
6871 6875 #: rhodecode/templates/admin/notifications/notifications_show_all.mako:39
6872 6876 msgid "Unread"
6873 6877 msgstr ""
6874 6878
6875 6879 #: rhodecode/templates/admin/notifications/notifications_show_all.mako:41
6876 6880 #: rhodecode/templates/changeset/changeset.mako:252
6877 6881 #: rhodecode/templates/changeset/changeset.mako:262
6878 6882 #: rhodecode/templates/pullrequests/pullrequest_show.mako:759
6879 6883 #: rhodecode/templates/pullrequests/pullrequest_show.mako:769
6880 6884 msgid "Comments"
6881 6885 msgstr ""
6882 6886
6883 6887 #: rhodecode/templates/admin/permissions/permissions.mako:5
6884 6888 msgid "Permissions Administration"
6885 6889 msgstr ""
6886 6890
6887 6891 #: rhodecode/templates/admin/permissions/permissions.mako:33
6888 6892 msgid "Application"
6889 6893 msgstr ""
6890 6894
6891 6895 #: rhodecode/templates/admin/permissions/permissions.mako:39
6892 6896 msgid "Object"
6893 6897 msgstr ""
6894 6898
6895 6899 #: rhodecode/templates/admin/permissions/permissions.mako:45
6896 6900 msgid "IP Whitelist"
6897 6901 msgstr ""
6898 6902
6899 6903 #: rhodecode/templates/admin/permissions/permissions.mako:48
6900 6904 msgid "AuthToken Access"
6901 6905 msgstr ""
6902 6906
6903 6907 #: rhodecode/templates/admin/permissions/permissions.mako:54
6904 6908 msgid "Overview"
6905 6909 msgstr ""
6906 6910
6907 6911 #: rhodecode/templates/admin/permissions/permissions_application.mako:3
6908 6912 msgid "System Wide Application Permissions"
6909 6913 msgstr ""
6910 6914
6911 6915 #: rhodecode/templates/admin/permissions/permissions_application.mako:12
6912 6916 msgid "Anonymous Access"
6913 6917 msgstr ""
6914 6918
6915 6919 #: rhodecode/templates/admin/permissions/permissions_application.mako:18
6916 6920 #, python-format
6917 6921 msgid "Allow access to RhodeCode Enterprise without requiring users to login. Anonymous users get the %s permission settings."
6918 6922 msgstr ""
6919 6923
6920 6924 #: rhodecode/templates/admin/permissions/permissions_application.mako:24
6921 6925 msgid "Registration"
6922 6926 msgstr ""
6923 6927
6924 6928 #: rhodecode/templates/admin/permissions/permissions_application.mako:33
6925 6929 msgid "Password Reset"
6926 6930 msgstr ""
6927 6931
6928 6932 #: rhodecode/templates/admin/permissions/permissions_application.mako:42
6929 6933 msgid "Registration Page Message"
6930 6934 msgstr ""
6931 6935
6932 6936 #: rhodecode/templates/admin/permissions/permissions_application.mako:46
6933 6937 msgid "Custom message to be displayed on the registration page. HTML syntax is supported."
6934 6938 msgstr ""
6935 6939
6936 6940 #: rhodecode/templates/admin/permissions/permissions_application.mako:52
6937 6941 msgid "External Authentication Account Activation"
6938 6942 msgstr ""
6939 6943
6940 6944 #: rhodecode/templates/admin/permissions/permissions_auth_token_access.mako:5
6941 6945 msgid "View whitelist"
6942 6946 msgstr ""
6943 6947
6944 6948 #: rhodecode/templates/admin/permissions/permissions_auth_token_access.mako:36
6945 6949 msgid "List of views available for usage in whitelist access"
6946 6950 msgstr ""
6947 6951
6948 6952 #: rhodecode/templates/admin/permissions/permissions_branch.mako:3
6949 6953 msgid "Default Permissions for Branches."
6950 6954 msgstr ""
6951 6955
6952 6956 #: rhodecode/templates/admin/permissions/permissions_branch.mako:6
6953 6957 #: rhodecode/templates/admin/repos/repo_edit_automation.mako:6
6954 6958 #: rhodecode/templates/admin/repos/repo_edit_permissions_branch.mako:6
6955 6959 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:67
6956 6960 #: rhodecode/templates/admin/repos/repo_edit_reviewers.mako:6
6957 6961 #: rhodecode/templates/admin/settings/settings_automation.mako:6
6958 6962 #: rhodecode/templates/artifacts/artifact_list.mako:24
6959 6963 #: rhodecode/templates/pullrequests/pullrequest.mako:197
6960 6964 msgid "This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license."
6961 6965 msgstr ""
6962 6966
6963 6967 #: rhodecode/templates/admin/permissions/permissions_ips.mako:5
6964 6968 msgid "Default IP Whitelist For All Users"
6965 6969 msgstr ""
6966 6970
6967 6971 #: rhodecode/templates/admin/permissions/permissions_ips.mako:9
6968 6972 #: rhodecode/templates/admin/users/user_edit_ips.mako:12
6969 6973 msgid "Current IP address"
6970 6974 msgstr ""
6971 6975
6972 6976 #: rhodecode/templates/admin/permissions/permissions_ips.mako:30
6973 6977 #: rhodecode/templates/admin/users/user_edit_ips.mako:41
6974 6978 #, python-format
6975 6979 msgid "Confirm to delete this ip: %s"
6976 6980 msgstr ""
6977 6981
6978 6982 #: rhodecode/templates/admin/permissions/permissions_ips.mako:37
6979 6983 #: rhodecode/templates/admin/users/user_edit_ips.mako:49
6980 6984 msgid "All IP addresses are allowed"
6981 6985 msgstr ""
6982 6986
6983 6987 #: rhodecode/templates/admin/permissions/permissions_ips.mako:52
6984 6988 #: rhodecode/templates/admin/users/user_edit_ips.mako:65
6985 6989 msgid "New IP Address"
6986 6990 msgstr ""
6987 6991
6988 6992 #: rhodecode/templates/admin/permissions/permissions_ips.mako:56
6989 6993 #: rhodecode/templates/admin/users/user_edit_ips.mako:68
6990 6994 msgid "Description..."
6991 6995 msgstr ""
6992 6996
6993 6997 #: rhodecode/templates/admin/permissions/permissions_ips.mako:57
6994 6998 msgid ""
6995 6999 "Enter a comma separated list of IP Addresses like 127.0.0.1,\n"
6996 7000 "or use an IP Address with a mask 127.0.0.1/24, to create a network range.\n"
6997 7001 "To specify multiple addresses in a range, use the 127.0.0.1-127.0.0.10 syntax"
6998 7002 msgstr ""
6999 7003
7000 7004 #: rhodecode/templates/admin/permissions/permissions_objects.mako:3
7001 7005 msgid "Default Permissions for Repositories, User Groups and Repository Groups."
7002 7006 msgstr ""
7003 7007
7004 7008 #: rhodecode/templates/admin/permissions/permissions_objects.mako:7
7005 7009 msgid "Default access permissions. This defines permissions for the `default` user from which other users inherit permissions."
7006 7010 msgstr ""
7007 7011
7008 7012 #: rhodecode/templates/admin/permissions/permissions_objects.mako:9
7009 7013 msgid "Check the overwrite checkbox to force change all previously defined permissions for `default` user to the new selected value."
7010 7014 msgstr ""
7011 7015
7012 7016 #: rhodecode/templates/admin/permissions/permissions_objects.mako:23
7013 7017 msgid "All default permissions on each repository will be reset to chosen permission, note that all custom default permission on repositories will be lost"
7014 7018 msgstr ""
7015 7019
7016 7020 #: rhodecode/templates/admin/permissions/permissions_objects.mako:24
7017 7021 #: rhodecode/templates/admin/permissions/permissions_objects.mako:38
7018 7022 #: rhodecode/templates/admin/permissions/permissions_objects.mako:52
7019 7023 msgid "Overwrite Existing Settings"
7020 7024 msgstr ""
7021 7025
7022 7026 #: rhodecode/templates/admin/permissions/permissions_objects.mako:37
7023 7027 msgid "All default permissions on each repository group will be reset to chosen permission, note that all custom default permission on repository groups will be lost"
7024 7028 msgstr ""
7025 7029
7026 7030 #: rhodecode/templates/admin/permissions/permissions_objects.mako:45
7027 7031 #: rhodecode/templates/admin/user_groups/user_group_edit.mako:14
7028 7032 msgid "User Groups"
7029 7033 msgstr ""
7030 7034
7031 7035 #: rhodecode/templates/admin/permissions/permissions_objects.mako:51
7032 7036 msgid "All default permissions on each user group will be reset to chosen permission, note that all custom default permission on user groups will be lost"
7033 7037 msgstr ""
7034 7038
7035 7039 #: rhodecode/templates/admin/permissions/permissions_perms.mako:1
7036 7040 msgid "Default User Permissions Overview"
7037 7041 msgstr ""
7038 7042
7039 7043 #: rhodecode/templates/admin/permissions/permissions_ssh_keys.mako:7
7040 7044 msgid "Update SSH keys file"
7041 7045 msgstr ""
7042 7046
7043 7047 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:5
7044 7048 msgid "Add repository group"
7045 7049 msgstr ""
7046 7050
7047 7051 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:14
7048 7052 #: rhodecode/templates/admin/users/user_edit_advanced.mako:13
7049 7053 #: rhodecode/templates/base/base.mako:115
7050 #: rhodecode/templates/base/base.mako:136
7054 #: rhodecode/templates/base/base.mako:137
7051 7055 msgid "Repository groups"
7052 7056 msgstr ""
7053 7057
7054 7058 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:35
7055 7059 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:15
7056 7060 #: rhodecode/templates/admin/user_groups/user_group_add.mako:34
7057 7061 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:19
7058 7062 msgid "Group name"
7059 7063 msgstr ""
7060 7064
7061 7065 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:44
7062 7066 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:25
7063 7067 #: rhodecode/templates/admin/repos/repo_add_base.mako:43
7064 7068 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:33
7065 #: rhodecode/templates/base/base.mako:671
7069 #: rhodecode/templates/base/base.mako:672
7066 7070 #: rhodecode/templates/data_table/_dt_elements.mako:217
7067 7071 #: rhodecode/templates/forks/fork.mako:41
7068 7072 msgid "Repository group"
7069 7073 msgstr ""
7070 7074
7071 7075 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:50
7072 7076 #: rhodecode/templates/admin/repos/repo_add_base.mako:49
7073 7077 msgid "Select my personal group ({})"
7074 7078 msgstr ""
7075 7079
7076 7080 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:65
7077 7081 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:64
7078 7082 #: rhodecode/templates/admin/repos/repo_add_base.mako:90
7079 7083 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:179
7080 7084 #: rhodecode/templates/admin/users/user_edit_profile.mako:81
7081 7085 #: rhodecode/templates/forks/fork.mako:63
7082 7086 msgid "Plain text format with {metatags} support."
7083 7087 msgstr ""
7084 7088
7085 7089 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:67
7086 7090 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:66
7087 7091 #: rhodecode/templates/admin/repos/repo_add_base.mako:92
7088 7092 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:181
7089 7093 #: rhodecode/templates/admin/users/user_edit_profile.mako:83
7090 7094 #: rhodecode/templates/forks/fork.mako:65
7091 7095 msgid "Plain text format."
7092 7096 msgstr ""
7093 7097
7094 7098 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:79
7095 7099 #: rhodecode/templates/admin/repos/repo_add_base.mako:104
7096 7100 msgid "Copy Parent Group Permissions"
7097 7101 msgstr ""
7098 7102
7099 7103 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:83
7100 7104 #: rhodecode/templates/admin/repos/repo_add_base.mako:108
7101 7105 msgid "Copy permissions from parent repository group."
7102 7106 msgstr ""
7103 7107
7104 7108 #: rhodecode/templates/admin/repo_groups/repo_group_add.mako:88
7105 7109 msgid "Create Repository Group"
7106 7110 msgstr ""
7107 7111
7108 7112 #: rhodecode/templates/admin/repo_groups/repo_group_edit.mako:5
7109 7113 #, python-format
7110 7114 msgid "%s repository group settings"
7111 7115 msgstr ""
7112 7116
7113 7117 #: rhodecode/templates/admin/repo_groups/repo_group_edit.mako:31
7114 7118 #: rhodecode/templates/admin/repos/repo_edit.mako:45
7115 7119 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:8
7116 7120 #: rhodecode/templates/admin/users/user_edit_advanced.mako:30
7117 7121 msgid "Access Permissions"
7118 7122 msgstr ""
7119 7123
7120 7124 #: rhodecode/templates/admin/repo_groups/repo_group_edit.mako:32
7121 7125 #: rhodecode/templates/admin/repos/repo_edit.mako:51
7122 7126 #: rhodecode/templates/admin/user_groups/user_group_edit.mako:36
7123 7127 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:24
7124 7128 #: rhodecode/templates/admin/users/user_edit.mako:42
7125 7129 msgid "Advanced"
7126 7130 msgstr ""
7127 7131
7128 7132 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:7
7129 7133 msgid "Repository Group ID"
7130 7134 msgstr ""
7131 7135
7132 7136 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:10
7133 7137 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:8
7134 7138 msgid "Updated on"
7135 7139 msgstr ""
7136 7140
7137 7141 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:11
7138 7142 msgid "Cached Commit source"
7139 7143 msgstr ""
7140 7144
7141 7145 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:12
7142 7146 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:9
7143 7147 msgid "Cached Commit id"
7144 7148 msgstr ""
7145 7149
7146 7150 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:13
7147 7151 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:10
7148 7152 msgid "Cached Commit date"
7149 7153 msgstr ""
7150 7154
7151 7155 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:15
7152 7156 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:11
7153 7157 msgid "Cached Commit data"
7154 7158 msgstr ""
7155 7159
7156 7160 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:17
7157 7161 msgid "Is Personal Group"
7158 7162 msgstr ""
7159 7163
7160 7164 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:19
7161 7165 msgid "Total repositories"
7162 7166 msgstr ""
7163 7167
7164 7168 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:20
7165 7169 msgid "Top level repositories"
7166 7170 msgstr ""
7167 7171
7168 7172 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:22
7169 7173 msgid "Children groups"
7170 7174 msgstr ""
7171 7175
7172 7176 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:28
7173 7177 msgid "Repository Group Advanced: {}"
7174 7178 msgstr ""
7175 7179
7176 7180 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:38
7177 7181 msgid "Delete repository group"
7178 7182 msgstr ""
7179 7183
7180 7184 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:67
7181 7185 #, python-format
7182 7186 msgid "Confirm to delete this group: %s"
7183 7187 msgstr ""
7184 7188
7185 7189 #: rhodecode/templates/admin/repo_groups/repo_group_edit_advanced.mako:68
7186 7190 msgid "Delete this repository group"
7187 7191 msgstr ""
7188 7192
7189 7193 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:5
7190 7194 msgid "Repository Group Permissions: {}"
7191 7195 msgstr ""
7192 7196
7193 7197 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:15
7194 7198 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:15
7195 7199 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:19
7196 7200 msgid "User/User Group"
7197 7201 msgstr ""
7198 7202
7199 7203 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:32
7200 7204 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:31
7201 7205 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:37
7202 7206 msgid "super-admin"
7203 7207 msgstr ""
7204 7208
7205 7209 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:35
7206 7210 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:34
7207 7211 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:40
7208 7212 msgid "owner"
7209 7213 msgstr ""
7210 7214
7211 7215 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:66
7212 7216 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:119
7213 7217 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:92
7214 7218 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:72
7215 7219 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:125
7216 7220 msgid "permission for other logged in and anonymous users"
7217 7221 msgstr ""
7218 7222
7219 7223 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:68
7220 7224 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:121
7221 7225 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:94
7222 7226 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:74
7223 7227 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:127
7224 7228 msgid "permission for other logged in users"
7225 7229 msgstr ""
7226 7230
7227 7231 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:74
7228 7232 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:127
7229 7233 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:100
7230 7234 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:80
7231 7235 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:133
7232 7236 msgid "This entry is a duplicate, most probably left-over from previously set permission. This user has a higher permission set, so this entry is inactive. Please revoke this permission manually."
7233 7237 msgstr ""
7234 7238
7235 7239 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:74
7236 7240 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:127
7237 7241 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:100
7238 7242 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:80
7239 7243 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:133
7240 7244 msgid "inactive duplicate"
7241 7245 msgstr ""
7242 7246
7243 7247 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:87
7244 7248 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:177
7245 7249 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:121
7246 7250 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:170
7247 7251 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:46
7248 7252 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:93
7249 7253 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:182
7250 7254 msgid "Remove"
7251 7255 msgstr ""
7252 7256
7253 7257 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:134
7254 7258 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:140
7255 7259 msgid "delegated admin"
7256 7260 msgstr ""
7257 7261
7258 7262 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:172
7259 7263 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:165
7260 7264 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:177
7261 7265 msgid "members"
7262 7266 msgstr ""
7263 7267
7264 7268 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:204
7265 7269 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:197
7266 7270 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:208
7267 7271 msgid "Add user/user group"
7268 7272 msgstr ""
7269 7273
7270 7274 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:214
7271 7275 msgid "Apply to children"
7272 7276 msgstr ""
7273 7277
7274 7278 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:220
7275 7279 msgid "Both"
7276 7280 msgstr ""
7277 7281
7278 7282 #: rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako:221
7279 7283 msgid "Set or revoke permissions to selected types of children of this group, including non-private repositories and other groups if chosen."
7280 7284 msgstr ""
7281 7285
7282 7286 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:6
7283 7287 msgid "Repository Group Settings: {}"
7284 7288 msgstr ""
7285 7289
7286 7290 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:31
7287 7291 msgid "Optional select a parent group to move this repository group into."
7288 7292 msgstr ""
7289 7293
7290 7294 #: rhodecode/templates/admin/repo_groups/repo_group_edit_settings.mako:49
7291 7295 msgid "Change owner of this repository group."
7292 7296 msgstr ""
7293 7297
7294 7298 #: rhodecode/templates/admin/repo_groups/repo_groups.mako:5
7295 7299 msgid "Repository groups administration"
7296 7300 msgstr ""
7297 7301
7298 7302 #: rhodecode/templates/admin/repo_groups/repo_groups.mako:82
7299 7303 msgid "Number of top level repositories"
7300 7304 msgstr ""
7301 7305
7302 7306 #: rhodecode/templates/admin/repos/repo_add.mako:5
7303 7307 msgid "Add repository"
7304 7308 msgstr ""
7305 7309
7306 7310 #: rhodecode/templates/admin/repos/repo_add_base.mako:9
7307 7311 msgid "Repository name"
7308 7312 msgstr ""
7309 7313
7310 7314 #: rhodecode/templates/admin/repos/repo_add_base.mako:14
7311 7315 msgid "Import Existing Repository ?"
7312 7316 msgstr ""
7313 7317
7314 7318 #: rhodecode/templates/admin/repos/repo_add_base.mako:23
7315 #: rhodecode/templates/base/base.mako:332
7319 #: rhodecode/templates/base/base.mako:333
7316 7320 msgid "Clone from"
7317 7321 msgstr ""
7318 7322
7319 7323 #: rhodecode/templates/admin/repos/repo_add_base.mako:52
7320 7324 #: rhodecode/templates/forks/fork.mako:50
7321 7325 msgid "Optionally select a group to put this repository into."
7322 7326 msgstr ""
7323 7327
7324 7328 #: rhodecode/templates/admin/repos/repo_add_base.mako:78
7325 7329 msgid "Set the type of repository to create."
7326 7330 msgstr ""
7327 7331
7328 7332 #: rhodecode/templates/admin/repos/repo_add_base.mako:94
7329 7333 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:183
7330 7334 #: rhodecode/templates/forks/fork.mako:67
7331 7335 msgid "Add a README file for longer descriptions"
7332 7336 msgstr ""
7333 7337
7334 7338 #: rhodecode/templates/admin/repos/repo_add_base.mako:121
7335 7339 msgid "Create Repository"
7336 7340 msgstr ""
7337 7341
7338 7342 #: rhodecode/templates/admin/repos/repo_creating.mako:5
7339 7343 msgid "{} Creating repository"
7340 7344 msgstr ""
7341 7345
7342 7346 #: rhodecode/templates/admin/repos/repo_creating.mako:12
7343 7347 msgid "Creating repository"
7344 7348 msgstr ""
7345 7349
7346 7350 #: rhodecode/templates/admin/repos/repo_creating.mako:26
7347 7351 #, python-format
7348 7352 msgid "Repository \"%(repo_name)s\" is being created, you will be redirected when this process is finished.repo_name"
7349 7353 msgstr ""
7350 7354
7351 7355 #: rhodecode/templates/admin/repos/repo_edit.mako:8
7352 7356 msgid "{} repository settings"
7353 7357 msgstr ""
7354 7358
7355 7359 #: rhodecode/templates/admin/repos/repo_edit.mako:48
7356 7360 msgid "Branch Permissions"
7357 7361 msgstr ""
7358 7362
7359 7363 #: rhodecode/templates/admin/repos/repo_edit.mako:54
7360 7364 msgid "VCS"
7361 7365 msgstr ""
7362 7366
7363 7367 #: rhodecode/templates/admin/repos/repo_edit.mako:57
7364 7368 msgid "Extra Fields"
7365 7369 msgstr ""
7366 7370
7367 7371 #: rhodecode/templates/admin/repos/repo_edit.mako:60
7368 7372 msgid "Issue Tracker"
7369 7373 msgstr ""
7370 7374
7371 7375 #: rhodecode/templates/admin/repos/repo_edit.mako:63
7372 7376 #: rhodecode/templates/admin/users/user_edit.mako:49
7373 7377 #: rhodecode/templates/admin/users/user_edit_caches.mako:7
7374 7378 msgid "Caches"
7375 7379 msgstr ""
7376 7380
7377 7381 #: rhodecode/templates/admin/repos/repo_edit.mako:67
7378 7382 msgid "Remote sync"
7379 7383 msgstr ""
7380 7384
7381 7385 #: rhodecode/templates/admin/repos/repo_edit.mako:71
7382 7386 msgid "Statistics"
7383 7387 msgstr ""
7384 7388
7385 7389 #: rhodecode/templates/admin/repos/repo_edit.mako:78
7386 7390 msgid "Reviewer Rules"
7387 7391 msgstr ""
7388 7392
7389 7393 #: rhodecode/templates/admin/repos/repo_edit.mako:82
7390 7394 msgid "Automation"
7391 7395 msgstr ""
7392 7396
7393 7397 #: rhodecode/templates/admin/repos/repo_edit.mako:85
7394 7398 #: rhodecode/templates/admin/repos/repo_edit_maintenance.mako:3
7395 7399 msgid "Maintenance"
7396 7400 msgstr ""
7397 7401
7398 7402 #: rhodecode/templates/admin/repos/repo_edit.mako:88
7399 7403 msgid "Strip"
7400 7404 msgstr ""
7401 7405
7402 7406 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:5
7403 7407 msgid "Repository ID"
7404 7408 msgstr ""
7405 7409
7406 7410 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:12
7407 7411 msgid "Attached scoped tokens"
7408 7412 msgstr ""
7409 7413
7410 7414 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:13
7411 7415 msgid "Pull requests source"
7412 7416 msgstr ""
7413 7417
7414 7418 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:14
7415 7419 msgid "Pull requests target"
7416 7420 msgstr ""
7417 7421
7418 7422 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:15
7419 7423 msgid "Attached Artifacts"
7420 7424 msgstr ""
7421 7425
7422 7426 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:21
7423 7427 #, python-format
7424 7428 msgid "Repository: %s"
7425 7429 msgstr ""
7426 7430
7427 7431 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:31
7428 7432 msgid "Fork Reference"
7429 7433 msgstr ""
7430 7434
7431 7435 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:37
7432 7436 #, python-format
7433 7437 msgid "This repository is a fork of %(repo_link)s"
7434 7438 msgstr ""
7435 7439
7436 7440 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:43
7437 7441 msgid "Set"
7438 7442 msgstr ""
7439 7443
7440 7444 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:46
7441 7445 msgid "Manually set this repository as a fork of another from the list"
7442 7446 msgstr ""
7443 7447
7444 7448 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:55
7445 7449 msgid "Public Journal Visibility"
7446 7450 msgstr ""
7447 7451
7448 7452 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:62
7449 7453 msgid "Remove from Public Journal"
7450 7454 msgstr ""
7451 7455
7452 7456 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:66
7453 7457 msgid "Add to Public Journal"
7454 7458 msgstr ""
7455 7459
7456 7460 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:71
7457 7461 msgid "All actions made on this repository will be visible to everyone following the public journal."
7458 7462 msgstr ""
7459 7463
7460 7464 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:80
7461 7465 msgid "Locking state"
7462 7466 msgstr ""
7463 7467
7464 7468 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:89
7465 7469 msgid "This Repository is not currently locked."
7466 7470 msgstr ""
7467 7471
7468 7472 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:99
7469 7473 msgid "Unlock repository"
7470 7474 msgstr ""
7471 7475
7472 7476 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:107
7473 7477 msgid "Lock repository"
7474 7478 msgstr ""
7475 7479
7476 7480 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:113
7477 7481 msgid "Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again."
7478 7482 msgstr ""
7479 7483
7480 7484 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:123
7481 7485 msgid "Hooks"
7482 7486 msgstr ""
7483 7487
7484 7488 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:127
7485 7489 msgid "Hook type"
7486 7490 msgstr ""
7487 7491
7488 7492 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:128
7489 7493 msgid "Hook version"
7490 7494 msgstr ""
7491 7495
7492 7496 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:129
7493 7497 msgid "Current version"
7494 7498 msgstr ""
7495 7499
7496 7500 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:132
7497 7501 msgid "PRE HOOK"
7498 7502 msgstr ""
7499 7503
7500 7504 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:137
7501 7505 msgid "POST HOOK"
7502 7506 msgstr ""
7503 7507
7504 7508 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:143
7505 7509 msgid "Unable to read hook information from VCS Server"
7506 7510 msgstr ""
7507 7511
7508 7512 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:149
7509 7513 msgid "Confirm to reinstall hooks for this repository."
7510 7514 msgstr ""
7511 7515
7512 7516 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:150
7513 7517 msgid "Update Hooks"
7514 7518 msgstr ""
7515 7519
7516 7520 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:154
7517 7521 msgid "Outdated hooks detected, please update hooks using `Update Hooks` action."
7518 7522 msgstr ""
7519 7523
7520 7524 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:162
7521 7525 msgid "Archive repository"
7522 7526 msgstr ""
7523 7527
7524 7528 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:176
7525 7529 msgid "Archive this repository"
7526 7530 msgstr ""
7527 7531
7528 7532 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:183
7529 7533 msgid "Archiving the repository will make it entirely read-only. The repository cannot be committed to.It is hidden from the search results and dashboard. "
7530 7534 msgstr ""
7531 7535
7532 7536 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:195
7533 7537 msgid "Delete repository"
7534 7538 msgstr ""
7535 7539
7536 7540 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:206
7537 7541 msgid "Detach forks"
7538 7542 msgstr ""
7539 7543
7540 7544 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:211
7541 7545 msgid "Delete forks"
7542 7546 msgstr ""
7543 7547
7544 7548 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:221
7545 7549 msgid "Consider to archive this repository instead."
7546 7550 msgstr ""
7547 7551
7548 7552 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:234
7549 7553 msgid "Delete this repository"
7550 7554 msgstr ""
7551 7555
7552 7556 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:239
7553 7557 msgid "This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools."
7554 7558 msgstr ""
7555 7559
7556 7560 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:273
7557 7561 msgid "Change repository"
7558 7562 msgstr ""
7559 7563
7560 7564 #: rhodecode/templates/admin/repos/repo_edit_advanced.mako:273
7561 7565 msgid "Pick repository"
7562 7566 msgstr ""
7563 7567
7564 7568 #: rhodecode/templates/admin/repos/repo_edit_audit.mako:7
7565 7569 msgid "Repository Audit Logs"
7566 7570 msgstr ""
7567 7571
7568 7572 #: rhodecode/templates/admin/repos/repo_edit_audit.mako:14
7569 7573 #: rhodecode/templates/admin/users/user_edit_audit.mako:17
7570 7574 msgid "audit filter..."
7571 7575 msgstr ""
7572 7576
7573 7577 #: rhodecode/templates/admin/repos/repo_edit_automation.mako:3
7574 7578 msgid "Repo Automation"
7575 7579 msgstr ""
7576 7580
7577 7581 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:3
7578 7582 msgid "Invalidate Cache for Repository"
7579 7583 msgstr ""
7580 7584
7581 7585 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:7
7582 7586 msgid "Manually invalidate the repository cache. On the next access a repository cache will be recreated."
7583 7587 msgstr ""
7584 7588
7585 7589 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:10
7586 7590 msgid "Cache purge can be automated by such api call. Can be called periodically in crontab etc."
7587 7591 msgstr ""
7588 7592
7589 7593 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:23
7590 7594 msgid "Invalidate repository cache"
7591 7595 msgstr ""
7592 7596
7593 7597 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:36
7594 7598 msgid "Invalidation keys"
7595 7599 msgstr ""
7596 7600
7597 7601 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:44
7598 7602 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:88
7599 7603 #: rhodecode/templates/admin/users/user_edit_caches.mako:22
7600 7604 msgid "Show all"
7601 7605 msgstr ""
7602 7606
7603 7607 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:48
7604 7608 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:92
7605 7609 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:11
7606 7610 msgid "Key"
7607 7611 msgstr ""
7608 7612
7609 7613 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:49
7610 7614 msgid "State UID"
7611 7615 msgstr ""
7612 7616
7613 7617 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:50
7614 7618 msgid "Namespace"
7615 7619 msgstr ""
7616 7620
7617 7621 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:69
7618 7622 msgid "Cache keys"
7619 7623 msgstr ""
7620 7624
7621 7625 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:93
7622 7626 msgid "Region"
7623 7627 msgstr ""
7624 7628
7625 7629 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:110
7626 7630 msgid "Shadow Repositories"
7627 7631 msgstr ""
7628 7632
7629 7633 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:122
7630 7634 msgid "No Shadow repositories exist for this repository."
7631 7635 msgstr ""
7632 7636
7633 7637 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:133
7634 7638 msgid "Diff Caches"
7635 7639 msgstr ""
7636 7640
7637 7641 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:141
7638 7642 msgid "Cached diff name"
7639 7643 msgstr ""
7640 7644
7641 7645 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:149
7642 7646 msgid "Cached diff files"
7643 7647 msgstr ""
7644 7648
7645 7649 #: rhodecode/templates/admin/repos/repo_edit_caches.mako:153
7646 7650 msgid "Cached diff size"
7647 7651 msgstr ""
7648 7652
7649 7653 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:3
7650 7654 msgid "Custom extra fields for this repository"
7651 7655 msgstr ""
7652 7656
7653 7657 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:10
7654 7658 msgid "Label"
7655 7659 msgstr ""
7656 7660
7657 7661 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:24
7658 7662 #, python-format
7659 7663 msgid "Confirm to delete this field: %s"
7660 7664 msgstr ""
7661 7665
7662 7666 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:40
7663 7667 msgid "New Field Key"
7664 7668 msgstr ""
7665 7669
7666 7670 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:48
7667 7671 msgid "New Field Label"
7668 7672 msgstr ""
7669 7673
7670 7674 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:51
7671 7675 msgid "Enter short label"
7672 7676 msgstr ""
7673 7677
7674 7678 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:57
7675 7679 msgid "New Field Description"
7676 7680 msgstr ""
7677 7681
7678 7682 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:60
7679 7683 msgid "Enter a full description for the field"
7680 7684 msgstr ""
7681 7685
7682 7686 #: rhodecode/templates/admin/repos/repo_edit_fields.mako:73
7683 7687 msgid "Extra fields are disabled. You can enable them from the Admin/Settings/Visual page."
7684 7688 msgstr ""
7685 7689
7686 7690 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:9
7687 7691 #: rhodecode/templates/admin/repos/repo_edit_vcs.mako:9
7688 7692 msgid "Inherit from global settings"
7689 7693 msgstr ""
7690 7694
7691 7695 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:14
7692 7696 msgid "Select to inherit global patterns for issue tracker."
7693 7697 msgstr ""
7694 7698
7695 7699 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:24
7696 7700 msgid "Inherited Issue Tracker Patterns"
7697 7701 msgstr ""
7698 7702
7699 7703 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:31
7700 7704 #: rhodecode/templates/base/issue_tracker_settings.mako:80
7701 7705 #: rhodecode/templates/base/perms_summary.mako:174
7702 7706 msgid "Pattern"
7703 7707 msgstr ""
7704 7708
7705 7709 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:32
7706 7710 #: rhodecode/templates/base/issue_tracker_settings.mako:81
7707 7711 msgid "Url"
7708 7712 msgstr ""
7709 7713
7710 7714 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:33
7711 7715 msgid "Prefix"
7712 7716 msgstr ""
7713 7717
7714 7718 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:75
7715 7719 #: rhodecode/templates/admin/settings/settings_issuetracker.mako:5
7716 7720 msgid "Issue Tracker / Wiki Patterns"
7717 7721 msgstr ""
7718 7722
7719 7723 #: rhodecode/templates/admin/repos/repo_edit_issuetracker.mako:95
7720 7724 #: rhodecode/templates/admin/settings/settings_issuetracker.mako:24
7721 7725 msgid "Test Patterns"
7722 7726 msgstr ""
7723 7727
7724 7728 #: rhodecode/templates/admin/repos/repo_edit_maintenance.mako:8
7725 7729 msgid "Perform maintenance tasks for this repo"
7726 7730 msgstr ""
7727 7731
7728 7732 #: rhodecode/templates/admin/repos/repo_edit_maintenance.mako:10
7729 7733 msgid "Following tasks will be performed"
7730 7734 msgstr ""
7731 7735
7732 7736 #: rhodecode/templates/admin/repos/repo_edit_maintenance.mako:17
7733 7737 msgid "Maintenance can be automated by such api call. Can be called periodically in crontab etc."
7734 7738 msgstr ""
7735 7739
7736 7740 #: rhodecode/templates/admin/repos/repo_edit_maintenance.mako:25
7737 7741 msgid "No maintenance tasks for this repo available"
7738 7742 msgstr ""
7739 7743
7740 7744 #: rhodecode/templates/admin/repos/repo_edit_maintenance.mako:34
7741 7745 msgid "Run Maintenance"
7742 7746 msgstr ""
7743 7747
7744 7748 #: rhodecode/templates/admin/repos/repo_edit_maintenance.mako:49
7745 7749 msgid "Performing Maintenance"
7746 7750 msgstr ""
7747 7751
7748 7752 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:5
7749 7753 msgid "Repository Access Permissions"
7750 7754 msgstr ""
7751 7755
7752 7756 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:55
7753 7757 msgid "private repository"
7754 7758 msgstr ""
7755 7759
7756 7760 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:60
7757 7761 msgid "only users/user groups explicitly added here will have access"
7758 7762 msgstr ""
7759 7763
7760 7764 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:62
7761 7765 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:124
7762 7766 msgid "Private repositories are only visible to people explicitly added as collaborators. Default permissions wont apply"
7763 7767 msgstr ""
7764 7768
7765 7769 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:63
7766 7770 msgid "un-set private mode"
7767 7771 msgstr ""
7768 7772
7769 7773 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:109
7770 7774 msgid "used by {} branch rule, requires write or higher permissions"
7771 7775 msgstr ""
7772 7776
7773 7777 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:111
7774 7778 msgid "used by {} branch rules, requires write or higher permissions"
7775 7779 msgstr ""
7776 7780
7777 7781 #: rhodecode/templates/admin/repos/repo_edit_permissions.mako:125
7778 7782 msgid "set private mode"
7779 7783 msgstr ""
7780 7784
7781 7785 #: rhodecode/templates/admin/repos/repo_edit_permissions_branch.mako:3
7782 7786 msgid "Repository Branch Permissions."
7783 7787 msgstr ""
7784 7788
7785 7789 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:3
7786 7790 msgid "Remote Sync"
7787 7791 msgstr ""
7788 7792
7789 7793 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:7
7790 7794 msgid "Manually pull/push changes from/to external URLs."
7791 7795 msgstr ""
7792 7796
7793 7797 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:11
7794 7798 msgid "Pull url"
7795 7799 msgstr ""
7796 7800
7797 7801 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:16
7798 7802 msgid "This repository does not have any pull url set."
7799 7803 msgstr ""
7800 7804
7801 7805 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:17
7802 7806 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:60
7803 7807 msgid "Set remote url."
7804 7808 msgstr ""
7805 7809
7806 7810 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:26
7807 7811 msgid "Pull can be automated by such api call. Can be called periodically in crontab etc."
7808 7812 msgstr ""
7809 7813
7810 7814 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:44
7811 7815 msgid "Pull changes from remote location"
7812 7816 msgstr ""
7813 7817
7814 7818 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:54
7815 7819 msgid "Push url"
7816 7820 msgstr ""
7817 7821
7818 7822 #: rhodecode/templates/admin/repos/repo_edit_remote.mako:59
7819 7823 msgid "This repository does not have any push url set."
7820 7824 msgstr ""
7821 7825
7822 7826 #: rhodecode/templates/admin/repos/repo_edit_reviewers.mako:3
7823 7827 msgid "Default Reviewer Rules"
7824 7828 msgstr ""
7825 7829
7826 7830 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:6
7827 7831 #, python-format
7828 7832 msgid "Settings for Repository: %s"
7829 7833 msgstr ""
7830 7834
7831 7835 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:22
7832 7836 msgid "permalink id"
7833 7837 msgstr ""
7834 7838
7835 7839 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:22
7836 7840 msgid "what is that ?"
7837 7841 msgstr ""
7838 7842
7839 7843 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:24
7840 7844 msgid "URL by id"
7841 7845 msgstr ""
7842 7846
7843 7847 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:25
7844 7848 msgid ""
7845 7849 "In case this repository is renamed or moved into another group the repository url changes.\n"
7846 7850 " Using above url guarantees that this repository will always be accessible under such url.\n"
7847 7851 " Useful for CI systems, or any other cases that you need to hardcode the url into 3rd party service."
7848 7852 msgstr ""
7849 7853
7850 7854 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:41
7851 7855 #, python-format
7852 7856 msgid "Select my personal group (`%(repo_group_name)s`)"
7853 7857 msgstr ""
7854 7858
7855 7859 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:44
7856 7860 msgid "Optional select a group to put this repository into."
7857 7861 msgstr ""
7858 7862
7859 7863 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:52
7860 7864 msgid "Remote pull uri"
7861 7865 msgstr ""
7862 7866
7863 7867 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:60
7864 7868 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:102
7865 7869 #: rhodecode/templates/base/perms_summary.mako:116
7866 7870 #: rhodecode/templates/base/perms_summary.mako:233
7867 7871 #: rhodecode/templates/base/perms_summary.mako:311
7868 7872 #: rhodecode/templates/base/perms_summary.mako:313
7869 7873 #: rhodecode/templates/debug_style/form-elements.html:45
7870 7874 msgid "edit"
7871 7875 msgstr ""
7872 7876
7873 7877 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:66
7874 7878 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:108
7875 7879 msgid "enter new value, or leave empty to remove"
7876 7880 msgstr ""
7877 7881
7878 7882 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:76
7879 7883 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:118
7880 7884 msgid "cancel"
7881 7885 msgstr ""
7882 7886
7883 7887 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:87
7884 7888 msgid "http[s] url where from repository was imported. This field can used for doing {sync_link}."
7885 7889 msgstr ""
7886 7890
7887 7891 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:88
7888 7892 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:130
7889 7893 msgid "This field is stored encrypted inside Database, a format of http://user:password@server.com/repo_name can be used and will be hidden from display."
7890 7894 msgstr ""
7891 7895
7892 7896 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:94
7893 7897 msgid "Remote push uri"
7894 7898 msgstr ""
7895 7899
7896 7900 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:129
7897 7901 msgid "http[s] url to sync data back. This field can used for doing {sync_link}."
7898 7902 msgstr ""
7899 7903
7900 7904 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:141
7901 7905 msgid "Landing commit"
7902 7906 msgstr ""
7903 7907
7904 7908 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:146
7905 7909 msgid "The default commit for file pages, downloads, full text search index, and README generation."
7906 7910 msgstr ""
7907 7911
7908 7912 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:164
7909 7913 msgid "Change owner of this repository."
7910 7914 msgstr ""
7911 7915
7912 7916 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:194
7913 7917 #: rhodecode/templates/data_table/_dt_elements.mako:104
7914 7918 msgid "Private repository"
7915 7919 msgstr ""
7916 7920
7917 7921 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:204
7918 7922 #: rhodecode/templates/summary/components.mako:262
7919 7923 msgid "Enable statistics"
7920 7924 msgstr ""
7921 7925
7922 7926 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:209
7923 7927 msgid "Enable statistics window on summary page."
7924 7928 msgstr ""
7925 7929
7926 7930 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:214
7927 7931 #: rhodecode/templates/summary/components.mako:184
7928 7932 msgid "Enable downloads"
7929 7933 msgstr ""
7930 7934
7931 7935 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:219
7932 7936 msgid "Enable download menu on summary page."
7933 7937 msgstr ""
7934 7938
7935 7939 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:224
7936 7940 msgid "Enable automatic locking"
7937 7941 msgstr ""
7938 7942
7939 7943 #: rhodecode/templates/admin/repos/repo_edit_settings.mako:229
7940 7944 msgid "Enable automatic locking on repository. Pulling from this repository creates a lock that can be released by pushing back by the same user"
7941 7945 msgstr ""
7942 7946
7943 7947 #: rhodecode/templates/admin/repos/repo_edit_statistics.mako:3
7944 7948 msgid "Repository statistics"
7945 7949 msgstr ""
7946 7950
7947 7951 #: rhodecode/templates/admin/repos/repo_edit_statistics.mako:11
7948 7952 msgid "Processed commits"
7949 7953 msgstr ""
7950 7954
7951 7955 #: rhodecode/templates/admin/repos/repo_edit_statistics.mako:12
7952 7956 msgid "Processed progress"
7953 7957 msgstr ""
7954 7958
7955 7959 #: rhodecode/templates/admin/repos/repo_edit_statistics.mako:15
7956 7960 msgid "Reset statistics"
7957 7961 msgstr ""
7958 7962
7959 7963 #: rhodecode/templates/admin/repos/repo_edit_statistics.mako:15
7960 7964 msgid "Confirm to remove current statistics"
7961 7965 msgstr ""
7962 7966
7963 7967 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:3
7964 7968 msgid "Strip commits from repository"
7965 7969 msgstr ""
7966 7970
7967 7971 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:7
7968 7972 #, python-format
7969 7973 msgid "Please provide up to %d commits commits to strip"
7970 7974 msgstr ""
7971 7975
7972 7976 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:9
7973 7977 msgid "In the first step commits will be verified for existance in the repository"
7974 7978 msgstr ""
7975 7979
7976 7980 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:10
7977 7981 msgid "In the second step, correct commits will be available for stripping"
7978 7982 msgstr ""
7979 7983
7980 7984 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:16
7981 7985 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:52
7982 7986 msgid "Enter full 40 character commit sha"
7983 7987 msgstr ""
7984 7988
7985 7989 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:18
7986 7990 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:54
7987 7991 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:95
7988 7992 msgid "Add another commit"
7989 7993 msgstr ""
7990 7994
7991 7995 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:27
7992 7996 msgid "Check commits"
7993 7997 msgstr ""
7994 7998
7995 7999 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:33
7996 8000 msgid "Sorry this functionality is not available for SVN repository"
7997 8001 msgstr ""
7998 8002
7999 8003 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:114
8000 8004 msgid "Checking commits"
8001 8005 msgstr ""
8002 8006
8003 8007 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:142
8004 8008 msgid " commit verified positive"
8005 8009 msgstr ""
8006 8010
8007 8011 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:154
8008 8012 msgid " commit verified negative"
8009 8013 msgstr ""
8010 8014
8011 8015 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:179
8012 8016 msgid " commit striped successfully"
8013 8017 msgstr ""
8014 8018
8015 8019 #: rhodecode/templates/admin/repos/repo_edit_strip.mako:182
8016 8020 msgid " commit strip failed"
8017 8021 msgstr ""
8018 8022
8019 8023 #: rhodecode/templates/admin/repos/repo_edit_vcs.mako:13
8020 8024 msgid "Select to inherit global vcs settings."
8021 8025 msgstr ""
8022 8026
8023 8027 #: rhodecode/templates/admin/repos/repo_edit_vcs.mako:43
8024 8028 #: rhodecode/templates/admin/settings/settings_global.mako:140
8025 8029 #: rhodecode/templates/admin/settings/settings_labs.mako:48
8026 8030 #: rhodecode/templates/admin/settings/settings_vcs.mako:13
8027 #: rhodecode/templates/admin/settings/settings_visual.mako:214
8031 #: rhodecode/templates/admin/settings/settings_visual.mako:217
8028 8032 msgid "Save settings"
8029 8033 msgstr ""
8030 8034
8031 8035 #: rhodecode/templates/admin/repos/repos.mako:5
8032 8036 msgid "Repositories administration"
8033 8037 msgstr ""
8034 8038
8035 8039 #: rhodecode/templates/admin/repos/repos.mako:110
8036 8040 msgid "State"
8037 8041 msgstr ""
8038 8042
8039 8043 #: rhodecode/templates/admin/settings/settings_automation.mako:3
8040 8044 msgid "Admin Automation"
8041 8045 msgstr ""
8042 8046
8043 8047 #: rhodecode/templates/admin/settings/settings_email.mako:3
8044 8048 msgid "Email Configuration"
8045 8049 msgstr ""
8046 8050
8047 8051 #: rhodecode/templates/admin/settings/settings_email.mako:8
8048 8052 msgid "Email prefix"
8049 8053 msgstr ""
8050 8054
8051 8055 #: rhodecode/templates/admin/settings/settings_email.mako:9
8052 8056 msgid "Email from"
8053 8057 msgstr ""
8054 8058
8055 8059 #: rhodecode/templates/admin/settings/settings_email.mako:11
8056 8060 msgid "SMTP server"
8057 8061 msgstr ""
8058 8062
8059 8063 #: rhodecode/templates/admin/settings/settings_email.mako:12
8060 8064 msgid "SMTP username"
8061 8065 msgstr ""
8062 8066
8063 8067 #: rhodecode/templates/admin/settings/settings_email.mako:13
8064 8068 msgid "SMTP password"
8065 8069 msgstr ""
8066 8070
8067 8071 #: rhodecode/templates/admin/settings/settings_email.mako:14
8068 8072 msgid "SMTP port"
8069 8073 msgstr ""
8070 8074
8071 8075 #: rhodecode/templates/admin/settings/settings_email.mako:16
8072 8076 msgid "SMTP use TLS"
8073 8077 msgstr ""
8074 8078
8075 8079 #: rhodecode/templates/admin/settings/settings_email.mako:17
8076 8080 msgid "SMTP use SSL"
8077 8081 msgstr ""
8078 8082
8079 8083 #: rhodecode/templates/admin/settings/settings_email.mako:28
8080 8084 msgid "You can adjust those settings in [DEFAULT] section of .ini file located at"
8081 8085 msgstr ""
8082 8086
8083 8087 #: rhodecode/templates/admin/settings/settings_email.mako:36
8084 8088 msgid "Test Email"
8085 8089 msgstr ""
8086 8090
8087 8091 #: rhodecode/templates/admin/settings/settings_email.mako:42
8088 8092 msgid "enter valid email"
8089 8093 msgstr ""
8090 8094
8091 8095 #: rhodecode/templates/admin/settings/settings_email.mako:46
8092 8096 msgid "Send an auto-generated email from this server to above email..."
8093 8097 msgstr ""
8094 8098
8095 8099 #: rhodecode/templates/admin/settings/settings_exceptions.mako:3
8096 8100 msgid "Exceptions Tracker - Exception ID"
8097 8101 msgstr ""
8098 8102
8099 8103 #: rhodecode/templates/admin/settings/settings_exceptions.mako:8
8100 8104 #: rhodecode/templates/email_templates/exception_tracker.mako:14
8101 8105 msgid "Exception `{}` generated on UTC date: {}"
8102 8106 msgstr ""
8103 8107
8104 8108 #: rhodecode/templates/admin/settings/settings_exceptions.mako:21
8105 8109 msgid "Unable to Read Exception. It might be removed or non-existing."
8106 8110 msgstr ""
8107 8111
8108 8112 #: rhodecode/templates/admin/settings/settings_exceptions.mako:30
8109 8113 msgid "Delete this Exception"
8110 8114 msgstr ""
8111 8115
8112 8116 #: rhodecode/templates/admin/settings/settings_exceptions.mako:40
8113 8117 msgid "Delete This Exception"
8114 8118 msgstr ""
8115 8119
8116 8120 #: rhodecode/templates/admin/settings/settings_exceptions_browse.mako:3
8117 8121 msgid "Exceptions Tracker "
8118 8122 msgstr ""
8119 8123
8120 8124 #: rhodecode/templates/admin/settings/settings_exceptions_browse.mako:7
8121 8125 msgid "There is {} stored exception."
8122 8126 msgstr ""
8123 8127
8124 8128 #: rhodecode/templates/admin/settings/settings_exceptions_browse.mako:9
8125 8129 msgid "There are total {} stored exceptions."
8126 8130 msgstr ""
8127 8131
8128 8132 #: rhodecode/templates/admin/settings/settings_exceptions_browse.mako:12
8129 8133 msgid "Store directory"
8130 8134 msgstr ""
8131 8135
8132 8136 #: rhodecode/templates/admin/settings/settings_exceptions_browse.mako:23
8133 8137 msgid "Delete All `{}`"
8134 8138 msgstr ""
8135 8139
8136 8140 #: rhodecode/templates/admin/settings/settings_exceptions_browse.mako:25
8137 8141 msgid "Delete All"
8138 8142 msgstr ""
8139 8143
8140 8144 #: rhodecode/templates/admin/settings/settings_exceptions_browse.mako:39
8141 8145 msgid "Exceptions Tracker - Showing the last {} Exceptions"
8142 8146 msgstr ""
8143 8147
8144 8148 #: rhodecode/templates/admin/settings/settings_global.mako:5
8145 8149 msgid "Branding"
8146 8150 msgstr ""
8147 8151
8148 8152 #: rhodecode/templates/admin/settings/settings_global.mako:16
8149 8153 msgid "Set a custom title for your RhodeCode instance (limited to 40 characters)."
8150 8154 msgstr ""
8151 8155
8152 8156 #: rhodecode/templates/admin/settings/settings_global.mako:20
8153 8157 msgid "HTTP[S] authentication realm"
8154 8158 msgstr ""
8155 8159
8156 8160 #: rhodecode/templates/admin/settings/settings_global.mako:27
8157 8161 msgid "Set a custom text that is shown as authentication message to clients trying to connect."
8158 8162 msgstr ""
8159 8163
8160 8164 #: rhodecode/templates/admin/settings/settings_global.mako:36
8161 8165 #: rhodecode/templates/admin/users/user_edit_advanced.mako:83
8162 8166 msgid "Personal Repository Group"
8163 8167 msgstr ""
8164 8168
8165 8169 #: rhodecode/templates/admin/settings/settings_global.mako:41
8166 8170 msgid "Create Personal Repository Group"
8167 8171 msgstr ""
8168 8172
8169 8173 #: rhodecode/templates/admin/settings/settings_global.mako:44
8170 8174 msgid "Always create Personal Repository Groups for new users."
8171 8175 msgstr ""
8172 8176
8173 8177 #: rhodecode/templates/admin/settings/settings_global.mako:45
8174 8178 msgid "When creating new users from add user form or API you can still turn this off via a checkbox or flag"
8175 8179 msgstr ""
8176 8180
8177 8181 #: rhodecode/templates/admin/settings/settings_global.mako:49
8178 8182 msgid "Personal Repo Group Pattern"
8179 8183 msgstr ""
8180 8184
8181 8185 #: rhodecode/templates/admin/settings/settings_global.mako:55
8182 8186 msgid "Pattern used to create Personal Repository Groups. Prefix can be other existing repository group path[s], eg. /u/${username}"
8183 8187 msgstr ""
8184 8188
8185 8189 #: rhodecode/templates/admin/settings/settings_global.mako:56
8186 8190 msgid "Available variables are currently ${username} and ${user_id}"
8187 8191 msgstr ""
8188 8192
8189 8193 #: rhodecode/templates/admin/settings/settings_global.mako:64
8190 8194 msgid "Registration Captcha"
8191 8195 msgstr ""
8192 8196
8193 8197 #: rhodecode/templates/admin/settings/settings_global.mako:68
8194 8198 msgid "Google reCaptcha v2 site key."
8195 8199 msgstr ""
8196 8200
8197 8201 #: rhodecode/templates/admin/settings/settings_global.mako:75
8198 8202 msgid "Site key for reCaptcha v2 system."
8199 8203 msgstr ""
8200 8204
8201 8205 #: rhodecode/templates/admin/settings/settings_global.mako:80
8202 8206 msgid "Google reCaptcha v2 secret key."
8203 8207 msgstr ""
8204 8208
8205 8209 #: rhodecode/templates/admin/settings/settings_global.mako:87
8206 8210 msgid "Secret key for reCaptcha v2 system. Setting this value will enable captcha on registration and password reset forms."
8207 8211 msgstr ""
8208 8212
8209 8213 #: rhodecode/templates/admin/settings/settings_global.mako:95
8210 8214 msgid "Custom Header Code"
8211 8215 msgstr ""
8212 8216
8213 8217 #: rhodecode/templates/admin/settings/settings_global.mako:100
8214 8218 #: rhodecode/templates/admin/settings/settings_global.mako:124
8215 8219 #: rhodecode/templates/debug_style/form-elements-small.html:59
8216 8220 #: rhodecode/templates/debug_style/form-elements.html:57
8217 8221 #: rhodecode/templates/debug_style/form-elements.html:82
8218 8222 #: rhodecode/templates/debug_style/form-elements.html:225
8219 8223 #: rhodecode/templates/debug_style/form-elements.html:381
8220 8224 #: rhodecode/templates/debug_style/form-elements.html:407
8221 8225 #: rhodecode/templates/debug_style/form-elements.html:515
8222 8226 #: rhodecode/templates/debug_style/form-elements.html:519
8223 8227 #: rhodecode/templates/debug_style/form-elements.html:537
8224 8228 #: rhodecode/templates/debug_style/form-elements.html:581
8225 8229 #: rhodecode/templates/debug_style/form-inline.html:38
8226 8230 #: rhodecode/templates/debug_style/form-inline.html:139
8227 8231 #: rhodecode/templates/debug_style/form-inline.html:147
8228 8232 #: rhodecode/templates/debug_style/form-vertical.html:60
8229 8233 #: rhodecode/templates/debug_style/forms.html:37
8230 8234 #: rhodecode/templates/debug_style/forms.html:60
8231 8235 #: rhodecode/templates/debug_style/forms.html:78
8232 8236 #: rhodecode/templates/debug_style/forms.html:96
8233 8237 #: rhodecode/templates/debug_style/layout-form-sidebar.html:44
8234 8238 msgid "Templates..."
8235 8239 msgstr ""
8236 8240
8237 8241 #: rhodecode/templates/admin/settings/settings_global.mako:103
8238 8242 #: rhodecode/templates/admin/settings/settings_global.mako:127
8239 8243 #: rhodecode/templates/debug_style/form-elements-small.html:62
8240 8244 #: rhodecode/templates/debug_style/form-elements.html:60
8241 8245 #: rhodecode/templates/debug_style/form-elements.html:85
8242 8246 #: rhodecode/templates/debug_style/form-elements.html:228
8243 8247 #: rhodecode/templates/debug_style/form-elements.html:384
8244 8248 #: rhodecode/templates/debug_style/form-elements.html:410
8245 8249 #: rhodecode/templates/debug_style/form-elements.html:518
8246 8250 #: rhodecode/templates/debug_style/form-elements.html:522
8247 8251 #: rhodecode/templates/debug_style/form-elements.html:540
8248 8252 #: rhodecode/templates/debug_style/form-elements.html:584
8249 8253 #: rhodecode/templates/debug_style/form-inline.html:41
8250 8254 #: rhodecode/templates/debug_style/form-inline.html:142
8251 8255 #: rhodecode/templates/debug_style/form-inline.html:150
8252 8256 #: rhodecode/templates/debug_style/form-vertical.html:63
8253 8257 #: rhodecode/templates/debug_style/forms.html:40
8254 8258 #: rhodecode/templates/debug_style/forms.html:63
8255 8259 #: rhodecode/templates/debug_style/forms.html:81
8256 8260 #: rhodecode/templates/debug_style/forms.html:99
8257 8261 #: rhodecode/templates/debug_style/layout-form-sidebar.html:47
8258 8262 msgid "Server Announcement"
8259 8263 msgstr ""
8260 8264
8261 8265 #: rhodecode/templates/admin/settings/settings_global.mako:104
8262 8266 msgid "Flash message filtering"
8263 8267 msgstr ""
8264 8268
8265 8269 #: rhodecode/templates/admin/settings/settings_global.mako:105
8266 8270 msgid "Custom logos"
8267 8271 msgstr ""
8268 8272
8269 8273 #: rhodecode/templates/admin/settings/settings_global.mako:111
8270 8274 msgid "Custom js/css code added at the end of the <head/> tag."
8271 8275 msgstr ""
8272 8276
8273 8277 #: rhodecode/templates/admin/settings/settings_global.mako:112
8274 8278 msgid "Use <script/> or <style/> tags to define custom scripting or styling."
8275 8279 msgstr ""
8276 8280
8277 8281 #: rhodecode/templates/admin/settings/settings_global.mako:119
8278 8282 msgid "Custom Footer Code"
8279 8283 msgstr ""
8280 8284
8281 8285 #: rhodecode/templates/admin/settings/settings_global.mako:133
8282 8286 msgid "Custom js/css code added at the end of the <body> tag."
8283 8287 msgstr ""
8284 8288
8285 8289 #: rhodecode/templates/admin/settings/settings_global.mako:134
8286 8290 msgid "Use <script> or <style> tags to define custom scripting or styling."
8287 8291 msgstr ""
8288 8292
8289 8293 #: rhodecode/templates/admin/settings/settings_hooks.mako:3
8290 8294 msgid "Built in Mercurial hooks - read only"
8291 8295 msgstr ""
8292 8296
8293 8297 #: rhodecode/templates/admin/settings/settings_hooks.mako:19
8294 8298 msgid "Hooks can be used to trigger actions on certain events such as push / pull. They can trigger Python functions or external applications."
8295 8299 msgstr ""
8296 8300
8297 8301 #: rhodecode/templates/admin/settings/settings_hooks.mako:27
8298 8302 msgid "Custom hooks"
8299 8303 msgstr ""
8300 8304
8301 8305 #: rhodecode/templates/admin/settings/settings_labs.mako:3
8302 8306 msgid "Labs Settings"
8303 8307 msgstr ""
8304 8308
8305 8309 #: rhodecode/templates/admin/settings/settings_labs.mako:10
8306 8310 msgid "There are no Labs settings currently"
8307 8311 msgstr ""
8308 8312
8309 8313 #: rhodecode/templates/admin/settings/settings_mapping.mako:5
8310 8314 msgid "Import New Groups or Repositories"
8311 8315 msgstr ""
8312 8316
8313 8317 #: rhodecode/templates/admin/settings/settings_mapping.mako:10
8314 8318 msgid "Destroy old data"
8315 8319 msgstr ""
8316 8320
8317 8321 #: rhodecode/templates/admin/settings/settings_mapping.mako:12
8318 8322 msgid "In case a repository or a group was deleted from the filesystem and it still exists in the database, check this option to remove obsolete data from the database."
8319 8323 msgstr ""
8320 8324
8321 8325 #: rhodecode/templates/admin/settings/settings_mapping.mako:16
8322 8326 msgid "Invalidate cache for all repositories"
8323 8327 msgstr ""
8324 8328
8325 8329 #: rhodecode/templates/admin/settings/settings_mapping.mako:18
8326 8330 msgid "Each cache data for repositories will be cleaned with this option selected. Use this to reload data and clear cache keys."
8327 8331 msgstr ""
8328 8332
8329 8333 #: rhodecode/templates/admin/settings/settings_mapping.mako:21
8330 8334 msgid "Rescan Filesystem"
8331 8335 msgstr ""
8332 8336
8333 8337 #: rhodecode/templates/admin/settings/settings_open_source.mako:11
8334 8338 msgid "Licenses of Third Party Packages"
8335 8339 msgstr ""
8336 8340
8337 8341 #: rhodecode/templates/admin/settings/settings_process_management.mako:3
8338 8342 #: rhodecode/templates/admin/settings/settings_system.mako:3
8339 8343 msgid "Checking for updates..."
8340 8344 msgstr ""
8341 8345
8342 8346 #: rhodecode/templates/admin/settings/settings_process_management.mako:9
8343 8347 msgid "Gunicorn process management"
8344 8348 msgstr ""
8345 8349
8346 8350 #: rhodecode/templates/admin/settings/settings_process_management.mako:11
8347 8351 msgid "start auto refresh"
8348 8352 msgstr ""
8349 8353
8350 8354 #: rhodecode/templates/admin/settings/settings_process_management.mako:12
8351 8355 msgid "stop auto refresh"
8352 8356 msgstr ""
8353 8357
8354 8358 #: rhodecode/templates/admin/settings/settings_search.mako:3
8355 8359 msgid "RhodeCode Full Text Search"
8356 8360 msgstr ""
8357 8361
8358 8362 #: rhodecode/templates/admin/settings/settings_sessions.mako:3
8359 8363 msgid "User Sessions Configuration"
8360 8364 msgstr ""
8361 8365
8362 8366 #: rhodecode/templates/admin/settings/settings_sessions.mako:8
8363 8367 msgid "Session type"
8364 8368 msgstr ""
8365 8369
8366 8370 #: rhodecode/templates/admin/settings/settings_sessions.mako:9
8367 8371 msgid "Session expiration period"
8368 8372 msgstr ""
8369 8373
8370 8374 #: rhodecode/templates/admin/settings/settings_sessions.mako:11
8371 8375 msgid "Total sessions"
8372 8376 msgstr ""
8373 8377
8374 8378 #: rhodecode/templates/admin/settings/settings_sessions.mako:12
8375 8379 msgid "Expired sessions ({} days)"
8376 8380 msgstr ""
8377 8381
8378 8382 #: rhodecode/templates/admin/settings/settings_sessions.mako:28
8379 8383 msgid "Cleanup Old Sessions"
8380 8384 msgstr ""
8381 8385
8382 8386 #: rhodecode/templates/admin/settings/settings_sessions.mako:34
8383 8387 msgid "Cleanup user sessions that were not active during chosen time frame."
8384 8388 msgstr ""
8385 8389
8386 8390 #: rhodecode/templates/admin/settings/settings_sessions.mako:35
8387 8391 msgid "After performing this action users whose session will be removed will be required to log in again."
8388 8392 msgstr ""
8389 8393
8390 8394 #: rhodecode/templates/admin/settings/settings_sessions.mako:36
8391 8395 msgid "Picking `All` will log-out you, and all users in the system."
8392 8396 msgstr ""
8393 8397
8394 8398 #: rhodecode/templates/admin/settings/settings_sessions.mako:55
8395 8399 msgid "Confirm to cleanup user sessions"
8396 8400 msgstr ""
8397 8401
8398 8402 #: rhodecode/templates/admin/settings/settings_sessions.mako:56
8399 8403 msgid "Cleanup sessions"
8400 8404 msgstr ""
8401 8405
8402 8406 #: rhodecode/templates/admin/settings/settings_system.mako:9
8403 8407 msgid "System Info"
8404 8408 msgstr ""
8405 8409
8406 8410 #: rhodecode/templates/admin/settings/settings_system.mako:11
8407 8411 msgid "create summary snapshot"
8408 8412 msgstr ""
8409 8413
8410 8414 #: rhodecode/templates/admin/settings/settings_system.mako:46
8411 8415 msgid "Python Packages"
8412 8416 msgstr ""
8413 8417
8414 8418 #: rhodecode/templates/admin/settings/settings_visual.mako:5
8415 8419 #: rhodecode/templates/base/vcs_settings.mako:10
8416 8420 msgid "General"
8417 8421 msgstr ""
8418 8422
8419 8423 #: rhodecode/templates/admin/settings/settings_visual.mako:10
8420 8424 msgid "Use repository extra fields"
8421 8425 msgstr ""
8422 8426
8423 8427 #: rhodecode/templates/admin/settings/settings_visual.mako:12
8424 8428 msgid "Allows storing additional customized fields per repository."
8425 8429 msgstr ""
8426 8430
8427 8431 #: rhodecode/templates/admin/settings/settings_visual.mako:17
8428 8432 msgid "Show RhodeCode version"
8429 8433 msgstr ""
8430 8434
8431 8435 #: rhodecode/templates/admin/settings/settings_visual.mako:19
8432 8436 msgid "Shows or hides a version number of RhodeCode displayed in the footer."
8433 8437 msgstr ""
8434 8438
8435 8439 #: rhodecode/templates/admin/settings/settings_visual.mako:26
8436 8440 msgid "Gravatars"
8437 8441 msgstr ""
8438 8442
8439 8443 #: rhodecode/templates/admin/settings/settings_visual.mako:31
8440 8444 msgid "Use Gravatars based avatars"
8441 8445 msgstr ""
8442 8446
8443 8447 #: rhodecode/templates/admin/settings/settings_visual.mako:33
8444 8448 msgid "Use gravatar.com as avatar system for RhodeCode accounts. If this is disabled avatars are generated based on initials and email."
8445 8449 msgstr ""
8446 8450
8447 8451 #: rhodecode/templates/admin/settings/settings_visual.mako:36
8448 8452 msgid "Gravatar URL"
8449 8453 msgstr ""
8450 8454
8451 8455 #: rhodecode/templates/admin/settings/settings_visual.mako:44
8452 8456 msgid ""
8453 8457 "Gravatar url allows you to use other avatar server application.\n"
8454 8458 " Following variables of the URL will be replaced accordingly.\n"
8455 8459 " {scheme} 'http' or 'https' sent from running RhodeCode server,\n"
8456 8460 " {email} user email,\n"
8457 8461 " {md5email} md5 hash of the user email (like at gravatar.com),\n"
8458 8462 " {size} size of the image that is expected from the server application,\n"
8459 8463 " {netloc} network location/server host of running RhodeCode server"
8460 8464 msgstr ""
8461 8465
8462 8466 #: rhodecode/templates/admin/settings/settings_visual.mako:59
8463 8467 msgid "Meta-Tagging"
8464 8468 msgstr ""
8465 8469
8466 8470 #: rhodecode/templates/admin/settings/settings_visual.mako:64
8467 8471 msgid "Stylify recognised meta tags"
8468 8472 msgstr ""
8469 8473
8470 8474 #: rhodecode/templates/admin/settings/settings_visual.mako:66
8471 8475 msgid "Parses meta tags from repository or repository group description fields and turns them into colored tags."
8472 8476 msgstr ""
8473 8477
8474 8478 #: rhodecode/templates/admin/settings/settings_visual.mako:77
8475 8479 msgid "Dashboard Items"
8476 8480 msgstr ""
8477 8481
8478 8482 #: rhodecode/templates/admin/settings/settings_visual.mako:81
8479 8483 msgid "Main page dashboard items"
8480 8484 msgstr ""
8481 8485
8482 8486 #: rhodecode/templates/admin/settings/settings_visual.mako:87
8483 8487 msgid "Number of items displayed in the main page dashboard before pagination is shown."
8484 8488 msgstr ""
8485 8489
8486 8490 #: rhodecode/templates/admin/settings/settings_visual.mako:91
8487 8491 msgid "Admin pages items"
8488 8492 msgstr ""
8489 8493
8490 8494 #: rhodecode/templates/admin/settings/settings_visual.mako:97
8491 8495 msgid "Number of items displayed in the admin pages grids before pagination is shown."
8492 8496 msgstr ""
8493 8497
8494 8498 #: rhodecode/templates/admin/settings/settings_visual.mako:106
8495 8499 msgid "Commit ID Style"
8496 8500 msgstr ""
8497 8501
8498 8502 #: rhodecode/templates/admin/settings/settings_visual.mako:110
8499 8503 msgid "Commit sha length"
8500 8504 msgstr ""
8501 8505
8502 8506 #: rhodecode/templates/admin/settings/settings_visual.mako:117
8503 8507 msgid ""
8504 8508 "Number of chars to show in commit sha displayed in web interface.\n"
8505 8509 " By default it's shown as r123:9043a6a4c226 this value defines the\n"
8506 8510 " length of the sha after the `r123:` part."
8507 8511 msgstr ""
8508 8512
8509 8513 #: rhodecode/templates/admin/settings/settings_visual.mako:125
8510 8514 msgid "Show commit ID numeric reference"
8511 8515 msgstr ""
8512 8516
8513 8517 #: rhodecode/templates/admin/settings/settings_visual.mako:125
8514 8518 msgid "Commit show revision number"
8515 8519 msgstr ""
8516 8520
8517 8521 #: rhodecode/templates/admin/settings/settings_visual.mako:127
8518 8522 msgid ""
8519 8523 "Show revision number in commit sha displayed in web interface.\n"
8520 8524 " By default it's shown as r123:9043a6a4c226 this value defines the\n"
8521 8525 " if the `r123:` part is shown."
8522 8526 msgstr ""
8523 8527
8524 8528 #: rhodecode/templates/admin/settings/settings_visual.mako:136
8525 8529 #: rhodecode/templates/debug_style/index.html:64
8526 8530 msgid "Icons"
8527 8531 msgstr ""
8528 8532
8529 8533 #: rhodecode/templates/admin/settings/settings_visual.mako:141
8530 8534 msgid "Show public repo icon on repositories"
8531 8535 msgstr ""
8532 8536
8533 8537 #: rhodecode/templates/admin/settings/settings_visual.mako:147
8534 8538 msgid "Show private repo icon on repositories"
8535 8539 msgstr ""
8536 8540
8537 8541 #: rhodecode/templates/admin/settings/settings_visual.mako:149
8538 8542 msgid "Show public/private icons next to repositories names."
8539 8543 msgstr ""
8540 8544
8541 8545 #: rhodecode/templates/admin/settings/settings_visual.mako:156
8542 8546 msgid "Markup Renderer"
8543 8547 msgstr ""
8544 8548
8545 8549 #: rhodecode/templates/admin/settings/settings_visual.mako:163
8546 8550 msgid "Default renderer used to render comments, pull request descriptions and other description elements. After change old entries will still work correctly."
8547 8551 msgstr ""
8548 8552
8549 8553 #: rhodecode/templates/admin/settings/settings_visual.mako:170
8550 8554 msgid "Clone URL templates"
8551 8555 msgstr ""
8552 8556
8553 #: rhodecode/templates/admin/settings/settings_visual.mako:181
8557 #: rhodecode/templates/admin/settings/settings_visual.mako:184
8554 8558 msgid ""
8555 8559 "Schema of clone url construction eg. '{scheme}://{user}@{netloc}/{repo}', available vars:\n"
8556 8560 " {scheme} 'http' or 'https' sent from running RhodeCode server,\n"
8557 8561 " {user} current user username,\n"
8558 8562 " {sys_user} current system user running this process, Useful for ssh,\n"
8559 8563 " {hostname} hostname of this server running RhodeCode,\n"
8560 8564 " {netloc} network location/server host of running RhodeCode server,\n"
8561 8565 " {repo} full repository name,\n"
8562 8566 " {repoid} ID of repository, can be used to contruct clone-by-id"
8563 8567 msgstr ""
8564 8568
8565 #: rhodecode/templates/admin/settings/settings_visual.mako:196
8569 #: rhodecode/templates/admin/settings/settings_visual.mako:199
8566 8570 msgid "Custom Support Link"
8567 8571 msgstr ""
8568 8572
8569 #: rhodecode/templates/admin/settings/settings_visual.mako:204
8573 #: rhodecode/templates/admin/settings/settings_visual.mako:207
8570 8574 #, python-format
8571 8575 msgid ""
8572 8576 "Custom url for the support link located at the bottom.\n"
8573 8577 " The default is set to %(default_url)s. In case there's a need\n"
8574 8578 " to change the support link to internal issue tracker, it should be done here.\n"
8575 8579 " "
8576 8580 msgstr ""
8577 8581
8578 8582 #: rhodecode/templates/admin/user_groups/user_group_add.mako:5
8579 8583 msgid "Add user group"
8580 8584 msgstr ""
8581 8585
8582 8586 #: rhodecode/templates/admin/user_groups/user_group_add.mako:13
8583 8587 #: rhodecode/templates/admin/users/user_edit_advanced.mako:14
8584 8588 #: rhodecode/templates/base/base.mako:117
8585 #: rhodecode/templates/base/base.mako:139
8589 #: rhodecode/templates/base/base.mako:140
8586 8590 msgid "User groups"
8587 8591 msgstr ""
8588 8592
8589 8593 #: rhodecode/templates/admin/user_groups/user_group_add.mako:46
8590 8594 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:50
8591 8595 msgid "Short, optional description for this user group."
8592 8596 msgstr ""
8593 8597
8594 8598 #: rhodecode/templates/admin/user_groups/user_group_add.mako:59
8595 8599 msgid "Create User Group"
8596 8600 msgstr ""
8597 8601
8598 8602 #: rhodecode/templates/admin/user_groups/user_group_edit.mako:5
8599 8603 msgid "{} user group settings"
8600 8604 msgstr ""
8601 8605
8602 8606 #: rhodecode/templates/admin/user_groups/user_group_edit.mako:37
8603 8607 #: rhodecode/templates/admin/users/user_edit.mako:43
8604 8608 msgid "Global permissions"
8605 8609 msgstr ""
8606 8610
8607 8611 #: rhodecode/templates/admin/user_groups/user_group_edit.mako:38
8608 8612 #: rhodecode/templates/admin/users/user_edit.mako:44
8609 8613 msgid "Permissions summary"
8610 8614 msgstr ""
8611 8615
8612 8616 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:5
8613 8617 msgid "User Group ID"
8614 8618 msgstr ""
8615 8619
8616 8620 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:9
8617 8621 #: rhodecode/templates/admin/user_groups/user_groups.mako:80
8618 8622 #: rhodecode/templates/debug_style/form-elements.html:509
8619 8623 #: rhodecode/templates/user_group/profile.mako:55
8620 8624 msgid "Members"
8621 8625 msgstr ""
8622 8626
8623 8627 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:10
8624 8628 msgid "Automatic member sync"
8625 8629 msgstr ""
8626 8630
8627 8631 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:12
8628 8632 msgid "Assigned to repositories"
8629 8633 msgstr ""
8630 8634
8631 8635 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:13
8632 8636 msgid "Assigned to repo groups"
8633 8637 msgstr ""
8634 8638
8635 8639 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:15
8636 8640 #: rhodecode/templates/admin/users/user_edit_advanced.mako:19
8637 8641 msgid "Assigned to review rules"
8638 8642 msgstr ""
8639 8643
8640 8644 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:22
8641 8645 #: rhodecode/templates/admin/user_groups/user_group_edit_perms.mako:6
8642 8646 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:7
8643 8647 #: rhodecode/templates/data_table/_dt_elements.mako:305
8644 8648 #: rhodecode/templates/user_group/user_group.mako:4
8645 8649 msgid "User group"
8646 8650 msgstr ""
8647 8651
8648 8652 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:35
8649 8653 msgid "Group members sync"
8650 8654 msgstr ""
8651 8655
8652 8656 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:42
8653 8657 msgid "This group is set to be automatically synchronised."
8654 8658 msgstr ""
8655 8659
8656 8660 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:43
8657 8661 msgid "This group synchronization was set by"
8658 8662 msgstr ""
8659 8663
8660 8664 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:47
8661 8665 msgid "This group is not set to be automatically synchronised"
8662 8666 msgstr ""
8663 8667
8664 8668 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:56
8665 8669 msgid "Disable synchronization"
8666 8670 msgstr ""
8667 8671
8668 8672 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:58
8669 8673 msgid "Enable synchronization"
8670 8674 msgstr ""
8671 8675
8672 8676 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:64
8673 8677 msgid "Users will be added or removed from this group when they authenticate with RhodeCode system, based on LDAP group membership. This requires `LDAP+User group` authentication plugin to be configured and enabled. (EE only feature)"
8674 8678 msgstr ""
8675 8679
8676 8680 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:77
8677 8681 msgid "Delete User Group"
8678 8682 msgstr ""
8679 8683
8680 8684 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:83
8681 8685 #, python-format
8682 8686 msgid "Confirm to delete user group `%(ugroup)s` with all permission assignments"
8683 8687 msgstr ""
8684 8688
8685 8689 #: rhodecode/templates/admin/user_groups/user_group_edit_advanced.mako:85
8686 8690 msgid "Delete This User Group"
8687 8691 msgstr ""
8688 8692
8689 8693 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:40
8690 8694 msgid "Change owner of this user group."
8691 8695 msgstr ""
8692 8696
8693 8697 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:64
8694 8698 msgid "Add members"
8695 8699 msgstr ""
8696 8700
8697 8701 #: rhodecode/templates/admin/user_groups/user_group_edit_settings.mako:100
8698 8702 #: rhodecode/templates/user_group/profile.mako:78
8699 8703 msgid "No members yet"
8700 8704 msgstr ""
8701 8705
8702 8706 #: rhodecode/templates/admin/user_groups/user_groups.mako:5
8703 8707 #: rhodecode/templates/admin/users/user_edit_groups.mako:8
8704 8708 msgid "User groups administration"
8705 8709 msgstr ""
8706 8710
8707 8711 #: rhodecode/templates/admin/user_groups/user_groups.mako:82
8708 8712 msgid "Sync"
8709 8713 msgstr ""
8710 8714
8711 8715 #: rhodecode/templates/admin/users/user_add.mako:5
8712 8716 msgid "Add user"
8713 8717 msgstr ""
8714 8718
8715 8719 #: rhodecode/templates/admin/users/user_add.mako:13
8716 8720 #: rhodecode/templates/admin/users/user_edit.mako:14
8717 8721 #: rhodecode/templates/base/base.mako:116
8718 8722 msgid "Users"
8719 8723 msgstr ""
8720 8724
8721 8725 #: rhodecode/templates/admin/users/user_add.mako:15
8722 8726 #: rhodecode/templates/admin/users/users.mako:31
8723 8727 msgid "Add User"
8724 8728 msgstr ""
8725 8729
8726 8730 #: rhodecode/templates/admin/users/user_add.mako:53
8727 8731 msgid "Password confirmation"
8728 8732 msgstr ""
8729 8733
8730 8734 #: rhodecode/templates/admin/users/user_add.mako:59
8731 8735 msgid "Generate password"
8732 8736 msgstr ""
8733 8737
8734 8738 #: rhodecode/templates/admin/users/user_add.mako:106
8735 8739 msgid "Password change"
8736 8740 msgstr ""
8737 8741
8738 8742 #: rhodecode/templates/admin/users/user_add.mako:110
8739 8743 msgid "Force user to change his password on the next login"
8740 8744 msgstr ""
8741 8745
8742 8746 #: rhodecode/templates/admin/users/user_add.mako:116
8743 8747 msgid "Add personal repository group"
8744 8748 msgstr ""
8745 8749
8746 8750 #: rhodecode/templates/admin/users/user_add.mako:121
8747 8751 msgid "New group will be created at: `/{path}`"
8748 8752 msgstr ""
8749 8753
8750 8754 #: rhodecode/templates/admin/users/user_add.mako:122
8751 8755 msgid "User will be automatically set as this group owner."
8752 8756 msgstr ""
8753 8757
8754 8758 #: rhodecode/templates/admin/users/user_add.mako:128
8755 8759 msgid "Create User"
8756 8760 msgstr ""
8757 8761
8758 8762 #: rhodecode/templates/admin/users/user_add.mako:139
8759 8763 msgid "generated password:"
8760 8764 msgstr ""
8761 8765
8762 8766 #: rhodecode/templates/admin/users/user_edit.mako:5
8763 8767 msgid "{} user settings"
8764 8768 msgstr ""
8765 8769
8766 8770 #: rhodecode/templates/admin/users/user_edit.mako:31
8767 8771 msgid "This user is set as non-active and disabled."
8768 8772 msgstr ""
8769 8773
8770 8774 #: rhodecode/templates/admin/users/user_edit.mako:39
8771 8775 #: rhodecode/templates/admin/users/user_edit_profile.mako:7
8772 8776 #: rhodecode/templates/users/user_profile.mako:6
8773 8777 msgid "User Profile"
8774 8778 msgstr ""
8775 8779
8776 8780 #: rhodecode/templates/admin/users/user_edit.mako:40
8777 8781 msgid "Auth tokens"
8778 8782 msgstr ""
8779 8783
8780 8784 #: rhodecode/templates/admin/users/user_edit.mako:46
8781 8785 msgid "Ip Whitelist"
8782 8786 msgstr ""
8783 8787
8784 8788 #: rhodecode/templates/admin/users/user_edit.mako:47
8785 8789 msgid "User Groups Management"
8786 8790 msgstr ""
8787 8791
8788 8792 #: rhodecode/templates/admin/users/user_edit_advanced.mako:5
8789 8793 msgid "User ID"
8790 8794 msgstr ""
8791 8795
8792 8796 #: rhodecode/templates/admin/users/user_edit_advanced.mako:7
8793 8797 msgid "Source of Record"
8794 8798 msgstr ""
8795 8799
8796 8800 #: rhodecode/templates/admin/users/user_edit_advanced.mako:9
8797 8801 msgid "Last login"
8798 8802 msgstr ""
8799 8803
8800 8804 #: rhodecode/templates/admin/users/user_edit_advanced.mako:10
8801 8805 #: rhodecode/templates/admin/users/users.mako:83
8802 8806 #: rhodecode/templates/forks/forks.mako:67
8803 8807 msgid "Last activity"
8804 8808 msgstr ""
8805 8809
8806 8810 #: rhodecode/templates/admin/users/user_edit_advanced.mako:16
8807 8811 msgid "Owned Artifacts"
8808 8812 msgstr ""
8809 8813
8810 8814 #: rhodecode/templates/admin/users/user_edit_advanced.mako:18
8811 8815 msgid "Reviewer of pull requests"
8812 8816 msgstr ""
8813 8817
8814 8818 #: rhodecode/templates/admin/users/user_edit_advanced.mako:21
8815 8819 msgid "Member of User groups"
8816 8820 msgstr ""
8817 8821
8818 8822 #: rhodecode/templates/admin/users/user_edit_advanced.mako:22
8819 8823 msgid "Force password change"
8820 8824 msgstr ""
8821 8825
8822 8826 #: rhodecode/templates/admin/users/user_edit_advanced.mako:49
8823 8827 msgid "Force Password Reset"
8824 8828 msgstr ""
8825 8829
8826 8830 #: rhodecode/templates/admin/users/user_edit_advanced.mako:55
8827 8831 msgid "Disable forced password reset"
8828 8832 msgstr ""
8829 8833
8830 8834 #: rhodecode/templates/admin/users/user_edit_advanced.mako:60
8831 8835 msgid "Clear the forced password change flag."
8832 8836 msgstr ""
8833 8837
8834 8838 #: rhodecode/templates/admin/users/user_edit_advanced.mako:67
8835 8839 msgid "Confirm to enable forced password change"
8836 8840 msgstr ""
8837 8841
8838 8842 #: rhodecode/templates/admin/users/user_edit_advanced.mako:68
8839 8843 msgid "Enable forced password reset"
8840 8844 msgstr ""
8841 8845
8842 8846 #: rhodecode/templates/admin/users/user_edit_advanced.mako:73
8843 8847 msgid "When this is enabled user will have to change they password when they next use RhodeCode system. This will also forbid vcs operations until someone makes a password change in the web interface"
8844 8848 msgstr ""
8845 8849
8846 8850 #: rhodecode/templates/admin/users/user_edit_advanced.mako:89
8847 8851 msgid "Users personal repository group"
8848 8852 msgstr ""
8849 8853
8850 8854 #: rhodecode/templates/admin/users/user_edit_advanced.mako:92
8851 8855 msgid "This user currently does not have a personal repository group"
8852 8856 msgstr ""
8853 8857
8854 8858 #: rhodecode/templates/admin/users/user_edit_advanced.mako:94
8855 8859 #, python-format
8856 8860 msgid "New group will be created at: `/%(path)s`"
8857 8861 msgstr ""
8858 8862
8859 8863 #: rhodecode/templates/admin/users/user_edit_advanced.mako:99
8860 8864 msgid "Create personal repository group"
8861 8865 msgstr ""
8862 8866
8863 8867 #: rhodecode/templates/admin/users/user_edit_advanced.mako:108
8864 8868 msgid "Delete User"
8865 8869 msgstr ""
8866 8870
8867 8871 #: rhodecode/templates/admin/users/user_edit_advanced.mako:119
8868 8872 msgid "Detach repositories"
8869 8873 msgstr ""
8870 8874
8871 8875 #: rhodecode/templates/admin/users/user_edit_advanced.mako:122
8872 8876 #: rhodecode/templates/admin/users/user_edit_advanced.mako:134
8873 8877 #: rhodecode/templates/admin/users/user_edit_advanced.mako:146
8874 8878 msgid "Delete repositories"
8875 8879 msgstr ""
8876 8880
8877 8881 #: rhodecode/templates/admin/users/user_edit_advanced.mako:131
8878 8882 msgid "Detach repository groups"
8879 8883 msgstr ""
8880 8884
8881 8885 #: rhodecode/templates/admin/users/user_edit_advanced.mako:143
8882 8886 msgid "Detach user groups"
8883 8887 msgstr ""
8884 8888
8885 8889 #: rhodecode/templates/admin/users/user_edit_advanced.mako:155
8886 8890 msgid "Detach pull requests"
8887 8891 msgstr ""
8888 8892
8889 8893 #: rhodecode/templates/admin/users/user_edit_advanced.mako:158
8890 8894 msgid "Delete pull requests"
8891 8895 msgstr ""
8892 8896
8893 8897 #: rhodecode/templates/admin/users/user_edit_advanced.mako:167
8894 8898 msgid "Detach Artifacts"
8895 8899 msgstr ""
8896 8900
8897 8901 #: rhodecode/templates/admin/users/user_edit_advanced.mako:170
8898 8902 msgid "Delete Artifacts"
8899 8903 msgstr ""
8900 8904
8901 8905 #: rhodecode/templates/admin/users/user_edit_advanced.mako:180
8902 8906 msgid "New owner for detached objects"
8903 8907 msgstr ""
8904 8908
8905 8909 #: rhodecode/templates/admin/users/user_edit_advanced.mako:188
8906 8910 msgid "When selecting the detach option, the depending objects owned by this user will be assigned to the above user."
8907 8911 msgstr ""
8908 8912
8909 8913 #: rhodecode/templates/admin/users/user_edit_advanced.mako:190
8910 8914 msgid "The delete option will delete the user and all his owned objects!"
8911 8915 msgstr ""
8912 8916
8913 8917 #: rhodecode/templates/admin/users/user_edit_advanced.mako:205
8914 8918 msgid "Delete this user"
8915 8919 msgstr ""
8916 8920
8917 8921 #: rhodecode/templates/admin/users/user_edit_audit.mako:9
8918 8922 msgid "Audit Logs"
8919 8923 msgstr ""
8920 8924
8921 8925 #: rhodecode/templates/admin/users/user_edit_audit.mako:12
8922 8926 msgid "Download as JSON"
8923 8927 msgstr ""
8924 8928
8925 8929 #: rhodecode/templates/admin/users/user_edit_auth_tokens.mako:68
8926 8930 #, python-format
8927 8931 msgid "Confirm to remove this auth token: %s"
8928 8932 msgstr ""
8929 8933
8930 8934 #: rhodecode/templates/admin/users/user_edit_caches.mako:36
8931 8935 msgid "Invalidate user cache"
8932 8936 msgstr ""
8933 8937
8934 8938 #: rhodecode/templates/admin/users/user_edit_caches.mako:36
8935 8939 msgid "Confirm to invalidate user cache"
8936 8940 msgstr ""
8937 8941
8938 8942 #: rhodecode/templates/admin/users/user_edit_emails.mako:7
8939 8943 msgid "Additional Email Addresses"
8940 8944 msgstr ""
8941 8945
8942 8946 #: rhodecode/templates/admin/users/user_edit_emails.mako:33
8943 8947 #, python-format
8944 8948 msgid "Confirm to delete this email: %s"
8945 8949 msgstr ""
8946 8950
8947 8951 #: rhodecode/templates/admin/users/user_edit_emails.mako:58
8948 8952 msgid "New email address"
8949 8953 msgstr ""
8950 8954
8951 8955 #: rhodecode/templates/admin/users/user_edit_groups.mako:15
8952 8956 #, python-format
8953 8957 msgid "Add `%s` to user group"
8954 8958 msgstr ""
8955 8959
8956 8960 #: rhodecode/templates/admin/users/user_edit_ips.mako:7
8957 8961 msgid "Custom IP Whitelist"
8958 8962 msgstr ""
8959 8963
8960 8964 #: rhodecode/templates/admin/users/user_edit_ips.mako:15
8961 8965 msgid "IP Address"
8962 8966 msgstr ""
8963 8967
8964 8968 #: rhodecode/templates/admin/users/user_edit_ips.mako:16
8965 8969 msgid "IP Range"
8966 8970 msgstr ""
8967 8971
8968 8972 #: rhodecode/templates/admin/users/user_edit_ips.mako:25
8969 8973 #, python-format
8970 8974 msgid "Inherited from %s"
8971 8975 msgstr ""
8972 8976
8973 8977 #: rhodecode/templates/admin/users/user_edit_ips.mako:69
8974 8978 msgid ""
8975 8979 "Enter comma separated list of ip addresses like 10.0.0.1,10.0.0.2.\n"
8976 8980 "Use a ip address with a mask 127.0.0.1/24, to create a network match pattern.\n"
8977 8981 "To specify multiple entries on an address range use 127.0.0.1-127.0.0.10 syntax"
8978 8982 msgstr ""
8979 8983
8980 8984 #: rhodecode/templates/admin/users/user_edit_profile.mako:31
8981 8985 msgid "Change the avatar at"
8982 8986 msgstr ""
8983 8987
8984 8988 #: rhodecode/templates/admin/users/user_edit_profile.mako:94
8985 8989 msgid "New Password"
8986 8990 msgstr ""
8987 8991
8988 8992 #: rhodecode/templates/admin/users/user_edit_profile.mako:102
8989 8993 msgid "New Password Confirmation"
8990 8994 msgstr ""
8991 8995
8992 8996 #: rhodecode/templates/admin/users/user_edit_profile.mako:118
8993 8997 #: rhodecode/templates/admin/users/users.mako:87
8994 8998 #: rhodecode/templates/base/perms_summary.mako:150
8995 8999 msgid "Super-admin"
8996 9000 msgstr ""
8997 9001
8998 9002 #: rhodecode/templates/admin/users/user_edit_profile.mako:126
8999 9003 msgid "Authentication type"
9000 9004 msgstr ""
9001 9005
9002 9006 #: rhodecode/templates/admin/users/user_edit_profile.mako:130
9003 9007 msgid "When user was created using an external source. He is bound to authentication using this method."
9004 9008 msgstr ""
9005 9009
9006 9010 #: rhodecode/templates/admin/users/user_edit_profile.mako:135
9007 9011 msgid "Name in Source of Record"
9008 9012 msgstr ""
9009 9013
9010 9014 #: rhodecode/templates/admin/users/user_edit_profile.mako:144
9011 9015 msgid "Language"
9012 9016 msgstr ""
9013 9017
9014 9018 #: rhodecode/templates/admin/users/user_edit_profile.mako:150
9015 9019 #, python-format
9016 9020 msgid "User interface language. Help translate %(rc_link)s into your language."
9017 9021 msgstr ""
9018 9022
9019 9023 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:34
9020 9024 #, python-format
9021 9025 msgid "Confirm to remove ssh key %s"
9022 9026 msgstr ""
9023 9027
9024 9028 #: rhodecode/templates/admin/users/user_edit_ssh_keys.mako:75
9025 9029 msgid "Click add to use this generate SSH key"
9026 9030 msgstr ""
9027 9031
9028 9032 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:7
9029 9033 msgid "New SSH Key generation"
9030 9034 msgstr ""
9031 9035
9032 9036 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:13
9033 9037 msgid "Below is a 2048 bit generated SSH RSA key."
9034 9038 msgstr ""
9035 9039
9036 9040 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:13
9037 9041 msgid "If you use older systems please try to generate a"
9038 9042 msgstr ""
9039 9043
9040 9044 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:13
9041 9045 msgid "legacy format"
9042 9046 msgstr ""
9043 9047
9044 9048 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:14
9045 9049 msgid "If You wish to use it to access RhodeCode via the SSH please save the private key and click `Use this generated key` at the bottom."
9046 9050 msgstr ""
9047 9051
9048 9052 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:16
9049 9053 msgid "Private key"
9050 9054 msgstr ""
9051 9055
9052 9056 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:32
9053 9057 msgid "Public key"
9054 9058 msgstr ""
9055 9059
9056 9060 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:43
9057 9061 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:45
9058 9062 msgid "Use this generated key"
9059 9063 msgstr ""
9060 9064
9061 9065 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:47
9062 9066 msgid "Confirmation required on the next screen"
9063 9067 msgstr ""
9064 9068
9065 9069 #: rhodecode/templates/admin/users/user_edit_ssh_keys_generate.mako:51
9066 9070 msgid "SSH key generator has been disabled."
9067 9071 msgstr ""
9068 9072
9069 9073 #: rhodecode/templates/admin/users/users.mako:5
9070 9074 msgid "Users administration"
9071 9075 msgstr ""
9072 9076
9073 9077 #: rhodecode/templates/admin/users/users.mako:89
9074 9078 msgid "Auth type"
9075 9079 msgstr ""
9076 9080
9077 9081 #: rhodecode/templates/artifacts/artifact_list.mako:5
9078 9082 msgid "{} Artifacts"
9079 9083 msgstr ""
9080 9084
9081 9085 #: rhodecode/templates/base/base.mako:73
9082 9086 msgid "RhodeCode instance id: {}"
9083 9087 msgstr ""
9084 9088
9085 9089 #: rhodecode/templates/base/base.mako:101
9086 9090 msgid "Super-admin Panel"
9087 9091 msgstr ""
9088 9092
9089 9093 #: rhodecode/templates/base/base.mako:103
9090 9094 msgid "Delegated Admin Panel"
9091 9095 msgstr ""
9092 9096
9093 #: rhodecode/templates/base/base.mako:119
9097 #: rhodecode/templates/base/base.mako:118
9098 #: rhodecode/templates/base/base.mako:395
9099 msgid "Artifacts"
9100 msgstr ""
9101
9102 #: rhodecode/templates/base/base.mako:120
9094 9103 msgid "Authentication"
9095 9104 msgstr ""
9096 9105
9097 #: rhodecode/templates/base/base.mako:121
9106 #: rhodecode/templates/base/base.mako:122
9098 9107 msgid "Defaults"
9099 9108 msgstr ""
9100 9109
9101 #: rhodecode/templates/base/base.mako:161
9102 #: rhodecode/templates/base/base.mako:201
9110 #: rhodecode/templates/base/base.mako:162
9111 #: rhodecode/templates/base/base.mako:202
9103 9112 #: rhodecode/templates/changeset/changeset.mako:140
9104 9113 #: rhodecode/templates/files/files_source_header.mako:57
9105 9114 #: rhodecode/templates/files/files_tree_header.mako:44
9106 9115 #: rhodecode/templates/summary/components.mako:274
9107 9116 msgid "Show More"
9108 9117 msgstr ""
9109 9118
9110 #: rhodecode/templates/base/base.mako:304
9111 #: rhodecode/templates/base/base.mako:315
9119 #: rhodecode/templates/base/base.mako:305
9120 #: rhodecode/templates/base/base.mako:316
9112 9121 msgid "RSS Feed"
9113 9122 msgstr ""
9114 9123
9115 #: rhodecode/templates/base/base.mako:306
9124 #: rhodecode/templates/base/base.mako:307
9116 9125 msgid "Watch this Repository and actions on it in your personalized journal"
9117 9126 msgstr ""
9118 9127
9119 #: rhodecode/templates/base/base.mako:324
9128 #: rhodecode/templates/base/base.mako:325
9120 9129 msgid "Fork of"
9121 9130 msgstr ""
9122 9131
9123 #: rhodecode/templates/base/base.mako:341
9132 #: rhodecode/templates/base/base.mako:342
9124 9133 #, python-format
9125 9134 msgid "Repository locked by %(user)s"
9126 9135 msgstr ""
9127 9136
9128 #: rhodecode/templates/base/base.mako:346
9137 #: rhodecode/templates/base/base.mako:347
9129 9138 msgid "Repository not locked. Pull repository to lock it."
9130 9139 msgstr ""
9131 9140
9132 #: rhodecode/templates/base/base.mako:362
9141 #: rhodecode/templates/base/base.mako:363
9133 9142 msgid "This repository has been archived. It is now read-only."
9134 9143 msgstr ""
9135 9144
9136 #: rhodecode/templates/base/base.mako:375
9145 #: rhodecode/templates/base/base.mako:376
9137 9146 #: rhodecode/templates/data_table/_dt_elements.mako:58
9138 9147 #: rhodecode/templates/data_table/_dt_elements.mako:59
9139 9148 #: rhodecode/templates/data_table/_dt_elements.mako:207
9140 9149 msgid "Summary"
9141 9150 msgstr ""
9142 9151
9143 #: rhodecode/templates/base/base.mako:376
9152 #: rhodecode/templates/base/base.mako:377
9144 9153 #: rhodecode/templates/data_table/_dt_elements.mako:63
9145 9154 #: rhodecode/templates/data_table/_dt_elements.mako:64
9146 9155 #: rhodecode/templates/files/file_authors_box.mako:30
9147 9156 #: rhodecode/templates/search/search.mako:99
9148 9157 #: rhodecode/templates/summary/components.mako:119
9149 9158 #: rhodecode/templates/summary/components.mako:127
9150 9159 msgid "Commits"
9151 9160 msgstr ""
9152 9161
9153 #: rhodecode/templates/base/base.mako:377
9162 #: rhodecode/templates/base/base.mako:378
9154 9163 #: rhodecode/templates/data_table/_dt_elements.mako:68
9155 9164 #: rhodecode/templates/data_table/_dt_elements.mako:69
9156 9165 #: rhodecode/templates/files/files.mako:15
9157 9166 #: rhodecode/templates/search/search.mako:99
9158 9167 msgid "Files"
9159 9168 msgstr ""
9160 9169
9161 #: rhodecode/templates/base/base.mako:378
9170 #: rhodecode/templates/base/base.mako:379
9162 9171 #: rhodecode/templates/bookmarks/bookmarks.mako:78
9163 9172 #: rhodecode/templates/branches/branches.mako:77
9164 9173 #: rhodecode/templates/tags/tags.mako:78
9165 9174 msgid "Compare"
9166 9175 msgstr ""
9167 9176
9168 #: rhodecode/templates/base/base.mako:383
9177 #: rhodecode/templates/base/base.mako:384
9169 9178 #, python-format
9170 9179 msgid "Show Pull Requests for %s"
9171 9180 msgstr ""
9172 9181
9173 #: rhodecode/templates/base/base.mako:394
9174 msgid "Artifacts"
9175 msgstr ""
9176
9177 #: rhodecode/templates/base/base.mako:400
9182 #: rhodecode/templates/base/base.mako:401
9178 9183 msgid "Repository Settings"
9179 9184 msgstr ""
9180 9185
9181 #: rhodecode/templates/base/base.mako:406
9186 #: rhodecode/templates/base/base.mako:407
9182 9187 msgid "Options"
9183 9188 msgstr ""
9184 9189
9185 #: rhodecode/templates/base/base.mako:411
9190 #: rhodecode/templates/base/base.mako:412
9186 9191 msgid "Unlock Repository"
9187 9192 msgstr ""
9188 9193
9189 #: rhodecode/templates/base/base.mako:413
9194 #: rhodecode/templates/base/base.mako:414
9190 9195 msgid "Lock Repository"
9191 9196 msgstr ""
9192 9197
9193 #: rhodecode/templates/base/base.mako:466
9198 #: rhodecode/templates/base/base.mako:467
9194 9199 msgid "Group Home"
9195 9200 msgstr ""
9196 9201
9197 #: rhodecode/templates/base/base.mako:470
9202 #: rhodecode/templates/base/base.mako:471
9198 9203 msgid "You have admin right to this group, and can edit it"
9199 9204 msgstr ""
9200 9205
9201 #: rhodecode/templates/base/base.mako:470
9206 #: rhodecode/templates/base/base.mako:471
9202 9207 msgid "Group Settings"
9203 9208 msgstr ""
9204 9209
9205 #: rhodecode/templates/base/base.mako:521
9210 #: rhodecode/templates/base/base.mako:522
9206 9211 msgid "This Repository"
9207 9212 msgstr ""
9208 9213
9209 #: rhodecode/templates/base/base.mako:523
9214 #: rhodecode/templates/base/base.mako:524
9210 9215 msgid "Create Pull Request"
9211 9216 msgstr ""
9212 9217
9213 #: rhodecode/templates/base/base.mako:527
9218 #: rhodecode/templates/base/base.mako:528
9214 9219 msgid "Fork this repository"
9215 9220 msgstr ""
9216 9221
9217 #: rhodecode/templates/base/base.mako:534
9222 #: rhodecode/templates/base/base.mako:535
9218 9223 msgid "This Repository Group"
9219 9224 msgstr ""
9220 9225
9221 #: rhodecode/templates/base/base.mako:538
9222 #: rhodecode/templates/base/base.mako:554
9223 #: rhodecode/templates/base/base.mako:566
9226 #: rhodecode/templates/base/base.mako:539
9227 #: rhodecode/templates/base/base.mako:555
9228 #: rhodecode/templates/base/base.mako:567
9224 9229 msgid "New Repository"
9225 9230 msgstr ""
9226 9231
9227 #: rhodecode/templates/base/base.mako:544
9228 #: rhodecode/templates/base/base.mako:558
9229 #: rhodecode/templates/base/base.mako:572
9232 #: rhodecode/templates/base/base.mako:545
9233 #: rhodecode/templates/base/base.mako:559
9234 #: rhodecode/templates/base/base.mako:573
9230 9235 msgid "New Repository Group"
9231 9236 msgstr ""
9232 9237
9233 #: rhodecode/templates/base/base.mako:599
9238 #: rhodecode/templates/base/base.mako:600
9234 9239 msgid "Sign in"
9235 9240 msgstr ""
9236 9241
9237 #: rhodecode/templates/base/base.mako:624
9242 #: rhodecode/templates/base/base.mako:625
9238 9243 msgid "My personal group"
9239 9244 msgstr ""
9240 9245
9241 #: rhodecode/templates/base/base.mako:630
9246 #: rhodecode/templates/base/base.mako:631
9242 9247 #: rhodecode/templates/debug_style/alerts.html:5
9243 9248 #: rhodecode/templates/debug_style/buttons.html:5
9244 9249 #: rhodecode/templates/debug_style/code-block.html:6
9245 9250 #: rhodecode/templates/debug_style/collapsable-content.html:5
9246 9251 #: rhodecode/templates/debug_style/emails.html:5
9247 9252 #: rhodecode/templates/debug_style/form-elements-small.html:5
9248 9253 #: rhodecode/templates/debug_style/form-elements.html:5
9249 9254 #: rhodecode/templates/debug_style/form-inline.html:5
9250 9255 #: rhodecode/templates/debug_style/form-vertical.html:5
9251 9256 #: rhodecode/templates/debug_style/forms.html:5
9252 9257 #: rhodecode/templates/debug_style/icons.html:5
9253 9258 #: rhodecode/templates/debug_style/index.html:12
9254 9259 #: rhodecode/templates/debug_style/labels.html:5
9255 9260 #: rhodecode/templates/debug_style/layout-form-sidebar.html:5
9256 9261 #: rhodecode/templates/debug_style/login.html:6
9257 9262 #: rhodecode/templates/debug_style/panels.html:5
9258 9263 #: rhodecode/templates/debug_style/tables-wide.html:5
9259 9264 #: rhodecode/templates/debug_style/tables.html:5
9260 9265 #: rhodecode/templates/debug_style/typography.html:5
9261 9266 msgid "Style"
9262 9267 msgstr ""
9263 9268
9264 #: rhodecode/templates/base/base.mako:631
9269 #: rhodecode/templates/base/base.mako:632
9265 9270 msgid "[Style]"
9266 9271 msgstr ""
9267 9272
9268 #: rhodecode/templates/base/base.mako:648
9273 #: rhodecode/templates/base/base.mako:649
9269 9274 msgid "No Bookmarks yet."
9270 9275 msgstr ""
9271 9276
9272 #: rhodecode/templates/base/base.mako:686
9277 #: rhodecode/templates/base/base.mako:687
9273 9278 msgid "Sign Out"
9274 9279 msgstr ""
9275 9280
9276 #: rhodecode/templates/base/base.mako:731
9281 #: rhodecode/templates/base/base.mako:732
9277 9282 #: rhodecode/templates/changeset/changeset_file_comment.mako:538
9278 9283 msgid "dismiss"
9279 9284 msgstr ""
9280 9285
9281 #: rhodecode/templates/base/base.mako:772
9286 #: rhodecode/templates/base/base.mako:773
9282 9287 msgid "search / go to..."
9283 9288 msgstr ""
9284 9289
9285 #: rhodecode/templates/base/base.mako:817
9286 msgid "Show activity journal"
9287 msgstr ""
9288
9289 9290 #: rhodecode/templates/base/base.mako:818
9291 msgid "Show activity journal"
9292 msgstr ""
9293
9294 #: rhodecode/templates/base/base.mako:819
9290 9295 #: rhodecode/templates/journal/journal.mako:4
9291 9296 #: rhodecode/templates/journal/journal.mako:14
9292 9297 msgid "Journal"
9293 9298 msgstr ""
9294 9299
9295 #: rhodecode/templates/base/base.mako:823
9296 msgid "Show Public activity journal"
9297 msgstr ""
9298
9299 9300 #: rhodecode/templates/base/base.mako:824
9301 msgid "Show Public activity journal"
9302 msgstr ""
9303
9304 #: rhodecode/templates/base/base.mako:825
9300 9305 msgid "Public journal"
9301 9306 msgstr ""
9302 9307
9303 #: rhodecode/templates/base/base.mako:830
9304 msgid "Show Gists"
9305 msgstr ""
9306
9307 9308 #: rhodecode/templates/base/base.mako:831
9309 msgid "Show Gists"
9310 msgstr ""
9311
9312 #: rhodecode/templates/base/base.mako:832
9308 9313 msgid "Gists"
9309 9314 msgstr ""
9310 9315
9311 #: rhodecode/templates/base/base.mako:837
9316 #: rhodecode/templates/base/base.mako:838
9312 9317 msgid "Admin settings"
9313 9318 msgstr ""
9314 9319
9315 #: rhodecode/templates/base/base.mako:1154
9320 #: rhodecode/templates/base/base.mako:1155
9316 9321 msgid "Keyboard shortcuts"
9317 9322 msgstr ""
9318 9323
9319 #: rhodecode/templates/base/base.mako:1162
9324 #: rhodecode/templates/base/base.mako:1163
9320 9325 msgid "Site-wide shortcuts"
9321 9326 msgstr ""
9322 9327
9323 9328 #: rhodecode/templates/base/default_perms_box.mako:19
9324 9329 msgid "Inherited Permissions"
9325 9330 msgstr ""
9326 9331
9327 9332 #: rhodecode/templates/base/default_perms_box.mako:25
9328 9333 msgid "Custom Permissions"
9329 9334 msgstr ""
9330 9335
9331 9336 #: rhodecode/templates/base/default_perms_box.mako:29
9332 9337 msgid "Default Global Permissions"
9333 9338 msgstr ""
9334 9339
9335 9340 #: rhodecode/templates/base/default_perms_box.mako:36
9336 9341 msgid "The following options configure the default permissions each user or group will inherit. You can override these permissions for each individual user or user group using individual permissions settings."
9337 9342 msgstr ""
9338 9343
9339 9344 #: rhodecode/templates/base/default_perms_box.mako:40
9340 9345 msgid "Repository Creation"
9341 9346 msgstr ""
9342 9347
9343 9348 #: rhodecode/templates/base/default_perms_box.mako:45
9344 9349 msgid "Permission to create root level repositories. When disabled, users can still create repositories inside their own repository groups."
9345 9350 msgstr ""
9346 9351
9347 9352 #: rhodecode/templates/base/default_perms_box.mako:50
9348 9353 msgid "Repository Creation With Group Write Access"
9349 9354 msgstr ""
9350 9355
9351 9356 #: rhodecode/templates/base/default_perms_box.mako:55
9352 9357 msgid "Write permission given on a repository group will allow creating repositories inside that group."
9353 9358 msgstr ""
9354 9359
9355 9360 #: rhodecode/templates/base/default_perms_box.mako:60
9356 9361 msgid "Repository Forking"
9357 9362 msgstr ""
9358 9363
9359 9364 #: rhodecode/templates/base/default_perms_box.mako:65
9360 msgid "Permission to create root level repository forks. When disabled, users can still fork repositories inside their own repository groups."
9365 msgid "Permission to create repository forks. Root level forks will only work if repository creation is enabled."
9361 9366 msgstr ""
9362 9367
9363 9368 #: rhodecode/templates/base/default_perms_box.mako:70
9364 9369 msgid "Repository Group Creation"
9365 9370 msgstr ""
9366 9371
9367 9372 #: rhodecode/templates/base/default_perms_box.mako:75
9368 9373 msgid "Permission to create root level repository groups. When disabled, repository group admins can still create repository subgroups within their repository groups."
9369 9374 msgstr ""
9370 9375
9371 9376 #: rhodecode/templates/base/default_perms_box.mako:80
9372 9377 msgid "User Group Creation"
9373 9378 msgstr ""
9374 9379
9375 9380 #: rhodecode/templates/base/default_perms_box.mako:85
9376 9381 msgid "Permission to allow user group creation."
9377 9382 msgstr ""
9378 9383
9379 9384 #: rhodecode/templates/base/default_perms_box.mako:91
9380 9385 msgid "Inherit Permissions From The Default User"
9381 9386 msgstr ""
9382 9387
9383 9388 #: rhodecode/templates/base/default_perms_box.mako:96
9384 9389 msgid "Inherit default permissions from the default user. Turn off this option to force explicit permissions for users, even if they are more restrictive than the default user permissions."
9385 9390 msgstr ""
9386 9391
9387 9392 #: rhodecode/templates/base/default_perms_box.mako:115
9388 9393 msgid "Inherit from default settings"
9389 9394 msgstr ""
9390 9395
9391 9396 #: rhodecode/templates/base/default_perms_box.mako:120
9392 9397 #, python-format
9393 9398 msgid ""
9394 9399 "Select to inherit permissions from %s permissions settings, including default IP address whitelist and inheritance of \n"
9395 9400 "permission by members of user groups."
9396 9401 msgstr ""
9397 9402
9398 9403 #: rhodecode/templates/base/issue_tracker_settings.mako:82
9399 9404 msgid "Extra Prefix"
9400 9405 msgstr ""
9401 9406
9402 9407 #: rhodecode/templates/base/issue_tracker_settings.mako:93
9403 9408 msgid "show examples"
9404 9409 msgstr ""
9405 9410
9406 9411 #: rhodecode/templates/base/issue_tracker_settings.mako:161
9407 9412 msgid "Add new"
9408 9413 msgstr ""
9409 9414
9410 9415 #: rhodecode/templates/base/issue_tracker_settings.mako:169
9411 9416 msgid "New Entry"
9412 9417 msgstr ""
9413 9418
9414 9419 #: rhodecode/templates/base/issue_tracker_settings.mako:173
9415 9420 msgid "Confirm to remove this pattern:"
9416 9421 msgstr ""
9417 9422
9418 9423 #: rhodecode/templates/base/issue_tracker_settings.mako:300
9419 9424 #: rhodecode/templates/changeset/changeset_file_comment.mako:395
9420 9425 #: rhodecode/templates/changeset/changeset_file_comment.mako:446
9421 #: rhodecode/templates/data_table/_dt_elements.mako:456
9426 #: rhodecode/templates/data_table/_dt_elements.mako:463
9422 9427 #: rhodecode/templates/files/files_add.mako:59
9423 9428 #: rhodecode/templates/files/files_edit.mako:61
9424 9429 msgid "Preview"
9425 9430 msgstr ""
9426 9431
9427 9432 #: rhodecode/templates/base/issue_tracker_settings.mako:301
9428 9433 msgid "Test Pattern Preview"
9429 9434 msgstr ""
9430 9435
9431 9436 #: rhodecode/templates/base/perms_summary.mako:31
9432 9437 msgid "in JSON format"
9433 9438 msgstr ""
9434 9439
9435 9440 #: rhodecode/templates/base/perms_summary.mako:40
9436 9441 #: rhodecode/templates/base/perms_summary.mako:48
9437 9442 msgid "show"
9438 9443 msgstr ""
9439 9444
9440 9445 #: rhodecode/templates/base/perms_summary.mako:41
9441 9446 #: rhodecode/templates/base/perms_summary.mako:49
9442 9447 msgid "none"
9443 9448 msgstr ""
9444 9449
9445 9450 #: rhodecode/templates/base/perms_summary.mako:42
9446 9451 #: rhodecode/templates/commits/changelog_elements.mako:102
9447 9452 #: rhodecode/templates/files/base.mako:26
9448 9453 msgid "merge"
9449 9454 msgstr ""
9450 9455
9451 9456 #: rhodecode/templates/base/perms_summary.mako:43
9452 9457 msgid "push"
9453 9458 msgstr ""
9454 9459
9455 9460 #: rhodecode/templates/base/perms_summary.mako:44
9456 9461 msgid "push force"
9457 9462 msgstr ""
9458 9463
9459 9464 #: rhodecode/templates/base/perms_summary.mako:50
9460 9465 msgid "read"
9461 9466 msgstr ""
9462 9467
9463 9468 #: rhodecode/templates/base/perms_summary.mako:51
9464 9469 msgid "write"
9465 9470 msgstr ""
9466 9471
9467 9472 #: rhodecode/templates/base/perms_summary.mako:52
9468 9473 msgid "admin"
9469 9474 msgstr ""
9470 9475
9471 9476 #: rhodecode/templates/base/perms_summary.mako:60
9472 9477 msgid "No permissions defined"
9473 9478 msgstr ""
9474 9479
9475 9480 #: rhodecode/templates/base/perms_summary.mako:68
9476 9481 #: rhodecode/templates/base/perms_summary.mako:175
9477 9482 #: rhodecode/templates/base/perms_summary.mako:248
9478 9483 msgid "Permission"
9479 9484 msgstr ""
9480 9485
9481 9486 #: rhodecode/templates/base/perms_summary.mako:70
9482 9487 #: rhodecode/templates/base/perms_summary.mako:250
9483 9488 msgid "Edit Permission"
9484 9489 msgstr ""
9485 9490
9486 9491 #: rhodecode/templates/base/perms_summary.mako:124
9487 9492 #: rhodecode/templates/base/perms_summary.mako:133
9488 9493 msgid "edit global"
9489 9494 msgstr ""
9490 9495
9491 9496 #: rhodecode/templates/base/perms_summary.mako:141
9492 9497 msgid "Repository default permission"
9493 9498 msgstr ""
9494 9499
9495 9500 #: rhodecode/templates/base/perms_summary.mako:144
9496 9501 msgid "Repository group default permission"
9497 9502 msgstr ""
9498 9503
9499 9504 #: rhodecode/templates/base/perms_summary.mako:147
9500 9505 msgid "User group default permission"
9501 9506 msgstr ""
9502 9507
9503 9508 #: rhodecode/templates/base/perms_summary.mako:153
9504 9509 msgid "Inherit permissions"
9505 9510 msgstr ""
9506 9511
9507 9512 #: rhodecode/templates/base/perms_summary.mako:156
9508 9513 msgid "Create repositories"
9509 9514 msgstr ""
9510 9515
9511 9516 #: rhodecode/templates/base/perms_summary.mako:159
9512 9517 msgid "Fork repositories"
9513 9518 msgstr ""
9514 9519
9515 9520 #: rhodecode/templates/base/perms_summary.mako:162
9516 9521 msgid "Create repository groups"
9517 9522 msgstr ""
9518 9523
9519 9524 #: rhodecode/templates/base/perms_summary.mako:165
9520 9525 msgid "Create user groups"
9521 9526 msgstr ""
9522 9527
9523 9528 #: rhodecode/templates/base/perms_summary.mako:177
9524 9529 msgid "Edit Branch Permission"
9525 9530 msgstr ""
9526 9531
9527 9532 #: rhodecode/templates/base/perms_summary.mako:291
9528 9533 msgid "overridden by"
9529 9534 msgstr ""
9530 9535
9531 9536 #: rhodecode/templates/base/perms_summary.mako:326
9532 9537 msgid "No matching permission defined"
9533 9538 msgstr ""
9534 9539
9535 9540 #: rhodecode/templates/base/root.mako:156
9536 9541 msgid "Please enable JavaScript to use RhodeCode Enterprise"
9537 9542 msgstr ""
9538 9543
9539 9544 #: rhodecode/templates/base/vcs_settings.mako:16
9540 9545 msgid "Require SSL for vcs operations"
9541 9546 msgstr ""
9542 9547
9543 9548 #: rhodecode/templates/base/vcs_settings.mako:19
9544 9549 msgid "Activate to set RhodeCode to require SSL for pushing or pulling. If SSL certificate is missing it will return a HTTP Error 406: Not Acceptable."
9545 9550 msgstr ""
9546 9551
9547 9552 #: rhodecode/templates/base/vcs_settings.mako:29
9548 9553 msgid "Main Storage Location"
9549 9554 msgstr ""
9550 9555
9551 9556 #: rhodecode/templates/base/vcs_settings.mako:37
9552 9557 msgid "Click to unlock. You must restart RhodeCode in order to make this setting take effect."
9553 9558 msgstr ""
9554 9559
9555 9560 #: rhodecode/templates/base/vcs_settings.mako:41
9556 9561 msgid "Repository location change is disabled. You can enable this by changing the `allow_repo_location_change` inside .ini file."
9557 9562 msgstr ""
9558 9563
9559 9564 #: rhodecode/templates/base/vcs_settings.mako:48
9560 9565 msgid "Filesystem location where repositories should be stored. After changing this value a restart and rescan of the repository folder are required."
9561 9566 msgstr ""
9562 9567
9563 9568 #: rhodecode/templates/base/vcs_settings.mako:57
9564 9569 msgid "Internal Hooks"
9565 9570 msgstr ""
9566 9571
9567 9572 #: rhodecode/templates/base/vcs_settings.mako:63
9568 9573 msgid "Show repository size after push"
9569 9574 msgstr ""
9570 9575
9571 9576 #: rhodecode/templates/base/vcs_settings.mako:67
9572 9577 msgid "Trigger a hook that calculates repository size after each push."
9573 9578 msgstr ""
9574 9579
9575 9580 #: rhodecode/templates/base/vcs_settings.mako:71
9576 9581 msgid "Execute pre/post push hooks"
9577 9582 msgstr ""
9578 9583
9579 9584 #: rhodecode/templates/base/vcs_settings.mako:74
9580 9585 msgid "Execute Built in pre/post push hooks. This also executes rcextensions hooks."
9581 9586 msgstr ""
9582 9587
9583 9588 #: rhodecode/templates/base/vcs_settings.mako:78
9584 9589 msgid "Execute pre/post pull hooks"
9585 9590 msgstr ""
9586 9591
9587 9592 #: rhodecode/templates/base/vcs_settings.mako:81
9588 9593 msgid "Execute Built in pre/post pull hooks. This also executes rcextensions hooks."
9589 9594 msgstr ""
9590 9595
9591 9596 #: rhodecode/templates/base/vcs_settings.mako:91
9592 9597 msgid "Mercurial Settings"
9593 9598 msgstr ""
9594 9599
9595 9600 #: rhodecode/templates/base/vcs_settings.mako:96
9596 9601 msgid "Enable largefiles extension"
9597 9602 msgstr ""
9598 9603
9599 9604 #: rhodecode/templates/base/vcs_settings.mako:100
9600 9605 msgid "Enable Largefiles extensions for all repositories."
9601 9606 msgstr ""
9602 9607
9603 9608 #: rhodecode/templates/base/vcs_settings.mako:102
9604 9609 msgid "Enable Largefiles extensions for this repository."
9605 9610 msgstr ""
9606 9611
9607 9612 #: rhodecode/templates/base/vcs_settings.mako:113
9608 9613 msgid "Filesystem location where Mercurial largefile objects should be stored."
9609 9614 msgstr ""
9610 9615
9611 9616 #: rhodecode/templates/base/vcs_settings.mako:119
9612 9617 msgid "Set repositories as publishing"
9613 9618 msgstr ""
9614 9619
9615 9620 #: rhodecode/templates/base/vcs_settings.mako:119
9616 9621 msgid "Set repository as publishing"
9617 9622 msgstr ""
9618 9623
9619 9624 #: rhodecode/templates/base/vcs_settings.mako:122
9620 9625 msgid "When this is enabled all commits in the repository are seen as public commits by clients."
9621 9626 msgstr ""
9622 9627
9623 9628 #: rhodecode/templates/base/vcs_settings.mako:127
9624 9629 msgid "Enable hgsubversion extension"
9625 9630 msgstr ""
9626 9631
9627 9632 #: rhodecode/templates/base/vcs_settings.mako:130
9628 9633 msgid "Requires hgsubversion library to be installed. Allows cloning remote SVN repositories and migrates them to Mercurial type."
9629 9634 msgstr ""
9630 9635
9631 9636 #: rhodecode/templates/base/vcs_settings.mako:136
9632 9637 msgid "Enable Evolve and Topic extension"
9633 9638 msgstr ""
9634 9639
9635 9640 #: rhodecode/templates/base/vcs_settings.mako:140
9636 9641 msgid "Enable Evolve and Topic extensions for all repositories."
9637 9642 msgstr ""
9638 9643
9639 9644 #: rhodecode/templates/base/vcs_settings.mako:142
9640 9645 msgid "Enable Evolve and Topic extensions for this repository."
9641 9646 msgstr ""
9642 9647
9643 9648 #: rhodecode/templates/base/vcs_settings.mako:153
9644 9649 msgid "Git Settings"
9645 9650 msgstr ""
9646 9651
9647 9652 #: rhodecode/templates/base/vcs_settings.mako:158
9648 9653 msgid "Enable lfs extension"
9649 9654 msgstr ""
9650 9655
9651 9656 #: rhodecode/templates/base/vcs_settings.mako:162
9652 9657 msgid "Enable lfs extensions for all repositories."
9653 9658 msgstr ""
9654 9659
9655 9660 #: rhodecode/templates/base/vcs_settings.mako:164
9656 9661 msgid "Enable lfs extensions for this repository."
9657 9662 msgstr ""
9658 9663
9659 9664 #: rhodecode/templates/base/vcs_settings.mako:175
9660 9665 msgid "Filesystem location where Git lfs objects should be stored."
9661 9666 msgstr ""
9662 9667
9663 9668 #: rhodecode/templates/base/vcs_settings.mako:186
9664 9669 msgid "Global Subversion Settings"
9665 9670 msgstr ""
9666 9671
9667 9672 #: rhodecode/templates/base/vcs_settings.mako:192
9668 9673 msgid "Proxy subversion HTTP requests"
9669 9674 msgstr ""
9670 9675
9671 9676 #: rhodecode/templates/base/vcs_settings.mako:196
9672 9677 msgid "Subversion HTTP Support. Enables communication with SVN over HTTP protocol."
9673 9678 msgstr ""
9674 9679
9675 9680 #: rhodecode/templates/base/vcs_settings.mako:197
9676 9681 msgid "SVN Protocol setup Documentation"
9677 9682 msgstr ""
9678 9683
9679 9684 #: rhodecode/templates/base/vcs_settings.mako:203
9680 9685 msgid "Subversion HTTP Server URL"
9681 9686 msgstr ""
9682 9687
9683 9688 #: rhodecode/templates/base/vcs_settings.mako:209
9684 9689 msgid "Generate Apache Config"
9685 9690 msgstr ""
9686 9691
9687 9692 #: rhodecode/templates/base/vcs_settings.mako:221
9688 9693 msgid "Subversion Settings"
9689 9694 msgstr ""
9690 9695
9691 9696 #: rhodecode/templates/base/vcs_settings.mako:226
9692 9697 msgid "Repository patterns"
9693 9698 msgstr ""
9694 9699
9695 9700 #: rhodecode/templates/base/vcs_settings.mako:230
9696 9701 msgid "Patterns for identifying SVN branches and tags. For recursive search, use \"*\". Eg.: \"/branches/*\""
9697 9702 msgstr ""
9698 9703
9699 9704 #: rhodecode/templates/base/vcs_settings.mako:294
9700 9705 msgid "Pull Request Settings"
9701 9706 msgstr ""
9702 9707
9703 9708 #: rhodecode/templates/base/vcs_settings.mako:299
9704 9709 msgid "Enable server-side merge for pull requests"
9705 9710 msgstr ""
9706 9711
9707 9712 #: rhodecode/templates/base/vcs_settings.mako:302
9708 9713 msgid "Note: when this feature is enabled, it only runs hooks defined in the rcextension package. Custom hooks added on the Admin -> Settings -> Hooks page will not be run when pull requests are automatically merged from the web interface."
9709 9714 msgstr ""
9710 9715
9711 9716 #: rhodecode/templates/base/vcs_settings.mako:306
9712 9717 msgid "Invalidate and relocate inline comments during update"
9713 9718 msgstr ""
9714 9719
9715 9720 #: rhodecode/templates/base/vcs_settings.mako:309
9716 9721 msgid "During the update of a pull request, the position of inline comments will be updated and outdated inline comments will be hidden."
9717 9722 msgstr ""
9718 9723
9719 9724 #: rhodecode/templates/base/vcs_settings.mako:318
9720 9725 msgid "Diff cache"
9721 9726 msgstr ""
9722 9727
9723 9728 #: rhodecode/templates/base/vcs_settings.mako:323
9724 9729 msgid "Enable caching diffs for pull requests cache and commits"
9725 9730 msgstr ""
9726 9731
9727 9732 #: rhodecode/templates/base/vcs_settings.mako:332
9728 9733 msgid "Mercurial Pull Request Settings"
9729 9734 msgstr ""
9730 9735
9731 9736 #: rhodecode/templates/base/vcs_settings.mako:338
9732 9737 msgid "Use rebase as merge strategy"
9733 9738 msgstr ""
9734 9739
9735 9740 #: rhodecode/templates/base/vcs_settings.mako:341
9736 9741 msgid "Use rebase instead of creating a merge commit when merging via web interface."
9737 9742 msgstr ""
9738 9743
9739 9744 #: rhodecode/templates/base/vcs_settings.mako:346
9740 9745 msgid "Close branch before merging it"
9741 9746 msgstr ""
9742 9747
9743 9748 #: rhodecode/templates/base/vcs_settings.mako:349
9744 9749 msgid "Close branch before merging it into destination branch. No effect when rebase strategy is use."
9745 9750 msgstr ""
9746 9751
9747 9752 #: rhodecode/templates/bookmarks/bookmarks.mako:5
9748 9753 #, python-format
9749 9754 msgid "%s Bookmarks"
9750 9755 msgstr ""
9751 9756
9752 9757 #: rhodecode/templates/bookmarks/bookmarks.mako:28
9753 9758 msgid "Compare Selected Bookmarks"
9754 9759 msgstr ""
9755 9760
9756 9761 #: rhodecode/templates/branches/branches.mako:5
9757 9762 #, python-format
9758 9763 msgid "%s Branches"
9759 9764 msgstr ""
9760 9765
9761 9766 #: rhodecode/templates/branches/branches.mako:28
9762 9767 msgid "Compare Selected Branches"
9763 9768 msgstr ""
9764 9769
9765 9770 #: rhodecode/templates/changeset/changeset.mako:11
9766 9771 msgid "{} Commit"
9767 9772 msgstr ""
9768 9773
9769 9774 #: rhodecode/templates/changeset/changeset.mako:78
9770 9775 #: rhodecode/templates/commits/changelog_elements.mako:56
9771 9776 #: rhodecode/templates/files/files_source_header.mako:48
9772 9777 #: rhodecode/templates/files/files_tree_header.mako:35
9773 9778 #: rhodecode/templates/summary/summary_commits.mako:43
9774 9779 msgid "Copy the full commit id"
9775 9780 msgstr ""
9776 9781
9777 9782 #: rhodecode/templates/changeset/changeset.mako:83
9778 9783 msgid "Commit phase"
9779 9784 msgstr ""
9780 9785
9781 9786 #: rhodecode/templates/changeset/changeset.mako:90
9782 9787 #: rhodecode/templates/changeset/changeset.mako:97
9783 9788 msgid "Evolve State"
9784 9789 msgstr ""
9785 9790
9786 9791 #: rhodecode/templates/changeset/changeset.mako:91
9787 9792 msgid "obsolete"
9788 9793 msgstr ""
9789 9794
9790 9795 #: rhodecode/templates/changeset/changeset.mako:98
9791 9796 msgid "hidden"
9792 9797 msgstr ""
9793 9798
9794 9799 #: rhodecode/templates/changeset/changeset.mako:104
9795 9800 msgid "Parent Commit"
9796 9801 msgstr ""
9797 9802
9798 9803 #: rhodecode/templates/changeset/changeset.mako:104
9799 9804 msgid "parent"
9800 9805 msgstr ""
9801 9806
9802 9807 #: rhodecode/templates/changeset/changeset.mako:108
9803 9808 msgid "Child Commit"
9804 9809 msgstr ""
9805 9810
9806 9811 #: rhodecode/templates/changeset/changeset.mako:108
9807 9812 msgid "child"
9808 9813 msgstr ""
9809 9814
9810 9815 #: rhodecode/templates/changeset/changeset.mako:118
9811 9816 msgid "Diff options"
9812 9817 msgstr ""
9813 9818
9814 9819 #: rhodecode/templates/changeset/changeset.mako:122
9815 9820 msgid "Raw Diff"
9816 9821 msgstr ""
9817 9822
9818 9823 #: rhodecode/templates/changeset/changeset.mako:126
9819 9824 msgid "Patch Diff"
9820 9825 msgstr ""
9821 9826
9822 9827 #: rhodecode/templates/changeset/changeset.mako:130
9823 9828 msgid "Download Diff"
9824 9829 msgstr ""
9825 9830
9826 9831 #: rhodecode/templates/changeset/changeset.mako:160
9827 9832 #: rhodecode/templates/pullrequests/pullrequest_show.mako:502
9828 9833 msgid "General Comments"
9829 9834 msgstr ""
9830 9835
9831 9836 #: rhodecode/templates/changeset/changeset.mako:201
9832 9837 #: rhodecode/templates/pullrequests/pullrequest_show.mako:605
9833 9838 msgid "Reviewers"
9834 9839 msgstr ""
9835 9840
9836 9841 #: rhodecode/templates/changeset/changeset.mako:242
9837 9842 #: rhodecode/templates/pullrequests/pullrequest_show.mako:579
9838 9843 #: rhodecode/templates/pullrequests/pullrequest_show.mako:748
9839 9844 msgid "No TODOs yet"
9840 9845 msgstr ""
9841 9846
9842 9847 #: rhodecode/templates/changeset/changeset.mako:274
9843 9848 #: rhodecode/templates/pullrequests/pullrequest_show.mako:799
9844 9849 msgid "No Comments yet"
9845 9850 msgstr ""
9846 9851
9847 9852 #: rhodecode/templates/changeset/changeset.mako:330
9848 9853 msgid "No Child Commits"
9849 9854 msgstr ""
9850 9855
9851 9856 #: rhodecode/templates/changeset/changeset.mako:377
9852 9857 msgid "No Parent Commits"
9853 9858 msgstr ""
9854 9859
9855 9860 #: rhodecode/templates/changeset/changeset_file_comment.mako:51
9856 9861 msgid "Draft comments are only visible to the author until submitted"
9857 9862 msgstr ""
9858 9863
9859 9864 #: rhodecode/templates/changeset/changeset_file_comment.mako:55
9860 9865 msgid "This comment was added while you browsed this page"
9861 9866 msgstr ""
9862 9867
9863 9868 #: rhodecode/templates/changeset/changeset_file_comment.mako:65
9864 9869 msgid "Resolved by comment #{}"
9865 9870 msgstr ""
9866 9871
9867 9872 #: rhodecode/templates/changeset/changeset_file_comment.mako:73
9868 9873 msgid "Click to create resolution comment."
9869 9874 msgstr ""
9870 9875
9871 9876 #: rhodecode/templates/changeset/changeset_file_comment.mako:82
9872 9877 msgid "This comment resolves TODO #{}"
9873 9878 msgstr ""
9874 9879
9875 9880 #: rhodecode/templates/changeset/changeset_file_comment.mako:114
9876 9881 msgid "Status from pull request."
9877 9882 msgstr ""
9878 9883
9879 9884 #: rhodecode/templates/changeset/changeset_file_comment.mako:133
9880 9885 msgid "Pull request author"
9881 9886 msgstr ""
9882 9887
9883 9888 #: rhodecode/templates/changeset/changeset_file_comment.mako:134
9884 9889 msgid "author"
9885 9890 msgstr ""
9886 9891
9887 9892 #: rhodecode/templates/changeset/changeset_file_comment.mako:187
9888 9893 #: rhodecode/templates/changeset/changeset_file_comment.mako:201
9889 9894 msgid "Outdated comment from pull request version v{0}, latest v{1}"
9890 9895 msgstr ""
9891 9896
9892 9897 #: rhodecode/templates/changeset/changeset_file_comment.mako:190
9893 9898 #: rhodecode/templates/changeset/changeset_file_comment.mako:206
9894 9899 msgid "Comment from pull request version v{0}, latest v{1}"
9895 9900 msgstr ""
9896 9901
9897 9902 #: rhodecode/templates/changeset/changeset_file_comment.mako:222
9898 9903 #: rhodecode/templates/compare/compare_diff.mako:108
9899 9904 #: rhodecode/templates/compare/compare_diff.mako:116
9900 9905 #: rhodecode/templates/compare/compare_diff.mako:124
9901 9906 #: rhodecode/templates/compare/compare_diff.mako:126
9902 9907 msgid "Comment"
9903 9908 msgstr ""
9904 9909
9905 9910 #: rhodecode/templates/changeset/changeset_file_comment.mako:223
9906 9911 #: rhodecode/templates/files/files_source.mako:117
9907 9912 msgid "Copy permalink"
9908 9913 msgstr ""
9909 9914
9910 9915 #: rhodecode/templates/changeset/changeset_file_comment.mako:241
9911 9916 msgid "Submit draft"
9912 9917 msgstr ""
9913 9918
9914 9919 #: rhodecode/templates/changeset/changeset_file_comment.mako:247
9915 9920 #: rhodecode/templates/changeset/changeset_file_comment.mako:250
9916 9921 #: rhodecode/templates/changeset/changeset_file_comment.mako:256
9917 9922 #: rhodecode/templates/changeset/changeset_file_comment.mako:259
9918 9923 msgid "Action unavailable"
9919 9924 msgstr ""
9920 9925
9921 9926 #: rhodecode/templates/changeset/changeset_file_comment.mako:267
9922 9927 msgid "Jump to the previous outdated comment"
9923 9928 msgstr ""
9924 9929
9925 9930 #: rhodecode/templates/changeset/changeset_file_comment.mako:268
9926 9931 msgid "Jump to the next outdated comment"
9927 9932 msgstr ""
9928 9933
9929 9934 #: rhodecode/templates/changeset/changeset_file_comment.mako:270
9930 9935 msgid "Jump to the previous comment"
9931 9936 msgstr ""
9932 9937
9933 9938 #: rhodecode/templates/changeset/changeset_file_comment.mako:271
9934 9939 msgid "Jump to the next comment"
9935 9940 msgstr ""
9936 9941
9937 9942 #: rhodecode/templates/changeset/changeset_file_comment.mako:311
9938 9943 msgid "Leave a comment on this Pull Request."
9939 9944 msgstr ""
9940 9945
9941 9946 #: rhodecode/templates/changeset/changeset_file_comment.mako:313
9942 9947 msgid "Leave a comment on {} commits in this range."
9943 9948 msgstr ""
9944 9949
9945 9950 #: rhodecode/templates/changeset/changeset_file_comment.mako:315
9946 9951 msgid "Leave a comment on this Commit."
9947 9952 msgstr ""
9948 9953
9949 9954 #: rhodecode/templates/changeset/changeset_file_comment.mako:403
9950 9955 msgid "You need to be logged in to leave comments."
9951 9956 msgstr ""
9952 9957
9953 9958 #: rhodecode/templates/changeset/changeset_file_comment.mako:404
9954 9959 msgid "Login now"
9955 9960 msgstr ""
9956 9961
9957 9962 #: rhodecode/templates/changeset/changeset_file_comment.mako:451
9958 9963 msgid "Mark as"
9959 9964 msgstr ""
9960 9965
9961 9966 #: rhodecode/templates/changeset/changeset_file_comment.mako:474
9962 9967 #: rhodecode/templates/files/files_upload.mako:86
9963 9968 msgid "Drag'n Drop files here or"
9964 9969 msgstr ""
9965 9970
9966 9971 #: rhodecode/templates/changeset/changeset_file_comment.mako:474
9967 9972 #: rhodecode/templates/files/files_upload.mako:86
9968 9973 msgid "Choose your files"
9969 9974 msgstr ""
9970 9975
9971 9976 #: rhodecode/templates/changeset/changeset_file_comment.mako:477
9972 9977 msgid "uploading..."
9973 9978 msgstr ""
9974 9979
9975 9980 #: rhodecode/templates/changeset/changeset_file_comment.mako:508
9976 9981 msgid "Add comment"
9977 9982 msgstr ""
9978 9983
9979 9984 #: rhodecode/templates/changeset/changeset_file_comment.mako:513
9980 9985 #: rhodecode/templates/changeset/changeset_file_comment.mako:515
9981 9986 msgid "Add draft"
9982 9987 msgstr ""
9983 9988
9984 9989 #: rhodecode/templates/changeset/changeset_file_comment.mako:526
9985 9990 #: rhodecode/templates/pullrequests/pullrequest_show.mako:46
9986 9991 #: rhodecode/templates/pullrequests/pullrequests.mako:31
9987 9992 msgid "Closed"
9988 9993 msgstr ""
9989 9994
9990 9995 #: rhodecode/templates/changeset/changeset_file_comment.mako:545
9991 9996 msgid "{} is supported."
9992 9997 msgstr ""
9993 9998
9994 9999 #: rhodecode/templates/changeset/changeset_range.mako:5
9995 10000 #, python-format
9996 10001 msgid "%s Commits"
9997 10002 msgstr ""
9998 10003
9999 10004 #: rhodecode/templates/changeset/changeset_range.mako:33
10000 10005 msgid "Commit Range"
10001 10006 msgstr ""
10002 10007
10003 10008 #: rhodecode/templates/changeset/changeset_range.mako:42
10004 10009 msgid "Range"
10005 10010 msgstr ""
10006 10011
10007 10012 #: rhodecode/templates/changeset/changeset_range.mako:60
10008 10013 msgid "Diff Option"
10009 10014 msgstr ""
10010 10015
10011 10016 #: rhodecode/templates/changeset/changeset_range.mako:71
10012 10017 #: rhodecode/templates/commits/changelog.mako:124
10013 10018 msgid "Show combined diff"
10014 10019 msgstr ""
10015 10020
10016 10021 #: rhodecode/templates/changeset/diff_block.mako:7
10017 10022 msgid "The requested commit is too big and content was truncated."
10018 10023 msgstr ""
10019 10024
10020 10025 #: rhodecode/templates/changeset/diff_block.mako:7
10021 10026 #: rhodecode/templates/changeset/diff_block.mako:10
10022 10027 #: rhodecode/templates/changeset/diff_block.mako:25
10023 10028 #: rhodecode/templates/changeset/diff_block.mako:46
10024 10029 #: rhodecode/templates/codeblocks/diffs.mako:196
10025 10030 #: rhodecode/templates/codeblocks/diffs.mako:327
10026 10031 msgid "Showing a big diff might take some time and resources, continue?"
10027 10032 msgstr ""
10028 10033
10029 10034 #: rhodecode/templates/changeset/diff_block.mako:7
10030 10035 #: rhodecode/templates/changeset/diff_block.mako:10
10031 10036 #: rhodecode/templates/changeset/diff_block.mako:25
10032 10037 #: rhodecode/templates/changeset/diff_block.mako:46
10033 10038 #: rhodecode/templates/codeblocks/diffs.mako:196
10034 10039 #: rhodecode/templates/codeblocks/diffs.mako:327
10035 10040 msgid "Show full diff"
10036 10041 msgstr ""
10037 10042
10038 10043 #: rhodecode/templates/changeset/diff_block.mako:10
10039 10044 msgid "The requested file is too big and its content is not shown."
10040 10045 msgstr ""
10041 10046
10042 10047 #: rhodecode/templates/changeset/diff_block.mako:25
10043 10048 #: rhodecode/templates/changeset/diff_block.mako:46
10044 10049 msgid "Diff was truncated. File content available only in full diff."
10045 10050 msgstr ""
10046 10051
10047 10052 #: rhodecode/templates/codeblocks/diffs.mako:129
10048 10053 msgid "not available in this view"
10049 10054 msgstr ""
10050 10055
10051 10056 #: rhodecode/templates/codeblocks/diffs.mako:138
10052 10057 msgid "{} unresolved"
10053 10058 msgstr ""
10054 10059
10055 10060 #: rhodecode/templates/codeblocks/diffs.mako:141
10056 10061 msgid "0 unresolved"
10057 10062 msgstr ""
10058 10063
10059 10064 #: rhodecode/templates/codeblocks/diffs.mako:144
10060 10065 msgid "{} Resolved"
10061 10066 msgstr ""
10062 10067
10063 10068 #: rhodecode/templates/codeblocks/diffs.mako:195
10064 10069 msgid "The requested changes are too big and content was truncated."
10065 10070 msgstr ""
10066 10071
10067 10072 #: rhodecode/templates/codeblocks/diffs.mako:212
10068 10073 msgid "Some changes may be hidden"
10069 10074 msgstr ""
10070 10075
10071 10076 #: rhodecode/templates/codeblocks/diffs.mako:214
10072 10077 msgid "No files"
10073 10078 msgstr ""
10074 10079
10075 10080 #: rhodecode/templates/codeblocks/diffs.mako:327
10076 10081 msgid "The requested commit or file is too big and content was truncated."
10077 10082 msgstr ""
10078 10083
10079 10084 #: rhodecode/templates/codeblocks/diffs.mako:334
10080 10085 #, python-format
10081 10086 msgid "This diff has been collapsed as it changes many lines, (%i lines changed)"
10082 10087 msgstr ""
10083 10088
10084 10089 #: rhodecode/templates/codeblocks/diffs.mako:336
10085 10090 msgid "Show them"
10086 10091 msgstr ""
10087 10092
10088 10093 #: rhodecode/templates/codeblocks/diffs.mako:339
10089 10094 msgid "Hide them"
10090 10095 msgstr ""
10091 10096
10092 10097 #: rhodecode/templates/codeblocks/diffs.mako:376
10093 10098 #: rhodecode/templates/codeblocks/diffs.mako:395
10094 10099 msgid "Unmatched/outdated inline comments below"
10095 10100 msgstr ""
10096 10101
10097 10102 #: rhodecode/templates/codeblocks/diffs.mako:401
10098 10103 msgid "Unmatched/outdated comments below"
10099 10104 msgstr ""
10100 10105
10101 10106 #: rhodecode/templates/codeblocks/diffs.mako:475
10102 10107 msgid "This file was removed from diff during updates to this pull-request."
10103 10108 msgstr ""
10104 10109
10105 10110 #: rhodecode/templates/codeblocks/diffs.mako:476
10106 10111 msgid "There are still outdated/unresolved comments attached to it."
10107 10112 msgstr ""
10108 10113
10109 10114 #: rhodecode/templates/codeblocks/diffs.mako:582
10110 10115 #: rhodecode/templates/codeblocks/diffs.mako:600
10111 10116 #, python-format
10112 10117 msgid "Show file at commit: %(commit_id)s"
10113 10118 msgstr ""
10114 10119
10115 10120 #: rhodecode/templates/codeblocks/diffs.mako:584
10116 10121 #: rhodecode/templates/codeblocks/diffs.mako:591
10117 10122 msgid "Show file before"
10118 10123 msgstr ""
10119 10124
10120 10125 #: rhodecode/templates/codeblocks/diffs.mako:589
10121 10126 #: rhodecode/templates/codeblocks/diffs.mako:607
10122 10127 #, python-format
10123 10128 msgid "File not present at commit: %(commit_id)s"
10124 10129 msgstr ""
10125 10130
10126 10131 #: rhodecode/templates/codeblocks/diffs.mako:602
10127 10132 #: rhodecode/templates/codeblocks/diffs.mako:609
10128 10133 msgid "Show file after"
10129 10134 msgstr ""
10130 10135
10131 10136 #: rhodecode/templates/codeblocks/diffs.mako:616
10132 10137 #: rhodecode/templates/codeblocks/diffs.mako:618
10133 10138 msgid "Hide comments"
10134 10139 msgstr ""
10135 10140
10136 10141 #: rhodecode/templates/codeblocks/diffs.mako:617
10137 10142 msgid "Show comments"
10138 10143 msgstr ""
10139 10144
10140 10145 #: rhodecode/templates/codeblocks/diffs.mako:732
10141 10146 #: rhodecode/templates/codeblocks/diffs.mako:779
10142 10147 #: rhodecode/templates/codeblocks/diffs.mako:846
10143 10148 msgid "Comments including outdated: {}. Click here to toggle them."
10144 10149 msgstr ""
10145 10150
10146 10151 #: rhodecode/templates/codeblocks/diffs.mako:734
10147 10152 #: rhodecode/templates/codeblocks/diffs.mako:781
10148 10153 #: rhodecode/templates/codeblocks/diffs.mako:848
10149 10154 msgid "Comments: {}. Click to toggle them."
10150 10155 msgstr ""
10151 10156
10152 10157 #: rhodecode/templates/codeblocks/diffs.mako:921
10153 10158 msgid "Scroll to page bottom"
10154 10159 msgstr ""
10155 10160
10156 10161 #: rhodecode/templates/codeblocks/diffs.mako:924
10157 10162 msgid "Scroll to page top"
10158 10163 msgstr ""
10159 10164
10160 10165 #: rhodecode/templates/codeblocks/diffs.mako:930
10161 10166 msgid "Toggle wide diff"
10162 10167 msgstr ""
10163 10168
10164 10169 #: rhodecode/templates/codeblocks/diffs.mako:938
10165 10170 msgid "View diff as side by side"
10166 10171 msgstr ""
10167 10172
10168 10173 #: rhodecode/templates/codeblocks/diffs.mako:940
10169 10174 msgid "Side by Side"
10170 10175 msgstr ""
10171 10176
10172 10177 #: rhodecode/templates/codeblocks/diffs.mako:945
10173 10178 msgid "View diff as unified"
10174 10179 msgstr ""
10175 10180
10176 10181 #: rhodecode/templates/codeblocks/diffs.mako:946
10177 10182 msgid "Unified"
10178 10183 msgstr ""
10179 10184
10180 10185 #: rhodecode/templates/codeblocks/diffs.mako:951
10181 10186 msgid "Turn off: Show the diff as commit range"
10182 10187 msgstr ""
10183 10188
10184 10189 #: rhodecode/templates/codeblocks/diffs.mako:954
10185 10190 #: rhodecode/templates/codeblocks/diffs.mako:961
10186 10191 msgid "Range Diff"
10187 10192 msgstr ""
10188 10193
10189 10194 #: rhodecode/templates/codeblocks/diffs.mako:958
10190 10195 msgid "Show the diff as commit range"
10191 10196 msgstr ""
10192 10197
10193 10198 #: rhodecode/templates/codeblocks/diffs.mako:1050
10194 10199 msgid "Disabled on range diff"
10195 10200 msgstr ""
10196 10201
10197 10202 #: rhodecode/templates/codeblocks/source.mako:22
10198 10203 msgid "view annotation from before this change"
10199 10204 msgstr ""
10200 10205
10201 10206 #: rhodecode/templates/commits/changelog.mako:6
10202 10207 #, python-format
10203 10208 msgid "%s Changelog"
10204 10209 msgstr ""
10205 10210
10206 10211 #: rhodecode/templates/commits/changelog.mako:38
10207 10212 msgid "Clear filter"
10208 10213 msgstr ""
10209 10214
10210 10215 #: rhodecode/templates/commits/changelog.mako:45
10211 10216 msgid "Hide obsolete/hidden"
10212 10217 msgstr ""
10213 10218
10214 10219 #: rhodecode/templates/commits/changelog.mako:47
10215 10220 msgid "Show obsolete/hidden"
10216 10221 msgstr ""
10217 10222
10218 10223 #: rhodecode/templates/commits/changelog.mako:50
10219 10224 msgid "Show hidden"
10220 10225 msgstr ""
10221 10226
10222 10227 #: rhodecode/templates/commits/changelog.mako:59
10223 10228 #: rhodecode/templates/compare/compare_diff.mako:93
10224 10229 #, python-format
10225 10230 msgid "Compare fork with %s"
10226 10231 msgstr ""
10227 10232
10228 10233 #: rhodecode/templates/commits/changelog.mako:69
10229 10234 #, python-format
10230 10235 msgid "Compare fork with Parent (%s)"
10231 10236 msgstr ""
10232 10237
10233 10238 #: rhodecode/templates/commits/changelog.mako:107
10234 10239 msgid "Clear selection"
10235 10240 msgstr ""
10236 10241
10237 10242 #: rhodecode/templates/commits/changelog.mako:110
10238 10243 msgid "Select second commit"
10239 10244 msgstr ""
10240 10245
10241 10246 #: rhodecode/templates/commits/changelog.mako:135
10242 10247 msgid "Commit Message"
10243 10248 msgstr ""
10244 10249
10245 10250 #: rhodecode/templates/commits/changelog.mako:137
10246 10251 #: rhodecode/templates/summary/summary_commits.mako:10
10247 10252 msgid "Age"
10248 10253 msgstr ""
10249 10254
10250 10255 #: rhodecode/templates/commits/changelog.mako:140
10251 10256 #: rhodecode/templates/summary/summary_commits.mako:12
10252 10257 msgid "Refs"
10253 10258 msgstr ""
10254 10259
10255 10260 #: rhodecode/templates/commits/changelog.mako:289
10256 10261 msgid "Branch filter"
10257 10262 msgstr ""
10258 10263
10259 10264 #: rhodecode/templates/commits/changelog.mako:350
10260 10265 msgid "There are no changes yet"
10261 10266 msgstr ""
10262 10267
10263 10268 #: rhodecode/templates/commits/changelog_elements.mako:8
10264 10269 msgid "load previous"
10265 10270 msgstr ""
10266 10271
10267 10272 #: rhodecode/templates/commits/changelog_elements.mako:35
10268 10273 #: rhodecode/templates/summary/summary_commits.mako:27
10269 10274 #, python-format
10270 10275 msgid ""
10271 10276 "Commit status: %s\n"
10272 10277 "Click to open associated pull request #%s"
10273 10278 msgstr ""
10274 10279
10275 10280 #: rhodecode/templates/commits/changelog_elements.mako:39
10276 10281 #: rhodecode/templates/summary/summary_commits.mako:31
10277 10282 msgid "Commit status: {}"
10278 10283 msgstr ""
10279 10284
10280 10285 #: rhodecode/templates/commits/changelog_elements.mako:45
10281 10286 #: rhodecode/templates/summary/summary_commits.mako:37
10282 10287 msgid "Commit status: Not Reviewed"
10283 10288 msgstr ""
10284 10289
10285 10290 #: rhodecode/templates/commits/changelog_elements.mako:63
10286 10291 msgid "{} commit phase"
10287 10292 msgstr ""
10288 10293
10289 10294 #: rhodecode/templates/commits/changelog_elements.mako:69
10290 10295 msgid "Obsolete Evolve State"
10291 10296 msgstr ""
10292 10297
10293 10298 #: rhodecode/templates/commits/changelog_elements.mako:74
10294 10299 msgid "Hidden Evolve State"
10295 10300 msgstr ""
10296 10301
10297 10302 #: rhodecode/templates/commits/changelog_elements.mako:80
10298 10303 #: rhodecode/templates/compare/compare_commits.mako:55
10299 10304 #: rhodecode/templates/pullrequests/pullrequest_show.mako:433
10300 10305 #: rhodecode/templates/search/search_commit.mako:34
10301 10306 msgid "Expand commit message"
10302 10307 msgstr ""
10303 10308
10304 10309 #: rhodecode/templates/commits/changelog_elements.mako:108
10305 10310 #: rhodecode/templates/files/base.mako:51
10306 10311 #: rhodecode/templates/summary/summary_commits.mako:78
10307 10312 #, python-format
10308 10313 msgid "Branch %s"
10309 10314 msgstr ""
10310 10315
10311 10316 #: rhodecode/templates/commits/changelog_elements.mako:116
10312 10317 #: rhodecode/templates/files/base.mako:32
10313 10318 #: rhodecode/templates/summary/summary_commits.mako:64
10314 10319 #, python-format
10315 10320 msgid "Bookmark %s"
10316 10321 msgstr ""
10317 10322
10318 10323 #: rhodecode/templates/commits/changelog_elements.mako:124
10319 10324 #: rhodecode/templates/files/base.mako:42
10320 10325 #: rhodecode/templates/summary/summary_commits.mako:71
10321 10326 #, python-format
10322 10327 msgid "Tag %s"
10323 10328 msgstr ""
10324 10329
10325 10330 #: rhodecode/templates/commits/changelog_elements.mako:135
10326 10331 #: rhodecode/templates/summary/summary_commits.mako:87
10327 10332 msgid "Commit has comments"
10328 10333 msgstr ""
10329 10334
10330 10335 #: rhodecode/templates/commits/changelog_elements.mako:150
10331 10336 msgid "load next"
10332 10337 msgstr ""
10333 10338
10334 10339 #: rhodecode/templates/commits/changelog_file_history.mako:33
10335 10340 msgid "Show File"
10336 10341 msgstr ""
10337 10342
10338 10343 #: rhodecode/templates/commits/changelog_file_history.mako:38
10339 10344 msgid "Diff File"
10340 10345 msgstr ""
10341 10346
10342 10347 #: rhodecode/templates/commits/changelog_file_history.mako:46
10343 10348 msgid "Show Full History"
10344 10349 msgstr ""
10345 10350
10346 10351 #: rhodecode/templates/compare/compare_commits.mako:5
10347 10352 msgid "Compare was calculated based on this common ancestor commit"
10348 10353 msgstr ""
10349 10354
10350 10355 #: rhodecode/templates/compare/compare_commits.mako:18
10351 10356 #: rhodecode/templates/pullrequests/pullrequest_show.mako:394
10352 10357 msgid "Time"
10353 10358 msgstr ""
10354 10359
10355 10360 #: rhodecode/templates/compare/compare_commits.mako:37
10356 10361 msgid "Pull request version this commit was introduced"
10357 10362 msgstr ""
10358 10363
10359 10364 #: rhodecode/templates/compare/compare_commits.mako:73
10360 10365 msgid "No commits in this compare"
10361 10366 msgstr ""
10362 10367
10363 10368 #: rhodecode/templates/compare/compare_diff.mako:7
10364 10369 #: rhodecode/templates/compare/compare_diff.mako:9
10365 10370 #, python-format
10366 10371 msgid "%s Compare"
10367 10372 msgstr ""
10368 10373
10369 10374 #: rhodecode/templates/compare/compare_diff.mako:38
10370 10375 #: rhodecode/templates/compare/compare_diff.mako:90
10371 10376 #: rhodecode/templates/compare/compare_diff.mako:114
10372 10377 #: rhodecode/templates/compare/compare_diff.mako:119
10373 10378 msgid "Compare Commits"
10374 10379 msgstr ""
10375 10380
10376 10381 #: rhodecode/templates/compare/compare_diff.mako:40
10377 10382 msgid "for file"
10378 10383 msgstr ""
10379 10384
10380 10385 #: rhodecode/templates/compare/compare_diff.mako:56
10381 10386 msgid "Target"
10382 10387 msgstr ""
10383 10388
10384 10389 #: rhodecode/templates/compare/compare_diff.mako:70
10385 10390 #: rhodecode/templates/files/files_source.mako:110
10386 10391 msgid "Source"
10387 10392 msgstr ""
10388 10393
10389 10394 #: rhodecode/templates/compare/compare_diff.mako:102
10390 10395 msgid "Compare with origin"
10391 10396 msgstr ""
10392 10397
10393 10398 #: rhodecode/templates/compare/compare_diff.mako:107
10394 10399 #: rhodecode/templates/compare/compare_diff.mako:108
10395 10400 #: rhodecode/templates/compare/compare_diff.mako:114
10396 10401 #: rhodecode/templates/compare/compare_diff.mako:115
10397 10402 #: rhodecode/templates/compare/compare_diff.mako:116
10398 10403 #: rhodecode/templates/compare/compare_diff.mako:126
10399 10404 msgid "Action unavailable in current view"
10400 10405 msgstr ""
10401 10406
10402 10407 #: rhodecode/templates/compare/compare_diff.mako:107
10403 10408 #: rhodecode/templates/compare/compare_diff.mako:115
10404 10409 #: rhodecode/templates/compare/compare_diff.mako:120
10405 10410 msgid "Swap"
10406 10411 msgstr ""
10407 10412
10408 10413 #: rhodecode/templates/compare/compare_diff.mako:110
10409 10414 msgid "Compare commits, branches, bookmarks or tags."
10410 10415 msgstr ""
10411 10416
10412 10417 #: rhodecode/templates/compare/compare_diff.mako:139
10413 10418 #: rhodecode/templates/email_templates/commit_comment.mako:57
10414 10419 msgid "Commit status"
10415 10420 msgstr ""
10416 10421
10417 10422 #: rhodecode/templates/data_table/_dt_elements.mako:73
10418 10423 #: rhodecode/templates/data_table/_dt_elements.mako:74
10419 10424 msgid "Fork"
10420 10425 msgstr ""
10421 10426
10422 10427 #: rhodecode/templates/data_table/_dt_elements.mako:95
10423 10428 msgid "Mercurial repository"
10424 10429 msgstr ""
10425 10430
10426 10431 #: rhodecode/templates/data_table/_dt_elements.mako:97
10427 10432 msgid "Git repository"
10428 10433 msgstr ""
10429 10434
10430 10435 #: rhodecode/templates/data_table/_dt_elements.mako:99
10431 10436 msgid "Subversion repository"
10432 10437 msgstr ""
10433 10438
10434 10439 #: rhodecode/templates/data_table/_dt_elements.mako:106
10435 10440 msgid "Public repository"
10436 10441 msgstr ""
10437 10442
10438 10443 #: rhodecode/templates/data_table/_dt_elements.mako:116
10439 10444 msgid "This repository is being created in a background task"
10440 10445 msgstr ""
10441 10446
10442 10447 #: rhodecode/templates/data_table/_dt_elements.mako:117
10443 10448 msgid "creating..."
10444 10449 msgstr ""
10445 10450
10446 10451 #: rhodecode/templates/data_table/_dt_elements.mako:149
10447 10452 msgid "No commits yet"
10448 10453 msgstr ""
10449 10454
10450 10455 #: rhodecode/templates/data_table/_dt_elements.mako:156
10451 10456 #: rhodecode/templates/data_table/_dt_elements.mako:158
10452 10457 #, python-format
10453 10458 msgid "Subscribe to %s rss feed"
10454 10459 msgstr ""
10455 10460
10456 10461 #: rhodecode/templates/data_table/_dt_elements.mako:164
10457 10462 #: rhodecode/templates/data_table/_dt_elements.mako:166
10458 10463 #, python-format
10459 10464 msgid "Subscribe to %s atom feed"
10460 10465 msgstr ""
10461 10466
10462 10467 #: rhodecode/templates/data_table/_dt_elements.mako:191
10463 10468 msgid "Creating"
10464 10469 msgstr ""
10465 10470
10466 10471 #: rhodecode/templates/data_table/_dt_elements.mako:193
10467 10472 #: rhodecode/templates/hovercards/hovercard_pull_request.mako:10
10468 10473 msgid "Created"
10469 10474 msgstr ""
10470 10475
10471 10476 #: rhodecode/templates/data_table/_dt_elements.mako:236
10472 10477 msgid "personal"
10473 10478 msgstr ""
10474 10479
10475 10480 #: rhodecode/templates/data_table/_dt_elements.mako:387
10476 10481 #: rhodecode/templates/pullrequests/pullrequest_show.mako:57
10477 10482 msgid "Pull request !{}"
10478 10483 msgstr ""
10479 10484
10480 10485 #: rhodecode/templates/data_table/_dt_elements.mako:396
10481 10486 msgid "Work in progress"
10482 10487 msgstr ""
10483 10488
10484 #: rhodecode/templates/data_table/_dt_elements.mako:432
10489 #: rhodecode/templates/data_table/_dt_elements.mako:438
10485 10490 msgid "Info"
10486 10491 msgstr ""
10487 10492
10488 #: rhodecode/templates/data_table/_dt_elements.mako:473
10493 #: rhodecode/templates/data_table/_dt_elements.mako:480
10489 10494 #, python-format
10490 10495 msgid "Parsed using %s syntax"
10491 10496 msgstr ""
10492 10497
10493 10498 #: rhodecode/templates/debug_style/buttons.html:131
10494 10499 msgid "Confirm to remove this field: Field"
10495 10500 msgstr ""
10496 10501
10497 10502 #: rhodecode/templates/debug_style/form-elements.html:107
10498 10503 msgid "Default"
10499 10504 msgstr ""
10500 10505
10501 10506 #: rhodecode/templates/debug_style/form-elements.html:513
10502 10507 #: rhodecode/templates/debug_style/form-elements.html:565
10503 10508 #: rhodecode/templates/debug_style/forms.html:236
10504 10509 msgid "Chosen group members"
10505 10510 msgstr ""
10506 10511
10507 10512 #: rhodecode/templates/debug_style/form-elements.html:525
10508 10513 #: rhodecode/templates/debug_style/form-elements.html:569
10509 10514 #: rhodecode/templates/debug_style/forms.html:240
10510 10515 msgid "Remove all elements"
10511 10516 msgstr ""
10512 10517
10513 10518 #: rhodecode/templates/debug_style/form-elements.html:535
10514 10519 #: rhodecode/templates/debug_style/form-elements.html:579
10515 10520 #: rhodecode/templates/debug_style/forms.html:250
10516 10521 msgid "Available members"
10517 10522 msgstr ""
10518 10523
10519 10524 #: rhodecode/templates/debug_style/form-elements.html:543
10520 10525 #: rhodecode/templates/debug_style/form-elements.html:587
10521 10526 #: rhodecode/templates/debug_style/forms.html:258
10522 10527 msgid "Add all elements"
10523 10528 msgstr ""
10524 10529
10525 10530 #: rhodecode/templates/debug_style/forms.html:119
10526 10531 msgid "Some text..."
10527 10532 msgstr ""
10528 10533
10529 10534 #: rhodecode/templates/debug_style/forms.html:122
10530 10535 #: rhodecode/templates/debug_style/forms.html:255
10531 10536 msgid "Variable Item"
10532 10537 msgstr ""
10533 10538
10534 10539 #: rhodecode/templates/debug_style/forms.html:252
10535 10540 msgid "Some example text..."
10536 10541 msgstr ""
10537 10542
10538 10543 #: rhodecode/templates/debug_style/index.html:5
10539 10544 msgid "Debug Style"
10540 10545 msgstr ""
10541 10546
10542 10547 #: rhodecode/templates/debug_style/index.html:54
10543 10548 msgid "Index"
10544 10549 msgstr ""
10545 10550
10546 10551 #: rhodecode/templates/debug_style/index.html:56
10547 10552 msgid "Typography"
10548 10553 msgstr ""
10549 10554
10550 10555 #: rhodecode/templates/debug_style/index.html:57
10551 10556 msgid "Forms"
10552 10557 msgstr ""
10553 10558
10554 10559 #: rhodecode/templates/debug_style/index.html:58
10555 10560 msgid "Buttons"
10556 10561 msgstr ""
10557 10562
10558 10563 #: rhodecode/templates/debug_style/index.html:59
10559 10564 msgid "Labels"
10560 10565 msgstr ""
10561 10566
10562 10567 #: rhodecode/templates/debug_style/index.html:60
10563 10568 msgid "Alerts"
10564 10569 msgstr ""
10565 10570
10566 10571 #: rhodecode/templates/debug_style/index.html:61
10567 10572 msgid "Tables"
10568 10573 msgstr ""
10569 10574
10570 10575 #: rhodecode/templates/debug_style/index.html:62
10571 10576 msgid "Tables wide"
10572 10577 msgstr ""
10573 10578
10574 10579 #: rhodecode/templates/debug_style/index.html:63
10575 10580 msgid "Collapsable Content"
10576 10581 msgstr ""
10577 10582
10578 10583 #: rhodecode/templates/debug_style/index.html:65
10579 10584 msgid "Layout form with sidebar"
10580 10585 msgstr ""
10581 10586
10582 10587 #: rhodecode/templates/debug_style/index.html:66
10583 10588 msgid "Login"
10584 10589 msgstr ""
10585 10590
10586 10591 #: rhodecode/templates/debug_style/index.html:67
10587 10592 msgid "Login 2"
10588 10593 msgstr ""
10589 10594
10590 10595 #: rhodecode/templates/debug_style/index.html:68
10591 10596 msgid "Code blocks"
10592 10597 msgstr ""
10593 10598
10594 10599 #: rhodecode/templates/debug_style/index.html:71
10595 10600 msgid "Panels"
10596 10601 msgstr ""
10597 10602
10598 10603 #: rhodecode/templates/debug_style/index.html:74
10599 10604 msgid "Form elements"
10600 10605 msgstr ""
10601 10606
10602 10607 #: rhodecode/templates/debug_style/index.html:75
10603 10608 msgid "Form elements small"
10604 10609 msgstr ""
10605 10610
10606 10611 #: rhodecode/templates/debug_style/index.html:76
10607 10612 msgid "Form inline elements"
10608 10613 msgstr ""
10609 10614
10610 10615 #: rhodecode/templates/debug_style/index.html:77
10611 10616 msgid "Form vertical"
10612 10617 msgstr ""
10613 10618
10614 10619 #: rhodecode/templates/debug_style/login.html:28
10615 10620 msgid "Sign in to your account"
10616 10621 msgstr ""
10617 10622
10618 10623 #: rhodecode/templates/debug_style/login.html:46
10619 10624 msgid "(Forgot password?)"
10620 10625 msgstr ""
10621 10626
10622 10627 #: rhodecode/templates/debug_style/login.html:56
10623 10628 msgid "Don't have an account ?"
10624 10629 msgstr ""
10625 10630
10626 10631 #: rhodecode/templates/email_templates/base.mako:72
10627 10632 #: rhodecode/templates/email_templates/base.mako:639
10628 10633 msgid "This is a notification from RhodeCode."
10629 10634 msgstr ""
10630 10635
10631 10636 #: rhodecode/templates/email_templates/base.mako:619
10632 10637 msgid "RhodeCode"
10633 10638 msgstr ""
10634 10639
10635 10640 #: rhodecode/templates/email_templates/commit_comment.mako:24
10636 10641 msgid "{mention_prefix}{user} left a {comment_type} on file `{comment_file}` in commit `{commit_id}` in the `{repo_name}` repository"
10637 10642 msgstr ""
10638 10643
10639 10644 #: rhodecode/templates/email_templates/commit_comment.mako:28
10640 10645 msgid "{mention_prefix}[status: {status}] {user} left a {comment_type} on commit `{commit_id}` in the `{repo_name}` repository"
10641 10646 msgstr ""
10642 10647
10643 10648 #: rhodecode/templates/email_templates/commit_comment.mako:31
10644 10649 msgid "{mention_prefix}{user} left a {comment_type} on commit `{commit_id}` in the `{repo_name}` repository"
10645 10650 msgstr ""
10646 10651
10647 10652 #: rhodecode/templates/email_templates/commit_comment.mako:54
10648 #: rhodecode/templates/email_templates/pull_request_comment.mako:63
10653 #: rhodecode/templates/email_templates/pull_request_comment.mako:62
10649 10654 msgid "Comment link"
10650 10655 msgstr ""
10651 10656
10652 10657 #: rhodecode/templates/email_templates/commit_comment.mako:57
10653 10658 #: rhodecode/templates/email_templates/commit_comment.mako:135
10654 10659 msgid "Status was changed to"
10655 10660 msgstr ""
10656 10661
10657 10662 #: rhodecode/templates/email_templates/commit_comment.mako:62
10658 10663 #: rhodecode/templates/email_templates/commit_comment.mako:147
10659 10664 #: rhodecode/templates/search/search_commit.mako:12
10660 10665 #: rhodecode/templates/summary/summary_commits.mako:9
10661 10666 msgid "Commit message"
10662 10667 msgstr ""
10663 10668
10664 10669 #: rhodecode/templates/email_templates/commit_comment.mako:65
10665 #: rhodecode/templates/email_templates/pull_request_comment.mako:77
10670 #: rhodecode/templates/email_templates/pull_request_comment.mako:76
10666 10671 msgid "File: {comment_file} on line {comment_line}"
10667 10672 msgstr ""
10668 10673
10669 10674 #: rhodecode/templates/email_templates/commit_comment.mako:69
10670 10675 #: rhodecode/templates/email_templates/commit_comment.mako:161
10671 #: rhodecode/templates/email_templates/pull_request_comment.mako:81
10672 #: rhodecode/templates/email_templates/pull_request_comment.mako:191
10676 #: rhodecode/templates/email_templates/pull_request_comment.mako:80
10677 #: rhodecode/templates/email_templates/pull_request_comment.mako:190
10673 10678 msgid "`TODO` number"
10674 10679 msgstr ""
10675 10680
10676 10681 #: rhodecode/templates/email_templates/commit_comment.mako:71
10677 10682 #: rhodecode/templates/email_templates/commit_comment.mako:163
10678 #: rhodecode/templates/email_templates/pull_request_comment.mako:83
10679 #: rhodecode/templates/email_templates/pull_request_comment.mako:193
10683 #: rhodecode/templates/email_templates/pull_request_comment.mako:82
10684 #: rhodecode/templates/email_templates/pull_request_comment.mako:192
10680 10685 msgid "`Note` number"
10681 10686 msgstr ""
10682 10687
10683 10688 #: rhodecode/templates/email_templates/commit_comment.mako:104
10684 #: rhodecode/templates/email_templates/pull_request_comment.mako:124
10689 #: rhodecode/templates/email_templates/pull_request_comment.mako:123
10685 10690 msgid "left a"
10686 10691 msgstr ""
10687 10692
10688 10693 #: rhodecode/templates/email_templates/commit_comment.mako:107
10689 10694 msgid "{comment_type} on file `{comment_file}` in commit."
10690 10695 msgstr ""
10691 10696
10692 10697 #: rhodecode/templates/email_templates/commit_comment.mako:109
10693 10698 msgid "{comment_type} on commit."
10694 10699 msgstr ""
10695 10700
10696 10701 #: rhodecode/templates/email_templates/commit_comment.mako:114
10697 10702 msgid "of repository"
10698 10703 msgstr ""
10699 10704
10700 10705 #: rhodecode/templates/email_templates/commit_comment.mako:133
10701 10706 msgid "Commit Status"
10702 10707 msgstr ""
10703 10708
10704 10709 #: rhodecode/templates/email_templates/commit_comment.mako:153
10705 #: rhodecode/templates/email_templates/pull_request_comment.mako:183
10710 #: rhodecode/templates/email_templates/pull_request_comment.mako:182
10706 10711 #: rhodecode/templates/search/search_path.mako:9
10707 10712 msgid "File"
10708 10713 msgstr ""
10709 10714
10710 10715 #: rhodecode/templates/email_templates/commit_comment.mako:154
10711 #: rhodecode/templates/email_templates/pull_request_comment.mako:184
10716 #: rhodecode/templates/email_templates/pull_request_comment.mako:183
10712 10717 msgid "`{comment_file}` on line {comment_line}"
10713 10718 msgstr ""
10714 10719
10715 10720 #: rhodecode/templates/email_templates/commit_comment.mako:173
10716 #: rhodecode/templates/email_templates/pull_request_comment.mako:203
10721 #: rhodecode/templates/email_templates/pull_request_comment.mako:202
10717 10722 msgid "Reply"
10718 10723 msgstr ""
10719 10724
10720 10725 #: rhodecode/templates/email_templates/pull_request_comment.mako:24
10721 10726 msgid "{mention_prefix}{user} left a {comment_type} on file `{comment_file}` in pull request !{pr_id}: \"{pr_title}\""
10722 10727 msgstr ""
10723 10728
10724 10729 #: rhodecode/templates/email_templates/pull_request_comment.mako:28
10725 10730 msgid "{mention_prefix}[status: {status}] {user} left a {comment_type} on pull request !{pr_id}: \"{pr_title}\""
10726 10731 msgstr ""
10727 10732
10728 10733 #: rhodecode/templates/email_templates/pull_request_comment.mako:31
10729 10734 msgid "{mention_prefix}{user} left a {comment_type} on pull request !{pr_id}: \"{pr_title}\""
10730 10735 msgstr ""
10731 10736
10732 #: rhodecode/templates/email_templates/pull_request_comment.mako:65
10737 #: rhodecode/templates/email_templates/pull_request_comment.mako:64
10733 10738 #: rhodecode/templates/hovercards/hovercard_pull_request.mako:24
10734 10739 msgid "Pull Request"
10735 10740 msgstr ""
10736 10741
10737 #: rhodecode/templates/email_templates/pull_request_comment.mako:67
10742 #: rhodecode/templates/email_templates/pull_request_comment.mako:66
10738 10743 #: rhodecode/templates/email_templates/pull_request_review.mako:45
10739 10744 #: rhodecode/templates/email_templates/pull_request_update.mako:41
10740 10745 msgid "Commit flow: {source_ref_type}:{source_ref_name} of {source_repo_url} into {target_ref_type}:{target_ref_name} of {target_repo_url}"
10741 10746 msgstr ""
10742 10747
10743 #: rhodecode/templates/email_templates/pull_request_comment.mako:70
10748 #: rhodecode/templates/email_templates/pull_request_comment.mako:69
10744 10749 msgid "{user} submitted pull request !{pr_id} status: *{status}*"
10745 10750 msgstr ""
10746 10751
10747 #: rhodecode/templates/email_templates/pull_request_comment.mako:73
10752 #: rhodecode/templates/email_templates/pull_request_comment.mako:72
10748 10753 msgid "{user} submitted pull request !{pr_id} status: *{status} and closed*"
10749 10754 msgstr ""
10750 10755
10751 #: rhodecode/templates/email_templates/pull_request_comment.mako:127
10756 #: rhodecode/templates/email_templates/pull_request_comment.mako:126
10752 10757 msgid "{comment_type} on file `{comment_file}` in pull request."
10753 10758 msgstr ""
10754 10759
10755 #: rhodecode/templates/email_templates/pull_request_comment.mako:129
10760 #: rhodecode/templates/email_templates/pull_request_comment.mako:128
10756 10761 msgid "{comment_type} on pull request."
10757 10762 msgstr ""
10758 10763
10759 #: rhodecode/templates/email_templates/pull_request_comment.mako:134
10760 #: rhodecode/templates/email_templates/pull_request_comment.mako:164
10764 #: rhodecode/templates/email_templates/pull_request_comment.mako:133
10765 #: rhodecode/templates/email_templates/pull_request_comment.mako:163
10761 10766 #: rhodecode/templates/email_templates/pull_request_review.mako:100
10762 10767 #: rhodecode/templates/email_templates/pull_request_review.mako:116
10763 10768 #: rhodecode/templates/email_templates/pull_request_update.mako:104
10764 10769 #: rhodecode/templates/email_templates/pull_request_update.mako:121
10765 10770 msgid "Pull request"
10766 10771 msgstr ""
10767 10772
10768 #: rhodecode/templates/email_templates/pull_request_comment.mako:153
10773 #: rhodecode/templates/email_templates/pull_request_comment.mako:152
10769 10774 msgid "Review Status"
10770 10775 msgstr ""
10771 10776
10772 #: rhodecode/templates/email_templates/pull_request_comment.mako:156
10777 #: rhodecode/templates/email_templates/pull_request_comment.mako:155
10773 10778 msgid "Closed pull request with status"
10774 10779 msgstr ""
10775 10780
10776 #: rhodecode/templates/email_templates/pull_request_comment.mako:158
10781 #: rhodecode/templates/email_templates/pull_request_comment.mako:157
10777 10782 msgid "Submitted review status"
10778 10783 msgstr ""
10779 10784
10780 #: rhodecode/templates/email_templates/pull_request_comment.mako:173
10785 #: rhodecode/templates/email_templates/pull_request_comment.mako:172
10781 10786 #: rhodecode/templates/email_templates/pull_request_review.mako:125
10782 10787 #: rhodecode/templates/email_templates/pull_request_update.mako:130
10783 10788 msgid "Commit Flow"
10784 10789 msgstr ""
10785 10790
10786 #: rhodecode/templates/email_templates/pull_request_comment.mako:175
10787 #: rhodecode/templates/email_templates/pull_request_comment.mako:177
10791 #: rhodecode/templates/email_templates/pull_request_comment.mako:174
10792 #: rhodecode/templates/email_templates/pull_request_comment.mako:176
10788 10793 #: rhodecode/templates/email_templates/pull_request_review.mako:127
10789 10794 #: rhodecode/templates/email_templates/pull_request_review.mako:129
10790 10795 #: rhodecode/templates/email_templates/pull_request_update.mako:132
10791 10796 #: rhodecode/templates/email_templates/pull_request_update.mako:134
10792 10797 #: rhodecode/templates/pullrequests/pullrequest_show.mako:112
10793 10798 #: rhodecode/templates/pullrequests/pullrequest_show.mako:121
10794 10799 msgid "of"
10795 10800 msgstr ""
10796 10801
10797 10802 #: rhodecode/templates/email_templates/pull_request_review.mako:15
10798 10803 msgid "{user} added you as observer to pull request. !{pr_id}: \"{pr_title}\""
10799 10804 msgstr ""
10800 10805
10801 10806 #: rhodecode/templates/email_templates/pull_request_review.mako:17
10802 10807 msgid "{user} requested a pull request review. !{pr_id}: \"{pr_title}\""
10803 10808 msgstr ""
10804 10809
10805 10810 #: rhodecode/templates/email_templates/pull_request_review.mako:43
10806 10811 #: rhodecode/templates/email_templates/pull_request_update.mako:39
10807 10812 msgid "Pull Request link"
10808 10813 msgstr ""
10809 10814
10810 10815 #: rhodecode/templates/email_templates/pull_request_review.mako:89
10811 10816 msgid "added you as observer to"
10812 10817 msgstr ""
10813 10818
10814 10819 #: rhodecode/templates/email_templates/pull_request_review.mako:95
10815 10820 msgid "requested a"
10816 10821 msgstr ""
10817 10822
10818 10823 #: rhodecode/templates/email_templates/pull_request_update.mako:14
10819 10824 msgid "{updating_user} updated pull request. !{pr_id}: \"{pr_title}\""
10820 10825 msgstr ""
10821 10826
10822 10827 #: rhodecode/templates/email_templates/pull_request_update.mako:98
10823 10828 msgid "updated"
10824 10829 msgstr ""
10825 10830
10826 10831 #: rhodecode/templates/email_templates/pull_request_update.mako:100
10827 10832 msgid "pull request."
10828 10833 msgstr ""
10829 10834
10830 10835 #: rhodecode/templates/email_templates/pull_request_update.mako:143
10831 10836 msgid "Changes"
10832 10837 msgstr ""
10833 10838
10834 10839 #: rhodecode/templates/email_templates/test.mako:5
10835 10840 msgid "hello \"world\""
10836 10841 msgstr ""
10837 10842
10838 10843 #: rhodecode/templates/email_templates/test.mako:18
10839 10844 msgid "Translation String"
10840 10845 msgstr ""
10841 10846
10842 10847 #: rhodecode/templates/email_templates/user_registration.mako:28
10843 10848 msgid "New user {user} has registered on {date}"
10844 10849 msgstr ""
10845 10850
10846 10851 #: rhodecode/templates/email_templates/user_registration.mako:47
10847 10852 msgid "Full Name"
10848 10853 msgstr ""
10849 10854
10850 10855 #: rhodecode/templates/errors/error_document.mako:48
10851 10856 #, python-format
10852 10857 msgid "You will be redirected to %s in %s seconds"
10853 10858 msgstr ""
10854 10859
10855 10860 #: rhodecode/templates/errors/error_document.mako:66
10856 10861 msgid "Support Page"
10857 10862 msgstr ""
10858 10863
10859 10864 #: rhodecode/templates/feed/atom_feed_entry.mako:3
10860 10865 #, python-format
10861 10866 msgid "%(user)s commited on %(date)s UTC"
10862 10867 msgstr ""
10863 10868
10864 10869 #: rhodecode/templates/feed/atom_feed_entry.mako:30
10865 10870 msgid "Commit was too big and was cut off..."
10866 10871 msgstr ""
10867 10872
10868 10873 #: rhodecode/templates/files/file_authors_box.mako:18
10869 10874 msgid "Load All Authors"
10870 10875 msgstr ""
10871 10876
10872 10877 #: rhodecode/templates/files/file_authors_box.mako:20
10873 10878 msgid "last author"
10874 10879 msgstr ""
10875 10880
10876 10881 #: rhodecode/templates/files/files.mako:4
10877 10882 #: rhodecode/templates/files/files_pjax.mako:2
10878 10883 msgid "{} Files"
10879 10884 msgstr ""
10880 10885
10881 10886 #: rhodecode/templates/files/files_add.mako:4
10882 10887 msgid "{} Files Add"
10883 10888 msgstr ""
10884 10889
10885 10890 #: rhodecode/templates/files/files_add.mako:25
10886 10891 msgid "Add new file"
10887 10892 msgstr ""
10888 10893
10889 10894 #: rhodecode/templates/files/files_add.mako:43
10890 10895 #: rhodecode/templates/files/files_edit.mako:44
10891 10896 msgid "Filename e.g example.py, or docs/readme.md"
10892 10897 msgstr ""
10893 10898
10894 10899 #: rhodecode/templates/files/files_add.mako:63
10895 10900 #: rhodecode/templates/files/files_edit.mako:65
10896 10901 msgid "Line wraps on"
10897 10902 msgstr ""
10898 10903
10899 10904 #: rhodecode/templates/files/files_add.mako:63
10900 10905 #: rhodecode/templates/files/files_edit.mako:65
10901 10906 msgid "line wraps off"
10902 10907 msgstr ""
10903 10908
10904 10909 #: rhodecode/templates/files/files_add.mako:86
10905 10910 #: rhodecode/templates/files/files_delete.mako:71
10906 10911 #: rhodecode/templates/files/files_edit.mako:88
10907 10912 #: rhodecode/templates/files/files_upload.mako:102
10908 10913 msgid "Commit changes"
10909 10914 msgstr ""
10910 10915
10911 10916 #: rhodecode/templates/files/files_browser.mako:9
10912 10917 msgid "Previous commit"
10913 10918 msgstr ""
10914 10919
10915 10920 #: rhodecode/templates/files/files_browser.mako:15
10916 10921 msgid "Next commit"
10917 10922 msgstr ""
10918 10923
10919 10924 #: rhodecode/templates/files/files_browser.mako:24
10920 10925 msgid "Add File"
10921 10926 msgstr ""
10922 10927
10923 10928 #: rhodecode/templates/files/files_browser.mako:27
10924 10929 msgid "more options"
10925 10930 msgstr ""
10926 10931
10927 10932 #: rhodecode/templates/files/files_browser.mako:36
10928 10933 msgid "Upload File"
10929 10934 msgstr ""
10930 10935
10931 10936 #: rhodecode/templates/files/files_browser.mako:50
10932 10937 msgid "Full tree as {}"
10933 10938 msgstr ""
10934 10939
10935 10940 #: rhodecode/templates/files/files_browser.mako:53
10936 10941 msgid "This tree as {}"
10937 10942 msgstr ""
10938 10943
10939 10944 #: rhodecode/templates/files/files_browser.mako:63
10940 10945 #: rhodecode/templates/summary/components.mako:196
10941 10946 msgid "more download options"
10942 10947 msgstr ""
10943 10948
10944 10949 #: rhodecode/templates/files/files_browser.mako:106
10945 10950 #: rhodecode/templates/summary/summary.mako:37
10946 10951 #, python-format
10947 10952 msgid "Readme file from commit %s:%s"
10948 10953 msgstr ""
10949 10954
10950 10955 #: rhodecode/templates/files/files_browser_tree.mako:17
10951 10956 #: rhodecode/templates/search/search_path.mako:12
10952 10957 msgid "Size"
10953 10958 msgstr ""
10954 10959
10955 10960 #: rhodecode/templates/files/files_browser_tree.mako:18
10956 10961 msgid "Modified"
10957 10962 msgstr ""
10958 10963
10959 10964 #: rhodecode/templates/files/files_browser_tree.mako:19
10960 10965 msgid "Last Commit"
10961 10966 msgstr ""
10962 10967
10963 10968 #: rhodecode/templates/files/files_delete.mako:4
10964 10969 msgid "{} Files Delete"
10965 10970 msgstr ""
10966 10971
10967 10972 #: rhodecode/templates/files/files_delete.mako:25
10968 10973 msgid "Delete file"
10969 10974 msgstr ""
10970 10975
10971 10976 #: rhodecode/templates/files/files_delete.mako:52
10972 10977 #, python-format
10973 10978 msgid "Binary file (%s)"
10974 10979 msgstr ""
10975 10980
10976 10981 #: rhodecode/templates/files/files_delete.mako:57
10977 10982 #: rhodecode/templates/files/files_source.mako:138
10978 10983 msgid "File size {} is bigger then allowed limit {}. "
10979 10984 msgstr ""
10980 10985
10981 10986 #: rhodecode/templates/files/files_edit.mako:4
10982 10987 msgid "{} Files Edit"
10983 10988 msgstr ""
10984 10989
10985 10990 #: rhodecode/templates/files/files_edit.mako:25
10986 10991 msgid "Edit file"
10987 10992 msgstr ""
10988 10993
10989 10994 #: rhodecode/templates/files/files_source.mako:26
10990 10995 msgid "Download largefile"
10991 10996 msgstr ""
10992 10997
10993 10998 #: rhodecode/templates/files/files_source.mako:30
10994 10999 msgid "Download file"
10995 11000 msgstr ""
10996 11001
10997 11002 #: rhodecode/templates/files/files_source.mako:39
10998 11003 msgid "Editing binary files not allowed"
10999 11004 msgstr ""
11000 11005
11001 11006 #: rhodecode/templates/files/files_source.mako:43
11002 11007 msgid "Edit on branch: "
11003 11008 msgstr ""
11004 11009
11005 11010 #: rhodecode/templates/files/files_source.mako:52
11006 11011 msgid "Editing files allowed only when on branch head commit"
11007 11012 msgstr ""
11008 11013
11009 11014 #: rhodecode/templates/files/files_source.mako:53
11010 11015 msgid "Deleting files allowed only when on branch head commit"
11011 11016 msgstr ""
11012 11017
11013 11018 #: rhodecode/templates/files/files_source.mako:75
11014 11019 msgid "lines"
11015 11020 msgstr ""
11016 11021
11017 11022 #: rhodecode/templates/files/files_source.mako:83
11018 11023 msgid "This file is a pointer to large binary file"
11019 11024 msgstr ""
11020 11025
11021 11026 #: rhodecode/templates/files/files_source.mako:83
11022 11027 msgid "LargeFile"
11023 11028 msgstr ""
11024 11029
11025 11030 #: rhodecode/templates/files/files_source.mako:106
11026 11031 msgid "History"
11027 11032 msgstr ""
11028 11033
11029 11034 #: rhodecode/templates/files/files_source.mako:112
11030 11035 msgid "Annotation"
11031 11036 msgstr ""
11032 11037
11033 11038 #: rhodecode/templates/files/files_source.mako:114
11034 11039 msgid "Raw"
11035 11040 msgstr ""
11036 11041
11037 11042 #: rhodecode/templates/files/files_source.mako:133
11038 11043 msgid "Binary file ({})"
11039 11044 msgstr ""
11040 11045
11041 11046 #: rhodecode/templates/files/files_source_header.mako:33
11042 11047 msgid "File last commit"
11043 11048 msgstr ""
11044 11049
11045 11050 #: rhodecode/templates/files/files_upload.mako:4
11046 11051 msgid "{} Files Upload"
11047 11052 msgstr ""
11048 11053
11049 11054 #: rhodecode/templates/files/files_upload.mako:36
11050 11055 msgid "Uploading..."
11051 11056 msgstr ""
11052 11057
11053 11058 #: rhodecode/templates/files/files_upload.mako:38
11054 11059 msgid "Uploaded"
11055 11060 msgstr ""
11056 11061
11057 11062 #: rhodecode/templates/files/files_upload.mako:52
11058 11063 msgid "Upload new file"
11059 11064 msgstr ""
11060 11065
11061 11066 #: rhodecode/templates/files/files_upload.mako:109
11062 11067 msgid "Committing..."
11063 11068 msgstr ""
11064 11069
11065 11070 #: rhodecode/templates/files/files_upload.mako:110
11066 11071 msgid "Please wait while the files are being uploaded"
11067 11072 msgstr ""
11068 11073
11069 11074 #: rhodecode/templates/forks/fork.mako:5
11070 11075 #, python-format
11071 11076 msgid "Fork repository %s"
11072 11077 msgstr ""
11073 11078
11074 11079 #: rhodecode/templates/forks/fork.mako:30
11075 11080 #: rhodecode/templates/forks/forks.mako:60
11076 11081 msgid "Fork name"
11077 11082 msgstr ""
11078 11083
11079 11084 #: rhodecode/templates/forks/fork.mako:47
11080 11085 #, python-format
11081 11086 msgid "Select my personal group (%(repo_group_name)s)"
11082 11087 msgstr ""
11083 11088
11084 11089 #: rhodecode/templates/forks/fork.mako:78
11085 11090 msgid "Copy permissions"
11086 11091 msgstr ""
11087 11092
11088 11093 #: rhodecode/templates/forks/fork.mako:82
11089 11094 msgid "Copy permissions from parent repository."
11090 11095 msgstr ""
11091 11096
11092 11097 #: rhodecode/templates/forks/fork.mako:88
11093 11098 msgid "Private"
11094 11099 msgstr ""
11095 11100
11096 11101 #: rhodecode/templates/forks/fork.mako:97
11097 11102 msgid "Fork this Repository"
11098 11103 msgstr ""
11099 11104
11100 11105 #: rhodecode/templates/forks/forks.mako:5
11101 11106 #, python-format
11102 11107 msgid "%s Forks"
11103 11108 msgstr ""
11104 11109
11105 11110 #: rhodecode/templates/forks/forks.mako:28
11106 11111 msgid "Create new fork"
11107 11112 msgstr ""
11108 11113
11109 11114 #: rhodecode/templates/forks/forks.mako:64
11110 11115 msgid "Forked"
11111 11116 msgstr ""
11112 11117
11113 11118 #: rhodecode/templates/journal/journal.mako:25
11114 11119 msgid "ATOM journal feed"
11115 11120 msgstr ""
11116 11121
11117 11122 #: rhodecode/templates/journal/journal.mako:26
11118 11123 msgid "RSS journal feed"
11119 11124 msgstr ""
11120 11125
11121 11126 #: rhodecode/templates/journal/journal_data.mako:45
11122 11127 msgid "No entries yet"
11123 11128 msgstr ""
11124 11129
11125 11130 #: rhodecode/templates/journal/public_journal.mako:4
11126 11131 #: rhodecode/templates/journal/public_journal.mako:12
11127 11132 msgid "Public Journal"
11128 11133 msgstr ""
11129 11134
11130 11135 #: rhodecode/templates/journal/public_journal.mako:21
11131 11136 msgid "ATOM public journal feed"
11132 11137 msgstr ""
11133 11138
11134 11139 #: rhodecode/templates/journal/public_journal.mako:22
11135 11140 msgid "RSS public journal feed"
11136 11141 msgstr ""
11137 11142
11138 11143 #: rhodecode/templates/pullrequests/pullrequest.mako:5
11139 11144 msgid "New pull request"
11140 11145 msgstr ""
11141 11146
11142 11147 #: rhodecode/templates/pullrequests/pullrequest.mako:33
11143 11148 #: rhodecode/templates/pullrequests/pullrequest_show.mako:102
11144 11149 msgid "Commit flow"
11145 11150 msgstr ""
11146 11151
11147 11152 #: rhodecode/templates/pullrequests/pullrequest.mako:41
11148 11153 msgid "Source repository"
11149 11154 msgstr ""
11150 11155
11151 11156 #: rhodecode/templates/pullrequests/pullrequest.mako:69
11152 11157 msgid "Target repository"
11153 11158 msgstr ""
11154 11159
11155 11160 #: rhodecode/templates/pullrequests/pullrequest.mako:76
11156 11161 msgid "Loading refs..."
11157 11162 msgstr ""
11158 11163
11159 11164 #: rhodecode/templates/pullrequests/pullrequest.mako:115
11160 11165 msgid "Reviewers / Observers"
11161 11166 msgstr ""
11162 11167
11163 11168 #: rhodecode/templates/pullrequests/pullrequest.mako:121
11164 11169 #: rhodecode/templates/pullrequests/pullrequest_show.mako:621
11165 11170 msgid "Reviewer rules"
11166 11171 msgstr ""
11167 11172
11168 11173 #: rhodecode/templates/pullrequests/pullrequest.mako:167
11169 11174 #: rhodecode/templates/pullrequests/pullrequest_show.mako:643
11170 11175 msgid "Add reviewer or reviewer group"
11171 11176 msgstr ""
11172 11177
11173 11178 #: rhodecode/templates/pullrequests/pullrequest.mako:191
11174 11179 #: rhodecode/templates/pullrequests/pullrequest_show.mako:696
11175 11180 msgid "Add observer or observer group"
11176 11181 msgstr ""
11177 11182
11178 11183 #: rhodecode/templates/pullrequests/pullrequest.mako:216
11179 11184 msgid "Submit Pull Request"
11180 11185 msgstr ""
11181 11186
11182 11187 #: rhodecode/templates/pullrequests/pullrequest.mako:392
11183 11188 msgid "Show detailed compare."
11184 11189 msgstr ""
11185 11190
11186 11191 #: rhodecode/templates/pullrequests/pullrequest.mako:498
11187 11192 #: rhodecode/templates/pullrequests/pullrequest.mako:526
11188 11193 msgid "Select commit reference"
11189 11194 msgstr ""
11190 11195
11191 11196 #: rhodecode/templates/pullrequests/pullrequest.mako:594
11192 11197 msgid "Please select source and target"
11193 11198 msgstr ""
11194 11199
11195 11200 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:7
11196 11201 msgid "This pull request can be merged automatically."
11197 11202 msgstr ""
11198 11203
11199 11204 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:12
11200 11205 msgid "Merge is not currently possible because of below failed checks."
11201 11206 msgstr ""
11202 11207
11203 11208 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:57
11204 11209 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:61
11205 11210 msgid "Close with status {}"
11206 11211 msgstr ""
11207 11212
11208 11213 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:66
11209 11214 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:76
11210 11215 msgid "Merge and close Pull Request"
11211 11216 msgstr ""
11212 11217
11213 11218 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:70
11214 11219 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:75
11215 11220 msgid "refresh checks"
11216 11221 msgstr ""
11217 11222
11218 11223 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:76
11219 11224 msgid "You are not allowed to merge this pull request."
11220 11225 msgstr ""
11221 11226
11222 11227 #: rhodecode/templates/pullrequests/pullrequest_merge_checks.mako:78
11223 11228 msgid "Login to Merge this Pull Request"
11224 11229 msgstr ""
11225 11230
11226 11231 #: rhodecode/templates/pullrequests/pullrequest_show.mako:8
11227 11232 msgid "{} Pull Request !{}"
11228 11233 msgstr ""
11229 11234
11230 11235 #: rhodecode/templates/pullrequests/pullrequest_show.mako:59
11231 11236 msgid "Last updated on"
11232 11237 msgstr ""
11233 11238
11234 11239 #: rhodecode/templates/pullrequests/pullrequest_show.mako:60
11235 11240 msgid "by"
11236 11241 msgstr ""
11237 11242
11238 11243 #: rhodecode/templates/pullrequests/pullrequest_show.mako:69
11239 11244 msgid "Update title & description"
11240 11245 msgstr ""
11241 11246
11242 11247 #: rhodecode/templates/pullrequests/pullrequest_show.mako:75
11243 11248 #: rhodecode/templates/pullrequests/pullrequest_show.mako:78
11244 11249 msgid "Delete pull request"
11245 11250 msgstr ""
11246 11251
11247 11252 #: rhodecode/templates/pullrequests/pullrequest_show.mako:78
11248 11253 msgid "Not allowed to delete this pull request"
11249 11254 msgstr ""
11250 11255
11251 11256 #: rhodecode/templates/pullrequests/pullrequest_show.mako:88
11252 11257 msgid "Rendered using {} renderer"
11253 11258 msgstr ""
11254 11259
11255 11260 #: rhodecode/templates/pullrequests/pullrequest_show.mako:135
11256 11261 msgid "Common ancestor"
11257 11262 msgstr ""
11258 11263
11259 11264 #: rhodecode/templates/pullrequests/pullrequest_show.mako:139
11260 11265 msgid "not available"
11261 11266 msgstr ""
11262 11267
11263 11268 #: rhodecode/templates/pullrequests/pullrequest_show.mako:151
11264 11269 msgid "Pull changes from source"
11265 11270 msgstr ""
11266 11271
11267 11272 #: rhodecode/templates/pullrequests/pullrequest_show.mako:152
11268 11273 msgid "Copy the pull url"
11269 11274 msgstr ""
11270 11275
11271 11276 #: rhodecode/templates/pullrequests/pullrequest_show.mako:164
11272 11277 msgid "Clone repository in its merged state using shadow repository"
11273 11278 msgstr ""
11274 11279
11275 11280 #: rhodecode/templates/pullrequests/pullrequest_show.mako:164
11276 11281 msgid "Clone from shadow repository"
11277 11282 msgstr ""
11278 11283
11279 11284 #: rhodecode/templates/pullrequests/pullrequest_show.mako:165
11280 11285 #: rhodecode/templates/summary/components.mako:78
11281 11286 msgid "Copy the clone url"
11282 11287 msgstr ""
11283 11288
11284 11289 #: rhodecode/templates/pullrequests/pullrequest_show.mako:169
11285 11290 msgid "Shadow repository data not available"
11286 11291 msgstr ""
11287 11292
11288 11293 #: rhodecode/templates/pullrequests/pullrequest_show.mako:185
11289 11294 msgid "Versions"
11290 11295 msgstr ""
11291 11296
11292 11297 #: rhodecode/templates/pullrequests/pullrequest_show.mako:197
11293 11298 #: rhodecode/templates/pullrequests/pullrequest_show.mako:199
11294 11299 msgid "show versions"
11295 11300 msgstr ""
11296 11301
11297 11302 #: rhodecode/templates/pullrequests/pullrequest_show.mako:198
11298 11303 msgid "hide versions"
11299 11304 msgstr ""
11300 11305
11301 11306 #: rhodecode/templates/pullrequests/pullrequest_show.mako:223
11302 11307 msgid "Your review status at this version"
11303 11308 msgstr ""
11304 11309
11305 11310 #: rhodecode/templates/pullrequests/pullrequest_show.mako:228
11306 11311 msgid "Comments from pull request version v{0}"
11307 11312 msgstr ""
11308 11313
11309 11314 #: rhodecode/templates/pullrequests/pullrequest_show.mako:246
11310 11315 #: rhodecode/templates/pullrequests/pullrequest_show.mako:250
11311 11316 msgid "select versions to show changes"
11312 11317 msgstr ""
11313 11318
11314 11319 #: rhodecode/templates/pullrequests/pullrequest_show.mako:247
11315 11320 msgid "show changes between versions"
11316 11321 msgstr ""
11317 11322
11318 11323 #: rhodecode/templates/pullrequests/pullrequest_show.mako:248
11319 11324 msgid "show pull request for this version"
11320 11325 msgstr ""
11321 11326
11322 11327 #: rhodecode/templates/pullrequests/pullrequest_show.mako:257
11323 11328 msgid "Pull request versions not available"
11324 11329 msgstr ""
11325 11330
11326 11331 #: rhodecode/templates/pullrequests/pullrequest_show.mako:277
11327 11332 msgid "Cannot show diff when pull request state is changing. Current progress state"
11328 11333 msgstr ""
11329 11334
11330 11335 #: rhodecode/templates/pullrequests/pullrequest_show.mako:295
11331 11336 msgid "Missing requirements:"
11332 11337 msgstr ""
11333 11338
11334 11339 #: rhodecode/templates/pullrequests/pullrequest_show.mako:296
11335 11340 msgid "These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled."
11336 11341 msgstr ""
11337 11342
11338 11343 #: rhodecode/templates/pullrequests/pullrequest_show.mako:304
11339 11344 msgid "Missing commits"
11340 11345 msgstr ""
11341 11346
11342 11347 #: rhodecode/templates/pullrequests/pullrequest_show.mako:305
11343 11348 msgid "This pull request cannot be displayed, because one or more commits no longer exist in the source repository."
11344 11349 msgstr ""
11345 11350
11346 11351 #: rhodecode/templates/pullrequests/pullrequest_show.mako:306
11347 11352 msgid "Please update this pull request, push the commits back into the source repository, or consider closing this pull request."
11348 11353 msgstr ""
11349 11354
11350 11355 #: rhodecode/templates/pullrequests/pullrequest_show.mako:307
11351 11356 msgid "Consider doing a `force update commits` in case you think this is an error."
11352 11357 msgstr ""
11353 11358
11354 11359 #: rhodecode/templates/pullrequests/pullrequest_show.mako:315
11355 11360 msgid "There are new changes for `{}:{}` in source repository, please consider updating this pull request."
11356 11361 msgstr ""
11357 11362
11358 11363 #: rhodecode/templates/pullrequests/pullrequest_show.mako:326
11359 11364 msgid "Showing changes at v{}, commenting is disabled."
11360 11365 msgstr ""
11361 11366
11362 11367 #: rhodecode/templates/pullrequests/pullrequest_show.mako:349
11363 11368 #: rhodecode/templates/pullrequests/pullrequest_show.mako:371
11364 11369 msgid "Update commits"
11365 11370 msgstr ""
11366 11371
11367 11372 #: rhodecode/templates/pullrequests/pullrequest_show.mako:352
11368 11373 msgid "more update options"
11369 11374 msgstr ""
11370 11375
11371 11376 #: rhodecode/templates/pullrequests/pullrequest_show.mako:360
11372 11377 msgid "Force update commits"
11373 11378 msgstr ""
11374 11379
11375 11380 #: rhodecode/templates/pullrequests/pullrequest_show.mako:363
11376 11381 msgid "Update commits and force refresh this pull request."
11377 11382 msgstr ""
11378 11383
11379 11384 #: rhodecode/templates/pullrequests/pullrequest_show.mako:371
11380 11385 msgid "Update is disabled for current view"
11381 11386 msgstr ""
11382 11387
11383 11388 #: rhodecode/templates/pullrequests/pullrequest_show.mako:383
11384 11389 msgid "Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled"
11385 11390 msgstr ""
11386 11391
11387 11392 #: rhodecode/templates/pullrequests/pullrequest_show.mako:387
11388 11393 msgid "commits added: {}, removed: {}"
11389 11394 msgstr ""
11390 11395
11391 11396 #: rhodecode/templates/pullrequests/pullrequest_show.mako:405
11392 11397 msgid "Commit added in displayed changes"
11393 11398 msgstr ""
11394 11399
11395 11400 #: rhodecode/templates/pullrequests/pullrequest_show.mako:407
11396 11401 msgid "Commit removed in displayed changes"
11397 11402 msgstr ""
11398 11403
11399 11404 #: rhodecode/templates/pullrequests/pullrequest_show.mako:510
11400 11405 msgid "there is {num} general comment from older versions"
11401 11406 msgstr ""
11402 11407
11403 11408 #: rhodecode/templates/pullrequests/pullrequest_show.mako:511
11404 11409 msgid "show it"
11405 11410 msgstr ""
11406 11411
11407 11412 #: rhodecode/templates/pullrequests/pullrequest_show.mako:513
11408 11413 msgid "there are {num} general comments from older versions"
11409 11414 msgstr ""
11410 11415
11411 11416 #: rhodecode/templates/pullrequests/pullrequest_show.mako:514
11412 11417 msgid "show them"
11413 11418 msgstr ""
11414 11419
11415 11420 #: rhodecode/templates/pullrequests/pullrequest_show.mako:556
11416 11421 #: rhodecode/templates/pullrequests/pullrequest_show.mako:567
11417 11422 msgid "Drafts"
11418 11423 msgstr ""
11419 11424
11420 11425 #: rhodecode/templates/pullrequests/pullrequest_show.mako:569
11421 11426 msgid "Submit"
11422 11427 msgstr ""
11423 11428
11424 11429 #: rhodecode/templates/pullrequests/pullrequest_show.mako:612
11425 11430 msgid "Show rules"
11426 11431 msgstr ""
11427 11432
11428 11433 #: rhodecode/templates/pullrequests/pullrequest_show.mako:648
11429 11434 #: rhodecode/templates/pullrequests/pullrequest_show.mako:701
11430 11435 msgid "Save Changes"
11431 11436 msgstr ""
11432 11437
11433 11438 #: rhodecode/templates/pullrequests/pullrequest_show.mako:672
11434 11439 msgid "Observers"
11435 11440 msgstr ""
11436 11441
11437 11442 #: rhodecode/templates/pullrequests/pullrequest_show.mako:738
11438 11443 msgid "TODOs unavailable when browsing versions"
11439 11444 msgstr ""
11440 11445
11441 11446 #: rhodecode/templates/pullrequests/pullrequest_show.mako:810
11442 11447 #: rhodecode/templates/pullrequests/pullrequest_show.mako:818
11443 11448 msgid "Referenced Tickets"
11444 11449 msgstr ""
11445 11450
11446 11451 #: rhodecode/templates/pullrequests/pullrequest_show.mako:824
11447 11452 msgid "In pull request description"
11448 11453 msgstr ""
11449 11454
11450 #: rhodecode/templates/pullrequests/pullrequest_show.mako:838
11451 #: rhodecode/templates/pullrequests/pullrequest_show.mako:857
11455 #: rhodecode/templates/pullrequests/pullrequest_show.mako:840
11456 #: rhodecode/templates/pullrequests/pullrequest_show.mako:860
11452 11457 msgid "No Ticket data found."
11453 11458 msgstr ""
11454 11459
11455 #: rhodecode/templates/pullrequests/pullrequest_show.mako:843
11460 #: rhodecode/templates/pullrequests/pullrequest_show.mako:845
11456 11461 msgid "In commit messages"
11457 11462 msgstr ""
11458 11463
11459 11464 #: rhodecode/templates/pullrequests/pullrequests.mako:4
11460 11465 msgid "{} Pull Requests"
11461 11466 msgstr ""
11462 11467
11463 11468 #: rhodecode/templates/pullrequests/pullrequests.mako:27
11464 11469 msgid "Opened"
11465 11470 msgstr ""
11466 11471
11467 11472 #: rhodecode/templates/pullrequests/pullrequests.mako:28
11468 11473 msgid "Opened by me"
11469 11474 msgstr ""
11470 11475
11471 11476 #: rhodecode/templates/pullrequests/pullrequests.mako:29
11472 11477 msgid "Awaiting review"
11473 11478 msgstr ""
11474 11479
11475 11480 #: rhodecode/templates/pullrequests/pullrequests.mako:30
11476 11481 msgid "Awaiting my review"
11477 11482 msgstr ""
11478 11483
11479 11484 #: rhodecode/templates/pullrequests/pullrequests.mako:32
11480 11485 msgid "From this repo"
11481 11486 msgstr ""
11482 11487
11483 11488 #: rhodecode/templates/pullrequests/pullrequests.mako:40
11484 11489 msgid "Open new Pull Request"
11485 11490 msgstr ""
11486 11491
11487 11492 #: rhodecode/templates/search/search.mako:6
11488 11493 #: rhodecode/templates/search/search.mako:19
11489 11494 msgid "Search inside repository {repo_name}"
11490 11495 msgstr ""
11491 11496
11492 11497 #: rhodecode/templates/search/search.mako:8
11493 11498 #: rhodecode/templates/search/search.mako:21
11494 11499 msgid "Search inside repository group {repo_group_name}"
11495 11500 msgstr ""
11496 11501
11497 11502 #: rhodecode/templates/search/search.mako:10
11498 11503 #: rhodecode/templates/search/search.mako:23
11499 11504 msgid "Search inside all accessible repositories"
11500 11505 msgstr ""
11501 11506
11502 11507 #: rhodecode/templates/search/search.mako:99
11503 11508 msgid "File path"
11504 11509 msgstr ""
11505 11510
11506 11511 #: rhodecode/templates/search/search.mako:102
11507 11512 msgid "Search"
11508 11513 msgstr ""
11509 11514
11510 11515 #: rhodecode/templates/search/search.mako:108
11511 11516 #: rhodecode/templates/search/search.mako:110
11512 11517 #: rhodecode/templates/search/search.mako:112
11513 11518 msgid "Global Search"
11514 11519 msgstr ""
11515 11520
11516 11521 #: rhodecode/templates/search/search.mako:138
11517 11522 msgid "sort"
11518 11523 msgstr ""
11519 11524
11520 11525 #: rhodecode/templates/search/search.mako:157
11521 11526 msgid "Query Language examples"
11522 11527 msgstr ""
11523 11528
11524 11529 #: rhodecode/templates/search/search_commit.mako:15
11525 11530 msgid "Commit date"
11526 11531 msgstr ""
11527 11532
11528 11533 #: rhodecode/templates/search/search_content.mako:7
11529 11534 msgid "No content matched"
11530 11535 msgstr ""
11531 11536
11532 11537 #: rhodecode/templates/search/search_content.mako:12
11533 11538 msgid "more matches in this file"
11534 11539 msgstr ""
11535 11540
11536 11541 #: rhodecode/templates/search/search_content.mako:112
11537 11542 msgid "Narrow to this repository group"
11538 11543 msgstr ""
11539 11544
11540 11545 #: rhodecode/templates/search/search_content.mako:119
11541 11546 msgid "Narrow to this repository"
11542 11547 msgstr ""
11543 11548
11544 11549 #: rhodecode/templates/search/search_path.mako:15
11545 11550 msgid "Lines"
11546 11551 msgstr ""
11547 11552
11548 11553 #: rhodecode/templates/summary/components.mako:20
11549 11554 msgid "Closed Branch"
11550 11555 msgstr ""
11551 11556
11552 11557 #: rhodecode/templates/summary/components.mako:83
11553 11558 msgid "Copy the clone by id url"
11554 11559 msgstr ""
11555 11560
11556 11561 #: rhodecode/templates/summary/components.mako:88
11557 11562 msgid "Copy the clone by ssh url"
11558 11563 msgstr ""
11559 11564
11560 11565 #: rhodecode/templates/summary/components.mako:92
11561 11566 msgid "SVN Protocol is disabled. To enable it, see the"
11562 11567 msgstr ""
11563 11568
11564 11569 #: rhodecode/templates/summary/components.mako:92
11565 11570 msgid "documentation here"
11566 11571 msgstr ""
11567 11572
11568 11573 #: rhodecode/templates/summary/components.mako:135
11569 11574 msgid "Number of Repository Forks"
11570 11575 msgstr ""
11571 11576
11572 11577 #: rhodecode/templates/summary/components.mako:172
11573 11578 msgid "Downloads"
11574 11579 msgstr ""
11575 11580
11576 11581 #: rhodecode/templates/summary/components.mako:177
11577 11582 msgid "There are no downloads yet"
11578 11583 msgstr ""
11579 11584
11580 11585 #: rhodecode/templates/summary/components.mako:181
11581 11586 msgid "Downloads are disabled for this repository"
11582 11587 msgstr ""
11583 11588
11584 11589 #: rhodecode/templates/summary/components.mako:227
11585 11590 msgid "Repository size"
11586 11591 msgstr ""
11587 11592
11588 11593 #: rhodecode/templates/summary/components.mako:239
11589 11594 msgid "Calculating Repository Size..."
11590 11595 msgstr ""
11591 11596
11592 11597 #: rhodecode/templates/summary/components.mako:250
11593 11598 msgid "Code Statistics"
11594 11599 msgstr ""
11595 11600
11596 11601 #: rhodecode/templates/summary/components.mako:259
11597 11602 msgid "Statistics are disabled for this repository"
11598 11603 msgstr ""
11599 11604
11600 11605 #: rhodecode/templates/summary/summary.mako:21
11601 11606 msgid "Quick start"
11602 11607 msgstr ""
11603 11608
11604 11609 #: rhodecode/templates/summary/summary_base.mako:5
11605 11610 msgid "{} Summary"
11606 11611 msgstr ""
11607 11612
11608 11613 #: rhodecode/templates/summary/summary_base.mako:13
11609 11614 #, python-format
11610 11615 msgid "%s ATOM feed"
11611 11616 msgstr ""
11612 11617
11613 11618 #: rhodecode/templates/summary/summary_base.mako:14
11614 11619 #, python-format
11615 11620 msgid "%s RSS feed"
11616 11621 msgstr ""
11617 11622
11618 11623 #: rhodecode/templates/summary/summary_commits.mako:117
11619 11624 msgid "Add or upload files directly via RhodeCode:"
11620 11625 msgstr ""
11621 11626
11622 11627 #: rhodecode/templates/summary/summary_commits.mako:119
11623 11628 msgid "Add New File"
11624 11629 msgstr ""
11625 11630
11626 11631 #: rhodecode/templates/summary/summary_commits.mako:122
11627 11632 msgid "Upload New File"
11628 11633 msgstr ""
11629 11634
11630 11635 #: rhodecode/templates/summary/summary_commits.mako:128
11631 11636 msgid "Push new repo:"
11632 11637 msgstr ""
11633 11638
11634 11639 #: rhodecode/templates/summary/summary_commits.mako:151
11635 11640 msgid "Existing repository?"
11636 11641 msgstr ""
11637 11642
11638 11643 #: rhodecode/templates/tags/tags.mako:5
11639 11644 #, python-format
11640 11645 msgid "%s Tags"
11641 11646 msgstr ""
11642 11647
11643 11648 #: rhodecode/templates/tags/tags.mako:28
11644 11649 msgid "Compare Selected Tags"
11645 11650 msgstr ""
11646 11651
11647 11652 #: rhodecode/templates/user_group/profile.mako:6
11648 11653 msgid "User Group Profile"
11649 11654 msgstr ""
11650 11655
11651 11656 #: rhodecode/templates/user_group/profile.mako:15
11652 11657 msgid "Group Name"
11653 11658 msgstr ""
11654 11659
11655 11660 #: rhodecode/templates/users/user_profile.mako:39
11656 11661 msgid "First name"
11657 11662 msgstr ""
11658 11663
11659 11664 #: rhodecode/templates/users/user_profile.mako:49
11660 11665 msgid "Last name"
11661 11666 msgstr ""
11662 11667
11663 11668 #: rhodecode/tests/lib/test_ext_json.py:162
11664 11669 msgid "hello"
11665 11670 msgstr ""
11666 11671
@@ -1,1937 +1,1938 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Base module for all VCS systems
23 23 """
24 24 import os
25 25 import re
26 26 import time
27 27 import shutil
28 28 import datetime
29 29 import fnmatch
30 30 import itertools
31 31 import logging
32 32 import collections
33 33 import warnings
34 34
35 35 from zope.cachedescriptors.property import Lazy as LazyProperty
36 36
37 37 from pyramid import compat
38 38
39 39 import rhodecode
40 40 from rhodecode.translation import lazy_ugettext
41 41 from rhodecode.lib.utils2 import safe_str, safe_unicode, CachedProperty
42 42 from rhodecode.lib.vcs import connection
43 43 from rhodecode.lib.vcs.utils import author_name, author_email
44 44 from rhodecode.lib.vcs.conf import settings
45 45 from rhodecode.lib.vcs.exceptions import (
46 46 CommitError, EmptyRepositoryError, NodeAlreadyAddedError,
47 47 NodeAlreadyChangedError, NodeAlreadyExistsError, NodeAlreadyRemovedError,
48 48 NodeDoesNotExistError, NodeNotChangedError, VCSError,
49 49 ImproperArchiveTypeError, BranchDoesNotExistError, CommitDoesNotExistError,
50 50 RepositoryError)
51 51
52 52
53 53 log = logging.getLogger(__name__)
54 54
55 55
56 56 FILEMODE_DEFAULT = 0o100644
57 57 FILEMODE_EXECUTABLE = 0o100755
58 58 EMPTY_COMMIT_ID = '0' * 40
59 59
60 60 _Reference = collections.namedtuple('Reference', ('type', 'name', 'commit_id'))
61 61
62 62
63 63 class Reference(_Reference):
64 64
65 65 @property
66 66 def branch(self):
67 67 if self.type == 'branch':
68 68 return self.name
69 69
70 70 @property
71 71 def bookmark(self):
72 72 if self.type == 'book':
73 73 return self.name
74 74
75 75 @property
76 76 def to_unicode(self):
77 77 return reference_to_unicode(self)
78 78
79 79
80 80 def unicode_to_reference(raw):
81 81 """
82 82 Convert a unicode (or string) to a reference object.
83 83 If unicode evaluates to False it returns None.
84 84 """
85 85 if raw:
86 86 refs = raw.split(':')
87 87 return Reference(*refs)
88 88 else:
89 89 return None
90 90
91 91
92 92 def reference_to_unicode(ref):
93 93 """
94 94 Convert a reference object to unicode.
95 95 If reference is None it returns None.
96 96 """
97 97 if ref:
98 98 return u':'.join(ref)
99 99 else:
100 100 return None
101 101
102 102
103 103 class MergeFailureReason(object):
104 104 """
105 105 Enumeration with all the reasons why the server side merge could fail.
106 106
107 107 DO NOT change the number of the reasons, as they may be stored in the
108 108 database.
109 109
110 110 Changing the name of a reason is acceptable and encouraged to deprecate old
111 111 reasons.
112 112 """
113 113
114 114 # Everything went well.
115 115 NONE = 0
116 116
117 117 # An unexpected exception was raised. Check the logs for more details.
118 118 UNKNOWN = 1
119 119
120 120 # The merge was not successful, there are conflicts.
121 121 MERGE_FAILED = 2
122 122
123 123 # The merge succeeded but we could not push it to the target repository.
124 124 PUSH_FAILED = 3
125 125
126 126 # The specified target is not a head in the target repository.
127 127 TARGET_IS_NOT_HEAD = 4
128 128
129 129 # The source repository contains more branches than the target. Pushing
130 130 # the merge will create additional branches in the target.
131 131 HG_SOURCE_HAS_MORE_BRANCHES = 5
132 132
133 133 # The target reference has multiple heads. That does not allow to correctly
134 134 # identify the target location. This could only happen for mercurial
135 135 # branches.
136 136 HG_TARGET_HAS_MULTIPLE_HEADS = 6
137 137
138 138 # The target repository is locked
139 139 TARGET_IS_LOCKED = 7
140 140
141 141 # Deprecated, use MISSING_TARGET_REF or MISSING_SOURCE_REF instead.
142 142 # A involved commit could not be found.
143 143 _DEPRECATED_MISSING_COMMIT = 8
144 144
145 145 # The target repo reference is missing.
146 146 MISSING_TARGET_REF = 9
147 147
148 148 # The source repo reference is missing.
149 149 MISSING_SOURCE_REF = 10
150 150
151 151 # The merge was not successful, there are conflicts related to sub
152 152 # repositories.
153 153 SUBREPO_MERGE_FAILED = 11
154 154
155 155
156 156 class UpdateFailureReason(object):
157 157 """
158 158 Enumeration with all the reasons why the pull request update could fail.
159 159
160 160 DO NOT change the number of the reasons, as they may be stored in the
161 161 database.
162 162
163 163 Changing the name of a reason is acceptable and encouraged to deprecate old
164 164 reasons.
165 165 """
166 166
167 167 # Everything went well.
168 168 NONE = 0
169 169
170 170 # An unexpected exception was raised. Check the logs for more details.
171 171 UNKNOWN = 1
172 172
173 173 # The pull request is up to date.
174 174 NO_CHANGE = 2
175 175
176 176 # The pull request has a reference type that is not supported for update.
177 177 WRONG_REF_TYPE = 3
178 178
179 179 # Update failed because the target reference is missing.
180 180 MISSING_TARGET_REF = 4
181 181
182 182 # Update failed because the source reference is missing.
183 183 MISSING_SOURCE_REF = 5
184 184
185 185
186 186 class MergeResponse(object):
187 187
188 188 # uses .format(**metadata) for variables
189 189 MERGE_STATUS_MESSAGES = {
190 190 MergeFailureReason.NONE: lazy_ugettext(
191 191 u'This pull request can be automatically merged.'),
192 192 MergeFailureReason.UNKNOWN: lazy_ugettext(
193 193 u'This pull request cannot be merged because of an unhandled exception. '
194 194 u'{exception}'),
195 195 MergeFailureReason.MERGE_FAILED: lazy_ugettext(
196 196 u'This pull request cannot be merged because of merge conflicts. {unresolved_files}'),
197 197 MergeFailureReason.PUSH_FAILED: lazy_ugettext(
198 198 u'This pull request could not be merged because push to '
199 199 u'target:`{target}@{merge_commit}` failed.'),
200 200 MergeFailureReason.TARGET_IS_NOT_HEAD: lazy_ugettext(
201 201 u'This pull request cannot be merged because the target '
202 202 u'`{target_ref.name}` is not a head.'),
203 203 MergeFailureReason.HG_SOURCE_HAS_MORE_BRANCHES: lazy_ugettext(
204 204 u'This pull request cannot be merged because the source contains '
205 205 u'more branches than the target.'),
206 206 MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS: lazy_ugettext(
207 207 u'This pull request cannot be merged because the target `{target_ref.name}` '
208 208 u'has multiple heads: `{heads}`.'),
209 209 MergeFailureReason.TARGET_IS_LOCKED: lazy_ugettext(
210 210 u'This pull request cannot be merged because the target repository is '
211 211 u'locked by {locked_by}.'),
212 212
213 213 MergeFailureReason.MISSING_TARGET_REF: lazy_ugettext(
214 214 u'This pull request cannot be merged because the target '
215 215 u'reference `{target_ref.name}` is missing.'),
216 216 MergeFailureReason.MISSING_SOURCE_REF: lazy_ugettext(
217 217 u'This pull request cannot be merged because the source '
218 218 u'reference `{source_ref.name}` is missing.'),
219 219 MergeFailureReason.SUBREPO_MERGE_FAILED: lazy_ugettext(
220 220 u'This pull request cannot be merged because of conflicts related '
221 221 u'to sub repositories.'),
222 222
223 223 # Deprecations
224 224 MergeFailureReason._DEPRECATED_MISSING_COMMIT: lazy_ugettext(
225 225 u'This pull request cannot be merged because the target or the '
226 226 u'source reference is missing.'),
227 227
228 228 }
229 229
230 230 def __init__(self, possible, executed, merge_ref, failure_reason, metadata=None):
231 231 self.possible = possible
232 232 self.executed = executed
233 233 self.merge_ref = merge_ref
234 234 self.failure_reason = failure_reason
235 235 self.metadata = metadata or {}
236 236
237 237 def __repr__(self):
238 238 return '<MergeResponse:{} {}>'.format(self.label, self.failure_reason)
239 239
240 240 def __eq__(self, other):
241 241 same_instance = isinstance(other, self.__class__)
242 242 return same_instance \
243 243 and self.possible == other.possible \
244 244 and self.executed == other.executed \
245 245 and self.failure_reason == other.failure_reason
246 246
247 247 @property
248 248 def label(self):
249 249 label_dict = dict((v, k) for k, v in MergeFailureReason.__dict__.items() if
250 250 not k.startswith('_'))
251 251 return label_dict.get(self.failure_reason)
252 252
253 253 @property
254 254 def merge_status_message(self):
255 255 """
256 256 Return a human friendly error message for the given merge status code.
257 257 """
258 258 msg = safe_unicode(self.MERGE_STATUS_MESSAGES[self.failure_reason])
259 259
260 260 try:
261 261 return msg.format(**self.metadata)
262 262 except Exception:
263 263 log.exception('Failed to format %s message', self)
264 264 return msg
265 265
266 266 def asdict(self):
267 267 data = {}
268 268 for k in ['possible', 'executed', 'merge_ref', 'failure_reason',
269 269 'merge_status_message']:
270 270 data[k] = getattr(self, k)
271 271 return data
272 272
273 273
274 274 class TargetRefMissing(ValueError):
275 275 pass
276 276
277 277
278 278 class SourceRefMissing(ValueError):
279 279 pass
280 280
281 281
282 282 class BaseRepository(object):
283 283 """
284 284 Base Repository for final backends
285 285
286 286 .. attribute:: DEFAULT_BRANCH_NAME
287 287
288 288 name of default branch (i.e. "trunk" for svn, "master" for git etc.
289 289
290 290 .. attribute:: commit_ids
291 291
292 292 list of all available commit ids, in ascending order
293 293
294 294 .. attribute:: path
295 295
296 296 absolute path to the repository
297 297
298 298 .. attribute:: bookmarks
299 299
300 300 Mapping from name to :term:`Commit ID` of the bookmark. Empty in case
301 301 there are no bookmarks or the backend implementation does not support
302 302 bookmarks.
303 303
304 304 .. attribute:: tags
305 305
306 306 Mapping from name to :term:`Commit ID` of the tag.
307 307
308 308 """
309 309
310 310 DEFAULT_BRANCH_NAME = None
311 311 DEFAULT_CONTACT = u"Unknown"
312 312 DEFAULT_DESCRIPTION = u"unknown"
313 313 EMPTY_COMMIT_ID = '0' * 40
314 COMMIT_ID_PAT = re.compile(r'[0-9a-fA-F]{40}')
314 315
315 316 path = None
316 317
317 318 _is_empty = None
318 319 _commit_ids = {}
319 320
320 321 def __init__(self, repo_path, config=None, create=False, **kwargs):
321 322 """
322 323 Initializes repository. Raises RepositoryError if repository could
323 324 not be find at the given ``repo_path`` or directory at ``repo_path``
324 325 exists and ``create`` is set to True.
325 326
326 327 :param repo_path: local path of the repository
327 328 :param config: repository configuration
328 329 :param create=False: if set to True, would try to create repository.
329 330 :param src_url=None: if set, should be proper url from which repository
330 331 would be cloned; requires ``create`` parameter to be set to True -
331 332 raises RepositoryError if src_url is set and create evaluates to
332 333 False
333 334 """
334 335 raise NotImplementedError
335 336
336 337 def __repr__(self):
337 338 return '<%s at %s>' % (self.__class__.__name__, self.path)
338 339
339 340 def __len__(self):
340 341 return self.count()
341 342
342 343 def __eq__(self, other):
343 344 same_instance = isinstance(other, self.__class__)
344 345 return same_instance and other.path == self.path
345 346
346 347 def __ne__(self, other):
347 348 return not self.__eq__(other)
348 349
349 350 def get_create_shadow_cache_pr_path(self, db_repo):
350 351 path = db_repo.cached_diffs_dir
351 352 if not os.path.exists(path):
352 353 os.makedirs(path, 0o755)
353 354 return path
354 355
355 356 @classmethod
356 357 def get_default_config(cls, default=None):
357 358 config = Config()
358 359 if default and isinstance(default, list):
359 360 for section, key, val in default:
360 361 config.set(section, key, val)
361 362 return config
362 363
363 364 @LazyProperty
364 365 def _remote(self):
365 366 raise NotImplementedError
366 367
367 368 def _heads(self, branch=None):
368 369 return []
369 370
370 371 @LazyProperty
371 372 def EMPTY_COMMIT(self):
372 373 return EmptyCommit(self.EMPTY_COMMIT_ID)
373 374
374 375 @LazyProperty
375 376 def alias(self):
376 377 for k, v in settings.BACKENDS.items():
377 378 if v.split('.')[-1] == str(self.__class__.__name__):
378 379 return k
379 380
380 381 @LazyProperty
381 382 def name(self):
382 383 return safe_unicode(os.path.basename(self.path))
383 384
384 385 @LazyProperty
385 386 def description(self):
386 387 raise NotImplementedError
387 388
388 389 def refs(self):
389 390 """
390 391 returns a `dict` with branches, bookmarks, tags, and closed_branches
391 392 for this repository
392 393 """
393 394 return dict(
394 395 branches=self.branches,
395 396 branches_closed=self.branches_closed,
396 397 tags=self.tags,
397 398 bookmarks=self.bookmarks
398 399 )
399 400
400 401 @LazyProperty
401 402 def branches(self):
402 403 """
403 404 A `dict` which maps branch names to commit ids.
404 405 """
405 406 raise NotImplementedError
406 407
407 408 @LazyProperty
408 409 def branches_closed(self):
409 410 """
410 411 A `dict` which maps tags names to commit ids.
411 412 """
412 413 raise NotImplementedError
413 414
414 415 @LazyProperty
415 416 def bookmarks(self):
416 417 """
417 418 A `dict` which maps tags names to commit ids.
418 419 """
419 420 raise NotImplementedError
420 421
421 422 @LazyProperty
422 423 def tags(self):
423 424 """
424 425 A `dict` which maps tags names to commit ids.
425 426 """
426 427 raise NotImplementedError
427 428
428 429 @LazyProperty
429 430 def size(self):
430 431 """
431 432 Returns combined size in bytes for all repository files
432 433 """
433 434 tip = self.get_commit()
434 435 return tip.size
435 436
436 437 def size_at_commit(self, commit_id):
437 438 commit = self.get_commit(commit_id)
438 439 return commit.size
439 440
440 441 def _check_for_empty(self):
441 442 no_commits = len(self._commit_ids) == 0
442 443 if no_commits:
443 444 # check on remote to be sure
444 445 return self._remote.is_empty()
445 446 else:
446 447 return False
447 448
448 449 def is_empty(self):
449 450 if rhodecode.is_test:
450 451 return self._check_for_empty()
451 452
452 453 if self._is_empty is None:
453 454 # cache empty for production, but not tests
454 455 self._is_empty = self._check_for_empty()
455 456
456 457 return self._is_empty
457 458
458 459 @staticmethod
459 460 def check_url(url, config):
460 461 """
461 462 Function will check given url and try to verify if it's a valid
462 463 link.
463 464 """
464 465 raise NotImplementedError
465 466
466 467 @staticmethod
467 468 def is_valid_repository(path):
468 469 """
469 470 Check if given `path` contains a valid repository of this backend
470 471 """
471 472 raise NotImplementedError
472 473
473 474 # ==========================================================================
474 475 # COMMITS
475 476 # ==========================================================================
476 477
477 478 @CachedProperty
478 479 def commit_ids(self):
479 480 raise NotImplementedError
480 481
481 482 def append_commit_id(self, commit_id):
482 483 if commit_id not in self.commit_ids:
483 484 self._rebuild_cache(self.commit_ids + [commit_id])
484 485
485 486 # clear cache
486 487 self._invalidate_prop_cache('commit_ids')
487 488 self._is_empty = False
488 489
489 490 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
490 491 translate_tag=None, maybe_unreachable=False, reference_obj=None):
491 492 """
492 493 Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx`
493 494 are both None, most recent commit is returned.
494 495
495 496 :param pre_load: Optional. List of commit attributes to load.
496 497
497 498 :raises ``EmptyRepositoryError``: if there are no commits
498 499 """
499 500 raise NotImplementedError
500 501
501 502 def __iter__(self):
502 503 for commit_id in self.commit_ids:
503 504 yield self.get_commit(commit_id=commit_id)
504 505
505 506 def get_commits(
506 507 self, start_id=None, end_id=None, start_date=None, end_date=None,
507 508 branch_name=None, show_hidden=False, pre_load=None, translate_tags=None):
508 509 """
509 510 Returns iterator of `BaseCommit` objects from start to end
510 511 not inclusive. This should behave just like a list, ie. end is not
511 512 inclusive.
512 513
513 514 :param start_id: None or str, must be a valid commit id
514 515 :param end_id: None or str, must be a valid commit id
515 516 :param start_date:
516 517 :param end_date:
517 518 :param branch_name:
518 519 :param show_hidden:
519 520 :param pre_load:
520 521 :param translate_tags:
521 522 """
522 523 raise NotImplementedError
523 524
524 525 def __getitem__(self, key):
525 526 """
526 527 Allows index based access to the commit objects of this repository.
527 528 """
528 529 pre_load = ["author", "branch", "date", "message", "parents"]
529 530 if isinstance(key, slice):
530 531 return self._get_range(key, pre_load)
531 532 return self.get_commit(commit_idx=key, pre_load=pre_load)
532 533
533 534 def _get_range(self, slice_obj, pre_load):
534 535 for commit_id in self.commit_ids.__getitem__(slice_obj):
535 536 yield self.get_commit(commit_id=commit_id, pre_load=pre_load)
536 537
537 538 def count(self):
538 539 return len(self.commit_ids)
539 540
540 541 def tag(self, name, user, commit_id=None, message=None, date=None, **opts):
541 542 """
542 543 Creates and returns a tag for the given ``commit_id``.
543 544
544 545 :param name: name for new tag
545 546 :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>"
546 547 :param commit_id: commit id for which new tag would be created
547 548 :param message: message of the tag's commit
548 549 :param date: date of tag's commit
549 550
550 551 :raises TagAlreadyExistError: if tag with same name already exists
551 552 """
552 553 raise NotImplementedError
553 554
554 555 def remove_tag(self, name, user, message=None, date=None):
555 556 """
556 557 Removes tag with the given ``name``.
557 558
558 559 :param name: name of the tag to be removed
559 560 :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>"
560 561 :param message: message of the tag's removal commit
561 562 :param date: date of tag's removal commit
562 563
563 564 :raises TagDoesNotExistError: if tag with given name does not exists
564 565 """
565 566 raise NotImplementedError
566 567
567 568 def get_diff(
568 569 self, commit1, commit2, path=None, ignore_whitespace=False,
569 570 context=3, path1=None):
570 571 """
571 572 Returns (git like) *diff*, as plain text. Shows changes introduced by
572 573 `commit2` since `commit1`.
573 574
574 575 :param commit1: Entry point from which diff is shown. Can be
575 576 ``self.EMPTY_COMMIT`` - in this case, patch showing all
576 577 the changes since empty state of the repository until `commit2`
577 578 :param commit2: Until which commit changes should be shown.
578 579 :param path: Can be set to a path of a file to create a diff of that
579 580 file. If `path1` is also set, this value is only associated to
580 581 `commit2`.
581 582 :param ignore_whitespace: If set to ``True``, would not show whitespace
582 583 changes. Defaults to ``False``.
583 584 :param context: How many lines before/after changed lines should be
584 585 shown. Defaults to ``3``.
585 586 :param path1: Can be set to a path to associate with `commit1`. This
586 587 parameter works only for backends which support diff generation for
587 588 different paths. Other backends will raise a `ValueError` if `path1`
588 589 is set and has a different value than `path`.
589 590 :param file_path: filter this diff by given path pattern
590 591 """
591 592 raise NotImplementedError
592 593
593 594 def strip(self, commit_id, branch=None):
594 595 """
595 596 Strip given commit_id from the repository
596 597 """
597 598 raise NotImplementedError
598 599
599 600 def get_common_ancestor(self, commit_id1, commit_id2, repo2):
600 601 """
601 602 Return a latest common ancestor commit if one exists for this repo
602 603 `commit_id1` vs `commit_id2` from `repo2`.
603 604
604 605 :param commit_id1: Commit it from this repository to use as a
605 606 target for the comparison.
606 607 :param commit_id2: Source commit id to use for comparison.
607 608 :param repo2: Source repository to use for comparison.
608 609 """
609 610 raise NotImplementedError
610 611
611 612 def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None):
612 613 """
613 614 Compare this repository's revision `commit_id1` with `commit_id2`.
614 615
615 616 Returns a tuple(commits, ancestor) that would be merged from
616 617 `commit_id2`. Doing a normal compare (``merge=False``), ``None``
617 618 will be returned as ancestor.
618 619
619 620 :param commit_id1: Commit it from this repository to use as a
620 621 target for the comparison.
621 622 :param commit_id2: Source commit id to use for comparison.
622 623 :param repo2: Source repository to use for comparison.
623 624 :param merge: If set to ``True`` will do a merge compare which also
624 625 returns the common ancestor.
625 626 :param pre_load: Optional. List of commit attributes to load.
626 627 """
627 628 raise NotImplementedError
628 629
629 630 def merge(self, repo_id, workspace_id, target_ref, source_repo, source_ref,
630 631 user_name='', user_email='', message='', dry_run=False,
631 632 use_rebase=False, close_branch=False):
632 633 """
633 634 Merge the revisions specified in `source_ref` from `source_repo`
634 635 onto the `target_ref` of this repository.
635 636
636 637 `source_ref` and `target_ref` are named tupls with the following
637 638 fields `type`, `name` and `commit_id`.
638 639
639 640 Returns a MergeResponse named tuple with the following fields
640 641 'possible', 'executed', 'source_commit', 'target_commit',
641 642 'merge_commit'.
642 643
643 644 :param repo_id: `repo_id` target repo id.
644 645 :param workspace_id: `workspace_id` unique identifier.
645 646 :param target_ref: `target_ref` points to the commit on top of which
646 647 the `source_ref` should be merged.
647 648 :param source_repo: The repository that contains the commits to be
648 649 merged.
649 650 :param source_ref: `source_ref` points to the topmost commit from
650 651 the `source_repo` which should be merged.
651 652 :param user_name: Merge commit `user_name`.
652 653 :param user_email: Merge commit `user_email`.
653 654 :param message: Merge commit `message`.
654 655 :param dry_run: If `True` the merge will not take place.
655 656 :param use_rebase: If `True` commits from the source will be rebased
656 657 on top of the target instead of being merged.
657 658 :param close_branch: If `True` branch will be close before merging it
658 659 """
659 660 if dry_run:
660 661 message = message or settings.MERGE_DRY_RUN_MESSAGE
661 662 user_email = user_email or settings.MERGE_DRY_RUN_EMAIL
662 663 user_name = user_name or settings.MERGE_DRY_RUN_USER
663 664 else:
664 665 if not user_name:
665 666 raise ValueError('user_name cannot be empty')
666 667 if not user_email:
667 668 raise ValueError('user_email cannot be empty')
668 669 if not message:
669 670 raise ValueError('message cannot be empty')
670 671
671 672 try:
672 673 return self._merge_repo(
673 674 repo_id, workspace_id, target_ref, source_repo,
674 675 source_ref, message, user_name, user_email, dry_run=dry_run,
675 676 use_rebase=use_rebase, close_branch=close_branch)
676 677 except RepositoryError as exc:
677 678 log.exception('Unexpected failure when running merge, dry-run=%s', dry_run)
678 679 return MergeResponse(
679 680 False, False, None, MergeFailureReason.UNKNOWN,
680 681 metadata={'exception': str(exc)})
681 682
682 683 def _merge_repo(self, repo_id, workspace_id, target_ref,
683 684 source_repo, source_ref, merge_message,
684 685 merger_name, merger_email, dry_run=False,
685 686 use_rebase=False, close_branch=False):
686 687 """Internal implementation of merge."""
687 688 raise NotImplementedError
688 689
689 690 def _maybe_prepare_merge_workspace(
690 691 self, repo_id, workspace_id, target_ref, source_ref):
691 692 """
692 693 Create the merge workspace.
693 694
694 695 :param workspace_id: `workspace_id` unique identifier.
695 696 """
696 697 raise NotImplementedError
697 698
698 699 @classmethod
699 700 def _get_legacy_shadow_repository_path(cls, repo_path, workspace_id):
700 701 """
701 702 Legacy version that was used before. We still need it for
702 703 backward compat
703 704 """
704 705 return os.path.join(
705 706 os.path.dirname(repo_path),
706 707 '.__shadow_%s_%s' % (os.path.basename(repo_path), workspace_id))
707 708
708 709 @classmethod
709 710 def _get_shadow_repository_path(cls, repo_path, repo_id, workspace_id):
710 711 # The name of the shadow repository must start with '.', so it is
711 712 # skipped by 'rhodecode.lib.utils.get_filesystem_repos'.
712 713 legacy_repository_path = cls._get_legacy_shadow_repository_path(repo_path, workspace_id)
713 714 if os.path.exists(legacy_repository_path):
714 715 return legacy_repository_path
715 716 else:
716 717 return os.path.join(
717 718 os.path.dirname(repo_path),
718 719 '.__shadow_repo_%s_%s' % (repo_id, workspace_id))
719 720
720 721 def cleanup_merge_workspace(self, repo_id, workspace_id):
721 722 """
722 723 Remove merge workspace.
723 724
724 725 This function MUST not fail in case there is no workspace associated to
725 726 the given `workspace_id`.
726 727
727 728 :param workspace_id: `workspace_id` unique identifier.
728 729 """
729 730 shadow_repository_path = self._get_shadow_repository_path(
730 731 self.path, repo_id, workspace_id)
731 732 shadow_repository_path_del = '{}.{}.delete'.format(
732 733 shadow_repository_path, time.time())
733 734
734 735 # move the shadow repo, so it never conflicts with the one used.
735 736 # we use this method because shutil.rmtree had some edge case problems
736 737 # removing symlinked repositories
737 738 if not os.path.isdir(shadow_repository_path):
738 739 return
739 740
740 741 shutil.move(shadow_repository_path, shadow_repository_path_del)
741 742 try:
742 743 shutil.rmtree(shadow_repository_path_del, ignore_errors=False)
743 744 except Exception:
744 745 log.exception('Failed to gracefully remove shadow repo under %s',
745 746 shadow_repository_path_del)
746 747 shutil.rmtree(shadow_repository_path_del, ignore_errors=True)
747 748
748 749 # ========== #
749 750 # COMMIT API #
750 751 # ========== #
751 752
752 753 @LazyProperty
753 754 def in_memory_commit(self):
754 755 """
755 756 Returns :class:`InMemoryCommit` object for this repository.
756 757 """
757 758 raise NotImplementedError
758 759
759 760 # ======================== #
760 761 # UTILITIES FOR SUBCLASSES #
761 762 # ======================== #
762 763
763 764 def _validate_diff_commits(self, commit1, commit2):
764 765 """
765 766 Validates that the given commits are related to this repository.
766 767
767 768 Intended as a utility for sub classes to have a consistent validation
768 769 of input parameters in methods like :meth:`get_diff`.
769 770 """
770 771 self._validate_commit(commit1)
771 772 self._validate_commit(commit2)
772 773 if (isinstance(commit1, EmptyCommit) and
773 774 isinstance(commit2, EmptyCommit)):
774 775 raise ValueError("Cannot compare two empty commits")
775 776
776 777 def _validate_commit(self, commit):
777 778 if not isinstance(commit, BaseCommit):
778 779 raise TypeError(
779 780 "%s is not of type BaseCommit" % repr(commit))
780 781 if commit.repository != self and not isinstance(commit, EmptyCommit):
781 782 raise ValueError(
782 783 "Commit %s must be a valid commit from this repository %s, "
783 784 "related to this repository instead %s." %
784 785 (commit, self, commit.repository))
785 786
786 787 def _validate_commit_id(self, commit_id):
787 788 if not isinstance(commit_id, compat.string_types):
788 789 raise TypeError("commit_id must be a string value got {} instead".format(type(commit_id)))
789 790
790 791 def _validate_commit_idx(self, commit_idx):
791 792 if not isinstance(commit_idx, (int, long)):
792 793 raise TypeError("commit_idx must be a numeric value")
793 794
794 795 def _validate_branch_name(self, branch_name):
795 796 if branch_name and branch_name not in self.branches_all:
796 797 msg = ("Branch %s not found in %s" % (branch_name, self))
797 798 raise BranchDoesNotExistError(msg)
798 799
799 800 #
800 801 # Supporting deprecated API parts
801 802 # TODO: johbo: consider to move this into a mixin
802 803 #
803 804
804 805 @property
805 806 def EMPTY_CHANGESET(self):
806 807 warnings.warn(
807 808 "Use EMPTY_COMMIT or EMPTY_COMMIT_ID instead", DeprecationWarning)
808 809 return self.EMPTY_COMMIT_ID
809 810
810 811 @property
811 812 def revisions(self):
812 813 warnings.warn("Use commits attribute instead", DeprecationWarning)
813 814 return self.commit_ids
814 815
815 816 @revisions.setter
816 817 def revisions(self, value):
817 818 warnings.warn("Use commits attribute instead", DeprecationWarning)
818 819 self.commit_ids = value
819 820
820 821 def get_changeset(self, revision=None, pre_load=None):
821 822 warnings.warn("Use get_commit instead", DeprecationWarning)
822 823 commit_id = None
823 824 commit_idx = None
824 825 if isinstance(revision, compat.string_types):
825 826 commit_id = revision
826 827 else:
827 828 commit_idx = revision
828 829 return self.get_commit(
829 830 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
830 831
831 832 def get_changesets(
832 833 self, start=None, end=None, start_date=None, end_date=None,
833 834 branch_name=None, pre_load=None):
834 835 warnings.warn("Use get_commits instead", DeprecationWarning)
835 836 start_id = self._revision_to_commit(start)
836 837 end_id = self._revision_to_commit(end)
837 838 return self.get_commits(
838 839 start_id=start_id, end_id=end_id, start_date=start_date,
839 840 end_date=end_date, branch_name=branch_name, pre_load=pre_load)
840 841
841 842 def _revision_to_commit(self, revision):
842 843 """
843 844 Translates a revision to a commit_id
844 845
845 846 Helps to support the old changeset based API which allows to use
846 847 commit ids and commit indices interchangeable.
847 848 """
848 849 if revision is None:
849 850 return revision
850 851
851 852 if isinstance(revision, compat.string_types):
852 853 commit_id = revision
853 854 else:
854 855 commit_id = self.commit_ids[revision]
855 856 return commit_id
856 857
857 858 @property
858 859 def in_memory_changeset(self):
859 860 warnings.warn("Use in_memory_commit instead", DeprecationWarning)
860 861 return self.in_memory_commit
861 862
862 863 def get_path_permissions(self, username):
863 864 """
864 865 Returns a path permission checker or None if not supported
865 866
866 867 :param username: session user name
867 868 :return: an instance of BasePathPermissionChecker or None
868 869 """
869 870 return None
870 871
871 872 def install_hooks(self, force=False):
872 873 return self._remote.install_hooks(force)
873 874
874 875 def get_hooks_info(self):
875 876 return self._remote.get_hooks_info()
876 877
877 878
878 879 class BaseCommit(object):
879 880 """
880 881 Each backend should implement it's commit representation.
881 882
882 883 **Attributes**
883 884
884 885 ``repository``
885 886 repository object within which commit exists
886 887
887 888 ``id``
888 889 The commit id, may be ``raw_id`` or i.e. for mercurial's tip
889 890 just ``tip``.
890 891
891 892 ``raw_id``
892 893 raw commit representation (i.e. full 40 length sha for git
893 894 backend)
894 895
895 896 ``short_id``
896 897 shortened (if apply) version of ``raw_id``; it would be simple
897 898 shortcut for ``raw_id[:12]`` for git/mercurial backends or same
898 899 as ``raw_id`` for subversion
899 900
900 901 ``idx``
901 902 commit index
902 903
903 904 ``files``
904 905 list of ``FileNode`` (``Node`` with NodeKind.FILE) objects
905 906
906 907 ``dirs``
907 908 list of ``DirNode`` (``Node`` with NodeKind.DIR) objects
908 909
909 910 ``nodes``
910 911 combined list of ``Node`` objects
911 912
912 913 ``author``
913 914 author of the commit, as unicode
914 915
915 916 ``message``
916 917 message of the commit, as unicode
917 918
918 919 ``parents``
919 920 list of parent commits
920 921
921 922 """
922 923 repository = None
923 924 branch = None
924 925
925 926 """
926 927 Depending on the backend this should be set to the branch name of the
927 928 commit. Backends not supporting branches on commits should leave this
928 929 value as ``None``.
929 930 """
930 931
931 932 _ARCHIVE_PREFIX_TEMPLATE = b'{repo_name}-{short_id}'
932 933 """
933 934 This template is used to generate a default prefix for repository archives
934 935 if no prefix has been specified.
935 936 """
936 937
937 938 def __str__(self):
938 939 return '<%s at %s:%s>' % (
939 940 self.__class__.__name__, self.idx, self.short_id)
940 941
941 942 def __repr__(self):
942 943 return self.__str__()
943 944
944 945 def __unicode__(self):
945 946 return u'%s:%s' % (self.idx, self.short_id)
946 947
947 948 def __eq__(self, other):
948 949 same_instance = isinstance(other, self.__class__)
949 950 return same_instance and self.raw_id == other.raw_id
950 951
951 952 def __json__(self):
952 953 parents = []
953 954 try:
954 955 for parent in self.parents:
955 956 parents.append({'raw_id': parent.raw_id})
956 957 except NotImplementedError:
957 958 # empty commit doesn't have parents implemented
958 959 pass
959 960
960 961 return {
961 962 'short_id': self.short_id,
962 963 'raw_id': self.raw_id,
963 964 'revision': self.idx,
964 965 'message': self.message,
965 966 'date': self.date,
966 967 'author': self.author,
967 968 'parents': parents,
968 969 'branch': self.branch
969 970 }
970 971
971 972 def __getstate__(self):
972 973 d = self.__dict__.copy()
973 974 d.pop('_remote', None)
974 975 d.pop('repository', None)
975 976 return d
976 977
977 978 def serialize(self):
978 979 return self.__json__()
979 980
980 981 def _get_refs(self):
981 982 return {
982 983 'branches': [self.branch] if self.branch else [],
983 984 'bookmarks': getattr(self, 'bookmarks', []),
984 985 'tags': self.tags
985 986 }
986 987
987 988 @LazyProperty
988 989 def last(self):
989 990 """
990 991 ``True`` if this is last commit in repository, ``False``
991 992 otherwise; trying to access this attribute while there is no
992 993 commits would raise `EmptyRepositoryError`
993 994 """
994 995 if self.repository is None:
995 996 raise CommitError("Cannot check if it's most recent commit")
996 997 return self.raw_id == self.repository.commit_ids[-1]
997 998
998 999 @LazyProperty
999 1000 def parents(self):
1000 1001 """
1001 1002 Returns list of parent commits.
1002 1003 """
1003 1004 raise NotImplementedError
1004 1005
1005 1006 @LazyProperty
1006 1007 def first_parent(self):
1007 1008 """
1008 1009 Returns list of parent commits.
1009 1010 """
1010 1011 return self.parents[0] if self.parents else EmptyCommit()
1011 1012
1012 1013 @property
1013 1014 def merge(self):
1014 1015 """
1015 1016 Returns boolean if commit is a merge.
1016 1017 """
1017 1018 return len(self.parents) > 1
1018 1019
1019 1020 @LazyProperty
1020 1021 def children(self):
1021 1022 """
1022 1023 Returns list of child commits.
1023 1024 """
1024 1025 raise NotImplementedError
1025 1026
1026 1027 @LazyProperty
1027 1028 def id(self):
1028 1029 """
1029 1030 Returns string identifying this commit.
1030 1031 """
1031 1032 raise NotImplementedError
1032 1033
1033 1034 @LazyProperty
1034 1035 def raw_id(self):
1035 1036 """
1036 1037 Returns raw string identifying this commit.
1037 1038 """
1038 1039 raise NotImplementedError
1039 1040
1040 1041 @LazyProperty
1041 1042 def short_id(self):
1042 1043 """
1043 1044 Returns shortened version of ``raw_id`` attribute, as string,
1044 1045 identifying this commit, useful for presentation to users.
1045 1046 """
1046 1047 raise NotImplementedError
1047 1048
1048 1049 @LazyProperty
1049 1050 def idx(self):
1050 1051 """
1051 1052 Returns integer identifying this commit.
1052 1053 """
1053 1054 raise NotImplementedError
1054 1055
1055 1056 @LazyProperty
1056 1057 def committer(self):
1057 1058 """
1058 1059 Returns committer for this commit
1059 1060 """
1060 1061 raise NotImplementedError
1061 1062
1062 1063 @LazyProperty
1063 1064 def committer_name(self):
1064 1065 """
1065 1066 Returns committer name for this commit
1066 1067 """
1067 1068
1068 1069 return author_name(self.committer)
1069 1070
1070 1071 @LazyProperty
1071 1072 def committer_email(self):
1072 1073 """
1073 1074 Returns committer email address for this commit
1074 1075 """
1075 1076
1076 1077 return author_email(self.committer)
1077 1078
1078 1079 @LazyProperty
1079 1080 def author(self):
1080 1081 """
1081 1082 Returns author for this commit
1082 1083 """
1083 1084
1084 1085 raise NotImplementedError
1085 1086
1086 1087 @LazyProperty
1087 1088 def author_name(self):
1088 1089 """
1089 1090 Returns author name for this commit
1090 1091 """
1091 1092
1092 1093 return author_name(self.author)
1093 1094
1094 1095 @LazyProperty
1095 1096 def author_email(self):
1096 1097 """
1097 1098 Returns author email address for this commit
1098 1099 """
1099 1100
1100 1101 return author_email(self.author)
1101 1102
1102 1103 def get_file_mode(self, path):
1103 1104 """
1104 1105 Returns stat mode of the file at `path`.
1105 1106 """
1106 1107 raise NotImplementedError
1107 1108
1108 1109 def is_link(self, path):
1109 1110 """
1110 1111 Returns ``True`` if given `path` is a symlink
1111 1112 """
1112 1113 raise NotImplementedError
1113 1114
1114 1115 def is_node_binary(self, path):
1115 1116 """
1116 1117 Returns ``True`` is given path is a binary file
1117 1118 """
1118 1119 raise NotImplementedError
1119 1120
1120 1121 def get_file_content(self, path):
1121 1122 """
1122 1123 Returns content of the file at the given `path`.
1123 1124 """
1124 1125 raise NotImplementedError
1125 1126
1126 1127 def get_file_content_streamed(self, path):
1127 1128 """
1128 1129 returns a streaming response from vcsserver with file content
1129 1130 """
1130 1131 raise NotImplementedError
1131 1132
1132 1133 def get_file_size(self, path):
1133 1134 """
1134 1135 Returns size of the file at the given `path`.
1135 1136 """
1136 1137 raise NotImplementedError
1137 1138
1138 1139 def get_path_commit(self, path, pre_load=None):
1139 1140 """
1140 1141 Returns last commit of the file at the given `path`.
1141 1142
1142 1143 :param pre_load: Optional. List of commit attributes to load.
1143 1144 """
1144 1145 commits = self.get_path_history(path, limit=1, pre_load=pre_load)
1145 1146 if not commits:
1146 1147 raise RepositoryError(
1147 1148 'Failed to fetch history for path {}. '
1148 1149 'Please check if such path exists in your repository'.format(
1149 1150 path))
1150 1151 return commits[0]
1151 1152
1152 1153 def get_path_history(self, path, limit=None, pre_load=None):
1153 1154 """
1154 1155 Returns history of file as reversed list of :class:`BaseCommit`
1155 1156 objects for which file at given `path` has been modified.
1156 1157
1157 1158 :param limit: Optional. Allows to limit the size of the returned
1158 1159 history. This is intended as a hint to the underlying backend, so
1159 1160 that it can apply optimizations depending on the limit.
1160 1161 :param pre_load: Optional. List of commit attributes to load.
1161 1162 """
1162 1163 raise NotImplementedError
1163 1164
1164 1165 def get_file_annotate(self, path, pre_load=None):
1165 1166 """
1166 1167 Returns a generator of four element tuples with
1167 1168 lineno, sha, commit lazy loader and line
1168 1169
1169 1170 :param pre_load: Optional. List of commit attributes to load.
1170 1171 """
1171 1172 raise NotImplementedError
1172 1173
1173 1174 def get_nodes(self, path):
1174 1175 """
1175 1176 Returns combined ``DirNode`` and ``FileNode`` objects list representing
1176 1177 state of commit at the given ``path``.
1177 1178
1178 1179 :raises ``CommitError``: if node at the given ``path`` is not
1179 1180 instance of ``DirNode``
1180 1181 """
1181 1182 raise NotImplementedError
1182 1183
1183 1184 def get_node(self, path):
1184 1185 """
1185 1186 Returns ``Node`` object from the given ``path``.
1186 1187
1187 1188 :raises ``NodeDoesNotExistError``: if there is no node at the given
1188 1189 ``path``
1189 1190 """
1190 1191 raise NotImplementedError
1191 1192
1192 1193 def get_largefile_node(self, path):
1193 1194 """
1194 1195 Returns the path to largefile from Mercurial/Git-lfs storage.
1195 1196 or None if it's not a largefile node
1196 1197 """
1197 1198 return None
1198 1199
1199 1200 def archive_repo(self, archive_dest_path, kind='tgz', subrepos=None,
1200 1201 archive_dir_name=None, write_metadata=False, mtime=None,
1201 1202 archive_at_path='/'):
1202 1203 """
1203 1204 Creates an archive containing the contents of the repository.
1204 1205
1205 1206 :param archive_dest_path: path to the file which to create the archive.
1206 1207 :param kind: one of following: ``"tbz2"``, ``"tgz"``, ``"zip"``.
1207 1208 :param archive_dir_name: name of root directory in archive.
1208 1209 Default is repository name and commit's short_id joined with dash:
1209 1210 ``"{repo_name}-{short_id}"``.
1210 1211 :param write_metadata: write a metadata file into archive.
1211 1212 :param mtime: custom modification time for archive creation, defaults
1212 1213 to time.time() if not given.
1213 1214 :param archive_at_path: pack files at this path (default '/')
1214 1215
1215 1216 :raise VCSError: If prefix has a problem.
1216 1217 """
1217 1218 allowed_kinds = [x[0] for x in settings.ARCHIVE_SPECS]
1218 1219 if kind not in allowed_kinds:
1219 1220 raise ImproperArchiveTypeError(
1220 1221 'Archive kind (%s) not supported use one of %s' %
1221 1222 (kind, allowed_kinds))
1222 1223
1223 1224 archive_dir_name = self._validate_archive_prefix(archive_dir_name)
1224 1225 mtime = mtime is not None or time.mktime(self.date.timetuple())
1225 1226 commit_id = self.raw_id
1226 1227
1227 1228 return self.repository._remote.archive_repo(
1228 1229 archive_dest_path, kind, mtime, archive_at_path,
1229 1230 archive_dir_name, commit_id)
1230 1231
1231 1232 def _validate_archive_prefix(self, archive_dir_name):
1232 1233 if archive_dir_name is None:
1233 1234 archive_dir_name = self._ARCHIVE_PREFIX_TEMPLATE.format(
1234 1235 repo_name=safe_str(self.repository.name),
1235 1236 short_id=self.short_id)
1236 1237 elif not isinstance(archive_dir_name, str):
1237 1238 raise ValueError("prefix not a bytes object: %s" % repr(archive_dir_name))
1238 1239 elif archive_dir_name.startswith('/'):
1239 1240 raise VCSError("Prefix cannot start with leading slash")
1240 1241 elif archive_dir_name.strip() == '':
1241 1242 raise VCSError("Prefix cannot be empty")
1242 1243 return archive_dir_name
1243 1244
1244 1245 @LazyProperty
1245 1246 def root(self):
1246 1247 """
1247 1248 Returns ``RootNode`` object for this commit.
1248 1249 """
1249 1250 return self.get_node('')
1250 1251
1251 1252 def next(self, branch=None):
1252 1253 """
1253 1254 Returns next commit from current, if branch is gives it will return
1254 1255 next commit belonging to this branch
1255 1256
1256 1257 :param branch: show commits within the given named branch
1257 1258 """
1258 1259 indexes = xrange(self.idx + 1, self.repository.count())
1259 1260 return self._find_next(indexes, branch)
1260 1261
1261 1262 def prev(self, branch=None):
1262 1263 """
1263 1264 Returns previous commit from current, if branch is gives it will
1264 1265 return previous commit belonging to this branch
1265 1266
1266 1267 :param branch: show commit within the given named branch
1267 1268 """
1268 1269 indexes = xrange(self.idx - 1, -1, -1)
1269 1270 return self._find_next(indexes, branch)
1270 1271
1271 1272 def _find_next(self, indexes, branch=None):
1272 1273 if branch and self.branch != branch:
1273 1274 raise VCSError('Branch option used on commit not belonging '
1274 1275 'to that branch')
1275 1276
1276 1277 for next_idx in indexes:
1277 1278 commit = self.repository.get_commit(commit_idx=next_idx)
1278 1279 if branch and branch != commit.branch:
1279 1280 continue
1280 1281 return commit
1281 1282 raise CommitDoesNotExistError
1282 1283
1283 1284 def diff(self, ignore_whitespace=True, context=3):
1284 1285 """
1285 1286 Returns a `Diff` object representing the change made by this commit.
1286 1287 """
1287 1288 parent = self.first_parent
1288 1289 diff = self.repository.get_diff(
1289 1290 parent, self,
1290 1291 ignore_whitespace=ignore_whitespace,
1291 1292 context=context)
1292 1293 return diff
1293 1294
1294 1295 @LazyProperty
1295 1296 def added(self):
1296 1297 """
1297 1298 Returns list of added ``FileNode`` objects.
1298 1299 """
1299 1300 raise NotImplementedError
1300 1301
1301 1302 @LazyProperty
1302 1303 def changed(self):
1303 1304 """
1304 1305 Returns list of modified ``FileNode`` objects.
1305 1306 """
1306 1307 raise NotImplementedError
1307 1308
1308 1309 @LazyProperty
1309 1310 def removed(self):
1310 1311 """
1311 1312 Returns list of removed ``FileNode`` objects.
1312 1313 """
1313 1314 raise NotImplementedError
1314 1315
1315 1316 @LazyProperty
1316 1317 def size(self):
1317 1318 """
1318 1319 Returns total number of bytes from contents of all filenodes.
1319 1320 """
1320 1321 return sum((node.size for node in self.get_filenodes_generator()))
1321 1322
1322 1323 def walk(self, topurl=''):
1323 1324 """
1324 1325 Similar to os.walk method. Insted of filesystem it walks through
1325 1326 commit starting at given ``topurl``. Returns generator of tuples
1326 1327 (topnode, dirnodes, filenodes).
1327 1328 """
1328 1329 topnode = self.get_node(topurl)
1329 1330 if not topnode.is_dir():
1330 1331 return
1331 1332 yield (topnode, topnode.dirs, topnode.files)
1332 1333 for dirnode in topnode.dirs:
1333 1334 for tup in self.walk(dirnode.path):
1334 1335 yield tup
1335 1336
1336 1337 def get_filenodes_generator(self):
1337 1338 """
1338 1339 Returns generator that yields *all* file nodes.
1339 1340 """
1340 1341 for topnode, dirs, files in self.walk():
1341 1342 for node in files:
1342 1343 yield node
1343 1344
1344 1345 #
1345 1346 # Utilities for sub classes to support consistent behavior
1346 1347 #
1347 1348
1348 1349 def no_node_at_path(self, path):
1349 1350 return NodeDoesNotExistError(
1350 1351 u"There is no file nor directory at the given path: "
1351 1352 u"`%s` at commit %s" % (safe_unicode(path), self.short_id))
1352 1353
1353 1354 def _fix_path(self, path):
1354 1355 """
1355 1356 Paths are stored without trailing slash so we need to get rid off it if
1356 1357 needed.
1357 1358 """
1358 1359 return path.rstrip('/')
1359 1360
1360 1361 #
1361 1362 # Deprecated API based on changesets
1362 1363 #
1363 1364
1364 1365 @property
1365 1366 def revision(self):
1366 1367 warnings.warn("Use idx instead", DeprecationWarning)
1367 1368 return self.idx
1368 1369
1369 1370 @revision.setter
1370 1371 def revision(self, value):
1371 1372 warnings.warn("Use idx instead", DeprecationWarning)
1372 1373 self.idx = value
1373 1374
1374 1375 def get_file_changeset(self, path):
1375 1376 warnings.warn("Use get_path_commit instead", DeprecationWarning)
1376 1377 return self.get_path_commit(path)
1377 1378
1378 1379
1379 1380 class BaseChangesetClass(type):
1380 1381
1381 1382 def __instancecheck__(self, instance):
1382 1383 return isinstance(instance, BaseCommit)
1383 1384
1384 1385
1385 1386 class BaseChangeset(BaseCommit):
1386 1387
1387 1388 __metaclass__ = BaseChangesetClass
1388 1389
1389 1390 def __new__(cls, *args, **kwargs):
1390 1391 warnings.warn(
1391 1392 "Use BaseCommit instead of BaseChangeset", DeprecationWarning)
1392 1393 return super(BaseChangeset, cls).__new__(cls, *args, **kwargs)
1393 1394
1394 1395
1395 1396 class BaseInMemoryCommit(object):
1396 1397 """
1397 1398 Represents differences between repository's state (most recent head) and
1398 1399 changes made *in place*.
1399 1400
1400 1401 **Attributes**
1401 1402
1402 1403 ``repository``
1403 1404 repository object for this in-memory-commit
1404 1405
1405 1406 ``added``
1406 1407 list of ``FileNode`` objects marked as *added*
1407 1408
1408 1409 ``changed``
1409 1410 list of ``FileNode`` objects marked as *changed*
1410 1411
1411 1412 ``removed``
1412 1413 list of ``FileNode`` or ``RemovedFileNode`` objects marked to be
1413 1414 *removed*
1414 1415
1415 1416 ``parents``
1416 1417 list of :class:`BaseCommit` instances representing parents of
1417 1418 in-memory commit. Should always be 2-element sequence.
1418 1419
1419 1420 """
1420 1421
1421 1422 def __init__(self, repository):
1422 1423 self.repository = repository
1423 1424 self.added = []
1424 1425 self.changed = []
1425 1426 self.removed = []
1426 1427 self.parents = []
1427 1428
1428 1429 def add(self, *filenodes):
1429 1430 """
1430 1431 Marks given ``FileNode`` objects as *to be committed*.
1431 1432
1432 1433 :raises ``NodeAlreadyExistsError``: if node with same path exists at
1433 1434 latest commit
1434 1435 :raises ``NodeAlreadyAddedError``: if node with same path is already
1435 1436 marked as *added*
1436 1437 """
1437 1438 # Check if not already marked as *added* first
1438 1439 for node in filenodes:
1439 1440 if node.path in (n.path for n in self.added):
1440 1441 raise NodeAlreadyAddedError(
1441 1442 "Such FileNode %s is already marked for addition"
1442 1443 % node.path)
1443 1444 for node in filenodes:
1444 1445 self.added.append(node)
1445 1446
1446 1447 def change(self, *filenodes):
1447 1448 """
1448 1449 Marks given ``FileNode`` objects to be *changed* in next commit.
1449 1450
1450 1451 :raises ``EmptyRepositoryError``: if there are no commits yet
1451 1452 :raises ``NodeAlreadyExistsError``: if node with same path is already
1452 1453 marked to be *changed*
1453 1454 :raises ``NodeAlreadyRemovedError``: if node with same path is already
1454 1455 marked to be *removed*
1455 1456 :raises ``NodeDoesNotExistError``: if node doesn't exist in latest
1456 1457 commit
1457 1458 :raises ``NodeNotChangedError``: if node hasn't really be changed
1458 1459 """
1459 1460 for node in filenodes:
1460 1461 if node.path in (n.path for n in self.removed):
1461 1462 raise NodeAlreadyRemovedError(
1462 1463 "Node at %s is already marked as removed" % node.path)
1463 1464 try:
1464 1465 self.repository.get_commit()
1465 1466 except EmptyRepositoryError:
1466 1467 raise EmptyRepositoryError(
1467 1468 "Nothing to change - try to *add* new nodes rather than "
1468 1469 "changing them")
1469 1470 for node in filenodes:
1470 1471 if node.path in (n.path for n in self.changed):
1471 1472 raise NodeAlreadyChangedError(
1472 1473 "Node at '%s' is already marked as changed" % node.path)
1473 1474 self.changed.append(node)
1474 1475
1475 1476 def remove(self, *filenodes):
1476 1477 """
1477 1478 Marks given ``FileNode`` (or ``RemovedFileNode``) objects to be
1478 1479 *removed* in next commit.
1479 1480
1480 1481 :raises ``NodeAlreadyRemovedError``: if node has been already marked to
1481 1482 be *removed*
1482 1483 :raises ``NodeAlreadyChangedError``: if node has been already marked to
1483 1484 be *changed*
1484 1485 """
1485 1486 for node in filenodes:
1486 1487 if node.path in (n.path for n in self.removed):
1487 1488 raise NodeAlreadyRemovedError(
1488 1489 "Node is already marked to for removal at %s" % node.path)
1489 1490 if node.path in (n.path for n in self.changed):
1490 1491 raise NodeAlreadyChangedError(
1491 1492 "Node is already marked to be changed at %s" % node.path)
1492 1493 # We only mark node as *removed* - real removal is done by
1493 1494 # commit method
1494 1495 self.removed.append(node)
1495 1496
1496 1497 def reset(self):
1497 1498 """
1498 1499 Resets this instance to initial state (cleans ``added``, ``changed``
1499 1500 and ``removed`` lists).
1500 1501 """
1501 1502 self.added = []
1502 1503 self.changed = []
1503 1504 self.removed = []
1504 1505 self.parents = []
1505 1506
1506 1507 def get_ipaths(self):
1507 1508 """
1508 1509 Returns generator of paths from nodes marked as added, changed or
1509 1510 removed.
1510 1511 """
1511 1512 for node in itertools.chain(self.added, self.changed, self.removed):
1512 1513 yield node.path
1513 1514
1514 1515 def get_paths(self):
1515 1516 """
1516 1517 Returns list of paths from nodes marked as added, changed or removed.
1517 1518 """
1518 1519 return list(self.get_ipaths())
1519 1520
1520 1521 def check_integrity(self, parents=None):
1521 1522 """
1522 1523 Checks in-memory commit's integrity. Also, sets parents if not
1523 1524 already set.
1524 1525
1525 1526 :raises CommitError: if any error occurs (i.e.
1526 1527 ``NodeDoesNotExistError``).
1527 1528 """
1528 1529 if not self.parents:
1529 1530 parents = parents or []
1530 1531 if len(parents) == 0:
1531 1532 try:
1532 1533 parents = [self.repository.get_commit(), None]
1533 1534 except EmptyRepositoryError:
1534 1535 parents = [None, None]
1535 1536 elif len(parents) == 1:
1536 1537 parents += [None]
1537 1538 self.parents = parents
1538 1539
1539 1540 # Local parents, only if not None
1540 1541 parents = [p for p in self.parents if p]
1541 1542
1542 1543 # Check nodes marked as added
1543 1544 for p in parents:
1544 1545 for node in self.added:
1545 1546 try:
1546 1547 p.get_node(node.path)
1547 1548 except NodeDoesNotExistError:
1548 1549 pass
1549 1550 else:
1550 1551 raise NodeAlreadyExistsError(
1551 1552 "Node `%s` already exists at %s" % (node.path, p))
1552 1553
1553 1554 # Check nodes marked as changed
1554 1555 missing = set(self.changed)
1555 1556 not_changed = set(self.changed)
1556 1557 if self.changed and not parents:
1557 1558 raise NodeDoesNotExistError(str(self.changed[0].path))
1558 1559 for p in parents:
1559 1560 for node in self.changed:
1560 1561 try:
1561 1562 old = p.get_node(node.path)
1562 1563 missing.remove(node)
1563 1564 # if content actually changed, remove node from not_changed
1564 1565 if old.content != node.content:
1565 1566 not_changed.remove(node)
1566 1567 except NodeDoesNotExistError:
1567 1568 pass
1568 1569 if self.changed and missing:
1569 1570 raise NodeDoesNotExistError(
1570 1571 "Node `%s` marked as modified but missing in parents: %s"
1571 1572 % (node.path, parents))
1572 1573
1573 1574 if self.changed and not_changed:
1574 1575 raise NodeNotChangedError(
1575 1576 "Node `%s` wasn't actually changed (parents: %s)"
1576 1577 % (not_changed.pop().path, parents))
1577 1578
1578 1579 # Check nodes marked as removed
1579 1580 if self.removed and not parents:
1580 1581 raise NodeDoesNotExistError(
1581 1582 "Cannot remove node at %s as there "
1582 1583 "were no parents specified" % self.removed[0].path)
1583 1584 really_removed = set()
1584 1585 for p in parents:
1585 1586 for node in self.removed:
1586 1587 try:
1587 1588 p.get_node(node.path)
1588 1589 really_removed.add(node)
1589 1590 except CommitError:
1590 1591 pass
1591 1592 not_removed = set(self.removed) - really_removed
1592 1593 if not_removed:
1593 1594 # TODO: johbo: This code branch does not seem to be covered
1594 1595 raise NodeDoesNotExistError(
1595 1596 "Cannot remove node at %s from "
1596 1597 "following parents: %s" % (not_removed, parents))
1597 1598
1598 1599 def commit(self, message, author, parents=None, branch=None, date=None, **kwargs):
1599 1600 """
1600 1601 Performs in-memory commit (doesn't check workdir in any way) and
1601 1602 returns newly created :class:`BaseCommit`. Updates repository's
1602 1603 attribute `commits`.
1603 1604
1604 1605 .. note::
1605 1606
1606 1607 While overriding this method each backend's should call
1607 1608 ``self.check_integrity(parents)`` in the first place.
1608 1609
1609 1610 :param message: message of the commit
1610 1611 :param author: full username, i.e. "Joe Doe <joe.doe@example.com>"
1611 1612 :param parents: single parent or sequence of parents from which commit
1612 1613 would be derived
1613 1614 :param date: ``datetime.datetime`` instance. Defaults to
1614 1615 ``datetime.datetime.now()``.
1615 1616 :param branch: branch name, as string. If none given, default backend's
1616 1617 branch would be used.
1617 1618
1618 1619 :raises ``CommitError``: if any error occurs while committing
1619 1620 """
1620 1621 raise NotImplementedError
1621 1622
1622 1623
1623 1624 class BaseInMemoryChangesetClass(type):
1624 1625
1625 1626 def __instancecheck__(self, instance):
1626 1627 return isinstance(instance, BaseInMemoryCommit)
1627 1628
1628 1629
1629 1630 class BaseInMemoryChangeset(BaseInMemoryCommit):
1630 1631
1631 1632 __metaclass__ = BaseInMemoryChangesetClass
1632 1633
1633 1634 def __new__(cls, *args, **kwargs):
1634 1635 warnings.warn(
1635 1636 "Use BaseCommit instead of BaseInMemoryCommit", DeprecationWarning)
1636 1637 return super(BaseInMemoryChangeset, cls).__new__(cls, *args, **kwargs)
1637 1638
1638 1639
1639 1640 class EmptyCommit(BaseCommit):
1640 1641 """
1641 1642 An dummy empty commit. It's possible to pass hash when creating
1642 1643 an EmptyCommit
1643 1644 """
1644 1645
1645 1646 def __init__(
1646 1647 self, commit_id=EMPTY_COMMIT_ID, repo=None, alias=None, idx=-1,
1647 1648 message='', author='', date=None):
1648 1649 self._empty_commit_id = commit_id
1649 1650 # TODO: johbo: Solve idx parameter, default value does not make
1650 1651 # too much sense
1651 1652 self.idx = idx
1652 1653 self.message = message
1653 1654 self.author = author
1654 1655 self.date = date or datetime.datetime.fromtimestamp(0)
1655 1656 self.repository = repo
1656 1657 self.alias = alias
1657 1658
1658 1659 @LazyProperty
1659 1660 def raw_id(self):
1660 1661 """
1661 1662 Returns raw string identifying this commit, useful for web
1662 1663 representation.
1663 1664 """
1664 1665
1665 1666 return self._empty_commit_id
1666 1667
1667 1668 @LazyProperty
1668 1669 def branch(self):
1669 1670 if self.alias:
1670 1671 from rhodecode.lib.vcs.backends import get_backend
1671 1672 return get_backend(self.alias).DEFAULT_BRANCH_NAME
1672 1673
1673 1674 @LazyProperty
1674 1675 def short_id(self):
1675 1676 return self.raw_id[:12]
1676 1677
1677 1678 @LazyProperty
1678 1679 def id(self):
1679 1680 return self.raw_id
1680 1681
1681 1682 def get_path_commit(self, path):
1682 1683 return self
1683 1684
1684 1685 def get_file_content(self, path):
1685 1686 return u''
1686 1687
1687 1688 def get_file_content_streamed(self, path):
1688 1689 yield self.get_file_content()
1689 1690
1690 1691 def get_file_size(self, path):
1691 1692 return 0
1692 1693
1693 1694
1694 1695 class EmptyChangesetClass(type):
1695 1696
1696 1697 def __instancecheck__(self, instance):
1697 1698 return isinstance(instance, EmptyCommit)
1698 1699
1699 1700
1700 1701 class EmptyChangeset(EmptyCommit):
1701 1702
1702 1703 __metaclass__ = EmptyChangesetClass
1703 1704
1704 1705 def __new__(cls, *args, **kwargs):
1705 1706 warnings.warn(
1706 1707 "Use EmptyCommit instead of EmptyChangeset", DeprecationWarning)
1707 1708 return super(EmptyCommit, cls).__new__(cls, *args, **kwargs)
1708 1709
1709 1710 def __init__(self, cs=EMPTY_COMMIT_ID, repo=None, requested_revision=None,
1710 1711 alias=None, revision=-1, message='', author='', date=None):
1711 1712 if requested_revision is not None:
1712 1713 warnings.warn(
1713 1714 "Parameter requested_revision not supported anymore",
1714 1715 DeprecationWarning)
1715 1716 super(EmptyChangeset, self).__init__(
1716 1717 commit_id=cs, repo=repo, alias=alias, idx=revision,
1717 1718 message=message, author=author, date=date)
1718 1719
1719 1720 @property
1720 1721 def revision(self):
1721 1722 warnings.warn("Use idx instead", DeprecationWarning)
1722 1723 return self.idx
1723 1724
1724 1725 @revision.setter
1725 1726 def revision(self, value):
1726 1727 warnings.warn("Use idx instead", DeprecationWarning)
1727 1728 self.idx = value
1728 1729
1729 1730
1730 1731 class EmptyRepository(BaseRepository):
1731 1732 def __init__(self, repo_path=None, config=None, create=False, **kwargs):
1732 1733 pass
1733 1734
1734 1735 def get_diff(self, *args, **kwargs):
1735 1736 from rhodecode.lib.vcs.backends.git.diff import GitDiff
1736 1737 return GitDiff('')
1737 1738
1738 1739
1739 1740 class CollectionGenerator(object):
1740 1741
1741 1742 def __init__(self, repo, commit_ids, collection_size=None, pre_load=None, translate_tag=None):
1742 1743 self.repo = repo
1743 1744 self.commit_ids = commit_ids
1744 1745 # TODO: (oliver) this isn't currently hooked up
1745 1746 self.collection_size = None
1746 1747 self.pre_load = pre_load
1747 1748 self.translate_tag = translate_tag
1748 1749
1749 1750 def __len__(self):
1750 1751 if self.collection_size is not None:
1751 1752 return self.collection_size
1752 1753 return self.commit_ids.__len__()
1753 1754
1754 1755 def __iter__(self):
1755 1756 for commit_id in self.commit_ids:
1756 1757 # TODO: johbo: Mercurial passes in commit indices or commit ids
1757 1758 yield self._commit_factory(commit_id)
1758 1759
1759 1760 def _commit_factory(self, commit_id):
1760 1761 """
1761 1762 Allows backends to override the way commits are generated.
1762 1763 """
1763 1764 return self.repo.get_commit(
1764 1765 commit_id=commit_id, pre_load=self.pre_load,
1765 1766 translate_tag=self.translate_tag)
1766 1767
1767 1768 def __getslice__(self, i, j):
1768 1769 """
1769 1770 Returns an iterator of sliced repository
1770 1771 """
1771 1772 commit_ids = self.commit_ids[i:j]
1772 1773 return self.__class__(
1773 1774 self.repo, commit_ids, pre_load=self.pre_load,
1774 1775 translate_tag=self.translate_tag)
1775 1776
1776 1777 def __repr__(self):
1777 1778 return '<CollectionGenerator[len:%s]>' % (self.__len__())
1778 1779
1779 1780
1780 1781 class Config(object):
1781 1782 """
1782 1783 Represents the configuration for a repository.
1783 1784
1784 1785 The API is inspired by :class:`ConfigParser.ConfigParser` from the
1785 1786 standard library. It implements only the needed subset.
1786 1787 """
1787 1788
1788 1789 def __init__(self):
1789 1790 self._values = {}
1790 1791
1791 1792 def copy(self):
1792 1793 clone = Config()
1793 1794 for section, values in self._values.items():
1794 1795 clone._values[section] = values.copy()
1795 1796 return clone
1796 1797
1797 1798 def __repr__(self):
1798 1799 return '<Config(%s sections) at %s>' % (
1799 1800 len(self._values), hex(id(self)))
1800 1801
1801 1802 def items(self, section):
1802 1803 return self._values.get(section, {}).iteritems()
1803 1804
1804 1805 def get(self, section, option):
1805 1806 return self._values.get(section, {}).get(option)
1806 1807
1807 1808 def set(self, section, option, value):
1808 1809 section_values = self._values.setdefault(section, {})
1809 1810 section_values[option] = value
1810 1811
1811 1812 def clear_section(self, section):
1812 1813 self._values[section] = {}
1813 1814
1814 1815 def serialize(self):
1815 1816 """
1816 1817 Creates a list of three tuples (section, key, value) representing
1817 1818 this config object.
1818 1819 """
1819 1820 items = []
1820 1821 for section in self._values:
1821 1822 for option, value in self._values[section].items():
1822 1823 items.append(
1823 1824 (safe_str(section), safe_str(option), safe_str(value)))
1824 1825 return items
1825 1826
1826 1827
1827 1828 class Diff(object):
1828 1829 """
1829 1830 Represents a diff result from a repository backend.
1830 1831
1831 1832 Subclasses have to provide a backend specific value for
1832 1833 :attr:`_header_re` and :attr:`_meta_re`.
1833 1834 """
1834 1835 _meta_re = None
1835 1836 _header_re = None
1836 1837
1837 1838 def __init__(self, raw_diff):
1838 1839 self.raw = raw_diff
1839 1840
1840 1841 def chunks(self):
1841 1842 """
1842 1843 split the diff in chunks of separate --git a/file b/file chunks
1843 1844 to make diffs consistent we must prepend with \n, and make sure
1844 1845 we can detect last chunk as this was also has special rule
1845 1846 """
1846 1847
1847 1848 diff_parts = ('\n' + self.raw).split('\ndiff --git')
1848 1849 header = diff_parts[0]
1849 1850
1850 1851 if self._meta_re:
1851 1852 match = self._meta_re.match(header)
1852 1853
1853 1854 chunks = diff_parts[1:]
1854 1855 total_chunks = len(chunks)
1855 1856
1856 1857 return (
1857 1858 DiffChunk(chunk, self, cur_chunk == total_chunks)
1858 1859 for cur_chunk, chunk in enumerate(chunks, start=1))
1859 1860
1860 1861
1861 1862 class DiffChunk(object):
1862 1863
1863 1864 def __init__(self, chunk, diff, last_chunk):
1864 1865 self._diff = diff
1865 1866
1866 1867 # since we split by \ndiff --git that part is lost from original diff
1867 1868 # we need to re-apply it at the end, EXCEPT ! if it's last chunk
1868 1869 if not last_chunk:
1869 1870 chunk += '\n'
1870 1871
1871 1872 match = self._diff._header_re.match(chunk)
1872 1873 self.header = match.groupdict()
1873 1874 self.diff = chunk[match.end():]
1874 1875 self.raw = chunk
1875 1876
1876 1877
1877 1878 class BasePathPermissionChecker(object):
1878 1879
1879 1880 @staticmethod
1880 1881 def create_from_patterns(includes, excludes):
1881 1882 if includes and '*' in includes and not excludes:
1882 1883 return AllPathPermissionChecker()
1883 1884 elif excludes and '*' in excludes:
1884 1885 return NonePathPermissionChecker()
1885 1886 else:
1886 1887 return PatternPathPermissionChecker(includes, excludes)
1887 1888
1888 1889 @property
1889 1890 def has_full_access(self):
1890 1891 raise NotImplemented()
1891 1892
1892 1893 def has_access(self, path):
1893 1894 raise NotImplemented()
1894 1895
1895 1896
1896 1897 class AllPathPermissionChecker(BasePathPermissionChecker):
1897 1898
1898 1899 @property
1899 1900 def has_full_access(self):
1900 1901 return True
1901 1902
1902 1903 def has_access(self, path):
1903 1904 return True
1904 1905
1905 1906
1906 1907 class NonePathPermissionChecker(BasePathPermissionChecker):
1907 1908
1908 1909 @property
1909 1910 def has_full_access(self):
1910 1911 return False
1911 1912
1912 1913 def has_access(self, path):
1913 1914 return False
1914 1915
1915 1916
1916 1917 class PatternPathPermissionChecker(BasePathPermissionChecker):
1917 1918
1918 1919 def __init__(self, includes, excludes):
1919 1920 self.includes = includes
1920 1921 self.excludes = excludes
1921 1922 self.includes_re = [] if not includes else [
1922 1923 re.compile(fnmatch.translate(pattern)) for pattern in includes]
1923 1924 self.excludes_re = [] if not excludes else [
1924 1925 re.compile(fnmatch.translate(pattern)) for pattern in excludes]
1925 1926
1926 1927 @property
1927 1928 def has_full_access(self):
1928 1929 return '*' in self.includes and not self.excludes
1929 1930
1930 1931 def has_access(self, path):
1931 1932 for regex in self.excludes_re:
1932 1933 if regex.match(path):
1933 1934 return False
1934 1935 for regex in self.includes_re:
1935 1936 if regex.match(path):
1936 1937 return True
1937 1938 return False
@@ -1,1051 +1,1051 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 GIT repository module
23 23 """
24 24
25 25 import logging
26 26 import os
27 27 import re
28 28
29 29 from zope.cachedescriptors.property import Lazy as LazyProperty
30 30
31 31 from rhodecode.lib.compat import OrderedDict
32 32 from rhodecode.lib.datelib import (
33 33 utcdate_fromtimestamp, makedate, date_astimestamp)
34 34 from rhodecode.lib.utils import safe_unicode, safe_str
35 35 from rhodecode.lib.utils2 import CachedProperty
36 36 from rhodecode.lib.vcs import connection, path as vcspath
37 37 from rhodecode.lib.vcs.backends.base import (
38 38 BaseRepository, CollectionGenerator, Config, MergeResponse,
39 39 MergeFailureReason, Reference)
40 40 from rhodecode.lib.vcs.backends.git.commit import GitCommit
41 41 from rhodecode.lib.vcs.backends.git.diff import GitDiff
42 42 from rhodecode.lib.vcs.backends.git.inmemory import GitInMemoryCommit
43 43 from rhodecode.lib.vcs.exceptions import (
44 44 CommitDoesNotExistError, EmptyRepositoryError,
45 45 RepositoryError, TagAlreadyExistError, TagDoesNotExistError, VCSError, UnresolvedFilesInRepo)
46 46
47 47
48 48 SHA_PATTERN = re.compile(r'^[[0-9a-fA-F]{12}|[0-9a-fA-F]{40}]$')
49 49
50 50 log = logging.getLogger(__name__)
51 51
52 52
53 53 class GitRepository(BaseRepository):
54 54 """
55 55 Git repository backend.
56 56 """
57 57 DEFAULT_BRANCH_NAME = 'master'
58 58
59 59 contact = BaseRepository.DEFAULT_CONTACT
60 60
61 61 def __init__(self, repo_path, config=None, create=False, src_url=None,
62 62 do_workspace_checkout=False, with_wire=None, bare=False):
63 63
64 64 self.path = safe_str(os.path.abspath(repo_path))
65 65 self.config = config if config else self.get_default_config()
66 66 self.with_wire = with_wire or {"cache": False} # default should not use cache
67 67
68 68 self._init_repo(create, src_url, do_workspace_checkout, bare)
69 69
70 70 # caches
71 71 self._commit_ids = {}
72 72
73 73 @LazyProperty
74 74 def _remote(self):
75 75 repo_id = self.path
76 76 return connection.Git(self.path, repo_id, self.config, with_wire=self.with_wire)
77 77
78 78 @LazyProperty
79 79 def bare(self):
80 80 return self._remote.bare()
81 81
82 82 @LazyProperty
83 83 def head(self):
84 84 return self._remote.head()
85 85
86 86 @CachedProperty
87 87 def commit_ids(self):
88 88 """
89 89 Returns list of commit ids, in ascending order. Being lazy
90 90 attribute allows external tools to inject commit ids from cache.
91 91 """
92 92 commit_ids = self._get_all_commit_ids()
93 93 self._rebuild_cache(commit_ids)
94 94 return commit_ids
95 95
96 96 def _rebuild_cache(self, commit_ids):
97 97 self._commit_ids = dict((commit_id, index)
98 98 for index, commit_id in enumerate(commit_ids))
99 99
100 100 def run_git_command(self, cmd, **opts):
101 101 """
102 102 Runs given ``cmd`` as git command and returns tuple
103 103 (stdout, stderr).
104 104
105 105 :param cmd: git command to be executed
106 106 :param opts: env options to pass into Subprocess command
107 107 """
108 108 if not isinstance(cmd, list):
109 109 raise ValueError('cmd must be a list, got %s instead' % type(cmd))
110 110
111 111 skip_stderr_log = opts.pop('skip_stderr_log', False)
112 112 out, err = self._remote.run_git_command(cmd, **opts)
113 113 if err and not skip_stderr_log:
114 114 log.debug('Stderr output of git command "%s":\n%s', cmd, err)
115 115 return out, err
116 116
117 117 @staticmethod
118 118 def check_url(url, config):
119 119 """
120 120 Function will check given url and try to verify if it's a valid
121 121 link. Sometimes it may happened that git will issue basic
122 122 auth request that can cause whole API to hang when used from python
123 123 or other external calls.
124 124
125 125 On failures it'll raise urllib2.HTTPError, exception is also thrown
126 126 when the return code is non 200
127 127 """
128 128 # check first if it's not an url
129 129 if os.path.isdir(url) or url.startswith('file:'):
130 130 return True
131 131
132 132 if '+' in url.split('://', 1)[0]:
133 133 url = url.split('+', 1)[1]
134 134
135 135 # Request the _remote to verify the url
136 136 return connection.Git.check_url(url, config.serialize())
137 137
138 138 @staticmethod
139 139 def is_valid_repository(path):
140 140 if os.path.isdir(os.path.join(path, '.git')):
141 141 return True
142 142 # check case of bare repository
143 143 try:
144 144 GitRepository(path)
145 145 return True
146 146 except VCSError:
147 147 pass
148 148 return False
149 149
150 150 def _init_repo(self, create, src_url=None, do_workspace_checkout=False,
151 151 bare=False):
152 152 if create and os.path.exists(self.path):
153 153 raise RepositoryError(
154 154 "Cannot create repository at %s, location already exist"
155 155 % self.path)
156 156
157 157 if bare and do_workspace_checkout:
158 158 raise RepositoryError("Cannot update a bare repository")
159 159 try:
160 160
161 161 if src_url:
162 162 # check URL before any actions
163 163 GitRepository.check_url(src_url, self.config)
164 164
165 165 if create:
166 166 os.makedirs(self.path, mode=0o755)
167 167
168 168 if bare:
169 169 self._remote.init_bare()
170 170 else:
171 171 self._remote.init()
172 172
173 173 if src_url and bare:
174 174 # bare repository only allows a fetch and checkout is not allowed
175 175 self.fetch(src_url, commit_ids=None)
176 176 elif src_url:
177 177 self.pull(src_url, commit_ids=None,
178 178 update_after=do_workspace_checkout)
179 179
180 180 else:
181 181 if not self._remote.assert_correct_path():
182 182 raise RepositoryError(
183 183 'Path "%s" does not contain a Git repository' %
184 184 (self.path,))
185 185
186 186 # TODO: johbo: check if we have to translate the OSError here
187 187 except OSError as err:
188 188 raise RepositoryError(err)
189 189
190 190 def _get_all_commit_ids(self):
191 191 return self._remote.get_all_commit_ids()
192 192
193 193 def _get_commit_ids(self, filters=None):
194 194 # we must check if this repo is not empty, since later command
195 195 # fails if it is. And it's cheaper to ask than throw the subprocess
196 196 # errors
197 197
198 198 head = self._remote.head(show_exc=False)
199 199
200 200 if not head:
201 201 return []
202 202
203 203 rev_filter = ['--branches', '--tags']
204 204 extra_filter = []
205 205
206 206 if filters:
207 207 if filters.get('since'):
208 208 extra_filter.append('--since=%s' % (filters['since']))
209 209 if filters.get('until'):
210 210 extra_filter.append('--until=%s' % (filters['until']))
211 211 if filters.get('branch_name'):
212 212 rev_filter = []
213 213 extra_filter.append(filters['branch_name'])
214 214 rev_filter.extend(extra_filter)
215 215
216 216 # if filters.get('start') or filters.get('end'):
217 217 # # skip is offset, max-count is limit
218 218 # if filters.get('start'):
219 219 # extra_filter += ' --skip=%s' % filters['start']
220 220 # if filters.get('end'):
221 221 # extra_filter += ' --max-count=%s' % (filters['end'] - (filters['start'] or 0))
222 222
223 223 cmd = ['rev-list', '--reverse', '--date-order'] + rev_filter
224 224 try:
225 225 output, __ = self.run_git_command(cmd)
226 226 except RepositoryError:
227 227 # Can be raised for empty repositories
228 228 return []
229 229 return output.splitlines()
230 230
231 231 def _lookup_commit(self, commit_id_or_idx, translate_tag=True, maybe_unreachable=False, reference_obj=None):
232 232
233 233 def is_null(value):
234 234 return len(value) == commit_id_or_idx.count('0')
235 235
236 236 if commit_id_or_idx in (None, '', 'tip', 'HEAD', 'head', -1):
237 237 return self.commit_ids[-1]
238 238
239 239 commit_missing_err = "Commit {} does not exist for `{}`".format(
240 240 *map(safe_str, [commit_id_or_idx, self.name]))
241 241
242 242 is_bstr = isinstance(commit_id_or_idx, (str, unicode))
243 243 is_branch = reference_obj and reference_obj.branch
244 244
245 245 lookup_ok = False
246 246 if is_bstr:
247 247 # Need to call remote to translate id for tagging scenarios,
248 248 # or branch that are numeric
249 249 try:
250 250 remote_data = self._remote.get_object(commit_id_or_idx,
251 251 maybe_unreachable=maybe_unreachable)
252 252 commit_id_or_idx = remote_data["commit_id"]
253 253 lookup_ok = True
254 254 except (CommitDoesNotExistError,):
255 255 lookup_ok = False
256 256
257 257 if lookup_ok is False:
258 258 is_numeric_idx = \
259 259 (is_bstr and commit_id_or_idx.isdigit() and len(commit_id_or_idx) < 12) \
260 260 or isinstance(commit_id_or_idx, int)
261 261 if not is_branch and (is_numeric_idx or is_null(commit_id_or_idx)):
262 262 try:
263 263 commit_id_or_idx = self.commit_ids[int(commit_id_or_idx)]
264 264 lookup_ok = True
265 265 except Exception:
266 266 raise CommitDoesNotExistError(commit_missing_err)
267 267
268 268 # we failed regular lookup, and by integer number lookup
269 269 if lookup_ok is False:
270 270 raise CommitDoesNotExistError(commit_missing_err)
271 271
272 272 # Ensure we return full id
273 273 if not SHA_PATTERN.match(str(commit_id_or_idx)):
274 274 raise CommitDoesNotExistError(
275 275 "Given commit id %s not recognized" % commit_id_or_idx)
276 276 return commit_id_or_idx
277 277
278 278 def get_hook_location(self):
279 279 """
280 280 returns absolute path to location where hooks are stored
281 281 """
282 282 loc = os.path.join(self.path, 'hooks')
283 283 if not self.bare:
284 284 loc = os.path.join(self.path, '.git', 'hooks')
285 285 return loc
286 286
287 287 @LazyProperty
288 288 def last_change(self):
289 289 """
290 290 Returns last change made on this repository as
291 291 `datetime.datetime` object.
292 292 """
293 293 try:
294 294 return self.get_commit().date
295 295 except RepositoryError:
296 296 tzoffset = makedate()[1]
297 297 return utcdate_fromtimestamp(self._get_fs_mtime(), tzoffset)
298 298
299 299 def _get_fs_mtime(self):
300 300 idx_loc = '' if self.bare else '.git'
301 301 # fallback to filesystem
302 302 in_path = os.path.join(self.path, idx_loc, "index")
303 303 he_path = os.path.join(self.path, idx_loc, "HEAD")
304 304 if os.path.exists(in_path):
305 305 return os.stat(in_path).st_mtime
306 306 else:
307 307 return os.stat(he_path).st_mtime
308 308
309 309 @LazyProperty
310 310 def description(self):
311 311 description = self._remote.get_description()
312 312 return safe_unicode(description or self.DEFAULT_DESCRIPTION)
313 313
314 314 def _get_refs_entries(self, prefix='', reverse=False, strip_prefix=True):
315 315 if self.is_empty():
316 316 return OrderedDict()
317 317
318 318 result = []
319 319 for ref, sha in self._refs.iteritems():
320 320 if ref.startswith(prefix):
321 321 ref_name = ref
322 322 if strip_prefix:
323 323 ref_name = ref[len(prefix):]
324 324 result.append((safe_unicode(ref_name), sha))
325 325
326 326 def get_name(entry):
327 327 return entry[0]
328 328
329 329 return OrderedDict(sorted(result, key=get_name, reverse=reverse))
330 330
331 331 def _get_branches(self):
332 332 return self._get_refs_entries(prefix='refs/heads/', strip_prefix=True)
333 333
334 334 @CachedProperty
335 335 def branches(self):
336 336 return self._get_branches()
337 337
338 338 @CachedProperty
339 339 def branches_closed(self):
340 340 return {}
341 341
342 342 @CachedProperty
343 343 def bookmarks(self):
344 344 return {}
345 345
346 346 @CachedProperty
347 347 def branches_all(self):
348 348 all_branches = {}
349 349 all_branches.update(self.branches)
350 350 all_branches.update(self.branches_closed)
351 351 return all_branches
352 352
353 353 @CachedProperty
354 354 def tags(self):
355 355 return self._get_tags()
356 356
357 357 def _get_tags(self):
358 358 return self._get_refs_entries(prefix='refs/tags/', strip_prefix=True, reverse=True)
359 359
360 360 def tag(self, name, user, commit_id=None, message=None, date=None,
361 361 **kwargs):
362 362 # TODO: fix this method to apply annotated tags correct with message
363 363 """
364 364 Creates and returns a tag for the given ``commit_id``.
365 365
366 366 :param name: name for new tag
367 367 :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>"
368 368 :param commit_id: commit id for which new tag would be created
369 369 :param message: message of the tag's commit
370 370 :param date: date of tag's commit
371 371
372 372 :raises TagAlreadyExistError: if tag with same name already exists
373 373 """
374 374 if name in self.tags:
375 375 raise TagAlreadyExistError("Tag %s already exists" % name)
376 376 commit = self.get_commit(commit_id=commit_id)
377 377 message = message or "Added tag %s for commit %s" % (name, commit.raw_id)
378 378
379 379 self._remote.set_refs('refs/tags/%s' % name, commit.raw_id)
380 380
381 381 self._invalidate_prop_cache('tags')
382 382 self._invalidate_prop_cache('_refs')
383 383
384 384 return commit
385 385
386 386 def remove_tag(self, name, user, message=None, date=None):
387 387 """
388 388 Removes tag with the given ``name``.
389 389
390 390 :param name: name of the tag to be removed
391 391 :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>"
392 392 :param message: message of the tag's removal commit
393 393 :param date: date of tag's removal commit
394 394
395 395 :raises TagDoesNotExistError: if tag with given name does not exists
396 396 """
397 397 if name not in self.tags:
398 398 raise TagDoesNotExistError("Tag %s does not exist" % name)
399 399
400 400 self._remote.tag_remove(name)
401 401 self._invalidate_prop_cache('tags')
402 402 self._invalidate_prop_cache('_refs')
403 403
404 404 def _get_refs(self):
405 405 return self._remote.get_refs()
406 406
407 407 @CachedProperty
408 408 def _refs(self):
409 409 return self._get_refs()
410 410
411 411 @property
412 412 def _ref_tree(self):
413 413 node = tree = {}
414 414 for ref, sha in self._refs.iteritems():
415 415 path = ref.split('/')
416 416 for bit in path[:-1]:
417 417 node = node.setdefault(bit, {})
418 418 node[path[-1]] = sha
419 419 node = tree
420 420 return tree
421 421
422 422 def get_remote_ref(self, ref_name):
423 423 ref_key = 'refs/remotes/origin/{}'.format(safe_str(ref_name))
424 424 try:
425 425 return self._refs[ref_key]
426 426 except Exception:
427 427 return
428 428
429 429 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
430 430 translate_tag=True, maybe_unreachable=False, reference_obj=None):
431 431 """
432 432 Returns `GitCommit` object representing commit from git repository
433 433 at the given `commit_id` or head (most recent commit) if None given.
434 434 """
435 435
436 436 if self.is_empty():
437 437 raise EmptyRepositoryError("There are no commits yet")
438 438
439 439 if commit_id is not None:
440 440 self._validate_commit_id(commit_id)
441 441 try:
442 442 # we have cached idx, use it without contacting the remote
443 443 idx = self._commit_ids[commit_id]
444 444 return GitCommit(self, commit_id, idx, pre_load=pre_load)
445 445 except KeyError:
446 446 pass
447 447
448 448 elif commit_idx is not None:
449 449 self._validate_commit_idx(commit_idx)
450 450 try:
451 451 _commit_id = self.commit_ids[commit_idx]
452 452 if commit_idx < 0:
453 453 commit_idx = self.commit_ids.index(_commit_id)
454 454 return GitCommit(self, _commit_id, commit_idx, pre_load=pre_load)
455 455 except IndexError:
456 456 commit_id = commit_idx
457 457 else:
458 458 commit_id = "tip"
459 459
460 460 if translate_tag:
461 461 commit_id = self._lookup_commit(
462 462 commit_id, maybe_unreachable=maybe_unreachable,
463 463 reference_obj=reference_obj)
464 464
465 465 try:
466 466 idx = self._commit_ids[commit_id]
467 467 except KeyError:
468 468 idx = -1
469 469
470 470 return GitCommit(self, commit_id, idx, pre_load=pre_load)
471 471
472 472 def get_commits(
473 473 self, start_id=None, end_id=None, start_date=None, end_date=None,
474 474 branch_name=None, show_hidden=False, pre_load=None, translate_tags=True):
475 475 """
476 476 Returns generator of `GitCommit` objects from start to end (both
477 477 are inclusive), in ascending date order.
478 478
479 479 :param start_id: None, str(commit_id)
480 480 :param end_id: None, str(commit_id)
481 481 :param start_date: if specified, commits with commit date less than
482 482 ``start_date`` would be filtered out from returned set
483 483 :param end_date: if specified, commits with commit date greater than
484 484 ``end_date`` would be filtered out from returned set
485 485 :param branch_name: if specified, commits not reachable from given
486 486 branch would be filtered out from returned set
487 487 :param show_hidden: Show hidden commits such as obsolete or hidden from
488 488 Mercurial evolve
489 489 :raise BranchDoesNotExistError: If given `branch_name` does not
490 490 exist.
491 491 :raise CommitDoesNotExistError: If commits for given `start` or
492 492 `end` could not be found.
493 493
494 494 """
495 495 if self.is_empty():
496 496 raise EmptyRepositoryError("There are no commits yet")
497 497
498 498 self._validate_branch_name(branch_name)
499 499
500 500 if start_id is not None:
501 501 self._validate_commit_id(start_id)
502 502 if end_id is not None:
503 503 self._validate_commit_id(end_id)
504 504
505 505 start_raw_id = self._lookup_commit(start_id)
506 506 start_pos = self._commit_ids[start_raw_id] if start_id else None
507 507 end_raw_id = self._lookup_commit(end_id)
508 508 end_pos = max(0, self._commit_ids[end_raw_id]) if end_id else None
509 509
510 510 if None not in [start_id, end_id] and start_pos > end_pos:
511 511 raise RepositoryError(
512 512 "Start commit '%s' cannot be after end commit '%s'" %
513 513 (start_id, end_id))
514 514
515 515 if end_pos is not None:
516 516 end_pos += 1
517 517
518 518 filter_ = []
519 519 if branch_name:
520 520 filter_.append({'branch_name': branch_name})
521 521 if start_date and not end_date:
522 522 filter_.append({'since': start_date})
523 523 if end_date and not start_date:
524 524 filter_.append({'until': end_date})
525 525 if start_date and end_date:
526 526 filter_.append({'since': start_date})
527 527 filter_.append({'until': end_date})
528 528
529 529 # if start_pos or end_pos:
530 530 # filter_.append({'start': start_pos})
531 531 # filter_.append({'end': end_pos})
532 532
533 533 if filter_:
534 534 revfilters = {
535 535 'branch_name': branch_name,
536 536 'since': start_date.strftime('%m/%d/%y %H:%M:%S') if start_date else None,
537 537 'until': end_date.strftime('%m/%d/%y %H:%M:%S') if end_date else None,
538 538 'start': start_pos,
539 539 'end': end_pos,
540 540 }
541 541 commit_ids = self._get_commit_ids(filters=revfilters)
542 542
543 543 else:
544 544 commit_ids = self.commit_ids
545 545
546 546 if start_pos or end_pos:
547 547 commit_ids = commit_ids[start_pos: end_pos]
548 548
549 549 return CollectionGenerator(self, commit_ids, pre_load=pre_load,
550 550 translate_tag=translate_tags)
551 551
552 552 def get_diff(
553 553 self, commit1, commit2, path='', ignore_whitespace=False,
554 554 context=3, path1=None):
555 555 """
556 556 Returns (git like) *diff*, as plain text. Shows changes introduced by
557 557 ``commit2`` since ``commit1``.
558 558
559 559 :param commit1: Entry point from which diff is shown. Can be
560 560 ``self.EMPTY_COMMIT`` - in this case, patch showing all
561 561 the changes since empty state of the repository until ``commit2``
562 562 :param commit2: Until which commits changes should be shown.
563 563 :param ignore_whitespace: If set to ``True``, would not show whitespace
564 564 changes. Defaults to ``False``.
565 565 :param context: How many lines before/after changed lines should be
566 566 shown. Defaults to ``3``.
567 567 """
568 568 self._validate_diff_commits(commit1, commit2)
569 569 if path1 is not None and path1 != path:
570 570 raise ValueError("Diff of two different paths not supported.")
571 571
572 572 if path:
573 573 file_filter = path
574 574 else:
575 575 file_filter = None
576 576
577 577 diff = self._remote.diff(
578 578 commit1.raw_id, commit2.raw_id, file_filter=file_filter,
579 579 opt_ignorews=ignore_whitespace,
580 580 context=context)
581 581 return GitDiff(diff)
582 582
583 583 def strip(self, commit_id, branch_name):
584 584 commit = self.get_commit(commit_id=commit_id)
585 585 if commit.merge:
586 586 raise Exception('Cannot reset to merge commit')
587 587
588 588 # parent is going to be the new head now
589 589 commit = commit.parents[0]
590 590 self._remote.set_refs('refs/heads/%s' % branch_name, commit.raw_id)
591 591
592 592 # clear cached properties
593 593 self._invalidate_prop_cache('commit_ids')
594 594 self._invalidate_prop_cache('_refs')
595 595 self._invalidate_prop_cache('branches')
596 596
597 597 return len(self.commit_ids)
598 598
599 599 def get_common_ancestor(self, commit_id1, commit_id2, repo2):
600 600 log.debug('Calculating common ancestor between %sc1:%s and %sc2:%s',
601 601 self, commit_id1, repo2, commit_id2)
602 602
603 603 if commit_id1 == commit_id2:
604 604 return commit_id1
605 605
606 606 if self != repo2:
607 607 commits = self._remote.get_missing_revs(
608 608 commit_id1, commit_id2, repo2.path)
609 609 if commits:
610 610 commit = repo2.get_commit(commits[-1])
611 611 if commit.parents:
612 612 ancestor_id = commit.parents[0].raw_id
613 613 else:
614 614 ancestor_id = None
615 615 else:
616 616 # no commits from other repo, ancestor_id is the commit_id2
617 617 ancestor_id = commit_id2
618 618 else:
619 619 output, __ = self.run_git_command(
620 620 ['merge-base', commit_id1, commit_id2])
621 ancestor_id = re.findall(r'[0-9a-fA-F]{40}', output)[0]
621 ancestor_id = self.COMMIT_ID_PAT.findall(output)[0]
622 622
623 623 log.debug('Found common ancestor with sha: %s', ancestor_id)
624 624
625 625 return ancestor_id
626 626
627 627 def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None):
628 628 repo1 = self
629 629 ancestor_id = None
630 630
631 631 if commit_id1 == commit_id2:
632 632 commits = []
633 633 elif repo1 != repo2:
634 634 missing_ids = self._remote.get_missing_revs(commit_id1, commit_id2,
635 635 repo2.path)
636 636 commits = [
637 637 repo2.get_commit(commit_id=commit_id, pre_load=pre_load)
638 638 for commit_id in reversed(missing_ids)]
639 639 else:
640 640 output, __ = repo1.run_git_command(
641 641 ['log', '--reverse', '--pretty=format: %H', '-s',
642 642 '%s..%s' % (commit_id1, commit_id2)])
643 643 commits = [
644 644 repo1.get_commit(commit_id=commit_id, pre_load=pre_load)
645 for commit_id in re.findall(r'[0-9a-fA-F]{40}', output)]
645 for commit_id in self.COMMIT_ID_PAT.findall(output)]
646 646
647 647 return commits
648 648
649 649 @LazyProperty
650 650 def in_memory_commit(self):
651 651 """
652 652 Returns ``GitInMemoryCommit`` object for this repository.
653 653 """
654 654 return GitInMemoryCommit(self)
655 655
656 656 def pull(self, url, commit_ids=None, update_after=False):
657 657 """
658 658 Pull changes from external location. Pull is different in GIT
659 659 that fetch since it's doing a checkout
660 660
661 661 :param commit_ids: Optional. Can be set to a list of commit ids
662 662 which shall be pulled from the other repository.
663 663 """
664 664 refs = None
665 665 if commit_ids is not None:
666 666 remote_refs = self._remote.get_remote_refs(url)
667 667 refs = [ref for ref in remote_refs if remote_refs[ref] in commit_ids]
668 668 self._remote.pull(url, refs=refs, update_after=update_after)
669 669 self._remote.invalidate_vcs_cache()
670 670
671 671 def fetch(self, url, commit_ids=None):
672 672 """
673 673 Fetch all git objects from external location.
674 674 """
675 675 self._remote.sync_fetch(url, refs=commit_ids)
676 676 self._remote.invalidate_vcs_cache()
677 677
678 678 def push(self, url):
679 679 refs = None
680 680 self._remote.sync_push(url, refs=refs)
681 681
682 682 def set_refs(self, ref_name, commit_id):
683 683 self._remote.set_refs(ref_name, commit_id)
684 684 self._invalidate_prop_cache('_refs')
685 685
686 686 def remove_ref(self, ref_name):
687 687 self._remote.remove_ref(ref_name)
688 688 self._invalidate_prop_cache('_refs')
689 689
690 690 def run_gc(self, prune=True):
691 691 cmd = ['gc', '--aggressive']
692 692 if prune:
693 693 cmd += ['--prune=now']
694 694 _stdout, stderr = self.run_git_command(cmd, fail_on_stderr=False)
695 695 return stderr
696 696
697 697 def _update_server_info(self):
698 698 """
699 699 runs gits update-server-info command in this repo instance
700 700 """
701 701 self._remote.update_server_info()
702 702
703 703 def _current_branch(self):
704 704 """
705 705 Return the name of the current branch.
706 706
707 707 It only works for non bare repositories (i.e. repositories with a
708 708 working copy)
709 709 """
710 710 if self.bare:
711 711 raise RepositoryError('Bare git repos do not have active branches')
712 712
713 713 if self.is_empty():
714 714 return None
715 715
716 716 stdout, _ = self.run_git_command(['rev-parse', '--abbrev-ref', 'HEAD'])
717 717 return stdout.strip()
718 718
719 719 def _checkout(self, branch_name, create=False, force=False):
720 720 """
721 721 Checkout a branch in the working directory.
722 722
723 723 It tries to create the branch if create is True, failing if the branch
724 724 already exists.
725 725
726 726 It only works for non bare repositories (i.e. repositories with a
727 727 working copy)
728 728 """
729 729 if self.bare:
730 730 raise RepositoryError('Cannot checkout branches in a bare git repo')
731 731
732 732 cmd = ['checkout']
733 733 if force:
734 734 cmd.append('-f')
735 735 if create:
736 736 cmd.append('-b')
737 737 cmd.append(branch_name)
738 738 self.run_git_command(cmd, fail_on_stderr=False)
739 739
740 740 def _create_branch(self, branch_name, commit_id):
741 741 """
742 742 creates a branch in a GIT repo
743 743 """
744 744 self._remote.create_branch(branch_name, commit_id)
745 745
746 746 def _identify(self):
747 747 """
748 748 Return the current state of the working directory.
749 749 """
750 750 if self.bare:
751 751 raise RepositoryError('Bare git repos do not have active branches')
752 752
753 753 if self.is_empty():
754 754 return None
755 755
756 756 stdout, _ = self.run_git_command(['rev-parse', 'HEAD'])
757 757 return stdout.strip()
758 758
759 759 def _local_clone(self, clone_path, branch_name, source_branch=None):
760 760 """
761 761 Create a local clone of the current repo.
762 762 """
763 763 # N.B.(skreft): the --branch option is required as otherwise the shallow
764 764 # clone will only fetch the active branch.
765 765 cmd = ['clone', '--branch', branch_name,
766 766 self.path, os.path.abspath(clone_path)]
767 767
768 768 self.run_git_command(cmd, fail_on_stderr=False)
769 769
770 770 # if we get the different source branch, make sure we also fetch it for
771 771 # merge conditions
772 772 if source_branch and source_branch != branch_name:
773 773 # check if the ref exists.
774 774 shadow_repo = GitRepository(os.path.abspath(clone_path))
775 775 if shadow_repo.get_remote_ref(source_branch):
776 776 cmd = ['fetch', self.path, source_branch]
777 777 self.run_git_command(cmd, fail_on_stderr=False)
778 778
779 779 def _local_fetch(self, repository_path, branch_name, use_origin=False):
780 780 """
781 781 Fetch a branch from a local repository.
782 782 """
783 783 repository_path = os.path.abspath(repository_path)
784 784 if repository_path == self.path:
785 785 raise ValueError('Cannot fetch from the same repository')
786 786
787 787 if use_origin:
788 788 branch_name = '+{branch}:refs/heads/{branch}'.format(
789 789 branch=branch_name)
790 790
791 791 cmd = ['fetch', '--no-tags', '--update-head-ok',
792 792 repository_path, branch_name]
793 793 self.run_git_command(cmd, fail_on_stderr=False)
794 794
795 795 def _local_reset(self, branch_name):
796 796 branch_name = '{}'.format(branch_name)
797 797 cmd = ['reset', '--hard', branch_name, '--']
798 798 self.run_git_command(cmd, fail_on_stderr=False)
799 799
800 800 def _last_fetch_heads(self):
801 801 """
802 802 Return the last fetched heads that need merging.
803 803
804 804 The algorithm is defined at
805 805 https://github.com/git/git/blob/v2.1.3/git-pull.sh#L283
806 806 """
807 807 if not self.bare:
808 808 fetch_heads_path = os.path.join(self.path, '.git', 'FETCH_HEAD')
809 809 else:
810 810 fetch_heads_path = os.path.join(self.path, 'FETCH_HEAD')
811 811
812 812 heads = []
813 813 with open(fetch_heads_path) as f:
814 814 for line in f:
815 815 if ' not-for-merge ' in line:
816 816 continue
817 817 line = re.sub('\t.*', '', line, flags=re.DOTALL)
818 818 heads.append(line)
819 819
820 820 return heads
821 821
822 822 def get_shadow_instance(self, shadow_repository_path, enable_hooks=False, cache=False):
823 823 return GitRepository(shadow_repository_path, with_wire={"cache": cache})
824 824
825 825 def _local_pull(self, repository_path, branch_name, ff_only=True):
826 826 """
827 827 Pull a branch from a local repository.
828 828 """
829 829 if self.bare:
830 830 raise RepositoryError('Cannot pull into a bare git repository')
831 831 # N.B.(skreft): The --ff-only option is to make sure this is a
832 832 # fast-forward (i.e., we are only pulling new changes and there are no
833 833 # conflicts with our current branch)
834 834 # Additionally, that option needs to go before --no-tags, otherwise git
835 835 # pull complains about it being an unknown flag.
836 836 cmd = ['pull']
837 837 if ff_only:
838 838 cmd.append('--ff-only')
839 839 cmd.extend(['--no-tags', repository_path, branch_name])
840 840 self.run_git_command(cmd, fail_on_stderr=False)
841 841
842 842 def _local_merge(self, merge_message, user_name, user_email, heads):
843 843 """
844 844 Merge the given head into the checked out branch.
845 845
846 846 It will force a merge commit.
847 847
848 848 Currently it raises an error if the repo is empty, as it is not possible
849 849 to create a merge commit in an empty repo.
850 850
851 851 :param merge_message: The message to use for the merge commit.
852 852 :param heads: the heads to merge.
853 853 """
854 854 if self.bare:
855 855 raise RepositoryError('Cannot merge into a bare git repository')
856 856
857 857 if not heads:
858 858 return
859 859
860 860 if self.is_empty():
861 861 # TODO(skreft): do something more robust in this case.
862 862 raise RepositoryError('Do not know how to merge into empty repositories yet')
863 863 unresolved = None
864 864
865 865 # N.B.(skreft): the --no-ff option is used to enforce the creation of a
866 866 # commit message. We also specify the user who is doing the merge.
867 867 cmd = ['-c', 'user.name="%s"' % safe_str(user_name),
868 868 '-c', 'user.email=%s' % safe_str(user_email),
869 869 'merge', '--no-ff', '-m', safe_str(merge_message)]
870 870
871 871 merge_cmd = cmd + heads
872 872
873 873 try:
874 874 self.run_git_command(merge_cmd, fail_on_stderr=False)
875 875 except RepositoryError:
876 876 files = self.run_git_command(['diff', '--name-only', '--diff-filter', 'U'],
877 877 fail_on_stderr=False)[0].splitlines()
878 878 # NOTE(marcink): we add U notation for consistent with HG backend output
879 879 unresolved = ['U {}'.format(f) for f in files]
880 880
881 881 # Cleanup any merge leftovers
882 882 self._remote.invalidate_vcs_cache()
883 883 self.run_git_command(['merge', '--abort'], fail_on_stderr=False)
884 884
885 885 if unresolved:
886 886 raise UnresolvedFilesInRepo(unresolved)
887 887 else:
888 888 raise
889 889
890 890 def _local_push(
891 891 self, source_branch, repository_path, target_branch,
892 892 enable_hooks=False, rc_scm_data=None):
893 893 """
894 894 Push the source_branch to the given repository and target_branch.
895 895
896 896 Currently it if the target_branch is not master and the target repo is
897 897 empty, the push will work, but then GitRepository won't be able to find
898 898 the pushed branch or the commits. As the HEAD will be corrupted (i.e.,
899 899 pointing to master, which does not exist).
900 900
901 901 It does not run the hooks in the target repo.
902 902 """
903 903 # TODO(skreft): deal with the case in which the target repo is empty,
904 904 # and the target_branch is not master.
905 905 target_repo = GitRepository(repository_path)
906 906 if (not target_repo.bare and
907 907 target_repo._current_branch() == target_branch):
908 908 # Git prevents pushing to the checked out branch, so simulate it by
909 909 # pulling into the target repository.
910 910 target_repo._local_pull(self.path, source_branch)
911 911 else:
912 912 cmd = ['push', os.path.abspath(repository_path),
913 913 '%s:%s' % (source_branch, target_branch)]
914 914 gitenv = {}
915 915 if rc_scm_data:
916 916 gitenv.update({'RC_SCM_DATA': rc_scm_data})
917 917
918 918 if not enable_hooks:
919 919 gitenv['RC_SKIP_HOOKS'] = '1'
920 920 self.run_git_command(cmd, fail_on_stderr=False, extra_env=gitenv)
921 921
922 922 def _get_new_pr_branch(self, source_branch, target_branch):
923 923 prefix = 'pr_%s-%s_' % (source_branch, target_branch)
924 924 pr_branches = []
925 925 for branch in self.branches:
926 926 if branch.startswith(prefix):
927 927 pr_branches.append(int(branch[len(prefix):]))
928 928
929 929 if not pr_branches:
930 930 branch_id = 0
931 931 else:
932 932 branch_id = max(pr_branches) + 1
933 933
934 934 return '%s%d' % (prefix, branch_id)
935 935
936 936 def _maybe_prepare_merge_workspace(
937 937 self, repo_id, workspace_id, target_ref, source_ref):
938 938 shadow_repository_path = self._get_shadow_repository_path(
939 939 self.path, repo_id, workspace_id)
940 940 if not os.path.exists(shadow_repository_path):
941 941 self._local_clone(
942 942 shadow_repository_path, target_ref.name, source_ref.name)
943 943 log.debug('Prepared %s shadow repository in %s',
944 944 self.alias, shadow_repository_path)
945 945
946 946 return shadow_repository_path
947 947
948 948 def _merge_repo(self, repo_id, workspace_id, target_ref,
949 949 source_repo, source_ref, merge_message,
950 950 merger_name, merger_email, dry_run=False,
951 951 use_rebase=False, close_branch=False):
952 952
953 953 log.debug('Executing merge_repo with %s strategy, dry_run mode:%s',
954 954 'rebase' if use_rebase else 'merge', dry_run)
955 955 if target_ref.commit_id != self.branches[target_ref.name]:
956 956 log.warning('Target ref %s commit mismatch %s vs %s', target_ref,
957 957 target_ref.commit_id, self.branches[target_ref.name])
958 958 return MergeResponse(
959 959 False, False, None, MergeFailureReason.TARGET_IS_NOT_HEAD,
960 960 metadata={'target_ref': target_ref})
961 961
962 962 shadow_repository_path = self._maybe_prepare_merge_workspace(
963 963 repo_id, workspace_id, target_ref, source_ref)
964 964 shadow_repo = self.get_shadow_instance(shadow_repository_path)
965 965
966 966 # checkout source, if it's different. Otherwise we could not
967 967 # fetch proper commits for merge testing
968 968 if source_ref.name != target_ref.name:
969 969 if shadow_repo.get_remote_ref(source_ref.name):
970 970 shadow_repo._checkout(source_ref.name, force=True)
971 971
972 972 # checkout target, and fetch changes
973 973 shadow_repo._checkout(target_ref.name, force=True)
974 974
975 975 # fetch/reset pull the target, in case it is changed
976 976 # this handles even force changes
977 977 shadow_repo._local_fetch(self.path, target_ref.name, use_origin=True)
978 978 shadow_repo._local_reset(target_ref.name)
979 979
980 980 # Need to reload repo to invalidate the cache, or otherwise we cannot
981 981 # retrieve the last target commit.
982 982 shadow_repo = self.get_shadow_instance(shadow_repository_path)
983 983 if target_ref.commit_id != shadow_repo.branches[target_ref.name]:
984 984 log.warning('Shadow Target ref %s commit mismatch %s vs %s',
985 985 target_ref, target_ref.commit_id,
986 986 shadow_repo.branches[target_ref.name])
987 987 return MergeResponse(
988 988 False, False, None, MergeFailureReason.TARGET_IS_NOT_HEAD,
989 989 metadata={'target_ref': target_ref})
990 990
991 991 # calculate new branch
992 992 pr_branch = shadow_repo._get_new_pr_branch(
993 993 source_ref.name, target_ref.name)
994 994 log.debug('using pull-request merge branch: `%s`', pr_branch)
995 995 # checkout to temp branch, and fetch changes
996 996 shadow_repo._checkout(pr_branch, create=True)
997 997 try:
998 998 shadow_repo._local_fetch(source_repo.path, source_ref.name)
999 999 except RepositoryError:
1000 1000 log.exception('Failure when doing local fetch on '
1001 1001 'shadow repo: %s', shadow_repo)
1002 1002 return MergeResponse(
1003 1003 False, False, None, MergeFailureReason.MISSING_SOURCE_REF,
1004 1004 metadata={'source_ref': source_ref})
1005 1005
1006 1006 merge_ref = None
1007 1007 merge_failure_reason = MergeFailureReason.NONE
1008 1008 metadata = {}
1009 1009 try:
1010 1010 shadow_repo._local_merge(merge_message, merger_name, merger_email,
1011 1011 [source_ref.commit_id])
1012 1012 merge_possible = True
1013 1013
1014 1014 # Need to invalidate the cache, or otherwise we
1015 1015 # cannot retrieve the merge commit.
1016 1016 shadow_repo = shadow_repo.get_shadow_instance(shadow_repository_path)
1017 1017 merge_commit_id = shadow_repo.branches[pr_branch]
1018 1018
1019 1019 # Set a reference pointing to the merge commit. This reference may
1020 1020 # be used to easily identify the last successful merge commit in
1021 1021 # the shadow repository.
1022 1022 shadow_repo.set_refs('refs/heads/pr-merge', merge_commit_id)
1023 1023 merge_ref = Reference('branch', 'pr-merge', merge_commit_id)
1024 1024 except RepositoryError as e:
1025 1025 log.exception('Failure when doing local merge on git shadow repo')
1026 1026 if isinstance(e, UnresolvedFilesInRepo):
1027 1027 metadata['unresolved_files'] = '\n* conflict: ' + ('\n * conflict: '.join(e.args[0]))
1028 1028
1029 1029 merge_possible = False
1030 1030 merge_failure_reason = MergeFailureReason.MERGE_FAILED
1031 1031
1032 1032 if merge_possible and not dry_run:
1033 1033 try:
1034 1034 shadow_repo._local_push(
1035 1035 pr_branch, self.path, target_ref.name, enable_hooks=True,
1036 1036 rc_scm_data=self.config.get('rhodecode', 'RC_SCM_DATA'))
1037 1037 merge_succeeded = True
1038 1038 except RepositoryError:
1039 1039 log.exception(
1040 1040 'Failure when doing local push from the shadow '
1041 1041 'repository to the target repository at %s.', self.path)
1042 1042 merge_succeeded = False
1043 1043 merge_failure_reason = MergeFailureReason.PUSH_FAILED
1044 1044 metadata['target'] = 'git shadow repo'
1045 1045 metadata['merge_commit'] = pr_branch
1046 1046 else:
1047 1047 merge_succeeded = False
1048 1048
1049 1049 return MergeResponse(
1050 1050 merge_possible, merge_succeeded, merge_ref, merge_failure_reason,
1051 1051 metadata=metadata)
@@ -1,396 +1,403 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import itertools
23 23 import logging
24 24 import collections
25 25
26 26 from rhodecode.model import BaseModel
27 27 from rhodecode.model.db import (
28 28 ChangesetStatus, ChangesetComment, PullRequest, PullRequestReviewers, Session)
29 29 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
30 30 from rhodecode.lib.markup_renderer import (
31 31 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
32 32
33 33 log = logging.getLogger(__name__)
34 34
35 35
36 36 class ChangesetStatusModel(BaseModel):
37 37
38 38 cls = ChangesetStatus
39 39
40 40 def __get_changeset_status(self, changeset_status):
41 41 return self._get_instance(ChangesetStatus, changeset_status)
42 42
43 43 def __get_pull_request(self, pull_request):
44 44 return self._get_instance(PullRequest, pull_request)
45 45
46 46 def _get_status_query(self, repo, revision, pull_request,
47 47 with_revisions=False):
48 48 repo = self._get_repo(repo)
49 49
50 50 q = ChangesetStatus.query()\
51 51 .filter(ChangesetStatus.repo == repo)
52 52 if not with_revisions:
53 53 q = q.filter(ChangesetStatus.version == 0)
54 54
55 55 if revision:
56 56 q = q.filter(ChangesetStatus.revision == revision)
57 57 elif pull_request:
58 58 pull_request = self.__get_pull_request(pull_request)
59 59 # TODO: johbo: Think about the impact of this join, there must
60 60 # be a reason why ChangesetStatus and ChanagesetComment is linked
61 61 # to the pull request. Might be that we want to do the same for
62 62 # the pull_request_version_id.
63 63 q = q.join(ChangesetComment).filter(
64 64 ChangesetStatus.pull_request == pull_request,
65 65 ChangesetComment.pull_request_version_id == None)
66 66 else:
67 67 raise Exception('Please specify revision or pull_request')
68 68 q = q.order_by(ChangesetStatus.version.asc())
69 69 return q
70 70
71 71 def calculate_group_vote(self, group_id, group_statuses_by_reviewers,
72 72 trim_votes=True):
73 73 """
74 74 Calculate status based on given group members, and voting rule
75 75
76 76
77 77 group1 - 4 members, 3 required for approval
78 78 user1 - approved
79 79 user2 - reject
80 80 user3 - approved
81 81 user4 - rejected
82 82
83 83 final_state: rejected, reasons not at least 3 votes
84 84
85 85
86 86 group1 - 4 members, 2 required for approval
87 87 user1 - approved
88 88 user2 - reject
89 89 user3 - approved
90 90 user4 - rejected
91 91
92 92 final_state: approved, reasons got at least 2 approvals
93 93
94 94 group1 - 4 members, ALL required for approval
95 95 user1 - approved
96 96 user2 - reject
97 97 user3 - approved
98 98 user4 - rejected
99 99
100 100 final_state: rejected, reasons not all approvals
101 101
102 102
103 103 group1 - 4 members, ALL required for approval
104 104 user1 - approved
105 105 user2 - approved
106 106 user3 - approved
107 107 user4 - approved
108 108
109 109 final_state: approved, reason all approvals received
110 110
111 111 group1 - 4 members, 5 required for approval
112 112 (approval should be shorted to number of actual members)
113 113
114 114 user1 - approved
115 115 user2 - approved
116 116 user3 - approved
117 117 user4 - approved
118 118
119 119 final_state: approved, reason all approvals received
120 120
121 121 """
122 122 group_vote_data = {}
123 123 got_rule = False
124 124 members = collections.OrderedDict()
125 125 for review_obj, user, reasons, mandatory, statuses \
126 126 in group_statuses_by_reviewers:
127 127
128 128 if not got_rule:
129 129 group_vote_data = review_obj.rule_user_group_data()
130 130 got_rule = bool(group_vote_data)
131 131
132 132 members[user.user_id] = statuses
133 133
134 134 if not group_vote_data:
135 135 return []
136 136
137 137 required_votes = group_vote_data['vote_rule']
138 138 if required_votes == -1:
139 139 # -1 means all required, so we replace it with how many people
140 140 # are in the members
141 141 required_votes = len(members)
142 142
143 143 if trim_votes and required_votes > len(members):
144 144 # we require more votes than we have members in the group
145 145 # in this case we trim the required votes to the number of members
146 146 required_votes = len(members)
147 147
148 148 approvals = sum([
149 149 1 for statuses in members.values()
150 150 if statuses and
151 151 statuses[0][1].status == ChangesetStatus.STATUS_APPROVED])
152 152
153 153 calculated_votes = []
154 154 # we have all votes from users, now check if we have enough votes
155 155 # to fill other
156 156 fill_in = ChangesetStatus.STATUS_UNDER_REVIEW
157 157 if approvals >= required_votes:
158 158 fill_in = ChangesetStatus.STATUS_APPROVED
159 159
160 160 for member, statuses in members.items():
161 161 if statuses:
162 162 ver, latest = statuses[0]
163 163 if fill_in == ChangesetStatus.STATUS_APPROVED:
164 164 calculated_votes.append(fill_in)
165 165 else:
166 166 calculated_votes.append(latest.status)
167 167 else:
168 168 calculated_votes.append(fill_in)
169 169
170 170 return calculated_votes
171 171
172 172 def calculate_status(self, statuses_by_reviewers):
173 173 """
174 174 Given the approval statuses from reviewers, calculates final approval
175 175 status. There can only be 3 results, all approved, all rejected. If
176 176 there is no consensus the PR is under review.
177 177
178 178 :param statuses_by_reviewers:
179 179 """
180 180
181 181 def group_rule(element):
182 182 review_obj = element[0]
183 183 rule_data = review_obj.rule_user_group_data()
184 184 if rule_data and rule_data['id']:
185 185 return rule_data['id']
186 186
187 187 voting_groups = itertools.groupby(
188 188 sorted(statuses_by_reviewers, key=group_rule), group_rule)
189 189
190 190 voting_by_groups = [(x, list(y)) for x, y in voting_groups]
191 191
192 192 reviewers_number = len(statuses_by_reviewers)
193 193 votes = collections.defaultdict(int)
194 194 for group, group_statuses_by_reviewers in voting_by_groups:
195 195 if group:
196 196 # calculate how the "group" voted
197 197 for vote_status in self.calculate_group_vote(
198 198 group, group_statuses_by_reviewers):
199 199 votes[vote_status] += 1
200 200 else:
201 201
202 202 for review_obj, user, reasons, mandatory, statuses \
203 203 in group_statuses_by_reviewers:
204 204 # individual vote
205 205 if statuses:
206 206 ver, latest = statuses[0]
207 207 votes[latest.status] += 1
208 208
209 209 approved_votes_count = votes[ChangesetStatus.STATUS_APPROVED]
210 210 rejected_votes_count = votes[ChangesetStatus.STATUS_REJECTED]
211 211
212 212 # TODO(marcink): with group voting, how does rejected work,
213 213 # do we ever get rejected state ?
214 214
215 215 if approved_votes_count and (approved_votes_count == reviewers_number):
216 216 return ChangesetStatus.STATUS_APPROVED
217 217
218 218 if rejected_votes_count and (rejected_votes_count == reviewers_number):
219 219 return ChangesetStatus.STATUS_REJECTED
220 220
221 221 return ChangesetStatus.STATUS_UNDER_REVIEW
222 222
223 223 def get_statuses(self, repo, revision=None, pull_request=None,
224 224 with_revisions=False):
225 225 q = self._get_status_query(repo, revision, pull_request,
226 226 with_revisions)
227 227 return q.all()
228 228
229 229 def get_status(self, repo, revision=None, pull_request=None, as_str=True):
230 230 """
231 231 Returns latest status of changeset for given revision or for given
232 232 pull request. Statuses are versioned inside a table itself and
233 233 version == 0 is always the current one
234 234
235 235 :param repo:
236 236 :param revision: 40char hash or None
237 237 :param pull_request: pull_request reference
238 238 :param as_str: return status as string not object
239 239 """
240 240 q = self._get_status_query(repo, revision, pull_request)
241 241
242 242 # need to use first here since there can be multiple statuses
243 243 # returned from pull_request
244 244 status = q.first()
245 245 if as_str:
246 246 status = status.status if status else status
247 247 st = status or ChangesetStatus.DEFAULT
248 248 return str(st)
249 249 return status
250 250
251 251 def _render_auto_status_message(
252 252 self, status, commit_id=None, pull_request=None):
253 253 """
254 254 render the message using DEFAULT_COMMENTS_RENDERER (RST renderer),
255 255 so it's always looking the same disregarding on which default
256 256 renderer system is using.
257 257
258 258 :param status: status text to change into
259 259 :param commit_id: the commit_id we change the status for
260 260 :param pull_request: the pull request we change the status for
261 261 """
262 262
263 263 new_status = ChangesetStatus.get_status_lbl(status)
264 264
265 265 params = {
266 266 'new_status_label': new_status,
267 267 'pull_request': pull_request,
268 268 'commit_id': commit_id,
269 269 }
270 270 renderer = RstTemplateRenderer()
271 271 return renderer.render('auto_status_change.mako', **params)
272 272
273 273 def set_status(self, repo, status, user, comment=None, revision=None,
274 274 pull_request=None, dont_allow_on_closed_pull_request=False):
275 275 """
276 276 Creates new status for changeset or updates the old ones bumping their
277 277 version, leaving the current status at
278 278
279 279 :param repo:
280 280 :param revision:
281 281 :param status:
282 282 :param user:
283 283 :param comment:
284 284 :param dont_allow_on_closed_pull_request: don't allow a status change
285 285 if last status was for pull request and it's closed. We shouldn't
286 286 mess around this manually
287 287 """
288 288 repo = self._get_repo(repo)
289 289
290 290 q = ChangesetStatus.query()
291 291
292 292 if revision:
293 293 q = q.filter(ChangesetStatus.repo == repo)
294 294 q = q.filter(ChangesetStatus.revision == revision)
295 295 elif pull_request:
296 296 pull_request = self.__get_pull_request(pull_request)
297 297 q = q.filter(ChangesetStatus.repo == pull_request.source_repo)
298 298 q = q.filter(ChangesetStatus.revision.in_(pull_request.revisions))
299 299 cur_statuses = q.all()
300 300
301 301 # if statuses exists and last is associated with a closed pull request
302 302 # we need to check if we can allow this status change
303 303 if (dont_allow_on_closed_pull_request and cur_statuses
304 304 and getattr(cur_statuses[0].pull_request, 'status', '')
305 305 == PullRequest.STATUS_CLOSED):
306 306 raise StatusChangeOnClosedPullRequestError(
307 307 'Changing status on closed pull request is not allowed'
308 308 )
309 309
310 310 # update all current statuses with older version
311 311 if cur_statuses:
312 312 for st in cur_statuses:
313 313 st.version += 1
314 314 Session().add(st)
315 315 Session().flush()
316 316
317 317 def _create_status(user, repo, status, comment, revision, pull_request):
318 318 new_status = ChangesetStatus()
319 319 new_status.author = self._get_user(user)
320 320 new_status.repo = self._get_repo(repo)
321 321 new_status.status = status
322 322 new_status.comment = comment
323 323 new_status.revision = revision
324 324 new_status.pull_request = pull_request
325 325 return new_status
326 326
327 327 if not comment:
328 328 from rhodecode.model.comment import CommentsModel
329 329 comment = CommentsModel().create(
330 330 text=self._render_auto_status_message(
331 331 status, commit_id=revision, pull_request=pull_request),
332 332 repo=repo,
333 333 user=user,
334 334 pull_request=pull_request,
335 335 send_email=False, renderer=DEFAULT_COMMENTS_RENDERER
336 336 )
337 337
338 338 if revision:
339 339 new_status = _create_status(
340 340 user=user, repo=repo, status=status, comment=comment,
341 341 revision=revision, pull_request=pull_request)
342 342 Session().add(new_status)
343 343 return new_status
344 344 elif pull_request:
345 345 # pull request can have more than one revision associated to it
346 346 # we need to create new version for each one
347 347 new_statuses = []
348 348 repo = pull_request.source_repo
349 349 for rev in pull_request.revisions:
350 350 new_status = _create_status(
351 351 user=user, repo=repo, status=status, comment=comment,
352 352 revision=rev, pull_request=pull_request)
353 353 new_statuses.append(new_status)
354 354 Session().add(new_status)
355 355 return new_statuses
356 356
357 def aggregate_votes_by_user(self, commit_statuses, reviewers_data):
357 def aggregate_votes_by_user(self, commit_statuses, reviewers_data, user=None):
358 358
359 359 commit_statuses_map = collections.defaultdict(list)
360 360 for st in commit_statuses:
361 361 commit_statuses_map[st.author.username] += [st]
362 362
363 363 reviewers = []
364 364
365 365 def version(commit_status):
366 366 return commit_status.version
367 367
368 368 for obj in reviewers_data:
369 369 if not obj.user:
370 370 continue
371 if user and obj.user.username != user.username:
372 # single user filter
373 continue
374
371 375 statuses = commit_statuses_map.get(obj.user.username, None)
372 376 if statuses:
373 377 status_groups = itertools.groupby(
374 378 sorted(statuses, key=version), version)
375 379 statuses = [(x, list(y)[0]) for x, y in status_groups]
376 380
377 381 reviewers.append((obj, obj.user, obj.reasons, obj.mandatory, statuses))
378 382
383 if user:
384 return reviewers[0] if reviewers else reviewers
385 else:
379 386 return reviewers
380 387
381 def reviewers_statuses(self, pull_request):
388 def reviewers_statuses(self, pull_request, user=None):
382 389 _commit_statuses = self.get_statuses(
383 390 pull_request.source_repo,
384 391 pull_request=pull_request,
385 392 with_revisions=True)
386 393 reviewers = pull_request.get_pull_request_reviewers(
387 394 role=PullRequestReviewers.ROLE_REVIEWER)
388 return self.aggregate_votes_by_user(_commit_statuses, reviewers)
395 return self.aggregate_votes_by_user(_commit_statuses, reviewers, user=user)
389 396
390 397 def calculated_review_status(self, pull_request):
391 398 """
392 399 calculate pull request status based on reviewers, it should be a list
393 400 of two element lists.
394 401 """
395 402 reviewers = self.reviewers_statuses(pull_request)
396 403 return self.calculate_status(reviewers)
@@ -1,5836 +1,5836 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Database Models for RhodeCode Enterprise
23 23 """
24 24
25 25 import re
26 26 import os
27 27 import time
28 28 import string
29 29 import hashlib
30 30 import logging
31 31 import datetime
32 32 import uuid
33 33 import warnings
34 34 import ipaddress
35 35 import functools
36 36 import traceback
37 37 import collections
38 38
39 39 from sqlalchemy import (
40 40 or_, and_, not_, func, cast, TypeDecorator, event,
41 41 Index, Sequence, UniqueConstraint, ForeignKey, CheckConstraint, Column,
42 42 Boolean, String, Unicode, UnicodeText, DateTime, Integer, LargeBinary,
43 43 Text, Float, PickleType, BigInteger)
44 from sqlalchemy.sql.expression import true, false, case
44 from sqlalchemy.sql.expression import true, false, case, null
45 45 from sqlalchemy.sql.functions import coalesce, count # pragma: no cover
46 46 from sqlalchemy.orm import (
47 relationship, joinedload, class_mapper, validates, aliased)
47 relationship, lazyload, joinedload, class_mapper, validates, aliased)
48 48 from sqlalchemy.ext.declarative import declared_attr
49 49 from sqlalchemy.ext.hybrid import hybrid_property
50 50 from sqlalchemy.exc import IntegrityError # pragma: no cover
51 51 from sqlalchemy.dialects.mysql import LONGTEXT
52 52 from zope.cachedescriptors.property import Lazy as LazyProperty
53 53 from pyramid import compat
54 54 from pyramid.threadlocal import get_current_request
55 55 from webhelpers2.text import remove_formatting
56 56
57 57 from rhodecode.translation import _
58 58 from rhodecode.lib.vcs import get_vcs_instance, VCSError
59 59 from rhodecode.lib.vcs.backends.base import (
60 60 EmptyCommit, Reference, unicode_to_reference, reference_to_unicode)
61 61 from rhodecode.lib.utils2 import (
62 62 str2bool, safe_str, get_commit_safe, safe_unicode, sha1_safe,
63 63 time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict,
64 64 glob2re, StrictAttributeDict, cleaned_uri, datetime_to_time, OrderedDefaultDict)
65 65 from rhodecode.lib.jsonalchemy import MutationObj, MutationList, JsonType, \
66 66 JsonRaw
67 67 from rhodecode.lib.ext_json import json
68 68 from rhodecode.lib.caching_query import FromCache
69 69 from rhodecode.lib.encrypt import AESCipher, validate_and_get_enc_data
70 70 from rhodecode.lib.encrypt2 import Encryptor
71 71 from rhodecode.lib.exceptions import (
72 72 ArtifactMetadataDuplicate, ArtifactMetadataBadValueType)
73 73 from rhodecode.model.meta import Base, Session
74 74
75 75 URL_SEP = '/'
76 76 log = logging.getLogger(__name__)
77 77
78 78 # =============================================================================
79 79 # BASE CLASSES
80 80 # =============================================================================
81 81
82 82 # this is propagated from .ini file rhodecode.encrypted_values.secret or
83 83 # beaker.session.secret if first is not set.
84 84 # and initialized at environment.py
85 85 ENCRYPTION_KEY = None
86 86
87 87 # used to sort permissions by types, '#' used here is not allowed to be in
88 88 # usernames, and it's very early in sorted string.printable table.
89 89 PERMISSION_TYPE_SORT = {
90 90 'admin': '####',
91 91 'write': '###',
92 92 'read': '##',
93 93 'none': '#',
94 94 }
95 95
96 96
97 97 def display_user_sort(obj):
98 98 """
99 99 Sort function used to sort permissions in .permissions() function of
100 100 Repository, RepoGroup, UserGroup. Also it put the default user in front
101 101 of all other resources
102 102 """
103 103
104 104 if obj.username == User.DEFAULT_USER:
105 105 return '#####'
106 106 prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '')
107 107 extra_sort_num = '1' # default
108 108
109 109 # NOTE(dan): inactive duplicates goes last
110 110 if getattr(obj, 'duplicate_perm', None):
111 111 extra_sort_num = '9'
112 112 return prefix + extra_sort_num + obj.username
113 113
114 114
115 115 def display_user_group_sort(obj):
116 116 """
117 117 Sort function used to sort permissions in .permissions() function of
118 118 Repository, RepoGroup, UserGroup. Also it put the default user in front
119 119 of all other resources
120 120 """
121 121
122 122 prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '')
123 123 return prefix + obj.users_group_name
124 124
125 125
126 126 def _hash_key(k):
127 127 return sha1_safe(k)
128 128
129 129
130 130 def in_filter_generator(qry, items, limit=500):
131 131 """
132 132 Splits IN() into multiple with OR
133 133 e.g.::
134 134 cnt = Repository.query().filter(
135 135 or_(
136 136 *in_filter_generator(Repository.repo_id, range(100000))
137 137 )).count()
138 138 """
139 139 if not items:
140 140 # empty list will cause empty query which might cause security issues
141 141 # this can lead to hidden unpleasant results
142 142 items = [-1]
143 143
144 144 parts = []
145 145 for chunk in xrange(0, len(items), limit):
146 146 parts.append(
147 147 qry.in_(items[chunk: chunk + limit])
148 148 )
149 149
150 150 return parts
151 151
152 152
153 153 base_table_args = {
154 154 'extend_existing': True,
155 155 'mysql_engine': 'InnoDB',
156 156 'mysql_charset': 'utf8',
157 157 'sqlite_autoincrement': True
158 158 }
159 159
160 160
161 161 class EncryptedTextValue(TypeDecorator):
162 162 """
163 163 Special column for encrypted long text data, use like::
164 164
165 165 value = Column("encrypted_value", EncryptedValue(), nullable=False)
166 166
167 167 This column is intelligent so if value is in unencrypted form it return
168 168 unencrypted form, but on save it always encrypts
169 169 """
170 170 impl = Text
171 171
172 172 def process_bind_param(self, value, dialect):
173 173 """
174 174 Setter for storing value
175 175 """
176 176 import rhodecode
177 177 if not value:
178 178 return value
179 179
180 180 # protect against double encrypting if values is already encrypted
181 181 if value.startswith('enc$aes$') \
182 182 or value.startswith('enc$aes_hmac$') \
183 183 or value.startswith('enc2$'):
184 184 raise ValueError('value needs to be in unencrypted format, '
185 185 'ie. not starting with enc$ or enc2$')
186 186
187 187 algo = rhodecode.CONFIG.get('rhodecode.encrypted_values.algorithm') or 'aes'
188 188 if algo == 'aes':
189 189 return 'enc$aes_hmac$%s' % AESCipher(ENCRYPTION_KEY, hmac=True).encrypt(value)
190 190 elif algo == 'fernet':
191 191 return Encryptor(ENCRYPTION_KEY).encrypt(value)
192 192 else:
193 193 ValueError('Bad encryption algorithm, should be fernet or aes, got: {}'.format(algo))
194 194
195 195 def process_result_value(self, value, dialect):
196 196 """
197 197 Getter for retrieving value
198 198 """
199 199
200 200 import rhodecode
201 201 if not value:
202 202 return value
203 203
204 204 algo = rhodecode.CONFIG.get('rhodecode.encrypted_values.algorithm') or 'aes'
205 205 enc_strict_mode = str2bool(rhodecode.CONFIG.get('rhodecode.encrypted_values.strict') or True)
206 206 if algo == 'aes':
207 207 decrypted_data = validate_and_get_enc_data(value, ENCRYPTION_KEY, enc_strict_mode)
208 208 elif algo == 'fernet':
209 209 return Encryptor(ENCRYPTION_KEY).decrypt(value)
210 210 else:
211 211 ValueError('Bad encryption algorithm, should be fernet or aes, got: {}'.format(algo))
212 212 return decrypted_data
213 213
214 214
215 215 class BaseModel(object):
216 216 """
217 217 Base Model for all classes
218 218 """
219 219
220 220 @classmethod
221 221 def _get_keys(cls):
222 222 """return column names for this model """
223 223 return class_mapper(cls).c.keys()
224 224
225 225 def get_dict(self):
226 226 """
227 227 return dict with keys and values corresponding
228 228 to this model data """
229 229
230 230 d = {}
231 231 for k in self._get_keys():
232 232 d[k] = getattr(self, k)
233 233
234 234 # also use __json__() if present to get additional fields
235 235 _json_attr = getattr(self, '__json__', None)
236 236 if _json_attr:
237 237 # update with attributes from __json__
238 238 if callable(_json_attr):
239 239 _json_attr = _json_attr()
240 240 for k, val in _json_attr.iteritems():
241 241 d[k] = val
242 242 return d
243 243
244 244 def get_appstruct(self):
245 245 """return list with keys and values tuples corresponding
246 246 to this model data """
247 247
248 248 lst = []
249 249 for k in self._get_keys():
250 250 lst.append((k, getattr(self, k),))
251 251 return lst
252 252
253 253 def populate_obj(self, populate_dict):
254 254 """populate model with data from given populate_dict"""
255 255
256 256 for k in self._get_keys():
257 257 if k in populate_dict:
258 258 setattr(self, k, populate_dict[k])
259 259
260 260 @classmethod
261 261 def query(cls):
262 262 return Session().query(cls)
263 263
264 264 @classmethod
265 265 def get(cls, id_):
266 266 if id_:
267 267 return cls.query().get(id_)
268 268
269 269 @classmethod
270 270 def get_or_404(cls, id_):
271 271 from pyramid.httpexceptions import HTTPNotFound
272 272
273 273 try:
274 274 id_ = int(id_)
275 275 except (TypeError, ValueError):
276 276 raise HTTPNotFound()
277 277
278 278 res = cls.query().get(id_)
279 279 if not res:
280 280 raise HTTPNotFound()
281 281 return res
282 282
283 283 @classmethod
284 284 def getAll(cls):
285 285 # deprecated and left for backward compatibility
286 286 return cls.get_all()
287 287
288 288 @classmethod
289 289 def get_all(cls):
290 290 return cls.query().all()
291 291
292 292 @classmethod
293 293 def delete(cls, id_):
294 294 obj = cls.query().get(id_)
295 295 Session().delete(obj)
296 296
297 297 @classmethod
298 298 def identity_cache(cls, session, attr_name, value):
299 299 exist_in_session = []
300 300 for (item_cls, pkey), instance in session.identity_map.items():
301 301 if cls == item_cls and getattr(instance, attr_name) == value:
302 302 exist_in_session.append(instance)
303 303 if exist_in_session:
304 304 if len(exist_in_session) == 1:
305 305 return exist_in_session[0]
306 306 log.exception(
307 307 'multiple objects with attr %s and '
308 308 'value %s found with same name: %r',
309 309 attr_name, value, exist_in_session)
310 310
311 311 def __repr__(self):
312 312 if hasattr(self, '__unicode__'):
313 313 # python repr needs to return str
314 314 try:
315 315 return safe_str(self.__unicode__())
316 316 except UnicodeDecodeError:
317 317 pass
318 318 return '<DB:%s>' % (self.__class__.__name__)
319 319
320 320
321 321 class RhodeCodeSetting(Base, BaseModel):
322 322 __tablename__ = 'rhodecode_settings'
323 323 __table_args__ = (
324 324 UniqueConstraint('app_settings_name'),
325 325 base_table_args
326 326 )
327 327
328 328 SETTINGS_TYPES = {
329 329 'str': safe_str,
330 330 'int': safe_int,
331 331 'unicode': safe_unicode,
332 332 'bool': str2bool,
333 333 'list': functools.partial(aslist, sep=',')
334 334 }
335 335 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
336 336 GLOBAL_CONF_KEY = 'app_settings'
337 337
338 338 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
339 339 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
340 340 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
341 341 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
342 342
343 343 def __init__(self, key='', val='', type='unicode'):
344 344 self.app_settings_name = key
345 345 self.app_settings_type = type
346 346 self.app_settings_value = val
347 347
348 348 @validates('_app_settings_value')
349 349 def validate_settings_value(self, key, val):
350 350 assert type(val) == unicode
351 351 return val
352 352
353 353 @hybrid_property
354 354 def app_settings_value(self):
355 355 v = self._app_settings_value
356 356 _type = self.app_settings_type
357 357 if _type:
358 358 _type = self.app_settings_type.split('.')[0]
359 359 # decode the encrypted value
360 360 if 'encrypted' in self.app_settings_type:
361 361 cipher = EncryptedTextValue()
362 362 v = safe_unicode(cipher.process_result_value(v, None))
363 363
364 364 converter = self.SETTINGS_TYPES.get(_type) or \
365 365 self.SETTINGS_TYPES['unicode']
366 366 return converter(v)
367 367
368 368 @app_settings_value.setter
369 369 def app_settings_value(self, val):
370 370 """
371 371 Setter that will always make sure we use unicode in app_settings_value
372 372
373 373 :param val:
374 374 """
375 375 val = safe_unicode(val)
376 376 # encode the encrypted value
377 377 if 'encrypted' in self.app_settings_type:
378 378 cipher = EncryptedTextValue()
379 379 val = safe_unicode(cipher.process_bind_param(val, None))
380 380 self._app_settings_value = val
381 381
382 382 @hybrid_property
383 383 def app_settings_type(self):
384 384 return self._app_settings_type
385 385
386 386 @app_settings_type.setter
387 387 def app_settings_type(self, val):
388 388 if val.split('.')[0] not in self.SETTINGS_TYPES:
389 389 raise Exception('type must be one of %s got %s'
390 390 % (self.SETTINGS_TYPES.keys(), val))
391 391 self._app_settings_type = val
392 392
393 393 @classmethod
394 394 def get_by_prefix(cls, prefix):
395 395 return RhodeCodeSetting.query()\
396 396 .filter(RhodeCodeSetting.app_settings_name.startswith(prefix))\
397 397 .all()
398 398
399 399 def __unicode__(self):
400 400 return u"<%s('%s:%s[%s]')>" % (
401 401 self.__class__.__name__,
402 402 self.app_settings_name, self.app_settings_value,
403 403 self.app_settings_type
404 404 )
405 405
406 406
407 407 class RhodeCodeUi(Base, BaseModel):
408 408 __tablename__ = 'rhodecode_ui'
409 409 __table_args__ = (
410 410 UniqueConstraint('ui_key'),
411 411 base_table_args
412 412 )
413 413
414 414 HOOK_REPO_SIZE = 'changegroup.repo_size'
415 415 # HG
416 416 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
417 417 HOOK_PULL = 'outgoing.pull_logger'
418 418 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
419 419 HOOK_PRETX_PUSH = 'pretxnchangegroup.pre_push'
420 420 HOOK_PUSH = 'changegroup.push_logger'
421 421 HOOK_PUSH_KEY = 'pushkey.key_push'
422 422
423 423 HOOKS_BUILTIN = [
424 424 HOOK_PRE_PULL,
425 425 HOOK_PULL,
426 426 HOOK_PRE_PUSH,
427 427 HOOK_PRETX_PUSH,
428 428 HOOK_PUSH,
429 429 HOOK_PUSH_KEY,
430 430 ]
431 431
432 432 # TODO: johbo: Unify way how hooks are configured for git and hg,
433 433 # git part is currently hardcoded.
434 434
435 435 # SVN PATTERNS
436 436 SVN_BRANCH_ID = 'vcs_svn_branch'
437 437 SVN_TAG_ID = 'vcs_svn_tag'
438 438
439 439 ui_id = Column(
440 440 "ui_id", Integer(), nullable=False, unique=True, default=None,
441 441 primary_key=True)
442 442 ui_section = Column(
443 443 "ui_section", String(255), nullable=True, unique=None, default=None)
444 444 ui_key = Column(
445 445 "ui_key", String(255), nullable=True, unique=None, default=None)
446 446 ui_value = Column(
447 447 "ui_value", String(255), nullable=True, unique=None, default=None)
448 448 ui_active = Column(
449 449 "ui_active", Boolean(), nullable=True, unique=None, default=True)
450 450
451 451 def __repr__(self):
452 452 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
453 453 self.ui_key, self.ui_value)
454 454
455 455
456 456 class RepoRhodeCodeSetting(Base, BaseModel):
457 457 __tablename__ = 'repo_rhodecode_settings'
458 458 __table_args__ = (
459 459 UniqueConstraint(
460 460 'app_settings_name', 'repository_id',
461 461 name='uq_repo_rhodecode_setting_name_repo_id'),
462 462 base_table_args
463 463 )
464 464
465 465 repository_id = Column(
466 466 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
467 467 nullable=False)
468 468 app_settings_id = Column(
469 469 "app_settings_id", Integer(), nullable=False, unique=True,
470 470 default=None, primary_key=True)
471 471 app_settings_name = Column(
472 472 "app_settings_name", String(255), nullable=True, unique=None,
473 473 default=None)
474 474 _app_settings_value = Column(
475 475 "app_settings_value", String(4096), nullable=True, unique=None,
476 476 default=None)
477 477 _app_settings_type = Column(
478 478 "app_settings_type", String(255), nullable=True, unique=None,
479 479 default=None)
480 480
481 481 repository = relationship('Repository')
482 482
483 483 def __init__(self, repository_id, key='', val='', type='unicode'):
484 484 self.repository_id = repository_id
485 485 self.app_settings_name = key
486 486 self.app_settings_type = type
487 487 self.app_settings_value = val
488 488
489 489 @validates('_app_settings_value')
490 490 def validate_settings_value(self, key, val):
491 491 assert type(val) == unicode
492 492 return val
493 493
494 494 @hybrid_property
495 495 def app_settings_value(self):
496 496 v = self._app_settings_value
497 497 type_ = self.app_settings_type
498 498 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
499 499 converter = SETTINGS_TYPES.get(type_) or SETTINGS_TYPES['unicode']
500 500 return converter(v)
501 501
502 502 @app_settings_value.setter
503 503 def app_settings_value(self, val):
504 504 """
505 505 Setter that will always make sure we use unicode in app_settings_value
506 506
507 507 :param val:
508 508 """
509 509 self._app_settings_value = safe_unicode(val)
510 510
511 511 @hybrid_property
512 512 def app_settings_type(self):
513 513 return self._app_settings_type
514 514
515 515 @app_settings_type.setter
516 516 def app_settings_type(self, val):
517 517 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
518 518 if val not in SETTINGS_TYPES:
519 519 raise Exception('type must be one of %s got %s'
520 520 % (SETTINGS_TYPES.keys(), val))
521 521 self._app_settings_type = val
522 522
523 523 def __unicode__(self):
524 524 return u"<%s('%s:%s:%s[%s]')>" % (
525 525 self.__class__.__name__, self.repository.repo_name,
526 526 self.app_settings_name, self.app_settings_value,
527 527 self.app_settings_type
528 528 )
529 529
530 530
531 531 class RepoRhodeCodeUi(Base, BaseModel):
532 532 __tablename__ = 'repo_rhodecode_ui'
533 533 __table_args__ = (
534 534 UniqueConstraint(
535 535 'repository_id', 'ui_section', 'ui_key',
536 536 name='uq_repo_rhodecode_ui_repository_id_section_key'),
537 537 base_table_args
538 538 )
539 539
540 540 repository_id = Column(
541 541 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
542 542 nullable=False)
543 543 ui_id = Column(
544 544 "ui_id", Integer(), nullable=False, unique=True, default=None,
545 545 primary_key=True)
546 546 ui_section = Column(
547 547 "ui_section", String(255), nullable=True, unique=None, default=None)
548 548 ui_key = Column(
549 549 "ui_key", String(255), nullable=True, unique=None, default=None)
550 550 ui_value = Column(
551 551 "ui_value", String(255), nullable=True, unique=None, default=None)
552 552 ui_active = Column(
553 553 "ui_active", Boolean(), nullable=True, unique=None, default=True)
554 554
555 555 repository = relationship('Repository')
556 556
557 557 def __repr__(self):
558 558 return '<%s[%s:%s]%s=>%s]>' % (
559 559 self.__class__.__name__, self.repository.repo_name,
560 560 self.ui_section, self.ui_key, self.ui_value)
561 561
562 562
563 563 class User(Base, BaseModel):
564 564 __tablename__ = 'users'
565 565 __table_args__ = (
566 566 UniqueConstraint('username'), UniqueConstraint('email'),
567 567 Index('u_username_idx', 'username'),
568 568 Index('u_email_idx', 'email'),
569 569 base_table_args
570 570 )
571 571
572 572 DEFAULT_USER = 'default'
573 573 DEFAULT_USER_EMAIL = 'anonymous@rhodecode.org'
574 574 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
575 575
576 576 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
577 577 username = Column("username", String(255), nullable=True, unique=None, default=None)
578 578 password = Column("password", String(255), nullable=True, unique=None, default=None)
579 579 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
580 580 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
581 581 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
582 582 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
583 583 _email = Column("email", String(255), nullable=True, unique=None, default=None)
584 584 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
585 585 last_activity = Column('last_activity', DateTime(timezone=False), nullable=True, unique=None, default=None)
586 586 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
587 587
588 588 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
589 589 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
590 590 _api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
591 591 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
592 592 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
593 593 _user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data
594 594
595 595 user_log = relationship('UserLog')
596 596 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all, delete-orphan')
597 597
598 598 repositories = relationship('Repository')
599 599 repository_groups = relationship('RepoGroup')
600 600 user_groups = relationship('UserGroup')
601 601
602 602 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
603 603 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
604 604
605 605 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all, delete-orphan')
606 606 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all, delete-orphan')
607 607 user_group_to_perm = relationship('UserUserGroupToPerm', primaryjoin='UserUserGroupToPerm.user_id==User.user_id', cascade='all, delete-orphan')
608 608
609 609 group_member = relationship('UserGroupMember', cascade='all')
610 610
611 611 notifications = relationship('UserNotification', cascade='all')
612 612 # notifications assigned to this user
613 613 user_created_notifications = relationship('Notification', cascade='all')
614 614 # comments created by this user
615 615 user_comments = relationship('ChangesetComment', cascade='all')
616 616 # user profile extra info
617 617 user_emails = relationship('UserEmailMap', cascade='all')
618 618 user_ip_map = relationship('UserIpMap', cascade='all')
619 619 user_auth_tokens = relationship('UserApiKeys', cascade='all')
620 620 user_ssh_keys = relationship('UserSshKeys', cascade='all')
621 621
622 622 # gists
623 623 user_gists = relationship('Gist', cascade='all')
624 624 # user pull requests
625 625 user_pull_requests = relationship('PullRequest', cascade='all')
626 626
627 627 # external identities
628 628 external_identities = relationship(
629 629 'ExternalIdentity',
630 630 primaryjoin="User.user_id==ExternalIdentity.local_user_id",
631 631 cascade='all')
632 632 # review rules
633 633 user_review_rules = relationship('RepoReviewRuleUser', cascade='all')
634 634
635 635 # artifacts owned
636 636 artifacts = relationship('FileStore', primaryjoin='FileStore.user_id==User.user_id')
637 637
638 638 # no cascade, set NULL
639 639 scope_artifacts = relationship('FileStore', primaryjoin='FileStore.scope_user_id==User.user_id')
640 640
641 641 def __unicode__(self):
642 642 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
643 643 self.user_id, self.username)
644 644
645 645 @hybrid_property
646 646 def email(self):
647 647 return self._email
648 648
649 649 @email.setter
650 650 def email(self, val):
651 651 self._email = val.lower() if val else None
652 652
653 653 @hybrid_property
654 654 def first_name(self):
655 655 from rhodecode.lib import helpers as h
656 656 if self.name:
657 657 return h.escape(self.name)
658 658 return self.name
659 659
660 660 @hybrid_property
661 661 def last_name(self):
662 662 from rhodecode.lib import helpers as h
663 663 if self.lastname:
664 664 return h.escape(self.lastname)
665 665 return self.lastname
666 666
667 667 @hybrid_property
668 668 def api_key(self):
669 669 """
670 670 Fetch if exist an auth-token with role ALL connected to this user
671 671 """
672 672 user_auth_token = UserApiKeys.query()\
673 673 .filter(UserApiKeys.user_id == self.user_id)\
674 674 .filter(or_(UserApiKeys.expires == -1,
675 675 UserApiKeys.expires >= time.time()))\
676 676 .filter(UserApiKeys.role == UserApiKeys.ROLE_ALL).first()
677 677 if user_auth_token:
678 678 user_auth_token = user_auth_token.api_key
679 679
680 680 return user_auth_token
681 681
682 682 @api_key.setter
683 683 def api_key(self, val):
684 684 # don't allow to set API key this is deprecated for now
685 685 self._api_key = None
686 686
687 687 @property
688 688 def reviewer_pull_requests(self):
689 689 return PullRequestReviewers.query() \
690 690 .options(joinedload(PullRequestReviewers.pull_request)) \
691 691 .filter(PullRequestReviewers.user_id == self.user_id) \
692 692 .all()
693 693
694 694 @property
695 695 def firstname(self):
696 696 # alias for future
697 697 return self.name
698 698
699 699 @property
700 700 def emails(self):
701 701 other = UserEmailMap.query()\
702 702 .filter(UserEmailMap.user == self) \
703 703 .order_by(UserEmailMap.email_id.asc()) \
704 704 .all()
705 705 return [self.email] + [x.email for x in other]
706 706
707 707 def emails_cached(self):
708 708 emails = UserEmailMap.query()\
709 709 .filter(UserEmailMap.user == self) \
710 710 .order_by(UserEmailMap.email_id.asc())
711 711
712 712 emails = emails.options(
713 713 FromCache("sql_cache_short", "get_user_{}_emails".format(self.user_id))
714 714 )
715 715
716 716 return [self.email] + [x.email for x in emails]
717 717
718 718 @property
719 719 def auth_tokens(self):
720 720 auth_tokens = self.get_auth_tokens()
721 721 return [x.api_key for x in auth_tokens]
722 722
723 723 def get_auth_tokens(self):
724 724 return UserApiKeys.query()\
725 725 .filter(UserApiKeys.user == self)\
726 726 .order_by(UserApiKeys.user_api_key_id.asc())\
727 727 .all()
728 728
729 729 @LazyProperty
730 730 def feed_token(self):
731 731 return self.get_feed_token()
732 732
733 733 def get_feed_token(self, cache=True):
734 734 feed_tokens = UserApiKeys.query()\
735 735 .filter(UserApiKeys.user == self)\
736 736 .filter(UserApiKeys.role == UserApiKeys.ROLE_FEED)
737 737 if cache:
738 738 feed_tokens = feed_tokens.options(
739 739 FromCache("sql_cache_short", "get_user_feed_token_%s" % self.user_id))
740 740
741 741 feed_tokens = feed_tokens.all()
742 742 if feed_tokens:
743 743 return feed_tokens[0].api_key
744 744 return 'NO_FEED_TOKEN_AVAILABLE'
745 745
746 746 @LazyProperty
747 747 def artifact_token(self):
748 748 return self.get_artifact_token()
749 749
750 750 def get_artifact_token(self, cache=True):
751 751 artifacts_tokens = UserApiKeys.query()\
752 752 .filter(UserApiKeys.user == self) \
753 753 .filter(or_(UserApiKeys.expires == -1,
754 754 UserApiKeys.expires >= time.time())) \
755 755 .filter(UserApiKeys.role == UserApiKeys.ROLE_ARTIFACT_DOWNLOAD)
756 756
757 757 if cache:
758 758 artifacts_tokens = artifacts_tokens.options(
759 759 FromCache("sql_cache_short", "get_user_artifact_token_%s" % self.user_id))
760 760
761 761 artifacts_tokens = artifacts_tokens.all()
762 762 if artifacts_tokens:
763 763 return artifacts_tokens[0].api_key
764 764 return 'NO_ARTIFACT_TOKEN_AVAILABLE'
765 765
766 766 def get_or_create_artifact_token(self):
767 767 artifacts_tokens = UserApiKeys.query()\
768 768 .filter(UserApiKeys.user == self) \
769 769 .filter(or_(UserApiKeys.expires == -1,
770 770 UserApiKeys.expires >= time.time())) \
771 771 .filter(UserApiKeys.role == UserApiKeys.ROLE_ARTIFACT_DOWNLOAD)
772 772
773 773 artifacts_tokens = artifacts_tokens.all()
774 774 if artifacts_tokens:
775 775 return artifacts_tokens[0].api_key
776 776 else:
777 777 from rhodecode.model.auth_token import AuthTokenModel
778 778 artifact_token = AuthTokenModel().create(
779 779 self, 'auto-generated-artifact-token',
780 780 lifetime=-1, role=UserApiKeys.ROLE_ARTIFACT_DOWNLOAD)
781 781 Session.commit()
782 782 return artifact_token.api_key
783 783
784 784 @classmethod
785 785 def get(cls, user_id, cache=False):
786 786 if not user_id:
787 787 return
788 788
789 789 user = cls.query()
790 790 if cache:
791 791 user = user.options(
792 792 FromCache("sql_cache_short", "get_users_%s" % user_id))
793 793 return user.get(user_id)
794 794
795 795 @classmethod
796 796 def extra_valid_auth_tokens(cls, user, role=None):
797 797 tokens = UserApiKeys.query().filter(UserApiKeys.user == user)\
798 798 .filter(or_(UserApiKeys.expires == -1,
799 799 UserApiKeys.expires >= time.time()))
800 800 if role:
801 801 tokens = tokens.filter(or_(UserApiKeys.role == role,
802 802 UserApiKeys.role == UserApiKeys.ROLE_ALL))
803 803 return tokens.all()
804 804
805 805 def authenticate_by_token(self, auth_token, roles=None, scope_repo_id=None):
806 806 from rhodecode.lib import auth
807 807
808 808 log.debug('Trying to authenticate user: %s via auth-token, '
809 809 'and roles: %s', self, roles)
810 810
811 811 if not auth_token:
812 812 return False
813 813
814 814 roles = (roles or []) + [UserApiKeys.ROLE_ALL]
815 815 tokens_q = UserApiKeys.query()\
816 816 .filter(UserApiKeys.user_id == self.user_id)\
817 817 .filter(or_(UserApiKeys.expires == -1,
818 818 UserApiKeys.expires >= time.time()))
819 819
820 820 tokens_q = tokens_q.filter(UserApiKeys.role.in_(roles))
821 821
822 822 crypto_backend = auth.crypto_backend()
823 823 enc_token_map = {}
824 824 plain_token_map = {}
825 825 for token in tokens_q:
826 826 if token.api_key.startswith(crypto_backend.ENC_PREF):
827 827 enc_token_map[token.api_key] = token
828 828 else:
829 829 plain_token_map[token.api_key] = token
830 830 log.debug(
831 831 'Found %s plain and %s encrypted tokens to check for authentication for this user',
832 832 len(plain_token_map), len(enc_token_map))
833 833
834 834 # plain token match comes first
835 835 match = plain_token_map.get(auth_token)
836 836
837 837 # check encrypted tokens now
838 838 if not match:
839 839 for token_hash, token in enc_token_map.items():
840 840 # NOTE(marcink): this is expensive to calculate, but most secure
841 841 if crypto_backend.hash_check(auth_token, token_hash):
842 842 match = token
843 843 break
844 844
845 845 if match:
846 846 log.debug('Found matching token %s', match)
847 847 if match.repo_id:
848 848 log.debug('Found scope, checking for scope match of token %s', match)
849 849 if match.repo_id == scope_repo_id:
850 850 return True
851 851 else:
852 852 log.debug(
853 853 'AUTH_TOKEN: scope mismatch, token has a set repo scope: %s, '
854 854 'and calling scope is:%s, skipping further checks',
855 855 match.repo, scope_repo_id)
856 856 return False
857 857 else:
858 858 return True
859 859
860 860 return False
861 861
862 862 @property
863 863 def ip_addresses(self):
864 864 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
865 865 return [x.ip_addr for x in ret]
866 866
867 867 @property
868 868 def username_and_name(self):
869 869 return '%s (%s %s)' % (self.username, self.first_name, self.last_name)
870 870
871 871 @property
872 872 def username_or_name_or_email(self):
873 873 full_name = self.full_name if self.full_name is not ' ' else None
874 874 return self.username or full_name or self.email
875 875
876 876 @property
877 877 def full_name(self):
878 878 return '%s %s' % (self.first_name, self.last_name)
879 879
880 880 @property
881 881 def full_name_or_username(self):
882 882 return ('%s %s' % (self.first_name, self.last_name)
883 883 if (self.first_name and self.last_name) else self.username)
884 884
885 885 @property
886 886 def full_contact(self):
887 887 return '%s %s <%s>' % (self.first_name, self.last_name, self.email)
888 888
889 889 @property
890 890 def short_contact(self):
891 891 return '%s %s' % (self.first_name, self.last_name)
892 892
893 893 @property
894 894 def is_admin(self):
895 895 return self.admin
896 896
897 897 @property
898 898 def language(self):
899 899 return self.user_data.get('language')
900 900
901 901 def AuthUser(self, **kwargs):
902 902 """
903 903 Returns instance of AuthUser for this user
904 904 """
905 905 from rhodecode.lib.auth import AuthUser
906 906 return AuthUser(user_id=self.user_id, username=self.username, **kwargs)
907 907
908 908 @hybrid_property
909 909 def user_data(self):
910 910 if not self._user_data:
911 911 return {}
912 912
913 913 try:
914 return json.loads(self._user_data)
914 return json.loads(self._user_data) or {}
915 915 except TypeError:
916 916 return {}
917 917
918 918 @user_data.setter
919 919 def user_data(self, val):
920 920 if not isinstance(val, dict):
921 921 raise Exception('user_data must be dict, got %s' % type(val))
922 922 try:
923 923 self._user_data = json.dumps(val)
924 924 except Exception:
925 925 log.error(traceback.format_exc())
926 926
927 927 @classmethod
928 928 def get_by_username(cls, username, case_insensitive=False,
929 929 cache=False, identity_cache=False):
930 930 session = Session()
931 931
932 932 if case_insensitive:
933 933 q = cls.query().filter(
934 934 func.lower(cls.username) == func.lower(username))
935 935 else:
936 936 q = cls.query().filter(cls.username == username)
937 937
938 938 if cache:
939 939 if identity_cache:
940 940 val = cls.identity_cache(session, 'username', username)
941 941 if val:
942 942 return val
943 943 else:
944 944 cache_key = "get_user_by_name_%s" % _hash_key(username)
945 945 q = q.options(
946 946 FromCache("sql_cache_short", cache_key))
947 947
948 948 return q.scalar()
949 949
950 950 @classmethod
951 951 def get_by_auth_token(cls, auth_token, cache=False):
952 952 q = UserApiKeys.query()\
953 953 .filter(UserApiKeys.api_key == auth_token)\
954 954 .filter(or_(UserApiKeys.expires == -1,
955 955 UserApiKeys.expires >= time.time()))
956 956 if cache:
957 957 q = q.options(
958 958 FromCache("sql_cache_short", "get_auth_token_%s" % auth_token))
959 959
960 960 match = q.first()
961 961 if match:
962 962 return match.user
963 963
964 964 @classmethod
965 965 def get_by_email(cls, email, case_insensitive=False, cache=False):
966 966
967 967 if case_insensitive:
968 968 q = cls.query().filter(func.lower(cls.email) == func.lower(email))
969 969
970 970 else:
971 971 q = cls.query().filter(cls.email == email)
972 972
973 973 email_key = _hash_key(email)
974 974 if cache:
975 975 q = q.options(
976 976 FromCache("sql_cache_short", "get_email_key_%s" % email_key))
977 977
978 978 ret = q.scalar()
979 979 if ret is None:
980 980 q = UserEmailMap.query()
981 981 # try fetching in alternate email map
982 982 if case_insensitive:
983 983 q = q.filter(func.lower(UserEmailMap.email) == func.lower(email))
984 984 else:
985 985 q = q.filter(UserEmailMap.email == email)
986 986 q = q.options(joinedload(UserEmailMap.user))
987 987 if cache:
988 988 q = q.options(
989 989 FromCache("sql_cache_short", "get_email_map_key_%s" % email_key))
990 990 ret = getattr(q.scalar(), 'user', None)
991 991
992 992 return ret
993 993
994 994 @classmethod
995 995 def get_from_cs_author(cls, author):
996 996 """
997 997 Tries to get User objects out of commit author string
998 998
999 999 :param author:
1000 1000 """
1001 1001 from rhodecode.lib.helpers import email, author_name
1002 1002 # Valid email in the attribute passed, see if they're in the system
1003 1003 _email = email(author)
1004 1004 if _email:
1005 1005 user = cls.get_by_email(_email, case_insensitive=True)
1006 1006 if user:
1007 1007 return user
1008 1008 # Maybe we can match by username?
1009 1009 _author = author_name(author)
1010 1010 user = cls.get_by_username(_author, case_insensitive=True)
1011 1011 if user:
1012 1012 return user
1013 1013
1014 1014 def update_userdata(self, **kwargs):
1015 1015 usr = self
1016 1016 old = usr.user_data
1017 1017 old.update(**kwargs)
1018 1018 usr.user_data = old
1019 1019 Session().add(usr)
1020 1020 log.debug('updated userdata with %s', kwargs)
1021 1021
1022 1022 def update_lastlogin(self):
1023 1023 """Update user lastlogin"""
1024 1024 self.last_login = datetime.datetime.now()
1025 1025 Session().add(self)
1026 1026 log.debug('updated user %s lastlogin', self.username)
1027 1027
1028 1028 def update_password(self, new_password):
1029 1029 from rhodecode.lib.auth import get_crypt_password
1030 1030
1031 1031 self.password = get_crypt_password(new_password)
1032 1032 Session().add(self)
1033 1033
1034 1034 @classmethod
1035 1035 def get_first_super_admin(cls):
1036 1036 user = User.query()\
1037 1037 .filter(User.admin == true()) \
1038 1038 .order_by(User.user_id.asc()) \
1039 1039 .first()
1040 1040
1041 1041 if user is None:
1042 1042 raise Exception('FATAL: Missing administrative account!')
1043 1043 return user
1044 1044
1045 1045 @classmethod
1046 1046 def get_all_super_admins(cls, only_active=False):
1047 1047 """
1048 1048 Returns all admin accounts sorted by username
1049 1049 """
1050 1050 qry = User.query().filter(User.admin == true()).order_by(User.username.asc())
1051 1051 if only_active:
1052 1052 qry = qry.filter(User.active == true())
1053 1053 return qry.all()
1054 1054
1055 1055 @classmethod
1056 1056 def get_all_user_ids(cls, only_active=True):
1057 1057 """
1058 1058 Returns all users IDs
1059 1059 """
1060 1060 qry = Session().query(User.user_id)
1061 1061
1062 1062 if only_active:
1063 1063 qry = qry.filter(User.active == true())
1064 1064 return [x.user_id for x in qry]
1065 1065
1066 1066 @classmethod
1067 1067 def get_default_user(cls, cache=False, refresh=False):
1068 1068 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
1069 1069 if user is None:
1070 1070 raise Exception('FATAL: Missing default account!')
1071 1071 if refresh:
1072 1072 # The default user might be based on outdated state which
1073 1073 # has been loaded from the cache.
1074 1074 # A call to refresh() ensures that the
1075 1075 # latest state from the database is used.
1076 1076 Session().refresh(user)
1077 1077 return user
1078 1078
1079 1079 @classmethod
1080 1080 def get_default_user_id(cls):
1081 1081 import rhodecode
1082 1082 return rhodecode.CONFIG['default_user_id']
1083 1083
1084 1084 def _get_default_perms(self, user, suffix=''):
1085 1085 from rhodecode.model.permission import PermissionModel
1086 1086 return PermissionModel().get_default_perms(user.user_perms, suffix)
1087 1087
1088 1088 def get_default_perms(self, suffix=''):
1089 1089 return self._get_default_perms(self, suffix)
1090 1090
1091 1091 def get_api_data(self, include_secrets=False, details='full'):
1092 1092 """
1093 1093 Common function for generating user related data for API
1094 1094
1095 1095 :param include_secrets: By default secrets in the API data will be replaced
1096 1096 by a placeholder value to prevent exposing this data by accident. In case
1097 1097 this data shall be exposed, set this flag to ``True``.
1098 1098
1099 1099 :param details: details can be 'basic|full' basic gives only a subset of
1100 1100 the available user information that includes user_id, name and emails.
1101 1101 """
1102 1102 user = self
1103 1103 user_data = self.user_data
1104 1104 data = {
1105 1105 'user_id': user.user_id,
1106 1106 'username': user.username,
1107 1107 'firstname': user.name,
1108 1108 'lastname': user.lastname,
1109 1109 'description': user.description,
1110 1110 'email': user.email,
1111 1111 'emails': user.emails,
1112 1112 }
1113 1113 if details == 'basic':
1114 1114 return data
1115 1115
1116 1116 auth_token_length = 40
1117 1117 auth_token_replacement = '*' * auth_token_length
1118 1118
1119 1119 extras = {
1120 1120 'auth_tokens': [auth_token_replacement],
1121 1121 'active': user.active,
1122 1122 'admin': user.admin,
1123 1123 'extern_type': user.extern_type,
1124 1124 'extern_name': user.extern_name,
1125 1125 'last_login': user.last_login,
1126 1126 'last_activity': user.last_activity,
1127 1127 'ip_addresses': user.ip_addresses,
1128 1128 'language': user_data.get('language')
1129 1129 }
1130 1130 data.update(extras)
1131 1131
1132 1132 if include_secrets:
1133 1133 data['auth_tokens'] = user.auth_tokens
1134 1134 return data
1135 1135
1136 1136 def __json__(self):
1137 1137 data = {
1138 1138 'full_name': self.full_name,
1139 1139 'full_name_or_username': self.full_name_or_username,
1140 1140 'short_contact': self.short_contact,
1141 1141 'full_contact': self.full_contact,
1142 1142 }
1143 1143 data.update(self.get_api_data())
1144 1144 return data
1145 1145
1146 1146
1147 1147 class UserApiKeys(Base, BaseModel):
1148 1148 __tablename__ = 'user_api_keys'
1149 1149 __table_args__ = (
1150 1150 Index('uak_api_key_idx', 'api_key'),
1151 1151 Index('uak_api_key_expires_idx', 'api_key', 'expires'),
1152 1152 base_table_args
1153 1153 )
1154 1154 __mapper_args__ = {}
1155 1155
1156 1156 # ApiKey role
1157 1157 ROLE_ALL = 'token_role_all'
1158 1158 ROLE_VCS = 'token_role_vcs'
1159 1159 ROLE_API = 'token_role_api'
1160 1160 ROLE_HTTP = 'token_role_http'
1161 1161 ROLE_FEED = 'token_role_feed'
1162 1162 ROLE_ARTIFACT_DOWNLOAD = 'role_artifact_download'
1163 1163 # The last one is ignored in the list as we only
1164 1164 # use it for one action, and cannot be created by users
1165 1165 ROLE_PASSWORD_RESET = 'token_password_reset'
1166 1166
1167 1167 ROLES = [ROLE_ALL, ROLE_VCS, ROLE_API, ROLE_HTTP, ROLE_FEED, ROLE_ARTIFACT_DOWNLOAD]
1168 1168
1169 1169 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1170 1170 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1171 1171 api_key = Column("api_key", String(255), nullable=False, unique=True)
1172 1172 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1173 1173 expires = Column('expires', Float(53), nullable=False)
1174 1174 role = Column('role', String(255), nullable=True)
1175 1175 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1176 1176
1177 1177 # scope columns
1178 1178 repo_id = Column(
1179 1179 'repo_id', Integer(), ForeignKey('repositories.repo_id'),
1180 1180 nullable=True, unique=None, default=None)
1181 1181 repo = relationship('Repository', lazy='joined')
1182 1182
1183 1183 repo_group_id = Column(
1184 1184 'repo_group_id', Integer(), ForeignKey('groups.group_id'),
1185 1185 nullable=True, unique=None, default=None)
1186 1186 repo_group = relationship('RepoGroup', lazy='joined')
1187 1187
1188 1188 user = relationship('User', lazy='joined')
1189 1189
1190 1190 def __unicode__(self):
1191 1191 return u"<%s('%s')>" % (self.__class__.__name__, self.role)
1192 1192
1193 1193 def __json__(self):
1194 1194 data = {
1195 1195 'auth_token': self.api_key,
1196 1196 'role': self.role,
1197 1197 'scope': self.scope_humanized,
1198 1198 'expired': self.expired
1199 1199 }
1200 1200 return data
1201 1201
1202 1202 def get_api_data(self, include_secrets=False):
1203 1203 data = self.__json__()
1204 1204 if include_secrets:
1205 1205 return data
1206 1206 else:
1207 1207 data['auth_token'] = self.token_obfuscated
1208 1208 return data
1209 1209
1210 1210 @hybrid_property
1211 1211 def description_safe(self):
1212 1212 from rhodecode.lib import helpers as h
1213 1213 return h.escape(self.description)
1214 1214
1215 1215 @property
1216 1216 def expired(self):
1217 1217 if self.expires == -1:
1218 1218 return False
1219 1219 return time.time() > self.expires
1220 1220
1221 1221 @classmethod
1222 1222 def _get_role_name(cls, role):
1223 1223 return {
1224 1224 cls.ROLE_ALL: _('all'),
1225 1225 cls.ROLE_HTTP: _('http/web interface'),
1226 1226 cls.ROLE_VCS: _('vcs (git/hg/svn protocol)'),
1227 1227 cls.ROLE_API: _('api calls'),
1228 1228 cls.ROLE_FEED: _('feed access'),
1229 1229 cls.ROLE_ARTIFACT_DOWNLOAD: _('artifacts downloads'),
1230 1230 }.get(role, role)
1231 1231
1232 1232 @classmethod
1233 1233 def _get_role_description(cls, role):
1234 1234 return {
1235 1235 cls.ROLE_ALL: _('Token for all actions.'),
1236 1236 cls.ROLE_HTTP: _('Token to access RhodeCode pages via web interface without '
1237 1237 'login using `api_access_controllers_whitelist` functionality.'),
1238 1238 cls.ROLE_VCS: _('Token to interact over git/hg/svn protocols. '
1239 1239 'Requires auth_token authentication plugin to be active. <br/>'
1240 1240 'Such Token should be used then instead of a password to '
1241 1241 'interact with a repository, and additionally can be '
1242 1242 'limited to single repository using repo scope.'),
1243 1243 cls.ROLE_API: _('Token limited to api calls.'),
1244 1244 cls.ROLE_FEED: _('Token to read RSS/ATOM feed.'),
1245 1245 cls.ROLE_ARTIFACT_DOWNLOAD: _('Token for artifacts downloads.'),
1246 1246 }.get(role, role)
1247 1247
1248 1248 @property
1249 1249 def role_humanized(self):
1250 1250 return self._get_role_name(self.role)
1251 1251
1252 1252 def _get_scope(self):
1253 1253 if self.repo:
1254 1254 return 'Repository: {}'.format(self.repo.repo_name)
1255 1255 if self.repo_group:
1256 1256 return 'RepositoryGroup: {} (recursive)'.format(self.repo_group.group_name)
1257 1257 return 'Global'
1258 1258
1259 1259 @property
1260 1260 def scope_humanized(self):
1261 1261 return self._get_scope()
1262 1262
1263 1263 @property
1264 1264 def token_obfuscated(self):
1265 1265 if self.api_key:
1266 1266 return self.api_key[:4] + "****"
1267 1267
1268 1268
1269 1269 class UserEmailMap(Base, BaseModel):
1270 1270 __tablename__ = 'user_email_map'
1271 1271 __table_args__ = (
1272 1272 Index('uem_email_idx', 'email'),
1273 1273 UniqueConstraint('email'),
1274 1274 base_table_args
1275 1275 )
1276 1276 __mapper_args__ = {}
1277 1277
1278 1278 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1279 1279 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1280 1280 _email = Column("email", String(255), nullable=True, unique=False, default=None)
1281 1281 user = relationship('User', lazy='joined')
1282 1282
1283 1283 @validates('_email')
1284 1284 def validate_email(self, key, email):
1285 1285 # check if this email is not main one
1286 1286 main_email = Session().query(User).filter(User.email == email).scalar()
1287 1287 if main_email is not None:
1288 1288 raise AttributeError('email %s is present is user table' % email)
1289 1289 return email
1290 1290
1291 1291 @hybrid_property
1292 1292 def email(self):
1293 1293 return self._email
1294 1294
1295 1295 @email.setter
1296 1296 def email(self, val):
1297 1297 self._email = val.lower() if val else None
1298 1298
1299 1299
1300 1300 class UserIpMap(Base, BaseModel):
1301 1301 __tablename__ = 'user_ip_map'
1302 1302 __table_args__ = (
1303 1303 UniqueConstraint('user_id', 'ip_addr'),
1304 1304 base_table_args
1305 1305 )
1306 1306 __mapper_args__ = {}
1307 1307
1308 1308 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1309 1309 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1310 1310 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
1311 1311 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
1312 1312 description = Column("description", String(10000), nullable=True, unique=None, default=None)
1313 1313 user = relationship('User', lazy='joined')
1314 1314
1315 1315 @hybrid_property
1316 1316 def description_safe(self):
1317 1317 from rhodecode.lib import helpers as h
1318 1318 return h.escape(self.description)
1319 1319
1320 1320 @classmethod
1321 1321 def _get_ip_range(cls, ip_addr):
1322 1322 net = ipaddress.ip_network(safe_unicode(ip_addr), strict=False)
1323 1323 return [str(net.network_address), str(net.broadcast_address)]
1324 1324
1325 1325 def __json__(self):
1326 1326 return {
1327 1327 'ip_addr': self.ip_addr,
1328 1328 'ip_range': self._get_ip_range(self.ip_addr),
1329 1329 }
1330 1330
1331 1331 def __unicode__(self):
1332 1332 return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__,
1333 1333 self.user_id, self.ip_addr)
1334 1334
1335 1335
1336 1336 class UserSshKeys(Base, BaseModel):
1337 1337 __tablename__ = 'user_ssh_keys'
1338 1338 __table_args__ = (
1339 1339 Index('usk_ssh_key_fingerprint_idx', 'ssh_key_fingerprint'),
1340 1340
1341 1341 UniqueConstraint('ssh_key_fingerprint'),
1342 1342
1343 1343 base_table_args
1344 1344 )
1345 1345 __mapper_args__ = {}
1346 1346
1347 1347 ssh_key_id = Column('ssh_key_id', Integer(), nullable=False, unique=True, default=None, primary_key=True)
1348 1348 ssh_key_data = Column('ssh_key_data', String(10240), nullable=False, unique=None, default=None)
1349 1349 ssh_key_fingerprint = Column('ssh_key_fingerprint', String(255), nullable=False, unique=None, default=None)
1350 1350
1351 1351 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1352 1352
1353 1353 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1354 1354 accessed_on = Column('accessed_on', DateTime(timezone=False), nullable=True, default=None)
1355 1355 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1356 1356
1357 1357 user = relationship('User', lazy='joined')
1358 1358
1359 1359 def __json__(self):
1360 1360 data = {
1361 1361 'ssh_fingerprint': self.ssh_key_fingerprint,
1362 1362 'description': self.description,
1363 1363 'created_on': self.created_on
1364 1364 }
1365 1365 return data
1366 1366
1367 1367 def get_api_data(self):
1368 1368 data = self.__json__()
1369 1369 return data
1370 1370
1371 1371
1372 1372 class UserLog(Base, BaseModel):
1373 1373 __tablename__ = 'user_logs'
1374 1374 __table_args__ = (
1375 1375 base_table_args,
1376 1376 )
1377 1377
1378 1378 VERSION_1 = 'v1'
1379 1379 VERSION_2 = 'v2'
1380 1380 VERSIONS = [VERSION_1, VERSION_2]
1381 1381
1382 1382 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1383 1383 user_id = Column("user_id", Integer(), ForeignKey('users.user_id',ondelete='SET NULL'), nullable=True, unique=None, default=None)
1384 1384 username = Column("username", String(255), nullable=True, unique=None, default=None)
1385 1385 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id', ondelete='SET NULL'), nullable=True, unique=None, default=None)
1386 1386 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
1387 1387 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
1388 1388 action = Column("action", Text().with_variant(Text(1200000), 'mysql'), nullable=True, unique=None, default=None)
1389 1389 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
1390 1390
1391 1391 version = Column("version", String(255), nullable=True, default=VERSION_1)
1392 1392 user_data = Column('user_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=LONGTEXT()))))
1393 1393 action_data = Column('action_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=LONGTEXT()))))
1394 1394
1395 1395 def __unicode__(self):
1396 1396 return u"<%s('id:%s:%s')>" % (
1397 1397 self.__class__.__name__, self.repository_name, self.action)
1398 1398
1399 1399 def __json__(self):
1400 1400 return {
1401 1401 'user_id': self.user_id,
1402 1402 'username': self.username,
1403 1403 'repository_id': self.repository_id,
1404 1404 'repository_name': self.repository_name,
1405 1405 'user_ip': self.user_ip,
1406 1406 'action_date': self.action_date,
1407 1407 'action': self.action,
1408 1408 }
1409 1409
1410 1410 @hybrid_property
1411 1411 def entry_id(self):
1412 1412 return self.user_log_id
1413 1413
1414 1414 @property
1415 1415 def action_as_day(self):
1416 1416 return datetime.date(*self.action_date.timetuple()[:3])
1417 1417
1418 1418 user = relationship('User')
1419 1419 repository = relationship('Repository', cascade='')
1420 1420
1421 1421
1422 1422 class UserGroup(Base, BaseModel):
1423 1423 __tablename__ = 'users_groups'
1424 1424 __table_args__ = (
1425 1425 base_table_args,
1426 1426 )
1427 1427
1428 1428 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1429 1429 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
1430 1430 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
1431 1431 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
1432 1432 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
1433 1433 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1434 1434 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1435 1435 _group_data = Column("group_data", LargeBinary(), nullable=True) # JSON data
1436 1436
1437 1437 members = relationship('UserGroupMember', cascade="all, delete-orphan", lazy="joined")
1438 1438 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
1439 1439 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1440 1440 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1441 1441 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
1442 1442 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
1443 1443
1444 1444 user_group_review_rules = relationship('RepoReviewRuleUserGroup', cascade='all')
1445 1445 user = relationship('User', primaryjoin="User.user_id==UserGroup.user_id")
1446 1446
1447 1447 @classmethod
1448 1448 def _load_group_data(cls, column):
1449 1449 if not column:
1450 1450 return {}
1451 1451
1452 1452 try:
1453 1453 return json.loads(column) or {}
1454 1454 except TypeError:
1455 1455 return {}
1456 1456
1457 1457 @hybrid_property
1458 1458 def description_safe(self):
1459 1459 from rhodecode.lib import helpers as h
1460 1460 return h.escape(self.user_group_description)
1461 1461
1462 1462 @hybrid_property
1463 1463 def group_data(self):
1464 1464 return self._load_group_data(self._group_data)
1465 1465
1466 1466 @group_data.expression
1467 1467 def group_data(self, **kwargs):
1468 1468 return self._group_data
1469 1469
1470 1470 @group_data.setter
1471 1471 def group_data(self, val):
1472 1472 try:
1473 1473 self._group_data = json.dumps(val)
1474 1474 except Exception:
1475 1475 log.error(traceback.format_exc())
1476 1476
1477 1477 @classmethod
1478 1478 def _load_sync(cls, group_data):
1479 1479 if group_data:
1480 1480 return group_data.get('extern_type')
1481 1481
1482 1482 @property
1483 1483 def sync(self):
1484 1484 return self._load_sync(self.group_data)
1485 1485
1486 1486 def __unicode__(self):
1487 1487 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
1488 1488 self.users_group_id,
1489 1489 self.users_group_name)
1490 1490
1491 1491 @classmethod
1492 1492 def get_by_group_name(cls, group_name, cache=False,
1493 1493 case_insensitive=False):
1494 1494 if case_insensitive:
1495 1495 q = cls.query().filter(func.lower(cls.users_group_name) ==
1496 1496 func.lower(group_name))
1497 1497
1498 1498 else:
1499 1499 q = cls.query().filter(cls.users_group_name == group_name)
1500 1500 if cache:
1501 1501 q = q.options(
1502 1502 FromCache("sql_cache_short", "get_group_%s" % _hash_key(group_name)))
1503 1503 return q.scalar()
1504 1504
1505 1505 @classmethod
1506 1506 def get(cls, user_group_id, cache=False):
1507 1507 if not user_group_id:
1508 1508 return
1509 1509
1510 1510 user_group = cls.query()
1511 1511 if cache:
1512 1512 user_group = user_group.options(
1513 1513 FromCache("sql_cache_short", "get_users_group_%s" % user_group_id))
1514 1514 return user_group.get(user_group_id)
1515 1515
1516 1516 def permissions(self, with_admins=True, with_owner=True,
1517 1517 expand_from_user_groups=False):
1518 1518 """
1519 1519 Permissions for user groups
1520 1520 """
1521 1521 _admin_perm = 'usergroup.admin'
1522 1522
1523 1523 owner_row = []
1524 1524 if with_owner:
1525 1525 usr = AttributeDict(self.user.get_dict())
1526 1526 usr.owner_row = True
1527 1527 usr.permission = _admin_perm
1528 1528 owner_row.append(usr)
1529 1529
1530 1530 super_admin_ids = []
1531 1531 super_admin_rows = []
1532 1532 if with_admins:
1533 1533 for usr in User.get_all_super_admins():
1534 1534 super_admin_ids.append(usr.user_id)
1535 1535 # if this admin is also owner, don't double the record
1536 1536 if usr.user_id == owner_row[0].user_id:
1537 1537 owner_row[0].admin_row = True
1538 1538 else:
1539 1539 usr = AttributeDict(usr.get_dict())
1540 1540 usr.admin_row = True
1541 1541 usr.permission = _admin_perm
1542 1542 super_admin_rows.append(usr)
1543 1543
1544 1544 q = UserUserGroupToPerm.query().filter(UserUserGroupToPerm.user_group == self)
1545 1545 q = q.options(joinedload(UserUserGroupToPerm.user_group),
1546 1546 joinedload(UserUserGroupToPerm.user),
1547 1547 joinedload(UserUserGroupToPerm.permission),)
1548 1548
1549 1549 # get owners and admins and permissions. We do a trick of re-writing
1550 1550 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1551 1551 # has a global reference and changing one object propagates to all
1552 1552 # others. This means if admin is also an owner admin_row that change
1553 1553 # would propagate to both objects
1554 1554 perm_rows = []
1555 1555 for _usr in q.all():
1556 1556 usr = AttributeDict(_usr.user.get_dict())
1557 1557 # if this user is also owner/admin, mark as duplicate record
1558 1558 if usr.user_id == owner_row[0].user_id or usr.user_id in super_admin_ids:
1559 1559 usr.duplicate_perm = True
1560 1560 usr.permission = _usr.permission.permission_name
1561 1561 perm_rows.append(usr)
1562 1562
1563 1563 # filter the perm rows by 'default' first and then sort them by
1564 1564 # admin,write,read,none permissions sorted again alphabetically in
1565 1565 # each group
1566 1566 perm_rows = sorted(perm_rows, key=display_user_sort)
1567 1567
1568 1568 user_groups_rows = []
1569 1569 if expand_from_user_groups:
1570 1570 for ug in self.permission_user_groups(with_members=True):
1571 1571 for user_data in ug.members:
1572 1572 user_groups_rows.append(user_data)
1573 1573
1574 1574 return super_admin_rows + owner_row + perm_rows + user_groups_rows
1575 1575
1576 1576 def permission_user_groups(self, with_members=False):
1577 1577 q = UserGroupUserGroupToPerm.query()\
1578 1578 .filter(UserGroupUserGroupToPerm.target_user_group == self)
1579 1579 q = q.options(joinedload(UserGroupUserGroupToPerm.user_group),
1580 1580 joinedload(UserGroupUserGroupToPerm.target_user_group),
1581 1581 joinedload(UserGroupUserGroupToPerm.permission),)
1582 1582
1583 1583 perm_rows = []
1584 1584 for _user_group in q.all():
1585 1585 entry = AttributeDict(_user_group.user_group.get_dict())
1586 1586 entry.permission = _user_group.permission.permission_name
1587 1587 if with_members:
1588 1588 entry.members = [x.user.get_dict()
1589 1589 for x in _user_group.user_group.members]
1590 1590 perm_rows.append(entry)
1591 1591
1592 1592 perm_rows = sorted(perm_rows, key=display_user_group_sort)
1593 1593 return perm_rows
1594 1594
1595 1595 def _get_default_perms(self, user_group, suffix=''):
1596 1596 from rhodecode.model.permission import PermissionModel
1597 1597 return PermissionModel().get_default_perms(user_group.users_group_to_perm, suffix)
1598 1598
1599 1599 def get_default_perms(self, suffix=''):
1600 1600 return self._get_default_perms(self, suffix)
1601 1601
1602 1602 def get_api_data(self, with_group_members=True, include_secrets=False):
1603 1603 """
1604 1604 :param include_secrets: See :meth:`User.get_api_data`, this parameter is
1605 1605 basically forwarded.
1606 1606
1607 1607 """
1608 1608 user_group = self
1609 1609 data = {
1610 1610 'users_group_id': user_group.users_group_id,
1611 1611 'group_name': user_group.users_group_name,
1612 1612 'group_description': user_group.user_group_description,
1613 1613 'active': user_group.users_group_active,
1614 1614 'owner': user_group.user.username,
1615 1615 'sync': user_group.sync,
1616 1616 'owner_email': user_group.user.email,
1617 1617 }
1618 1618
1619 1619 if with_group_members:
1620 1620 users = []
1621 1621 for user in user_group.members:
1622 1622 user = user.user
1623 1623 users.append(user.get_api_data(include_secrets=include_secrets))
1624 1624 data['users'] = users
1625 1625
1626 1626 return data
1627 1627
1628 1628
1629 1629 class UserGroupMember(Base, BaseModel):
1630 1630 __tablename__ = 'users_groups_members'
1631 1631 __table_args__ = (
1632 1632 base_table_args,
1633 1633 )
1634 1634
1635 1635 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1636 1636 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1637 1637 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1638 1638
1639 1639 user = relationship('User', lazy='joined')
1640 1640 users_group = relationship('UserGroup')
1641 1641
1642 1642 def __init__(self, gr_id='', u_id=''):
1643 1643 self.users_group_id = gr_id
1644 1644 self.user_id = u_id
1645 1645
1646 1646
1647 1647 class RepositoryField(Base, BaseModel):
1648 1648 __tablename__ = 'repositories_fields'
1649 1649 __table_args__ = (
1650 1650 UniqueConstraint('repository_id', 'field_key'), # no-multi field
1651 1651 base_table_args,
1652 1652 )
1653 1653
1654 1654 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
1655 1655
1656 1656 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1657 1657 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1658 1658 field_key = Column("field_key", String(250))
1659 1659 field_label = Column("field_label", String(1024), nullable=False)
1660 1660 field_value = Column("field_value", String(10000), nullable=False)
1661 1661 field_desc = Column("field_desc", String(1024), nullable=False)
1662 1662 field_type = Column("field_type", String(255), nullable=False, unique=None)
1663 1663 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1664 1664
1665 1665 repository = relationship('Repository')
1666 1666
1667 1667 @property
1668 1668 def field_key_prefixed(self):
1669 1669 return 'ex_%s' % self.field_key
1670 1670
1671 1671 @classmethod
1672 1672 def un_prefix_key(cls, key):
1673 1673 if key.startswith(cls.PREFIX):
1674 1674 return key[len(cls.PREFIX):]
1675 1675 return key
1676 1676
1677 1677 @classmethod
1678 1678 def get_by_key_name(cls, key, repo):
1679 1679 row = cls.query()\
1680 1680 .filter(cls.repository == repo)\
1681 1681 .filter(cls.field_key == key).scalar()
1682 1682 return row
1683 1683
1684 1684
1685 1685 class Repository(Base, BaseModel):
1686 1686 __tablename__ = 'repositories'
1687 1687 __table_args__ = (
1688 1688 Index('r_repo_name_idx', 'repo_name', mysql_length=255),
1689 1689 base_table_args,
1690 1690 )
1691 1691 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
1692 1692 DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
1693 1693 DEFAULT_CLONE_URI_SSH = 'ssh://{sys_user}@{hostname}/{repo}'
1694 1694
1695 1695 STATE_CREATED = 'repo_state_created'
1696 1696 STATE_PENDING = 'repo_state_pending'
1697 1697 STATE_ERROR = 'repo_state_error'
1698 1698
1699 1699 LOCK_AUTOMATIC = 'lock_auto'
1700 1700 LOCK_API = 'lock_api'
1701 1701 LOCK_WEB = 'lock_web'
1702 1702 LOCK_PULL = 'lock_pull'
1703 1703
1704 1704 NAME_SEP = URL_SEP
1705 1705
1706 1706 repo_id = Column(
1707 1707 "repo_id", Integer(), nullable=False, unique=True, default=None,
1708 1708 primary_key=True)
1709 1709 _repo_name = Column(
1710 1710 "repo_name", Text(), nullable=False, default=None)
1711 1711 repo_name_hash = Column(
1712 1712 "repo_name_hash", String(255), nullable=False, unique=True)
1713 1713 repo_state = Column("repo_state", String(255), nullable=True)
1714 1714
1715 1715 clone_uri = Column(
1716 1716 "clone_uri", EncryptedTextValue(), nullable=True, unique=False,
1717 1717 default=None)
1718 1718 push_uri = Column(
1719 1719 "push_uri", EncryptedTextValue(), nullable=True, unique=False,
1720 1720 default=None)
1721 1721 repo_type = Column(
1722 1722 "repo_type", String(255), nullable=False, unique=False, default=None)
1723 1723 user_id = Column(
1724 1724 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
1725 1725 unique=False, default=None)
1726 1726 private = Column(
1727 1727 "private", Boolean(), nullable=True, unique=None, default=None)
1728 1728 archived = Column(
1729 1729 "archived", Boolean(), nullable=True, unique=None, default=None)
1730 1730 enable_statistics = Column(
1731 1731 "statistics", Boolean(), nullable=True, unique=None, default=True)
1732 1732 enable_downloads = Column(
1733 1733 "downloads", Boolean(), nullable=True, unique=None, default=True)
1734 1734 description = Column(
1735 1735 "description", String(10000), nullable=True, unique=None, default=None)
1736 1736 created_on = Column(
1737 1737 'created_on', DateTime(timezone=False), nullable=True, unique=None,
1738 1738 default=datetime.datetime.now)
1739 1739 updated_on = Column(
1740 1740 'updated_on', DateTime(timezone=False), nullable=True, unique=None,
1741 1741 default=datetime.datetime.now)
1742 1742 _landing_revision = Column(
1743 1743 "landing_revision", String(255), nullable=False, unique=False,
1744 1744 default=None)
1745 1745 enable_locking = Column(
1746 1746 "enable_locking", Boolean(), nullable=False, unique=None,
1747 1747 default=False)
1748 1748 _locked = Column(
1749 1749 "locked", String(255), nullable=True, unique=False, default=None)
1750 1750 _changeset_cache = Column(
1751 1751 "changeset_cache", LargeBinary(), nullable=True) # JSON data
1752 1752
1753 1753 fork_id = Column(
1754 1754 "fork_id", Integer(), ForeignKey('repositories.repo_id'),
1755 1755 nullable=True, unique=False, default=None)
1756 1756 group_id = Column(
1757 1757 "group_id", Integer(), ForeignKey('groups.group_id'), nullable=True,
1758 1758 unique=False, default=None)
1759 1759
1760 1760 user = relationship('User', lazy='joined')
1761 1761 fork = relationship('Repository', remote_side=repo_id, lazy='joined')
1762 1762 group = relationship('RepoGroup', lazy='joined')
1763 1763 repo_to_perm = relationship(
1764 1764 'UserRepoToPerm', cascade='all',
1765 1765 order_by='UserRepoToPerm.repo_to_perm_id')
1766 1766 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1767 1767 stats = relationship('Statistics', cascade='all', uselist=False)
1768 1768
1769 1769 followers = relationship(
1770 1770 'UserFollowing',
1771 1771 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
1772 1772 cascade='all')
1773 1773 extra_fields = relationship(
1774 1774 'RepositoryField', cascade="all, delete-orphan")
1775 1775 logs = relationship('UserLog')
1776 1776 comments = relationship(
1777 1777 'ChangesetComment', cascade="all, delete-orphan")
1778 1778 pull_requests_source = relationship(
1779 1779 'PullRequest',
1780 1780 primaryjoin='PullRequest.source_repo_id==Repository.repo_id',
1781 1781 cascade="all, delete-orphan")
1782 1782 pull_requests_target = relationship(
1783 1783 'PullRequest',
1784 1784 primaryjoin='PullRequest.target_repo_id==Repository.repo_id',
1785 1785 cascade="all, delete-orphan")
1786 1786 ui = relationship('RepoRhodeCodeUi', cascade="all")
1787 1787 settings = relationship('RepoRhodeCodeSetting', cascade="all")
1788 1788 integrations = relationship('Integration', cascade="all, delete-orphan")
1789 1789
1790 1790 scoped_tokens = relationship('UserApiKeys', cascade="all")
1791 1791
1792 1792 # no cascade, set NULL
1793 1793 artifacts = relationship('FileStore', primaryjoin='FileStore.scope_repo_id==Repository.repo_id')
1794 1794
1795 1795 def __unicode__(self):
1796 1796 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
1797 1797 safe_unicode(self.repo_name))
1798 1798
1799 1799 @hybrid_property
1800 1800 def description_safe(self):
1801 1801 from rhodecode.lib import helpers as h
1802 1802 return h.escape(self.description)
1803 1803
1804 1804 @hybrid_property
1805 1805 def landing_rev(self):
1806 1806 # always should return [rev_type, rev], e.g ['branch', 'master']
1807 1807 if self._landing_revision:
1808 1808 _rev_info = self._landing_revision.split(':')
1809 1809 if len(_rev_info) < 2:
1810 1810 _rev_info.insert(0, 'rev')
1811 1811 return [_rev_info[0], _rev_info[1]]
1812 1812 return [None, None]
1813 1813
1814 1814 @property
1815 1815 def landing_ref_type(self):
1816 1816 return self.landing_rev[0]
1817 1817
1818 1818 @property
1819 1819 def landing_ref_name(self):
1820 1820 return self.landing_rev[1]
1821 1821
1822 1822 @landing_rev.setter
1823 1823 def landing_rev(self, val):
1824 1824 if ':' not in val:
1825 1825 raise ValueError('value must be delimited with `:` and consist '
1826 1826 'of <rev_type>:<rev>, got %s instead' % val)
1827 1827 self._landing_revision = val
1828 1828
1829 1829 @hybrid_property
1830 1830 def locked(self):
1831 1831 if self._locked:
1832 1832 user_id, timelocked, reason = self._locked.split(':')
1833 1833 lock_values = int(user_id), timelocked, reason
1834 1834 else:
1835 1835 lock_values = [None, None, None]
1836 1836 return lock_values
1837 1837
1838 1838 @locked.setter
1839 1839 def locked(self, val):
1840 1840 if val and isinstance(val, (list, tuple)):
1841 1841 self._locked = ':'.join(map(str, val))
1842 1842 else:
1843 1843 self._locked = None
1844 1844
1845 1845 @classmethod
1846 1846 def _load_changeset_cache(cls, repo_id, changeset_cache_raw):
1847 1847 from rhodecode.lib.vcs.backends.base import EmptyCommit
1848 1848 dummy = EmptyCommit().__json__()
1849 1849 if not changeset_cache_raw:
1850 1850 dummy['source_repo_id'] = repo_id
1851 1851 return json.loads(json.dumps(dummy))
1852 1852
1853 1853 try:
1854 1854 return json.loads(changeset_cache_raw)
1855 1855 except TypeError:
1856 1856 return dummy
1857 1857 except Exception:
1858 1858 log.error(traceback.format_exc())
1859 1859 return dummy
1860 1860
1861 1861 @hybrid_property
1862 1862 def changeset_cache(self):
1863 1863 return self._load_changeset_cache(self.repo_id, self._changeset_cache)
1864 1864
1865 1865 @changeset_cache.setter
1866 1866 def changeset_cache(self, val):
1867 1867 try:
1868 1868 self._changeset_cache = json.dumps(val)
1869 1869 except Exception:
1870 1870 log.error(traceback.format_exc())
1871 1871
1872 1872 @hybrid_property
1873 1873 def repo_name(self):
1874 1874 return self._repo_name
1875 1875
1876 1876 @repo_name.setter
1877 1877 def repo_name(self, value):
1878 1878 self._repo_name = value
1879 1879 self.repo_name_hash = hashlib.sha1(safe_str(value)).hexdigest()
1880 1880
1881 1881 @classmethod
1882 1882 def normalize_repo_name(cls, repo_name):
1883 1883 """
1884 1884 Normalizes os specific repo_name to the format internally stored inside
1885 1885 database using URL_SEP
1886 1886
1887 1887 :param cls:
1888 1888 :param repo_name:
1889 1889 """
1890 1890 return cls.NAME_SEP.join(repo_name.split(os.sep))
1891 1891
1892 1892 @classmethod
1893 1893 def get_by_repo_name(cls, repo_name, cache=False, identity_cache=False):
1894 1894 session = Session()
1895 1895 q = session.query(cls).filter(cls.repo_name == repo_name)
1896 1896
1897 1897 if cache:
1898 1898 if identity_cache:
1899 1899 val = cls.identity_cache(session, 'repo_name', repo_name)
1900 1900 if val:
1901 1901 return val
1902 1902 else:
1903 1903 cache_key = "get_repo_by_name_%s" % _hash_key(repo_name)
1904 1904 q = q.options(
1905 1905 FromCache("sql_cache_short", cache_key))
1906 1906
1907 1907 return q.scalar()
1908 1908
1909 1909 @classmethod
1910 1910 def get_by_id_or_repo_name(cls, repoid):
1911 1911 if isinstance(repoid, (int, long)):
1912 1912 try:
1913 1913 repo = cls.get(repoid)
1914 1914 except ValueError:
1915 1915 repo = None
1916 1916 else:
1917 1917 repo = cls.get_by_repo_name(repoid)
1918 1918 return repo
1919 1919
1920 1920 @classmethod
1921 1921 def get_by_full_path(cls, repo_full_path):
1922 1922 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
1923 1923 repo_name = cls.normalize_repo_name(repo_name)
1924 1924 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
1925 1925
1926 1926 @classmethod
1927 1927 def get_repo_forks(cls, repo_id):
1928 1928 return cls.query().filter(Repository.fork_id == repo_id)
1929 1929
1930 1930 @classmethod
1931 1931 def base_path(cls):
1932 1932 """
1933 1933 Returns base path when all repos are stored
1934 1934
1935 1935 :param cls:
1936 1936 """
1937 1937 q = Session().query(RhodeCodeUi)\
1938 1938 .filter(RhodeCodeUi.ui_key == cls.NAME_SEP)
1939 1939 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1940 1940 return q.one().ui_value
1941 1941
1942 1942 @classmethod
1943 1943 def get_all_repos(cls, user_id=Optional(None), group_id=Optional(None),
1944 1944 case_insensitive=True, archived=False):
1945 1945 q = Repository.query()
1946 1946
1947 1947 if not archived:
1948 1948 q = q.filter(Repository.archived.isnot(true()))
1949 1949
1950 1950 if not isinstance(user_id, Optional):
1951 1951 q = q.filter(Repository.user_id == user_id)
1952 1952
1953 1953 if not isinstance(group_id, Optional):
1954 1954 q = q.filter(Repository.group_id == group_id)
1955 1955
1956 1956 if case_insensitive:
1957 1957 q = q.order_by(func.lower(Repository.repo_name))
1958 1958 else:
1959 1959 q = q.order_by(Repository.repo_name)
1960 1960
1961 1961 return q.all()
1962 1962
1963 1963 @property
1964 1964 def repo_uid(self):
1965 1965 return '_{}'.format(self.repo_id)
1966 1966
1967 1967 @property
1968 1968 def forks(self):
1969 1969 """
1970 1970 Return forks of this repo
1971 1971 """
1972 1972 return Repository.get_repo_forks(self.repo_id)
1973 1973
1974 1974 @property
1975 1975 def parent(self):
1976 1976 """
1977 1977 Returns fork parent
1978 1978 """
1979 1979 return self.fork
1980 1980
1981 1981 @property
1982 1982 def just_name(self):
1983 1983 return self.repo_name.split(self.NAME_SEP)[-1]
1984 1984
1985 1985 @property
1986 1986 def groups_with_parents(self):
1987 1987 groups = []
1988 1988 if self.group is None:
1989 1989 return groups
1990 1990
1991 1991 cur_gr = self.group
1992 1992 groups.insert(0, cur_gr)
1993 1993 while 1:
1994 1994 gr = getattr(cur_gr, 'parent_group', None)
1995 1995 cur_gr = cur_gr.parent_group
1996 1996 if gr is None:
1997 1997 break
1998 1998 groups.insert(0, gr)
1999 1999
2000 2000 return groups
2001 2001
2002 2002 @property
2003 2003 def groups_and_repo(self):
2004 2004 return self.groups_with_parents, self
2005 2005
2006 2006 @LazyProperty
2007 2007 def repo_path(self):
2008 2008 """
2009 2009 Returns base full path for that repository means where it actually
2010 2010 exists on a filesystem
2011 2011 """
2012 2012 q = Session().query(RhodeCodeUi).filter(
2013 2013 RhodeCodeUi.ui_key == self.NAME_SEP)
2014 2014 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
2015 2015 return q.one().ui_value
2016 2016
2017 2017 @property
2018 2018 def repo_full_path(self):
2019 2019 p = [self.repo_path]
2020 2020 # we need to split the name by / since this is how we store the
2021 2021 # names in the database, but that eventually needs to be converted
2022 2022 # into a valid system path
2023 2023 p += self.repo_name.split(self.NAME_SEP)
2024 2024 return os.path.join(*map(safe_unicode, p))
2025 2025
2026 2026 @property
2027 2027 def cache_keys(self):
2028 2028 """
2029 2029 Returns associated cache keys for that repo
2030 2030 """
2031 2031 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
2032 2032 repo_id=self.repo_id)
2033 2033 return CacheKey.query()\
2034 2034 .filter(CacheKey.cache_args == invalidation_namespace)\
2035 2035 .order_by(CacheKey.cache_key)\
2036 2036 .all()
2037 2037
2038 2038 @property
2039 2039 def cached_diffs_relative_dir(self):
2040 2040 """
2041 2041 Return a relative to the repository store path of cached diffs
2042 2042 used for safe display for users, who shouldn't know the absolute store
2043 2043 path
2044 2044 """
2045 2045 return os.path.join(
2046 2046 os.path.dirname(self.repo_name),
2047 2047 self.cached_diffs_dir.split(os.path.sep)[-1])
2048 2048
2049 2049 @property
2050 2050 def cached_diffs_dir(self):
2051 2051 path = self.repo_full_path
2052 2052 return os.path.join(
2053 2053 os.path.dirname(path),
2054 2054 '.__shadow_diff_cache_repo_{}'.format(self.repo_id))
2055 2055
2056 2056 def cached_diffs(self):
2057 2057 diff_cache_dir = self.cached_diffs_dir
2058 2058 if os.path.isdir(diff_cache_dir):
2059 2059 return os.listdir(diff_cache_dir)
2060 2060 return []
2061 2061
2062 2062 def shadow_repos(self):
2063 2063 shadow_repos_pattern = '.__shadow_repo_{}'.format(self.repo_id)
2064 2064 return [
2065 2065 x for x in os.listdir(os.path.dirname(self.repo_full_path))
2066 2066 if x.startswith(shadow_repos_pattern)]
2067 2067
2068 2068 def get_new_name(self, repo_name):
2069 2069 """
2070 2070 returns new full repository name based on assigned group and new new
2071 2071
2072 2072 :param group_name:
2073 2073 """
2074 2074 path_prefix = self.group.full_path_splitted if self.group else []
2075 2075 return self.NAME_SEP.join(path_prefix + [repo_name])
2076 2076
2077 2077 @property
2078 2078 def _config(self):
2079 2079 """
2080 2080 Returns db based config object.
2081 2081 """
2082 2082 from rhodecode.lib.utils import make_db_config
2083 2083 return make_db_config(clear_session=False, repo=self)
2084 2084
2085 2085 def permissions(self, with_admins=True, with_owner=True,
2086 2086 expand_from_user_groups=False):
2087 2087 """
2088 2088 Permissions for repositories
2089 2089 """
2090 2090 _admin_perm = 'repository.admin'
2091 2091
2092 2092 owner_row = []
2093 2093 if with_owner:
2094 2094 usr = AttributeDict(self.user.get_dict())
2095 2095 usr.owner_row = True
2096 2096 usr.permission = _admin_perm
2097 2097 usr.permission_id = None
2098 2098 owner_row.append(usr)
2099 2099
2100 2100 super_admin_ids = []
2101 2101 super_admin_rows = []
2102 2102 if with_admins:
2103 2103 for usr in User.get_all_super_admins():
2104 2104 super_admin_ids.append(usr.user_id)
2105 2105 # if this admin is also owner, don't double the record
2106 2106 if usr.user_id == owner_row[0].user_id:
2107 2107 owner_row[0].admin_row = True
2108 2108 else:
2109 2109 usr = AttributeDict(usr.get_dict())
2110 2110 usr.admin_row = True
2111 2111 usr.permission = _admin_perm
2112 2112 usr.permission_id = None
2113 2113 super_admin_rows.append(usr)
2114 2114
2115 2115 q = UserRepoToPerm.query().filter(UserRepoToPerm.repository == self)
2116 2116 q = q.options(joinedload(UserRepoToPerm.repository),
2117 2117 joinedload(UserRepoToPerm.user),
2118 2118 joinedload(UserRepoToPerm.permission),)
2119 2119
2120 2120 # get owners and admins and permissions. We do a trick of re-writing
2121 2121 # objects from sqlalchemy to named-tuples due to sqlalchemy session
2122 2122 # has a global reference and changing one object propagates to all
2123 2123 # others. This means if admin is also an owner admin_row that change
2124 2124 # would propagate to both objects
2125 2125 perm_rows = []
2126 2126 for _usr in q.all():
2127 2127 usr = AttributeDict(_usr.user.get_dict())
2128 2128 # if this user is also owner/admin, mark as duplicate record
2129 2129 if usr.user_id == owner_row[0].user_id or usr.user_id in super_admin_ids:
2130 2130 usr.duplicate_perm = True
2131 2131 # also check if this permission is maybe used by branch_permissions
2132 2132 if _usr.branch_perm_entry:
2133 2133 usr.branch_rules = [x.branch_rule_id for x in _usr.branch_perm_entry]
2134 2134
2135 2135 usr.permission = _usr.permission.permission_name
2136 2136 usr.permission_id = _usr.repo_to_perm_id
2137 2137 perm_rows.append(usr)
2138 2138
2139 2139 # filter the perm rows by 'default' first and then sort them by
2140 2140 # admin,write,read,none permissions sorted again alphabetically in
2141 2141 # each group
2142 2142 perm_rows = sorted(perm_rows, key=display_user_sort)
2143 2143
2144 2144 user_groups_rows = []
2145 2145 if expand_from_user_groups:
2146 2146 for ug in self.permission_user_groups(with_members=True):
2147 2147 for user_data in ug.members:
2148 2148 user_groups_rows.append(user_data)
2149 2149
2150 2150 return super_admin_rows + owner_row + perm_rows + user_groups_rows
2151 2151
2152 2152 def permission_user_groups(self, with_members=True):
2153 2153 q = UserGroupRepoToPerm.query()\
2154 2154 .filter(UserGroupRepoToPerm.repository == self)
2155 2155 q = q.options(joinedload(UserGroupRepoToPerm.repository),
2156 2156 joinedload(UserGroupRepoToPerm.users_group),
2157 2157 joinedload(UserGroupRepoToPerm.permission),)
2158 2158
2159 2159 perm_rows = []
2160 2160 for _user_group in q.all():
2161 2161 entry = AttributeDict(_user_group.users_group.get_dict())
2162 2162 entry.permission = _user_group.permission.permission_name
2163 2163 if with_members:
2164 2164 entry.members = [x.user.get_dict()
2165 2165 for x in _user_group.users_group.members]
2166 2166 perm_rows.append(entry)
2167 2167
2168 2168 perm_rows = sorted(perm_rows, key=display_user_group_sort)
2169 2169 return perm_rows
2170 2170
2171 2171 def get_api_data(self, include_secrets=False):
2172 2172 """
2173 2173 Common function for generating repo api data
2174 2174
2175 2175 :param include_secrets: See :meth:`User.get_api_data`.
2176 2176
2177 2177 """
2178 2178 # TODO: mikhail: Here there is an anti-pattern, we probably need to
2179 2179 # move this methods on models level.
2180 2180 from rhodecode.model.settings import SettingsModel
2181 2181 from rhodecode.model.repo import RepoModel
2182 2182
2183 2183 repo = self
2184 2184 _user_id, _time, _reason = self.locked
2185 2185
2186 2186 data = {
2187 2187 'repo_id': repo.repo_id,
2188 2188 'repo_name': repo.repo_name,
2189 2189 'repo_type': repo.repo_type,
2190 2190 'clone_uri': repo.clone_uri or '',
2191 2191 'push_uri': repo.push_uri or '',
2192 2192 'url': RepoModel().get_url(self),
2193 2193 'private': repo.private,
2194 2194 'created_on': repo.created_on,
2195 2195 'description': repo.description_safe,
2196 2196 'landing_rev': repo.landing_rev,
2197 2197 'owner': repo.user.username,
2198 2198 'fork_of': repo.fork.repo_name if repo.fork else None,
2199 2199 'fork_of_id': repo.fork.repo_id if repo.fork else None,
2200 2200 'enable_statistics': repo.enable_statistics,
2201 2201 'enable_locking': repo.enable_locking,
2202 2202 'enable_downloads': repo.enable_downloads,
2203 2203 'last_changeset': repo.changeset_cache,
2204 2204 'locked_by': User.get(_user_id).get_api_data(
2205 2205 include_secrets=include_secrets) if _user_id else None,
2206 2206 'locked_date': time_to_datetime(_time) if _time else None,
2207 2207 'lock_reason': _reason if _reason else None,
2208 2208 }
2209 2209
2210 2210 # TODO: mikhail: should be per-repo settings here
2211 2211 rc_config = SettingsModel().get_all_settings()
2212 2212 repository_fields = str2bool(
2213 2213 rc_config.get('rhodecode_repository_fields'))
2214 2214 if repository_fields:
2215 2215 for f in self.extra_fields:
2216 2216 data[f.field_key_prefixed] = f.field_value
2217 2217
2218 2218 return data
2219 2219
2220 2220 @classmethod
2221 2221 def lock(cls, repo, user_id, lock_time=None, lock_reason=None):
2222 2222 if not lock_time:
2223 2223 lock_time = time.time()
2224 2224 if not lock_reason:
2225 2225 lock_reason = cls.LOCK_AUTOMATIC
2226 2226 repo.locked = [user_id, lock_time, lock_reason]
2227 2227 Session().add(repo)
2228 2228 Session().commit()
2229 2229
2230 2230 @classmethod
2231 2231 def unlock(cls, repo):
2232 2232 repo.locked = None
2233 2233 Session().add(repo)
2234 2234 Session().commit()
2235 2235
2236 2236 @classmethod
2237 2237 def getlock(cls, repo):
2238 2238 return repo.locked
2239 2239
2240 2240 def is_user_lock(self, user_id):
2241 2241 if self.lock[0]:
2242 2242 lock_user_id = safe_int(self.lock[0])
2243 2243 user_id = safe_int(user_id)
2244 2244 # both are ints, and they are equal
2245 2245 return all([lock_user_id, user_id]) and lock_user_id == user_id
2246 2246
2247 2247 return False
2248 2248
2249 2249 def get_locking_state(self, action, user_id, only_when_enabled=True):
2250 2250 """
2251 2251 Checks locking on this repository, if locking is enabled and lock is
2252 2252 present returns a tuple of make_lock, locked, locked_by.
2253 2253 make_lock can have 3 states None (do nothing) True, make lock
2254 2254 False release lock, This value is later propagated to hooks, which
2255 2255 do the locking. Think about this as signals passed to hooks what to do.
2256 2256
2257 2257 """
2258 2258 # TODO: johbo: This is part of the business logic and should be moved
2259 2259 # into the RepositoryModel.
2260 2260
2261 2261 if action not in ('push', 'pull'):
2262 2262 raise ValueError("Invalid action value: %s" % repr(action))
2263 2263
2264 2264 # defines if locked error should be thrown to user
2265 2265 currently_locked = False
2266 2266 # defines if new lock should be made, tri-state
2267 2267 make_lock = None
2268 2268 repo = self
2269 2269 user = User.get(user_id)
2270 2270
2271 2271 lock_info = repo.locked
2272 2272
2273 2273 if repo and (repo.enable_locking or not only_when_enabled):
2274 2274 if action == 'push':
2275 2275 # check if it's already locked !, if it is compare users
2276 2276 locked_by_user_id = lock_info[0]
2277 2277 if user.user_id == locked_by_user_id:
2278 2278 log.debug(
2279 2279 'Got `push` action from user %s, now unlocking', user)
2280 2280 # unlock if we have push from user who locked
2281 2281 make_lock = False
2282 2282 else:
2283 2283 # we're not the same user who locked, ban with
2284 2284 # code defined in settings (default is 423 HTTP Locked) !
2285 2285 log.debug('Repo %s is currently locked by %s', repo, user)
2286 2286 currently_locked = True
2287 2287 elif action == 'pull':
2288 2288 # [0] user [1] date
2289 2289 if lock_info[0] and lock_info[1]:
2290 2290 log.debug('Repo %s is currently locked by %s', repo, user)
2291 2291 currently_locked = True
2292 2292 else:
2293 2293 log.debug('Setting lock on repo %s by %s', repo, user)
2294 2294 make_lock = True
2295 2295
2296 2296 else:
2297 2297 log.debug('Repository %s do not have locking enabled', repo)
2298 2298
2299 2299 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s',
2300 2300 make_lock, currently_locked, lock_info)
2301 2301
2302 2302 from rhodecode.lib.auth import HasRepoPermissionAny
2303 2303 perm_check = HasRepoPermissionAny('repository.write', 'repository.admin')
2304 2304 if make_lock and not perm_check(repo_name=repo.repo_name, user=user):
2305 2305 # if we don't have at least write permission we cannot make a lock
2306 2306 log.debug('lock state reset back to FALSE due to lack '
2307 2307 'of at least read permission')
2308 2308 make_lock = False
2309 2309
2310 2310 return make_lock, currently_locked, lock_info
2311 2311
2312 2312 @property
2313 2313 def last_commit_cache_update_diff(self):
2314 2314 return time.time() - (safe_int(self.changeset_cache.get('updated_on')) or 0)
2315 2315
2316 2316 @classmethod
2317 2317 def _load_commit_change(cls, last_commit_cache):
2318 2318 from rhodecode.lib.vcs.utils.helpers import parse_datetime
2319 2319 empty_date = datetime.datetime.fromtimestamp(0)
2320 2320 date_latest = last_commit_cache.get('date', empty_date)
2321 2321 try:
2322 2322 return parse_datetime(date_latest)
2323 2323 except Exception:
2324 2324 return empty_date
2325 2325
2326 2326 @property
2327 2327 def last_commit_change(self):
2328 2328 return self._load_commit_change(self.changeset_cache)
2329 2329
2330 2330 @property
2331 2331 def last_db_change(self):
2332 2332 return self.updated_on
2333 2333
2334 2334 @property
2335 2335 def clone_uri_hidden(self):
2336 2336 clone_uri = self.clone_uri
2337 2337 if clone_uri:
2338 2338 import urlobject
2339 2339 url_obj = urlobject.URLObject(cleaned_uri(clone_uri))
2340 2340 if url_obj.password:
2341 2341 clone_uri = url_obj.with_password('*****')
2342 2342 return clone_uri
2343 2343
2344 2344 @property
2345 2345 def push_uri_hidden(self):
2346 2346 push_uri = self.push_uri
2347 2347 if push_uri:
2348 2348 import urlobject
2349 2349 url_obj = urlobject.URLObject(cleaned_uri(push_uri))
2350 2350 if url_obj.password:
2351 2351 push_uri = url_obj.with_password('*****')
2352 2352 return push_uri
2353 2353
2354 2354 def clone_url(self, **override):
2355 2355 from rhodecode.model.settings import SettingsModel
2356 2356
2357 2357 uri_tmpl = None
2358 2358 if 'with_id' in override:
2359 2359 uri_tmpl = self.DEFAULT_CLONE_URI_ID
2360 2360 del override['with_id']
2361 2361
2362 2362 if 'uri_tmpl' in override:
2363 2363 uri_tmpl = override['uri_tmpl']
2364 2364 del override['uri_tmpl']
2365 2365
2366 2366 ssh = False
2367 2367 if 'ssh' in override:
2368 2368 ssh = True
2369 2369 del override['ssh']
2370 2370
2371 2371 # we didn't override our tmpl from **overrides
2372 2372 request = get_current_request()
2373 2373 if not uri_tmpl:
2374 2374 if hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
2375 2375 rc_config = request.call_context.rc_config
2376 2376 else:
2377 2377 rc_config = SettingsModel().get_all_settings(cache=True)
2378 2378
2379 2379 if ssh:
2380 2380 uri_tmpl = rc_config.get(
2381 2381 'rhodecode_clone_uri_ssh_tmpl') or self.DEFAULT_CLONE_URI_SSH
2382 2382
2383 2383 else:
2384 2384 uri_tmpl = rc_config.get(
2385 2385 'rhodecode_clone_uri_tmpl') or self.DEFAULT_CLONE_URI
2386 2386
2387 2387 return get_clone_url(request=request,
2388 2388 uri_tmpl=uri_tmpl,
2389 2389 repo_name=self.repo_name,
2390 2390 repo_id=self.repo_id,
2391 2391 repo_type=self.repo_type,
2392 2392 **override)
2393 2393
2394 2394 def set_state(self, state):
2395 2395 self.repo_state = state
2396 2396 Session().add(self)
2397 2397 #==========================================================================
2398 2398 # SCM PROPERTIES
2399 2399 #==========================================================================
2400 2400
2401 2401 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, maybe_unreachable=False, reference_obj=None):
2402 2402 return get_commit_safe(
2403 2403 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load,
2404 2404 maybe_unreachable=maybe_unreachable, reference_obj=reference_obj)
2405 2405
2406 2406 def get_changeset(self, rev=None, pre_load=None):
2407 2407 warnings.warn("Use get_commit", DeprecationWarning)
2408 2408 commit_id = None
2409 2409 commit_idx = None
2410 2410 if isinstance(rev, compat.string_types):
2411 2411 commit_id = rev
2412 2412 else:
2413 2413 commit_idx = rev
2414 2414 return self.get_commit(commit_id=commit_id, commit_idx=commit_idx,
2415 2415 pre_load=pre_load)
2416 2416
2417 2417 def get_landing_commit(self):
2418 2418 """
2419 2419 Returns landing commit, or if that doesn't exist returns the tip
2420 2420 """
2421 2421 _rev_type, _rev = self.landing_rev
2422 2422 commit = self.get_commit(_rev)
2423 2423 if isinstance(commit, EmptyCommit):
2424 2424 return self.get_commit()
2425 2425 return commit
2426 2426
2427 2427 def flush_commit_cache(self):
2428 2428 self.update_commit_cache(cs_cache={'raw_id':'0'})
2429 2429 self.update_commit_cache()
2430 2430
2431 2431 def update_commit_cache(self, cs_cache=None, config=None):
2432 2432 """
2433 2433 Update cache of last commit for repository
2434 2434 cache_keys should be::
2435 2435
2436 2436 source_repo_id
2437 2437 short_id
2438 2438 raw_id
2439 2439 revision
2440 2440 parents
2441 2441 message
2442 2442 date
2443 2443 author
2444 2444 updated_on
2445 2445
2446 2446 """
2447 2447 from rhodecode.lib.vcs.backends.base import BaseChangeset
2448 2448 from rhodecode.lib.vcs.utils.helpers import parse_datetime
2449 2449 empty_date = datetime.datetime.fromtimestamp(0)
2450 2450
2451 2451 if cs_cache is None:
2452 2452 # use no-cache version here
2453 2453 try:
2454 2454 scm_repo = self.scm_instance(cache=False, config=config)
2455 2455 except VCSError:
2456 2456 scm_repo = None
2457 2457 empty = scm_repo is None or scm_repo.is_empty()
2458 2458
2459 2459 if not empty:
2460 2460 cs_cache = scm_repo.get_commit(
2461 2461 pre_load=["author", "date", "message", "parents", "branch"])
2462 2462 else:
2463 2463 cs_cache = EmptyCommit()
2464 2464
2465 2465 if isinstance(cs_cache, BaseChangeset):
2466 2466 cs_cache = cs_cache.__json__()
2467 2467
2468 2468 def is_outdated(new_cs_cache):
2469 2469 if (new_cs_cache['raw_id'] != self.changeset_cache['raw_id'] or
2470 2470 new_cs_cache['revision'] != self.changeset_cache['revision']):
2471 2471 return True
2472 2472 return False
2473 2473
2474 2474 # check if we have maybe already latest cached revision
2475 2475 if is_outdated(cs_cache) or not self.changeset_cache:
2476 2476 _current_datetime = datetime.datetime.utcnow()
2477 2477 last_change = cs_cache.get('date') or _current_datetime
2478 2478 # we check if last update is newer than the new value
2479 2479 # if yes, we use the current timestamp instead. Imagine you get
2480 2480 # old commit pushed 1y ago, we'd set last update 1y to ago.
2481 2481 last_change_timestamp = datetime_to_time(last_change)
2482 2482 current_timestamp = datetime_to_time(last_change)
2483 2483 if last_change_timestamp > current_timestamp and not empty:
2484 2484 cs_cache['date'] = _current_datetime
2485 2485
2486 2486 _date_latest = parse_datetime(cs_cache.get('date') or empty_date)
2487 2487 cs_cache['updated_on'] = time.time()
2488 2488 self.changeset_cache = cs_cache
2489 2489 self.updated_on = last_change
2490 2490 Session().add(self)
2491 2491 Session().commit()
2492 2492
2493 2493 else:
2494 2494 if empty:
2495 2495 cs_cache = EmptyCommit().__json__()
2496 2496 else:
2497 2497 cs_cache = self.changeset_cache
2498 2498
2499 2499 _date_latest = parse_datetime(cs_cache.get('date') or empty_date)
2500 2500
2501 2501 cs_cache['updated_on'] = time.time()
2502 2502 self.changeset_cache = cs_cache
2503 2503 self.updated_on = _date_latest
2504 2504 Session().add(self)
2505 2505 Session().commit()
2506 2506
2507 2507 log.debug('updated repo `%s` with new commit cache %s, and last update_date: %s',
2508 2508 self.repo_name, cs_cache, _date_latest)
2509 2509
2510 2510 @property
2511 2511 def tip(self):
2512 2512 return self.get_commit('tip')
2513 2513
2514 2514 @property
2515 2515 def author(self):
2516 2516 return self.tip.author
2517 2517
2518 2518 @property
2519 2519 def last_change(self):
2520 2520 return self.scm_instance().last_change
2521 2521
2522 2522 def get_comments(self, revisions=None):
2523 2523 """
2524 2524 Returns comments for this repository grouped by revisions
2525 2525
2526 2526 :param revisions: filter query by revisions only
2527 2527 """
2528 2528 cmts = ChangesetComment.query()\
2529 2529 .filter(ChangesetComment.repo == self)
2530 2530 if revisions:
2531 2531 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
2532 2532 grouped = collections.defaultdict(list)
2533 2533 for cmt in cmts.all():
2534 2534 grouped[cmt.revision].append(cmt)
2535 2535 return grouped
2536 2536
2537 2537 def statuses(self, revisions=None):
2538 2538 """
2539 2539 Returns statuses for this repository
2540 2540
2541 2541 :param revisions: list of revisions to get statuses for
2542 2542 """
2543 2543 statuses = ChangesetStatus.query()\
2544 2544 .filter(ChangesetStatus.repo == self)\
2545 2545 .filter(ChangesetStatus.version == 0)
2546 2546
2547 2547 if revisions:
2548 2548 # Try doing the filtering in chunks to avoid hitting limits
2549 2549 size = 500
2550 2550 status_results = []
2551 2551 for chunk in xrange(0, len(revisions), size):
2552 2552 status_results += statuses.filter(
2553 2553 ChangesetStatus.revision.in_(
2554 2554 revisions[chunk: chunk+size])
2555 2555 ).all()
2556 2556 else:
2557 2557 status_results = statuses.all()
2558 2558
2559 2559 grouped = {}
2560 2560
2561 2561 # maybe we have open new pullrequest without a status?
2562 2562 stat = ChangesetStatus.STATUS_UNDER_REVIEW
2563 2563 status_lbl = ChangesetStatus.get_status_lbl(stat)
2564 2564 for pr in PullRequest.query().filter(PullRequest.source_repo == self).all():
2565 2565 for rev in pr.revisions:
2566 2566 pr_id = pr.pull_request_id
2567 2567 pr_repo = pr.target_repo.repo_name
2568 2568 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
2569 2569
2570 2570 for stat in status_results:
2571 2571 pr_id = pr_repo = None
2572 2572 if stat.pull_request:
2573 2573 pr_id = stat.pull_request.pull_request_id
2574 2574 pr_repo = stat.pull_request.target_repo.repo_name
2575 2575 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
2576 2576 pr_id, pr_repo]
2577 2577 return grouped
2578 2578
2579 2579 # ==========================================================================
2580 2580 # SCM CACHE INSTANCE
2581 2581 # ==========================================================================
2582 2582
2583 2583 def scm_instance(self, **kwargs):
2584 2584 import rhodecode
2585 2585
2586 2586 # Passing a config will not hit the cache currently only used
2587 2587 # for repo2dbmapper
2588 2588 config = kwargs.pop('config', None)
2589 2589 cache = kwargs.pop('cache', None)
2590 2590 vcs_full_cache = kwargs.pop('vcs_full_cache', None)
2591 2591 if vcs_full_cache is not None:
2592 2592 # allows override global config
2593 2593 full_cache = vcs_full_cache
2594 2594 else:
2595 2595 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
2596 2596 # if cache is NOT defined use default global, else we have a full
2597 2597 # control over cache behaviour
2598 2598 if cache is None and full_cache and not config:
2599 2599 log.debug('Initializing pure cached instance for %s', self.repo_path)
2600 2600 return self._get_instance_cached()
2601 2601
2602 2602 # cache here is sent to the "vcs server"
2603 2603 return self._get_instance(cache=bool(cache), config=config)
2604 2604
2605 2605 def _get_instance_cached(self):
2606 2606 from rhodecode.lib import rc_cache
2607 2607
2608 2608 cache_namespace_uid = 'cache_repo_instance.{}'.format(self.repo_id)
2609 2609 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
2610 2610 repo_id=self.repo_id)
2611 2611 region = rc_cache.get_or_create_region('cache_repo_longterm', cache_namespace_uid)
2612 2612
2613 2613 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
2614 2614 def get_instance_cached(repo_id, context_id, _cache_state_uid):
2615 2615 return self._get_instance(repo_state_uid=_cache_state_uid)
2616 2616
2617 2617 # we must use thread scoped cache here,
2618 2618 # because each thread of gevent needs it's own not shared connection and cache
2619 2619 # we also alter `args` so the cache key is individual for every green thread.
2620 2620 inv_context_manager = rc_cache.InvalidationContext(
2621 2621 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace,
2622 2622 thread_scoped=True)
2623 2623 with inv_context_manager as invalidation_context:
2624 2624 cache_state_uid = invalidation_context.cache_data['cache_state_uid']
2625 2625 args = (self.repo_id, inv_context_manager.cache_key, cache_state_uid)
2626 2626
2627 2627 # re-compute and store cache if we get invalidate signal
2628 2628 if invalidation_context.should_invalidate():
2629 2629 instance = get_instance_cached.refresh(*args)
2630 2630 else:
2631 2631 instance = get_instance_cached(*args)
2632 2632
2633 2633 log.debug('Repo instance fetched in %.4fs', inv_context_manager.compute_time)
2634 2634 return instance
2635 2635
2636 2636 def _get_instance(self, cache=True, config=None, repo_state_uid=None):
2637 2637 log.debug('Initializing %s instance `%s` with cache flag set to: %s',
2638 2638 self.repo_type, self.repo_path, cache)
2639 2639 config = config or self._config
2640 2640 custom_wire = {
2641 2641 'cache': cache, # controls the vcs.remote cache
2642 2642 'repo_state_uid': repo_state_uid
2643 2643 }
2644 2644 repo = get_vcs_instance(
2645 2645 repo_path=safe_str(self.repo_full_path),
2646 2646 config=config,
2647 2647 with_wire=custom_wire,
2648 2648 create=False,
2649 2649 _vcs_alias=self.repo_type)
2650 2650 if repo is not None:
2651 2651 repo.count() # cache rebuild
2652 2652 return repo
2653 2653
2654 2654 def get_shadow_repository_path(self, workspace_id):
2655 2655 from rhodecode.lib.vcs.backends.base import BaseRepository
2656 2656 shadow_repo_path = BaseRepository._get_shadow_repository_path(
2657 2657 self.repo_full_path, self.repo_id, workspace_id)
2658 2658 return shadow_repo_path
2659 2659
2660 2660 def __json__(self):
2661 2661 return {'landing_rev': self.landing_rev}
2662 2662
2663 2663 def get_dict(self):
2664 2664
2665 2665 # Since we transformed `repo_name` to a hybrid property, we need to
2666 2666 # keep compatibility with the code which uses `repo_name` field.
2667 2667
2668 2668 result = super(Repository, self).get_dict()
2669 2669 result['repo_name'] = result.pop('_repo_name', None)
2670 2670 return result
2671 2671
2672 2672
2673 2673 class RepoGroup(Base, BaseModel):
2674 2674 __tablename__ = 'groups'
2675 2675 __table_args__ = (
2676 2676 UniqueConstraint('group_name', 'group_parent_id'),
2677 2677 base_table_args,
2678 2678 )
2679 2679 __mapper_args__ = {'order_by': 'group_name'}
2680 2680
2681 2681 CHOICES_SEPARATOR = '/' # used to generate select2 choices for nested groups
2682 2682
2683 2683 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2684 2684 _group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
2685 2685 group_name_hash = Column("repo_group_name_hash", String(1024), nullable=False, unique=False)
2686 2686 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
2687 2687 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
2688 2688 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
2689 2689 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
2690 2690 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2691 2691 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
2692 2692 personal = Column('personal', Boolean(), nullable=True, unique=None, default=None)
2693 2693 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) # JSON data
2694 2694
2695 2695 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
2696 2696 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
2697 2697 parent_group = relationship('RepoGroup', remote_side=group_id)
2698 2698 user = relationship('User')
2699 2699 integrations = relationship('Integration', cascade="all, delete-orphan")
2700 2700
2701 2701 # no cascade, set NULL
2702 2702 scope_artifacts = relationship('FileStore', primaryjoin='FileStore.scope_repo_group_id==RepoGroup.group_id')
2703 2703
2704 2704 def __init__(self, group_name='', parent_group=None):
2705 2705 self.group_name = group_name
2706 2706 self.parent_group = parent_group
2707 2707
2708 2708 def __unicode__(self):
2709 2709 return u"<%s('id:%s:%s')>" % (
2710 2710 self.__class__.__name__, self.group_id, self.group_name)
2711 2711
2712 2712 @hybrid_property
2713 2713 def group_name(self):
2714 2714 return self._group_name
2715 2715
2716 2716 @group_name.setter
2717 2717 def group_name(self, value):
2718 2718 self._group_name = value
2719 2719 self.group_name_hash = self.hash_repo_group_name(value)
2720 2720
2721 2721 @classmethod
2722 2722 def _load_changeset_cache(cls, repo_id, changeset_cache_raw):
2723 2723 from rhodecode.lib.vcs.backends.base import EmptyCommit
2724 2724 dummy = EmptyCommit().__json__()
2725 2725 if not changeset_cache_raw:
2726 2726 dummy['source_repo_id'] = repo_id
2727 2727 return json.loads(json.dumps(dummy))
2728 2728
2729 2729 try:
2730 2730 return json.loads(changeset_cache_raw)
2731 2731 except TypeError:
2732 2732 return dummy
2733 2733 except Exception:
2734 2734 log.error(traceback.format_exc())
2735 2735 return dummy
2736 2736
2737 2737 @hybrid_property
2738 2738 def changeset_cache(self):
2739 2739 return self._load_changeset_cache('', self._changeset_cache)
2740 2740
2741 2741 @changeset_cache.setter
2742 2742 def changeset_cache(self, val):
2743 2743 try:
2744 2744 self._changeset_cache = json.dumps(val)
2745 2745 except Exception:
2746 2746 log.error(traceback.format_exc())
2747 2747
2748 2748 @validates('group_parent_id')
2749 2749 def validate_group_parent_id(self, key, val):
2750 2750 """
2751 2751 Check cycle references for a parent group to self
2752 2752 """
2753 2753 if self.group_id and val:
2754 2754 assert val != self.group_id
2755 2755
2756 2756 return val
2757 2757
2758 2758 @hybrid_property
2759 2759 def description_safe(self):
2760 2760 from rhodecode.lib import helpers as h
2761 2761 return h.escape(self.group_description)
2762 2762
2763 2763 @classmethod
2764 2764 def hash_repo_group_name(cls, repo_group_name):
2765 2765 val = remove_formatting(repo_group_name)
2766 2766 val = safe_str(val).lower()
2767 2767 chars = []
2768 2768 for c in val:
2769 2769 if c not in string.ascii_letters:
2770 2770 c = str(ord(c))
2771 2771 chars.append(c)
2772 2772
2773 2773 return ''.join(chars)
2774 2774
2775 2775 @classmethod
2776 2776 def _generate_choice(cls, repo_group):
2777 2777 from webhelpers2.html import literal as _literal
2778 2778 _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k))
2779 2779 return repo_group.group_id, _name(repo_group.full_path_splitted)
2780 2780
2781 2781 @classmethod
2782 2782 def groups_choices(cls, groups=None, show_empty_group=True):
2783 2783 if not groups:
2784 2784 groups = cls.query().all()
2785 2785
2786 2786 repo_groups = []
2787 2787 if show_empty_group:
2788 2788 repo_groups = [(-1, u'-- %s --' % _('No parent'))]
2789 2789
2790 2790 repo_groups.extend([cls._generate_choice(x) for x in groups])
2791 2791
2792 2792 repo_groups = sorted(
2793 2793 repo_groups, key=lambda t: t[1].split(cls.CHOICES_SEPARATOR)[0])
2794 2794 return repo_groups
2795 2795
2796 2796 @classmethod
2797 2797 def url_sep(cls):
2798 2798 return URL_SEP
2799 2799
2800 2800 @classmethod
2801 2801 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
2802 2802 if case_insensitive:
2803 2803 gr = cls.query().filter(func.lower(cls.group_name)
2804 2804 == func.lower(group_name))
2805 2805 else:
2806 2806 gr = cls.query().filter(cls.group_name == group_name)
2807 2807 if cache:
2808 2808 name_key = _hash_key(group_name)
2809 2809 gr = gr.options(
2810 2810 FromCache("sql_cache_short", "get_group_%s" % name_key))
2811 2811 return gr.scalar()
2812 2812
2813 2813 @classmethod
2814 2814 def get_user_personal_repo_group(cls, user_id):
2815 2815 user = User.get(user_id)
2816 2816 if user.username == User.DEFAULT_USER:
2817 2817 return None
2818 2818
2819 2819 return cls.query()\
2820 2820 .filter(cls.personal == true()) \
2821 2821 .filter(cls.user == user) \
2822 2822 .order_by(cls.group_id.asc()) \
2823 2823 .first()
2824 2824
2825 2825 @classmethod
2826 2826 def get_all_repo_groups(cls, user_id=Optional(None), group_id=Optional(None),
2827 2827 case_insensitive=True):
2828 2828 q = RepoGroup.query()
2829 2829
2830 2830 if not isinstance(user_id, Optional):
2831 2831 q = q.filter(RepoGroup.user_id == user_id)
2832 2832
2833 2833 if not isinstance(group_id, Optional):
2834 2834 q = q.filter(RepoGroup.group_parent_id == group_id)
2835 2835
2836 2836 if case_insensitive:
2837 2837 q = q.order_by(func.lower(RepoGroup.group_name))
2838 2838 else:
2839 2839 q = q.order_by(RepoGroup.group_name)
2840 2840 return q.all()
2841 2841
2842 2842 @property
2843 2843 def parents(self, parents_recursion_limit=10):
2844 2844 groups = []
2845 2845 if self.parent_group is None:
2846 2846 return groups
2847 2847 cur_gr = self.parent_group
2848 2848 groups.insert(0, cur_gr)
2849 2849 cnt = 0
2850 2850 while 1:
2851 2851 cnt += 1
2852 2852 gr = getattr(cur_gr, 'parent_group', None)
2853 2853 cur_gr = cur_gr.parent_group
2854 2854 if gr is None:
2855 2855 break
2856 2856 if cnt == parents_recursion_limit:
2857 2857 # this will prevent accidental infinit loops
2858 2858 log.error('more than %s parents found for group %s, stopping '
2859 2859 'recursive parent fetching', parents_recursion_limit, self)
2860 2860 break
2861 2861
2862 2862 groups.insert(0, gr)
2863 2863 return groups
2864 2864
2865 2865 @property
2866 2866 def last_commit_cache_update_diff(self):
2867 2867 return time.time() - (safe_int(self.changeset_cache.get('updated_on')) or 0)
2868 2868
2869 2869 @classmethod
2870 2870 def _load_commit_change(cls, last_commit_cache):
2871 2871 from rhodecode.lib.vcs.utils.helpers import parse_datetime
2872 2872 empty_date = datetime.datetime.fromtimestamp(0)
2873 2873 date_latest = last_commit_cache.get('date', empty_date)
2874 2874 try:
2875 2875 return parse_datetime(date_latest)
2876 2876 except Exception:
2877 2877 return empty_date
2878 2878
2879 2879 @property
2880 2880 def last_commit_change(self):
2881 2881 return self._load_commit_change(self.changeset_cache)
2882 2882
2883 2883 @property
2884 2884 def last_db_change(self):
2885 2885 return self.updated_on
2886 2886
2887 2887 @property
2888 2888 def children(self):
2889 2889 return RepoGroup.query().filter(RepoGroup.parent_group == self)
2890 2890
2891 2891 @property
2892 2892 def name(self):
2893 2893 return self.group_name.split(RepoGroup.url_sep())[-1]
2894 2894
2895 2895 @property
2896 2896 def full_path(self):
2897 2897 return self.group_name
2898 2898
2899 2899 @property
2900 2900 def full_path_splitted(self):
2901 2901 return self.group_name.split(RepoGroup.url_sep())
2902 2902
2903 2903 @property
2904 2904 def repositories(self):
2905 2905 return Repository.query()\
2906 2906 .filter(Repository.group == self)\
2907 2907 .order_by(Repository.repo_name)
2908 2908
2909 2909 @property
2910 2910 def repositories_recursive_count(self):
2911 2911 cnt = self.repositories.count()
2912 2912
2913 2913 def children_count(group):
2914 2914 cnt = 0
2915 2915 for child in group.children:
2916 2916 cnt += child.repositories.count()
2917 2917 cnt += children_count(child)
2918 2918 return cnt
2919 2919
2920 2920 return cnt + children_count(self)
2921 2921
2922 2922 def _recursive_objects(self, include_repos=True, include_groups=True):
2923 2923 all_ = []
2924 2924
2925 2925 def _get_members(root_gr):
2926 2926 if include_repos:
2927 2927 for r in root_gr.repositories:
2928 2928 all_.append(r)
2929 2929 childs = root_gr.children.all()
2930 2930 if childs:
2931 2931 for gr in childs:
2932 2932 if include_groups:
2933 2933 all_.append(gr)
2934 2934 _get_members(gr)
2935 2935
2936 2936 root_group = []
2937 2937 if include_groups:
2938 2938 root_group = [self]
2939 2939
2940 2940 _get_members(self)
2941 2941 return root_group + all_
2942 2942
2943 2943 def recursive_groups_and_repos(self):
2944 2944 """
2945 2945 Recursive return all groups, with repositories in those groups
2946 2946 """
2947 2947 return self._recursive_objects()
2948 2948
2949 2949 def recursive_groups(self):
2950 2950 """
2951 2951 Returns all children groups for this group including children of children
2952 2952 """
2953 2953 return self._recursive_objects(include_repos=False)
2954 2954
2955 2955 def recursive_repos(self):
2956 2956 """
2957 2957 Returns all children repositories for this group
2958 2958 """
2959 2959 return self._recursive_objects(include_groups=False)
2960 2960
2961 2961 def get_new_name(self, group_name):
2962 2962 """
2963 2963 returns new full group name based on parent and new name
2964 2964
2965 2965 :param group_name:
2966 2966 """
2967 2967 path_prefix = (self.parent_group.full_path_splitted if
2968 2968 self.parent_group else [])
2969 2969 return RepoGroup.url_sep().join(path_prefix + [group_name])
2970 2970
2971 2971 def update_commit_cache(self, config=None):
2972 2972 """
2973 2973 Update cache of last commit for newest repository inside this repository group.
2974 2974 cache_keys should be::
2975 2975
2976 2976 source_repo_id
2977 2977 short_id
2978 2978 raw_id
2979 2979 revision
2980 2980 parents
2981 2981 message
2982 2982 date
2983 2983 author
2984 2984
2985 2985 """
2986 2986 from rhodecode.lib.vcs.utils.helpers import parse_datetime
2987 2987 empty_date = datetime.datetime.fromtimestamp(0)
2988 2988
2989 2989 def repo_groups_and_repos(root_gr):
2990 2990 for _repo in root_gr.repositories:
2991 2991 yield _repo
2992 2992 for child_group in root_gr.children.all():
2993 2993 yield child_group
2994 2994
2995 2995 latest_repo_cs_cache = {}
2996 2996 for obj in repo_groups_and_repos(self):
2997 2997 repo_cs_cache = obj.changeset_cache
2998 2998 date_latest = latest_repo_cs_cache.get('date', empty_date)
2999 2999 date_current = repo_cs_cache.get('date', empty_date)
3000 3000 current_timestamp = datetime_to_time(parse_datetime(date_latest))
3001 3001 if current_timestamp < datetime_to_time(parse_datetime(date_current)):
3002 3002 latest_repo_cs_cache = repo_cs_cache
3003 3003 if hasattr(obj, 'repo_id'):
3004 3004 latest_repo_cs_cache['source_repo_id'] = obj.repo_id
3005 3005 else:
3006 3006 latest_repo_cs_cache['source_repo_id'] = repo_cs_cache.get('source_repo_id')
3007 3007
3008 3008 _date_latest = parse_datetime(latest_repo_cs_cache.get('date') or empty_date)
3009 3009
3010 3010 latest_repo_cs_cache['updated_on'] = time.time()
3011 3011 self.changeset_cache = latest_repo_cs_cache
3012 3012 self.updated_on = _date_latest
3013 3013 Session().add(self)
3014 3014 Session().commit()
3015 3015
3016 3016 log.debug('updated repo group `%s` with new commit cache %s, and last update_date: %s',
3017 3017 self.group_name, latest_repo_cs_cache, _date_latest)
3018 3018
3019 3019 def permissions(self, with_admins=True, with_owner=True,
3020 3020 expand_from_user_groups=False):
3021 3021 """
3022 3022 Permissions for repository groups
3023 3023 """
3024 3024 _admin_perm = 'group.admin'
3025 3025
3026 3026 owner_row = []
3027 3027 if with_owner:
3028 3028 usr = AttributeDict(self.user.get_dict())
3029 3029 usr.owner_row = True
3030 3030 usr.permission = _admin_perm
3031 3031 owner_row.append(usr)
3032 3032
3033 3033 super_admin_ids = []
3034 3034 super_admin_rows = []
3035 3035 if with_admins:
3036 3036 for usr in User.get_all_super_admins():
3037 3037 super_admin_ids.append(usr.user_id)
3038 3038 # if this admin is also owner, don't double the record
3039 3039 if usr.user_id == owner_row[0].user_id:
3040 3040 owner_row[0].admin_row = True
3041 3041 else:
3042 3042 usr = AttributeDict(usr.get_dict())
3043 3043 usr.admin_row = True
3044 3044 usr.permission = _admin_perm
3045 3045 super_admin_rows.append(usr)
3046 3046
3047 3047 q = UserRepoGroupToPerm.query().filter(UserRepoGroupToPerm.group == self)
3048 3048 q = q.options(joinedload(UserRepoGroupToPerm.group),
3049 3049 joinedload(UserRepoGroupToPerm.user),
3050 3050 joinedload(UserRepoGroupToPerm.permission),)
3051 3051
3052 3052 # get owners and admins and permissions. We do a trick of re-writing
3053 3053 # objects from sqlalchemy to named-tuples due to sqlalchemy session
3054 3054 # has a global reference and changing one object propagates to all
3055 3055 # others. This means if admin is also an owner admin_row that change
3056 3056 # would propagate to both objects
3057 3057 perm_rows = []
3058 3058 for _usr in q.all():
3059 3059 usr = AttributeDict(_usr.user.get_dict())
3060 3060 # if this user is also owner/admin, mark as duplicate record
3061 3061 if usr.user_id == owner_row[0].user_id or usr.user_id in super_admin_ids:
3062 3062 usr.duplicate_perm = True
3063 3063 usr.permission = _usr.permission.permission_name
3064 3064 perm_rows.append(usr)
3065 3065
3066 3066 # filter the perm rows by 'default' first and then sort them by
3067 3067 # admin,write,read,none permissions sorted again alphabetically in
3068 3068 # each group
3069 3069 perm_rows = sorted(perm_rows, key=display_user_sort)
3070 3070
3071 3071 user_groups_rows = []
3072 3072 if expand_from_user_groups:
3073 3073 for ug in self.permission_user_groups(with_members=True):
3074 3074 for user_data in ug.members:
3075 3075 user_groups_rows.append(user_data)
3076 3076
3077 3077 return super_admin_rows + owner_row + perm_rows + user_groups_rows
3078 3078
3079 3079 def permission_user_groups(self, with_members=False):
3080 3080 q = UserGroupRepoGroupToPerm.query()\
3081 3081 .filter(UserGroupRepoGroupToPerm.group == self)
3082 3082 q = q.options(joinedload(UserGroupRepoGroupToPerm.group),
3083 3083 joinedload(UserGroupRepoGroupToPerm.users_group),
3084 3084 joinedload(UserGroupRepoGroupToPerm.permission),)
3085 3085
3086 3086 perm_rows = []
3087 3087 for _user_group in q.all():
3088 3088 entry = AttributeDict(_user_group.users_group.get_dict())
3089 3089 entry.permission = _user_group.permission.permission_name
3090 3090 if with_members:
3091 3091 entry.members = [x.user.get_dict()
3092 3092 for x in _user_group.users_group.members]
3093 3093 perm_rows.append(entry)
3094 3094
3095 3095 perm_rows = sorted(perm_rows, key=display_user_group_sort)
3096 3096 return perm_rows
3097 3097
3098 3098 def get_api_data(self):
3099 3099 """
3100 3100 Common function for generating api data
3101 3101
3102 3102 """
3103 3103 group = self
3104 3104 data = {
3105 3105 'group_id': group.group_id,
3106 3106 'group_name': group.group_name,
3107 3107 'group_description': group.description_safe,
3108 3108 'parent_group': group.parent_group.group_name if group.parent_group else None,
3109 3109 'repositories': [x.repo_name for x in group.repositories],
3110 3110 'owner': group.user.username,
3111 3111 }
3112 3112 return data
3113 3113
3114 3114 def get_dict(self):
3115 3115 # Since we transformed `group_name` to a hybrid property, we need to
3116 3116 # keep compatibility with the code which uses `group_name` field.
3117 3117 result = super(RepoGroup, self).get_dict()
3118 3118 result['group_name'] = result.pop('_group_name', None)
3119 3119 return result
3120 3120
3121 3121
3122 3122 class Permission(Base, BaseModel):
3123 3123 __tablename__ = 'permissions'
3124 3124 __table_args__ = (
3125 3125 Index('p_perm_name_idx', 'permission_name'),
3126 3126 base_table_args,
3127 3127 )
3128 3128
3129 3129 PERMS = [
3130 3130 ('hg.admin', _('RhodeCode Super Administrator')),
3131 3131
3132 3132 ('repository.none', _('Repository no access')),
3133 3133 ('repository.read', _('Repository read access')),
3134 3134 ('repository.write', _('Repository write access')),
3135 3135 ('repository.admin', _('Repository admin access')),
3136 3136
3137 3137 ('group.none', _('Repository group no access')),
3138 3138 ('group.read', _('Repository group read access')),
3139 3139 ('group.write', _('Repository group write access')),
3140 3140 ('group.admin', _('Repository group admin access')),
3141 3141
3142 3142 ('usergroup.none', _('User group no access')),
3143 3143 ('usergroup.read', _('User group read access')),
3144 3144 ('usergroup.write', _('User group write access')),
3145 3145 ('usergroup.admin', _('User group admin access')),
3146 3146
3147 3147 ('branch.none', _('Branch no permissions')),
3148 3148 ('branch.merge', _('Branch access by web merge')),
3149 3149 ('branch.push', _('Branch access by push')),
3150 3150 ('branch.push_force', _('Branch access by push with force')),
3151 3151
3152 3152 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
3153 3153 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
3154 3154
3155 3155 ('hg.usergroup.create.false', _('User Group creation disabled')),
3156 3156 ('hg.usergroup.create.true', _('User Group creation enabled')),
3157 3157
3158 3158 ('hg.create.none', _('Repository creation disabled')),
3159 3159 ('hg.create.repository', _('Repository creation enabled')),
3160 3160 ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
3161 3161 ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
3162 3162
3163 3163 ('hg.fork.none', _('Repository forking disabled')),
3164 3164 ('hg.fork.repository', _('Repository forking enabled')),
3165 3165
3166 3166 ('hg.register.none', _('Registration disabled')),
3167 3167 ('hg.register.manual_activate', _('User Registration with manual account activation')),
3168 3168 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
3169 3169
3170 3170 ('hg.password_reset.enabled', _('Password reset enabled')),
3171 3171 ('hg.password_reset.hidden', _('Password reset hidden')),
3172 3172 ('hg.password_reset.disabled', _('Password reset disabled')),
3173 3173
3174 3174 ('hg.extern_activate.manual', _('Manual activation of external account')),
3175 3175 ('hg.extern_activate.auto', _('Automatic activation of external account')),
3176 3176
3177 3177 ('hg.inherit_default_perms.false', _('Inherit object permissions from default user disabled')),
3178 3178 ('hg.inherit_default_perms.true', _('Inherit object permissions from default user enabled')),
3179 3179 ]
3180 3180
3181 3181 # definition of system default permissions for DEFAULT user, created on
3182 3182 # system setup
3183 3183 DEFAULT_USER_PERMISSIONS = [
3184 3184 # object perms
3185 3185 'repository.read',
3186 3186 'group.read',
3187 3187 'usergroup.read',
3188 3188 # branch, for backward compat we need same value as before so forced pushed
3189 3189 'branch.push_force',
3190 3190 # global
3191 3191 'hg.create.repository',
3192 3192 'hg.repogroup.create.false',
3193 3193 'hg.usergroup.create.false',
3194 3194 'hg.create.write_on_repogroup.true',
3195 3195 'hg.fork.repository',
3196 3196 'hg.register.manual_activate',
3197 3197 'hg.password_reset.enabled',
3198 3198 'hg.extern_activate.auto',
3199 3199 'hg.inherit_default_perms.true',
3200 3200 ]
3201 3201
3202 3202 # defines which permissions are more important higher the more important
3203 3203 # Weight defines which permissions are more important.
3204 3204 # The higher number the more important.
3205 3205 PERM_WEIGHTS = {
3206 3206 'repository.none': 0,
3207 3207 'repository.read': 1,
3208 3208 'repository.write': 3,
3209 3209 'repository.admin': 4,
3210 3210
3211 3211 'group.none': 0,
3212 3212 'group.read': 1,
3213 3213 'group.write': 3,
3214 3214 'group.admin': 4,
3215 3215
3216 3216 'usergroup.none': 0,
3217 3217 'usergroup.read': 1,
3218 3218 'usergroup.write': 3,
3219 3219 'usergroup.admin': 4,
3220 3220
3221 3221 'branch.none': 0,
3222 3222 'branch.merge': 1,
3223 3223 'branch.push': 3,
3224 3224 'branch.push_force': 4,
3225 3225
3226 3226 'hg.repogroup.create.false': 0,
3227 3227 'hg.repogroup.create.true': 1,
3228 3228
3229 3229 'hg.usergroup.create.false': 0,
3230 3230 'hg.usergroup.create.true': 1,
3231 3231
3232 3232 'hg.fork.none': 0,
3233 3233 'hg.fork.repository': 1,
3234 3234 'hg.create.none': 0,
3235 3235 'hg.create.repository': 1
3236 3236 }
3237 3237
3238 3238 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3239 3239 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
3240 3240 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
3241 3241
3242 3242 def __unicode__(self):
3243 3243 return u"<%s('%s:%s')>" % (
3244 3244 self.__class__.__name__, self.permission_id, self.permission_name
3245 3245 )
3246 3246
3247 3247 @classmethod
3248 3248 def get_by_key(cls, key):
3249 3249 return cls.query().filter(cls.permission_name == key).scalar()
3250 3250
3251 3251 @classmethod
3252 3252 def get_default_repo_perms(cls, user_id, repo_id=None):
3253 3253 q = Session().query(UserRepoToPerm, Repository, Permission)\
3254 3254 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
3255 3255 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
3256 3256 .filter(UserRepoToPerm.user_id == user_id)
3257 3257 if repo_id:
3258 3258 q = q.filter(UserRepoToPerm.repository_id == repo_id)
3259 3259 return q.all()
3260 3260
3261 3261 @classmethod
3262 3262 def get_default_repo_branch_perms(cls, user_id, repo_id=None):
3263 3263 q = Session().query(UserToRepoBranchPermission, UserRepoToPerm, Permission) \
3264 3264 .join(
3265 3265 Permission,
3266 3266 UserToRepoBranchPermission.permission_id == Permission.permission_id) \
3267 3267 .join(
3268 3268 UserRepoToPerm,
3269 3269 UserToRepoBranchPermission.rule_to_perm_id == UserRepoToPerm.repo_to_perm_id) \
3270 3270 .filter(UserRepoToPerm.user_id == user_id)
3271 3271
3272 3272 if repo_id:
3273 3273 q = q.filter(UserToRepoBranchPermission.repository_id == repo_id)
3274 3274 return q.order_by(UserToRepoBranchPermission.rule_order).all()
3275 3275
3276 3276 @classmethod
3277 3277 def get_default_repo_perms_from_user_group(cls, user_id, repo_id=None):
3278 3278 q = Session().query(UserGroupRepoToPerm, Repository, Permission)\
3279 3279 .join(
3280 3280 Permission,
3281 3281 UserGroupRepoToPerm.permission_id == Permission.permission_id)\
3282 3282 .join(
3283 3283 Repository,
3284 3284 UserGroupRepoToPerm.repository_id == Repository.repo_id)\
3285 3285 .join(
3286 3286 UserGroup,
3287 3287 UserGroupRepoToPerm.users_group_id ==
3288 3288 UserGroup.users_group_id)\
3289 3289 .join(
3290 3290 UserGroupMember,
3291 3291 UserGroupRepoToPerm.users_group_id ==
3292 3292 UserGroupMember.users_group_id)\
3293 3293 .filter(
3294 3294 UserGroupMember.user_id == user_id,
3295 3295 UserGroup.users_group_active == true())
3296 3296 if repo_id:
3297 3297 q = q.filter(UserGroupRepoToPerm.repository_id == repo_id)
3298 3298 return q.all()
3299 3299
3300 3300 @classmethod
3301 3301 def get_default_repo_branch_perms_from_user_group(cls, user_id, repo_id=None):
3302 3302 q = Session().query(UserGroupToRepoBranchPermission, UserGroupRepoToPerm, Permission) \
3303 3303 .join(
3304 3304 Permission,
3305 3305 UserGroupToRepoBranchPermission.permission_id == Permission.permission_id) \
3306 3306 .join(
3307 3307 UserGroupRepoToPerm,
3308 3308 UserGroupToRepoBranchPermission.rule_to_perm_id == UserGroupRepoToPerm.users_group_to_perm_id) \
3309 3309 .join(
3310 3310 UserGroup,
3311 3311 UserGroupRepoToPerm.users_group_id == UserGroup.users_group_id) \
3312 3312 .join(
3313 3313 UserGroupMember,
3314 3314 UserGroupRepoToPerm.users_group_id == UserGroupMember.users_group_id) \
3315 3315 .filter(
3316 3316 UserGroupMember.user_id == user_id,
3317 3317 UserGroup.users_group_active == true())
3318 3318
3319 3319 if repo_id:
3320 3320 q = q.filter(UserGroupToRepoBranchPermission.repository_id == repo_id)
3321 3321 return q.order_by(UserGroupToRepoBranchPermission.rule_order).all()
3322 3322
3323 3323 @classmethod
3324 3324 def get_default_group_perms(cls, user_id, repo_group_id=None):
3325 3325 q = Session().query(UserRepoGroupToPerm, RepoGroup, Permission)\
3326 3326 .join(
3327 3327 Permission,
3328 3328 UserRepoGroupToPerm.permission_id == Permission.permission_id)\
3329 3329 .join(
3330 3330 RepoGroup,
3331 3331 UserRepoGroupToPerm.group_id == RepoGroup.group_id)\
3332 3332 .filter(UserRepoGroupToPerm.user_id == user_id)
3333 3333 if repo_group_id:
3334 3334 q = q.filter(UserRepoGroupToPerm.group_id == repo_group_id)
3335 3335 return q.all()
3336 3336
3337 3337 @classmethod
3338 3338 def get_default_group_perms_from_user_group(
3339 3339 cls, user_id, repo_group_id=None):
3340 3340 q = Session().query(UserGroupRepoGroupToPerm, RepoGroup, Permission)\
3341 3341 .join(
3342 3342 Permission,
3343 3343 UserGroupRepoGroupToPerm.permission_id ==
3344 3344 Permission.permission_id)\
3345 3345 .join(
3346 3346 RepoGroup,
3347 3347 UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id)\
3348 3348 .join(
3349 3349 UserGroup,
3350 3350 UserGroupRepoGroupToPerm.users_group_id ==
3351 3351 UserGroup.users_group_id)\
3352 3352 .join(
3353 3353 UserGroupMember,
3354 3354 UserGroupRepoGroupToPerm.users_group_id ==
3355 3355 UserGroupMember.users_group_id)\
3356 3356 .filter(
3357 3357 UserGroupMember.user_id == user_id,
3358 3358 UserGroup.users_group_active == true())
3359 3359 if repo_group_id:
3360 3360 q = q.filter(UserGroupRepoGroupToPerm.group_id == repo_group_id)
3361 3361 return q.all()
3362 3362
3363 3363 @classmethod
3364 3364 def get_default_user_group_perms(cls, user_id, user_group_id=None):
3365 3365 q = Session().query(UserUserGroupToPerm, UserGroup, Permission)\
3366 3366 .join((Permission, UserUserGroupToPerm.permission_id == Permission.permission_id))\
3367 3367 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
3368 3368 .filter(UserUserGroupToPerm.user_id == user_id)
3369 3369 if user_group_id:
3370 3370 q = q.filter(UserUserGroupToPerm.user_group_id == user_group_id)
3371 3371 return q.all()
3372 3372
3373 3373 @classmethod
3374 3374 def get_default_user_group_perms_from_user_group(
3375 3375 cls, user_id, user_group_id=None):
3376 3376 TargetUserGroup = aliased(UserGroup, name='target_user_group')
3377 3377 q = Session().query(UserGroupUserGroupToPerm, UserGroup, Permission)\
3378 3378 .join(
3379 3379 Permission,
3380 3380 UserGroupUserGroupToPerm.permission_id ==
3381 3381 Permission.permission_id)\
3382 3382 .join(
3383 3383 TargetUserGroup,
3384 3384 UserGroupUserGroupToPerm.target_user_group_id ==
3385 3385 TargetUserGroup.users_group_id)\
3386 3386 .join(
3387 3387 UserGroup,
3388 3388 UserGroupUserGroupToPerm.user_group_id ==
3389 3389 UserGroup.users_group_id)\
3390 3390 .join(
3391 3391 UserGroupMember,
3392 3392 UserGroupUserGroupToPerm.user_group_id ==
3393 3393 UserGroupMember.users_group_id)\
3394 3394 .filter(
3395 3395 UserGroupMember.user_id == user_id,
3396 3396 UserGroup.users_group_active == true())
3397 3397 if user_group_id:
3398 3398 q = q.filter(
3399 3399 UserGroupUserGroupToPerm.user_group_id == user_group_id)
3400 3400
3401 3401 return q.all()
3402 3402
3403 3403
3404 3404 class UserRepoToPerm(Base, BaseModel):
3405 3405 __tablename__ = 'repo_to_perm'
3406 3406 __table_args__ = (
3407 3407 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
3408 3408 base_table_args
3409 3409 )
3410 3410
3411 3411 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3412 3412 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3413 3413 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3414 3414 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
3415 3415
3416 3416 user = relationship('User')
3417 3417 repository = relationship('Repository')
3418 3418 permission = relationship('Permission')
3419 3419
3420 3420 branch_perm_entry = relationship('UserToRepoBranchPermission', cascade="all, delete-orphan", lazy='joined')
3421 3421
3422 3422 @classmethod
3423 3423 def create(cls, user, repository, permission):
3424 3424 n = cls()
3425 3425 n.user = user
3426 3426 n.repository = repository
3427 3427 n.permission = permission
3428 3428 Session().add(n)
3429 3429 return n
3430 3430
3431 3431 def __unicode__(self):
3432 3432 return u'<%s => %s >' % (self.user, self.repository)
3433 3433
3434 3434
3435 3435 class UserUserGroupToPerm(Base, BaseModel):
3436 3436 __tablename__ = 'user_user_group_to_perm'
3437 3437 __table_args__ = (
3438 3438 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
3439 3439 base_table_args
3440 3440 )
3441 3441
3442 3442 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3443 3443 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3444 3444 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3445 3445 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3446 3446
3447 3447 user = relationship('User')
3448 3448 user_group = relationship('UserGroup')
3449 3449 permission = relationship('Permission')
3450 3450
3451 3451 @classmethod
3452 3452 def create(cls, user, user_group, permission):
3453 3453 n = cls()
3454 3454 n.user = user
3455 3455 n.user_group = user_group
3456 3456 n.permission = permission
3457 3457 Session().add(n)
3458 3458 return n
3459 3459
3460 3460 def __unicode__(self):
3461 3461 return u'<%s => %s >' % (self.user, self.user_group)
3462 3462
3463 3463
3464 3464 class UserToPerm(Base, BaseModel):
3465 3465 __tablename__ = 'user_to_perm'
3466 3466 __table_args__ = (
3467 3467 UniqueConstraint('user_id', 'permission_id'),
3468 3468 base_table_args
3469 3469 )
3470 3470
3471 3471 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3472 3472 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3473 3473 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3474 3474
3475 3475 user = relationship('User')
3476 3476 permission = relationship('Permission', lazy='joined')
3477 3477
3478 3478 def __unicode__(self):
3479 3479 return u'<%s => %s >' % (self.user, self.permission)
3480 3480
3481 3481
3482 3482 class UserGroupRepoToPerm(Base, BaseModel):
3483 3483 __tablename__ = 'users_group_repo_to_perm'
3484 3484 __table_args__ = (
3485 3485 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
3486 3486 base_table_args
3487 3487 )
3488 3488
3489 3489 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3490 3490 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3491 3491 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3492 3492 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
3493 3493
3494 3494 users_group = relationship('UserGroup')
3495 3495 permission = relationship('Permission')
3496 3496 repository = relationship('Repository')
3497 3497 user_group_branch_perms = relationship('UserGroupToRepoBranchPermission', cascade='all')
3498 3498
3499 3499 @classmethod
3500 3500 def create(cls, users_group, repository, permission):
3501 3501 n = cls()
3502 3502 n.users_group = users_group
3503 3503 n.repository = repository
3504 3504 n.permission = permission
3505 3505 Session().add(n)
3506 3506 return n
3507 3507
3508 3508 def __unicode__(self):
3509 3509 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
3510 3510
3511 3511
3512 3512 class UserGroupUserGroupToPerm(Base, BaseModel):
3513 3513 __tablename__ = 'user_group_user_group_to_perm'
3514 3514 __table_args__ = (
3515 3515 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
3516 3516 CheckConstraint('target_user_group_id != user_group_id'),
3517 3517 base_table_args
3518 3518 )
3519 3519
3520 3520 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3521 3521 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3522 3522 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3523 3523 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3524 3524
3525 3525 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
3526 3526 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
3527 3527 permission = relationship('Permission')
3528 3528
3529 3529 @classmethod
3530 3530 def create(cls, target_user_group, user_group, permission):
3531 3531 n = cls()
3532 3532 n.target_user_group = target_user_group
3533 3533 n.user_group = user_group
3534 3534 n.permission = permission
3535 3535 Session().add(n)
3536 3536 return n
3537 3537
3538 3538 def __unicode__(self):
3539 3539 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
3540 3540
3541 3541
3542 3542 class UserGroupToPerm(Base, BaseModel):
3543 3543 __tablename__ = 'users_group_to_perm'
3544 3544 __table_args__ = (
3545 3545 UniqueConstraint('users_group_id', 'permission_id',),
3546 3546 base_table_args
3547 3547 )
3548 3548
3549 3549 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3550 3550 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3551 3551 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3552 3552
3553 3553 users_group = relationship('UserGroup')
3554 3554 permission = relationship('Permission')
3555 3555
3556 3556
3557 3557 class UserRepoGroupToPerm(Base, BaseModel):
3558 3558 __tablename__ = 'user_repo_group_to_perm'
3559 3559 __table_args__ = (
3560 3560 UniqueConstraint('user_id', 'group_id', 'permission_id'),
3561 3561 base_table_args
3562 3562 )
3563 3563
3564 3564 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3565 3565 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3566 3566 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
3567 3567 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3568 3568
3569 3569 user = relationship('User')
3570 3570 group = relationship('RepoGroup')
3571 3571 permission = relationship('Permission')
3572 3572
3573 3573 @classmethod
3574 3574 def create(cls, user, repository_group, permission):
3575 3575 n = cls()
3576 3576 n.user = user
3577 3577 n.group = repository_group
3578 3578 n.permission = permission
3579 3579 Session().add(n)
3580 3580 return n
3581 3581
3582 3582
3583 3583 class UserGroupRepoGroupToPerm(Base, BaseModel):
3584 3584 __tablename__ = 'users_group_repo_group_to_perm'
3585 3585 __table_args__ = (
3586 3586 UniqueConstraint('users_group_id', 'group_id'),
3587 3587 base_table_args
3588 3588 )
3589 3589
3590 3590 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3591 3591 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
3592 3592 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
3593 3593 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
3594 3594
3595 3595 users_group = relationship('UserGroup')
3596 3596 permission = relationship('Permission')
3597 3597 group = relationship('RepoGroup')
3598 3598
3599 3599 @classmethod
3600 3600 def create(cls, user_group, repository_group, permission):
3601 3601 n = cls()
3602 3602 n.users_group = user_group
3603 3603 n.group = repository_group
3604 3604 n.permission = permission
3605 3605 Session().add(n)
3606 3606 return n
3607 3607
3608 3608 def __unicode__(self):
3609 3609 return u'<UserGroupRepoGroupToPerm:%s => %s >' % (self.users_group, self.group)
3610 3610
3611 3611
3612 3612 class Statistics(Base, BaseModel):
3613 3613 __tablename__ = 'statistics'
3614 3614 __table_args__ = (
3615 3615 base_table_args
3616 3616 )
3617 3617
3618 3618 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3619 3619 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
3620 3620 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
3621 3621 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
3622 3622 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
3623 3623 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
3624 3624
3625 3625 repository = relationship('Repository', single_parent=True)
3626 3626
3627 3627
3628 3628 class UserFollowing(Base, BaseModel):
3629 3629 __tablename__ = 'user_followings'
3630 3630 __table_args__ = (
3631 3631 UniqueConstraint('user_id', 'follows_repository_id'),
3632 3632 UniqueConstraint('user_id', 'follows_user_id'),
3633 3633 base_table_args
3634 3634 )
3635 3635
3636 3636 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3637 3637 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3638 3638 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
3639 3639 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
3640 3640 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
3641 3641
3642 3642 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
3643 3643
3644 3644 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
3645 3645 follows_repository = relationship('Repository', order_by='Repository.repo_name')
3646 3646
3647 3647 @classmethod
3648 3648 def get_repo_followers(cls, repo_id):
3649 3649 return cls.query().filter(cls.follows_repo_id == repo_id)
3650 3650
3651 3651
3652 3652 class CacheKey(Base, BaseModel):
3653 3653 __tablename__ = 'cache_invalidation'
3654 3654 __table_args__ = (
3655 3655 UniqueConstraint('cache_key'),
3656 3656 Index('key_idx', 'cache_key'),
3657 3657 base_table_args,
3658 3658 )
3659 3659
3660 3660 CACHE_TYPE_FEED = 'FEED'
3661 3661
3662 3662 # namespaces used to register process/thread aware caches
3663 3663 REPO_INVALIDATION_NAMESPACE = 'repo_cache:{repo_id}'
3664 3664 SETTINGS_INVALIDATION_NAMESPACE = 'system_settings'
3665 3665
3666 3666 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3667 3667 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
3668 3668 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
3669 3669 cache_state_uid = Column("cache_state_uid", String(255), nullable=True, unique=None, default=None)
3670 3670 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
3671 3671
3672 3672 def __init__(self, cache_key, cache_args='', cache_state_uid=None):
3673 3673 self.cache_key = cache_key
3674 3674 self.cache_args = cache_args
3675 3675 self.cache_active = False
3676 3676 # first key should be same for all entries, since all workers should share it
3677 3677 self.cache_state_uid = cache_state_uid or self.generate_new_state_uid()
3678 3678
3679 3679 def __unicode__(self):
3680 3680 return u"<%s('%s:%s[%s]')>" % (
3681 3681 self.__class__.__name__,
3682 3682 self.cache_id, self.cache_key, self.cache_active)
3683 3683
3684 3684 def _cache_key_partition(self):
3685 3685 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
3686 3686 return prefix, repo_name, suffix
3687 3687
3688 3688 def get_prefix(self):
3689 3689 """
3690 3690 Try to extract prefix from existing cache key. The key could consist
3691 3691 of prefix, repo_name, suffix
3692 3692 """
3693 3693 # this returns prefix, repo_name, suffix
3694 3694 return self._cache_key_partition()[0]
3695 3695
3696 3696 def get_suffix(self):
3697 3697 """
3698 3698 get suffix that might have been used in _get_cache_key to
3699 3699 generate self.cache_key. Only used for informational purposes
3700 3700 in repo_edit.mako.
3701 3701 """
3702 3702 # prefix, repo_name, suffix
3703 3703 return self._cache_key_partition()[2]
3704 3704
3705 3705 @classmethod
3706 3706 def generate_new_state_uid(cls, based_on=None):
3707 3707 if based_on:
3708 3708 return str(uuid.uuid5(uuid.NAMESPACE_URL, safe_str(based_on)))
3709 3709 else:
3710 3710 return str(uuid.uuid4())
3711 3711
3712 3712 @classmethod
3713 3713 def delete_all_cache(cls):
3714 3714 """
3715 3715 Delete all cache keys from database.
3716 3716 Should only be run when all instances are down and all entries
3717 3717 thus stale.
3718 3718 """
3719 3719 cls.query().delete()
3720 3720 Session().commit()
3721 3721
3722 3722 @classmethod
3723 3723 def set_invalidate(cls, cache_uid, delete=False):
3724 3724 """
3725 3725 Mark all caches of a repo as invalid in the database.
3726 3726 """
3727 3727
3728 3728 try:
3729 3729 qry = Session().query(cls).filter(cls.cache_args == cache_uid)
3730 3730 if delete:
3731 3731 qry.delete()
3732 3732 log.debug('cache objects deleted for cache args %s',
3733 3733 safe_str(cache_uid))
3734 3734 else:
3735 3735 qry.update({"cache_active": False,
3736 3736 "cache_state_uid": cls.generate_new_state_uid()})
3737 3737 log.debug('cache objects marked as invalid for cache args %s',
3738 3738 safe_str(cache_uid))
3739 3739
3740 3740 Session().commit()
3741 3741 except Exception:
3742 3742 log.exception(
3743 3743 'Cache key invalidation failed for cache args %s',
3744 3744 safe_str(cache_uid))
3745 3745 Session().rollback()
3746 3746
3747 3747 @classmethod
3748 3748 def get_active_cache(cls, cache_key):
3749 3749 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
3750 3750 if inv_obj:
3751 3751 return inv_obj
3752 3752 return None
3753 3753
3754 3754 @classmethod
3755 3755 def get_namespace_map(cls, namespace):
3756 3756 return {
3757 3757 x.cache_key: x
3758 3758 for x in cls.query().filter(cls.cache_args == namespace)}
3759 3759
3760 3760
3761 3761 class ChangesetComment(Base, BaseModel):
3762 3762 __tablename__ = 'changeset_comments'
3763 3763 __table_args__ = (
3764 3764 Index('cc_revision_idx', 'revision'),
3765 3765 base_table_args,
3766 3766 )
3767 3767
3768 3768 COMMENT_OUTDATED = u'comment_outdated'
3769 3769 COMMENT_TYPE_NOTE = u'note'
3770 3770 COMMENT_TYPE_TODO = u'todo'
3771 3771 COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO]
3772 3772
3773 3773 OP_IMMUTABLE = u'immutable'
3774 3774 OP_CHANGEABLE = u'changeable'
3775 3775
3776 3776 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
3777 3777 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3778 3778 revision = Column('revision', String(40), nullable=True)
3779 3779 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
3780 3780 pull_request_version_id = Column("pull_request_version_id", Integer(), ForeignKey('pull_request_versions.pull_request_version_id'), nullable=True)
3781 3781 line_no = Column('line_no', Unicode(10), nullable=True)
3782 3782 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
3783 3783 f_path = Column('f_path', Unicode(1000), nullable=True)
3784 3784 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
3785 3785 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
3786 3786 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3787 3787 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3788 3788 renderer = Column('renderer', Unicode(64), nullable=True)
3789 3789 display_state = Column('display_state', Unicode(128), nullable=True)
3790 3790 immutable_state = Column('immutable_state', Unicode(128), nullable=True, default=OP_CHANGEABLE)
3791 3791 draft = Column('draft', Boolean(), nullable=True, default=False)
3792 3792
3793 3793 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
3794 3794 resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True)
3795 3795
3796 3796 resolved_comment = relationship('ChangesetComment', remote_side=comment_id, back_populates='resolved_by')
3797 3797 resolved_by = relationship('ChangesetComment', back_populates='resolved_comment')
3798 3798
3799 3799 author = relationship('User', lazy='select')
3800 3800 repo = relationship('Repository')
3801 3801 status_change = relationship('ChangesetStatus', cascade="all, delete-orphan", lazy='select')
3802 3802 pull_request = relationship('PullRequest', lazy='select')
3803 3803 pull_request_version = relationship('PullRequestVersion', lazy='select')
3804 3804 history = relationship('ChangesetCommentHistory', cascade='all, delete-orphan', lazy='select', order_by='ChangesetCommentHistory.version')
3805 3805
3806 3806 @classmethod
3807 3807 def get_users(cls, revision=None, pull_request_id=None):
3808 3808 """
3809 3809 Returns user associated with this ChangesetComment. ie those
3810 3810 who actually commented
3811 3811
3812 3812 :param cls:
3813 3813 :param revision:
3814 3814 """
3815 3815 q = Session().query(User)\
3816 3816 .join(ChangesetComment.author)
3817 3817 if revision:
3818 3818 q = q.filter(cls.revision == revision)
3819 3819 elif pull_request_id:
3820 3820 q = q.filter(cls.pull_request_id == pull_request_id)
3821 3821 return q.all()
3822 3822
3823 3823 @classmethod
3824 3824 def get_index_from_version(cls, pr_version, versions=None, num_versions=None):
3825 3825
3826 3826 if versions is not None:
3827 3827 num_versions = [x.pull_request_version_id for x in versions]
3828 3828
3829 3829 num_versions = num_versions or []
3830 3830 try:
3831 3831 return num_versions.index(pr_version) + 1
3832 3832 except (IndexError, ValueError):
3833 3833 return
3834 3834
3835 3835 @property
3836 3836 def outdated(self):
3837 3837 return self.display_state == self.COMMENT_OUTDATED
3838 3838
3839 3839 @property
3840 3840 def outdated_js(self):
3841 3841 return json.dumps(self.display_state == self.COMMENT_OUTDATED)
3842 3842
3843 3843 @property
3844 3844 def immutable(self):
3845 3845 return self.immutable_state == self.OP_IMMUTABLE
3846 3846
3847 3847 def outdated_at_version(self, version):
3848 3848 """
3849 3849 Checks if comment is outdated for given pull request version
3850 3850 """
3851 3851 def version_check():
3852 3852 return self.pull_request_version_id and self.pull_request_version_id != version
3853 3853
3854 3854 if self.is_inline:
3855 3855 return self.outdated and version_check()
3856 3856 else:
3857 3857 # general comments don't have .outdated set, also latest don't have a version
3858 3858 return version_check()
3859 3859
3860 3860 def outdated_at_version_js(self, version):
3861 3861 """
3862 3862 Checks if comment is outdated for given pull request version
3863 3863 """
3864 3864 return json.dumps(self.outdated_at_version(version))
3865 3865
3866 3866 def older_than_version(self, version):
3867 3867 """
3868 3868 Checks if comment is made from previous version than given
3869 3869 """
3870 3870 if version is None:
3871 3871 return self.pull_request_version != version
3872 3872
3873 3873 return self.pull_request_version < version
3874 3874
3875 3875 def older_than_version_js(self, version):
3876 3876 """
3877 3877 Checks if comment is made from previous version than given
3878 3878 """
3879 3879 return json.dumps(self.older_than_version(version))
3880 3880
3881 3881 @property
3882 3882 def commit_id(self):
3883 3883 """New style naming to stop using .revision"""
3884 3884 return self.revision
3885 3885
3886 3886 @property
3887 3887 def resolved(self):
3888 3888 return self.resolved_by[0] if self.resolved_by else None
3889 3889
3890 3890 @property
3891 3891 def is_todo(self):
3892 3892 return self.comment_type == self.COMMENT_TYPE_TODO
3893 3893
3894 3894 @property
3895 3895 def is_inline(self):
3896 3896 if self.line_no and self.f_path:
3897 3897 return True
3898 3898 return False
3899 3899
3900 3900 @property
3901 3901 def last_version(self):
3902 3902 version = 0
3903 3903 if self.history:
3904 3904 version = self.history[-1].version
3905 3905 return version
3906 3906
3907 3907 def get_index_version(self, versions):
3908 3908 return self.get_index_from_version(
3909 3909 self.pull_request_version_id, versions)
3910 3910
3911 3911 @property
3912 3912 def review_status(self):
3913 3913 if self.status_change:
3914 3914 return self.status_change[0].status
3915 3915
3916 3916 @property
3917 3917 def review_status_lbl(self):
3918 3918 if self.status_change:
3919 3919 return self.status_change[0].status_lbl
3920 3920
3921 3921 def __repr__(self):
3922 3922 if self.comment_id:
3923 3923 return '<DB:Comment #%s>' % self.comment_id
3924 3924 else:
3925 3925 return '<DB:Comment at %#x>' % id(self)
3926 3926
3927 3927 def get_api_data(self):
3928 3928 comment = self
3929 3929
3930 3930 data = {
3931 3931 'comment_id': comment.comment_id,
3932 3932 'comment_type': comment.comment_type,
3933 3933 'comment_text': comment.text,
3934 3934 'comment_status': comment.status_change,
3935 3935 'comment_f_path': comment.f_path,
3936 3936 'comment_lineno': comment.line_no,
3937 3937 'comment_author': comment.author,
3938 3938 'comment_created_on': comment.created_on,
3939 3939 'comment_resolved_by': self.resolved,
3940 3940 'comment_commit_id': comment.revision,
3941 3941 'comment_pull_request_id': comment.pull_request_id,
3942 3942 'comment_last_version': self.last_version
3943 3943 }
3944 3944 return data
3945 3945
3946 3946 def __json__(self):
3947 3947 data = dict()
3948 3948 data.update(self.get_api_data())
3949 3949 return data
3950 3950
3951 3951
3952 3952 class ChangesetCommentHistory(Base, BaseModel):
3953 3953 __tablename__ = 'changeset_comments_history'
3954 3954 __table_args__ = (
3955 3955 Index('cch_comment_id_idx', 'comment_id'),
3956 3956 base_table_args,
3957 3957 )
3958 3958
3959 3959 comment_history_id = Column('comment_history_id', Integer(), nullable=False, primary_key=True)
3960 3960 comment_id = Column('comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=False)
3961 3961 version = Column("version", Integer(), nullable=False, default=0)
3962 3962 created_by_user_id = Column('created_by_user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
3963 3963 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
3964 3964 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3965 3965 deleted = Column('deleted', Boolean(), default=False)
3966 3966
3967 3967 author = relationship('User', lazy='joined')
3968 3968 comment = relationship('ChangesetComment', cascade="all, delete")
3969 3969
3970 3970 @classmethod
3971 3971 def get_version(cls, comment_id):
3972 3972 q = Session().query(ChangesetCommentHistory).filter(
3973 3973 ChangesetCommentHistory.comment_id == comment_id).order_by(ChangesetCommentHistory.version.desc())
3974 3974 if q.count() == 0:
3975 3975 return 1
3976 3976 elif q.count() >= q[0].version:
3977 3977 return q.count() + 1
3978 3978 else:
3979 3979 return q[0].version + 1
3980 3980
3981 3981
3982 3982 class ChangesetStatus(Base, BaseModel):
3983 3983 __tablename__ = 'changeset_statuses'
3984 3984 __table_args__ = (
3985 3985 Index('cs_revision_idx', 'revision'),
3986 3986 Index('cs_version_idx', 'version'),
3987 3987 UniqueConstraint('repo_id', 'revision', 'version'),
3988 3988 base_table_args
3989 3989 )
3990 3990
3991 3991 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
3992 3992 STATUS_APPROVED = 'approved'
3993 3993 STATUS_REJECTED = 'rejected'
3994 3994 STATUS_UNDER_REVIEW = 'under_review'
3995 3995 CheckConstraint,
3996 3996 STATUSES = [
3997 3997 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
3998 3998 (STATUS_APPROVED, _("Approved")),
3999 3999 (STATUS_REJECTED, _("Rejected")),
4000 4000 (STATUS_UNDER_REVIEW, _("Under Review")),
4001 4001 ]
4002 4002
4003 4003 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
4004 4004 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
4005 4005 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
4006 4006 revision = Column('revision', String(40), nullable=False)
4007 4007 status = Column('status', String(128), nullable=False, default=DEFAULT)
4008 4008 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
4009 4009 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
4010 4010 version = Column('version', Integer(), nullable=False, default=0)
4011 4011 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
4012 4012
4013 4013 author = relationship('User', lazy='select')
4014 4014 repo = relationship('Repository', lazy='select')
4015 4015 comment = relationship('ChangesetComment', lazy='select')
4016 4016 pull_request = relationship('PullRequest', lazy='select')
4017 4017
4018 4018 def __unicode__(self):
4019 4019 return u"<%s('%s[v%s]:%s')>" % (
4020 4020 self.__class__.__name__,
4021 4021 self.status, self.version, self.author
4022 4022 )
4023 4023
4024 4024 @classmethod
4025 4025 def get_status_lbl(cls, value):
4026 4026 return dict(cls.STATUSES).get(value)
4027 4027
4028 4028 @property
4029 4029 def status_lbl(self):
4030 4030 return ChangesetStatus.get_status_lbl(self.status)
4031 4031
4032 4032 def get_api_data(self):
4033 4033 status = self
4034 4034 data = {
4035 4035 'status_id': status.changeset_status_id,
4036 4036 'status': status.status,
4037 4037 }
4038 4038 return data
4039 4039
4040 4040 def __json__(self):
4041 4041 data = dict()
4042 4042 data.update(self.get_api_data())
4043 4043 return data
4044 4044
4045 4045
4046 4046 class _SetState(object):
4047 4047 """
4048 4048 Context processor allowing changing state for sensitive operation such as
4049 4049 pull request update or merge
4050 4050 """
4051 4051
4052 4052 def __init__(self, pull_request, pr_state, back_state=None):
4053 4053 self._pr = pull_request
4054 4054 self._org_state = back_state or pull_request.pull_request_state
4055 4055 self._pr_state = pr_state
4056 4056 self._current_state = None
4057 4057
4058 4058 def __enter__(self):
4059 4059 log.debug('StateLock: entering set state context of pr %s, setting state to: `%s`',
4060 4060 self._pr, self._pr_state)
4061 4061 self.set_pr_state(self._pr_state)
4062 4062 return self
4063 4063
4064 4064 def __exit__(self, exc_type, exc_val, exc_tb):
4065 4065 if exc_val is not None:
4066 4066 log.error(traceback.format_exc(exc_tb))
4067 4067 return None
4068 4068
4069 4069 self.set_pr_state(self._org_state)
4070 4070 log.debug('StateLock: exiting set state context of pr %s, setting state to: `%s`',
4071 4071 self._pr, self._org_state)
4072 4072
4073 4073 @property
4074 4074 def state(self):
4075 4075 return self._current_state
4076 4076
4077 4077 def set_pr_state(self, pr_state):
4078 4078 try:
4079 4079 self._pr.pull_request_state = pr_state
4080 4080 Session().add(self._pr)
4081 4081 Session().commit()
4082 4082 self._current_state = pr_state
4083 4083 except Exception:
4084 4084 log.exception('Failed to set PullRequest %s state to %s', self._pr, pr_state)
4085 4085 raise
4086 4086
4087 4087
4088 4088 class _PullRequestBase(BaseModel):
4089 4089 """
4090 4090 Common attributes of pull request and version entries.
4091 4091 """
4092 4092
4093 4093 # .status values
4094 4094 STATUS_NEW = u'new'
4095 4095 STATUS_OPEN = u'open'
4096 4096 STATUS_CLOSED = u'closed'
4097 4097
4098 4098 # available states
4099 4099 STATE_CREATING = u'creating'
4100 4100 STATE_UPDATING = u'updating'
4101 4101 STATE_MERGING = u'merging'
4102 4102 STATE_CREATED = u'created'
4103 4103
4104 4104 title = Column('title', Unicode(255), nullable=True)
4105 4105 description = Column(
4106 4106 'description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'),
4107 4107 nullable=True)
4108 4108 description_renderer = Column('description_renderer', Unicode(64), nullable=True)
4109 4109
4110 4110 # new/open/closed status of pull request (not approve/reject/etc)
4111 4111 status = Column('status', Unicode(255), nullable=False, default=STATUS_NEW)
4112 4112 created_on = Column(
4113 4113 'created_on', DateTime(timezone=False), nullable=False,
4114 4114 default=datetime.datetime.now)
4115 4115 updated_on = Column(
4116 4116 'updated_on', DateTime(timezone=False), nullable=False,
4117 4117 default=datetime.datetime.now)
4118 4118
4119 4119 pull_request_state = Column("pull_request_state", String(255), nullable=True)
4120 4120
4121 4121 @declared_attr
4122 4122 def user_id(cls):
4123 4123 return Column(
4124 4124 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
4125 4125 unique=None)
4126 4126
4127 4127 # 500 revisions max
4128 4128 _revisions = Column(
4129 4129 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
4130 4130
4131 4131 common_ancestor_id = Column('common_ancestor_id', Unicode(255), nullable=True)
4132 4132
4133 4133 @declared_attr
4134 4134 def source_repo_id(cls):
4135 4135 # TODO: dan: rename column to source_repo_id
4136 4136 return Column(
4137 4137 'org_repo_id', Integer(), ForeignKey('repositories.repo_id'),
4138 4138 nullable=False)
4139 4139
4140 4140 _source_ref = Column('org_ref', Unicode(255), nullable=False)
4141 4141
4142 4142 @hybrid_property
4143 4143 def source_ref(self):
4144 4144 return self._source_ref
4145 4145
4146 4146 @source_ref.setter
4147 4147 def source_ref(self, val):
4148 4148 parts = (val or '').split(':')
4149 4149 if len(parts) != 3:
4150 4150 raise ValueError(
4151 4151 'Invalid reference format given: {}, expected X:Y:Z'.format(val))
4152 4152 self._source_ref = safe_unicode(val)
4153 4153
4154 4154 _target_ref = Column('other_ref', Unicode(255), nullable=False)
4155 4155
4156 4156 @hybrid_property
4157 4157 def target_ref(self):
4158 4158 return self._target_ref
4159 4159
4160 4160 @target_ref.setter
4161 4161 def target_ref(self, val):
4162 4162 parts = (val or '').split(':')
4163 4163 if len(parts) != 3:
4164 4164 raise ValueError(
4165 4165 'Invalid reference format given: {}, expected X:Y:Z'.format(val))
4166 4166 self._target_ref = safe_unicode(val)
4167 4167
4168 4168 @declared_attr
4169 4169 def target_repo_id(cls):
4170 4170 # TODO: dan: rename column to target_repo_id
4171 4171 return Column(
4172 4172 'other_repo_id', Integer(), ForeignKey('repositories.repo_id'),
4173 4173 nullable=False)
4174 4174
4175 4175 _shadow_merge_ref = Column('shadow_merge_ref', Unicode(255), nullable=True)
4176 4176
4177 4177 # TODO: dan: rename column to last_merge_source_rev
4178 4178 _last_merge_source_rev = Column(
4179 4179 'last_merge_org_rev', String(40), nullable=True)
4180 4180 # TODO: dan: rename column to last_merge_target_rev
4181 4181 _last_merge_target_rev = Column(
4182 4182 'last_merge_other_rev', String(40), nullable=True)
4183 4183 _last_merge_status = Column('merge_status', Integer(), nullable=True)
4184 4184 last_merge_metadata = Column(
4185 4185 'last_merge_metadata', MutationObj.as_mutable(
4186 4186 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
4187 4187
4188 4188 merge_rev = Column('merge_rev', String(40), nullable=True)
4189 4189
4190 4190 reviewer_data = Column(
4191 4191 'reviewer_data_json', MutationObj.as_mutable(
4192 4192 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
4193 4193
4194 4194 @property
4195 4195 def reviewer_data_json(self):
4196 4196 return json.dumps(self.reviewer_data)
4197 4197
4198 4198 @property
4199 4199 def last_merge_metadata_parsed(self):
4200 4200 metadata = {}
4201 4201 if not self.last_merge_metadata:
4202 4202 return metadata
4203 4203
4204 4204 if hasattr(self.last_merge_metadata, 'de_coerce'):
4205 4205 for k, v in self.last_merge_metadata.de_coerce().items():
4206 4206 if k in ['target_ref', 'source_ref']:
4207 4207 metadata[k] = Reference(v['type'], v['name'], v['commit_id'])
4208 4208 else:
4209 4209 if hasattr(v, 'de_coerce'):
4210 4210 metadata[k] = v.de_coerce()
4211 4211 else:
4212 4212 metadata[k] = v
4213 4213 return metadata
4214 4214
4215 4215 @property
4216 4216 def work_in_progress(self):
4217 4217 """checks if pull request is work in progress by checking the title"""
4218 4218 title = self.title.upper()
4219 4219 if re.match(r'^(\[WIP\]\s*|WIP:\s*|WIP\s+)', title):
4220 4220 return True
4221 4221 return False
4222 4222
4223 4223 @property
4224 4224 def title_safe(self):
4225 4225 return self.title\
4226 4226 .replace('{', '{{')\
4227 4227 .replace('}', '}}')
4228 4228
4229 4229 @hybrid_property
4230 4230 def description_safe(self):
4231 4231 from rhodecode.lib import helpers as h
4232 4232 return h.escape(self.description)
4233 4233
4234 4234 @hybrid_property
4235 4235 def revisions(self):
4236 4236 return self._revisions.split(':') if self._revisions else []
4237 4237
4238 4238 @revisions.setter
4239 4239 def revisions(self, val):
4240 4240 self._revisions = u':'.join(val)
4241 4241
4242 4242 @hybrid_property
4243 4243 def last_merge_status(self):
4244 4244 return safe_int(self._last_merge_status)
4245 4245
4246 4246 @last_merge_status.setter
4247 4247 def last_merge_status(self, val):
4248 4248 self._last_merge_status = val
4249 4249
4250 4250 @declared_attr
4251 4251 def author(cls):
4252 4252 return relationship('User', lazy='joined')
4253 4253
4254 4254 @declared_attr
4255 4255 def source_repo(cls):
4256 4256 return relationship(
4257 4257 'Repository',
4258 4258 primaryjoin='%s.source_repo_id==Repository.repo_id' % cls.__name__)
4259 4259
4260 4260 @property
4261 4261 def source_ref_parts(self):
4262 4262 return self.unicode_to_reference(self.source_ref)
4263 4263
4264 4264 @declared_attr
4265 4265 def target_repo(cls):
4266 4266 return relationship(
4267 4267 'Repository',
4268 4268 primaryjoin='%s.target_repo_id==Repository.repo_id' % cls.__name__)
4269 4269
4270 4270 @property
4271 4271 def target_ref_parts(self):
4272 4272 return self.unicode_to_reference(self.target_ref)
4273 4273
4274 4274 @property
4275 4275 def shadow_merge_ref(self):
4276 4276 return self.unicode_to_reference(self._shadow_merge_ref)
4277 4277
4278 4278 @shadow_merge_ref.setter
4279 4279 def shadow_merge_ref(self, ref):
4280 4280 self._shadow_merge_ref = self.reference_to_unicode(ref)
4281 4281
4282 4282 @staticmethod
4283 4283 def unicode_to_reference(raw):
4284 4284 return unicode_to_reference(raw)
4285 4285
4286 4286 @staticmethod
4287 4287 def reference_to_unicode(ref):
4288 4288 return reference_to_unicode(ref)
4289 4289
4290 4290 def get_api_data(self, with_merge_state=True):
4291 4291 from rhodecode.model.pull_request import PullRequestModel
4292 4292
4293 4293 pull_request = self
4294 4294 if with_merge_state:
4295 4295 merge_response, merge_status, msg = \
4296 4296 PullRequestModel().merge_status(pull_request)
4297 4297 merge_state = {
4298 4298 'status': merge_status,
4299 4299 'message': safe_unicode(msg),
4300 4300 }
4301 4301 else:
4302 4302 merge_state = {'status': 'not_available',
4303 4303 'message': 'not_available'}
4304 4304
4305 4305 merge_data = {
4306 4306 'clone_url': PullRequestModel().get_shadow_clone_url(pull_request),
4307 4307 'reference': (
4308 4308 pull_request.shadow_merge_ref._asdict()
4309 4309 if pull_request.shadow_merge_ref else None),
4310 4310 }
4311 4311
4312 4312 data = {
4313 4313 'pull_request_id': pull_request.pull_request_id,
4314 4314 'url': PullRequestModel().get_url(pull_request),
4315 4315 'title': pull_request.title,
4316 4316 'description': pull_request.description,
4317 4317 'status': pull_request.status,
4318 4318 'state': pull_request.pull_request_state,
4319 4319 'created_on': pull_request.created_on,
4320 4320 'updated_on': pull_request.updated_on,
4321 4321 'commit_ids': pull_request.revisions,
4322 4322 'review_status': pull_request.calculated_review_status(),
4323 4323 'mergeable': merge_state,
4324 4324 'source': {
4325 4325 'clone_url': pull_request.source_repo.clone_url(),
4326 4326 'repository': pull_request.source_repo.repo_name,
4327 4327 'reference': {
4328 4328 'name': pull_request.source_ref_parts.name,
4329 4329 'type': pull_request.source_ref_parts.type,
4330 4330 'commit_id': pull_request.source_ref_parts.commit_id,
4331 4331 },
4332 4332 },
4333 4333 'target': {
4334 4334 'clone_url': pull_request.target_repo.clone_url(),
4335 4335 'repository': pull_request.target_repo.repo_name,
4336 4336 'reference': {
4337 4337 'name': pull_request.target_ref_parts.name,
4338 4338 'type': pull_request.target_ref_parts.type,
4339 4339 'commit_id': pull_request.target_ref_parts.commit_id,
4340 4340 },
4341 4341 },
4342 4342 'merge': merge_data,
4343 4343 'author': pull_request.author.get_api_data(include_secrets=False,
4344 4344 details='basic'),
4345 4345 'reviewers': [
4346 4346 {
4347 4347 'user': reviewer.get_api_data(include_secrets=False,
4348 4348 details='basic'),
4349 4349 'reasons': reasons,
4350 4350 'review_status': st[0][1].status if st else 'not_reviewed',
4351 4351 }
4352 4352 for obj, reviewer, reasons, mandatory, st in
4353 4353 pull_request.reviewers_statuses()
4354 4354 ]
4355 4355 }
4356 4356
4357 4357 return data
4358 4358
4359 4359 def set_state(self, pull_request_state, final_state=None):
4360 4360 """
4361 4361 # goes from initial state to updating to initial state.
4362 4362 # initial state can be changed by specifying back_state=
4363 4363 with pull_request_obj.set_state(PullRequest.STATE_UPDATING):
4364 4364 pull_request.merge()
4365 4365
4366 4366 :param pull_request_state:
4367 4367 :param final_state:
4368 4368
4369 4369 """
4370 4370
4371 4371 return _SetState(self, pull_request_state, back_state=final_state)
4372 4372
4373 4373
4374 4374 class PullRequest(Base, _PullRequestBase):
4375 4375 __tablename__ = 'pull_requests'
4376 4376 __table_args__ = (
4377 4377 base_table_args,
4378 4378 )
4379 4379 LATEST_VER = 'latest'
4380 4380
4381 4381 pull_request_id = Column(
4382 4382 'pull_request_id', Integer(), nullable=False, primary_key=True)
4383 4383
4384 4384 def __repr__(self):
4385 4385 if self.pull_request_id:
4386 4386 return '<DB:PullRequest #%s>' % self.pull_request_id
4387 4387 else:
4388 4388 return '<DB:PullRequest at %#x>' % id(self)
4389 4389
4390 4390 reviewers = relationship('PullRequestReviewers', cascade="all, delete-orphan")
4391 4391 statuses = relationship('ChangesetStatus', cascade="all, delete-orphan")
4392 4392 comments = relationship('ChangesetComment', cascade="all, delete-orphan")
4393 4393 versions = relationship('PullRequestVersion', cascade="all, delete-orphan",
4394 4394 lazy='dynamic')
4395 4395
4396 4396 @classmethod
4397 4397 def get_pr_display_object(cls, pull_request_obj, org_pull_request_obj,
4398 4398 internal_methods=None):
4399 4399
4400 4400 class PullRequestDisplay(object):
4401 4401 """
4402 4402 Special object wrapper for showing PullRequest data via Versions
4403 4403 It mimics PR object as close as possible. This is read only object
4404 4404 just for display
4405 4405 """
4406 4406
4407 4407 def __init__(self, attrs, internal=None):
4408 4408 self.attrs = attrs
4409 4409 # internal have priority over the given ones via attrs
4410 4410 self.internal = internal or ['versions']
4411 4411
4412 4412 def __getattr__(self, item):
4413 4413 if item in self.internal:
4414 4414 return getattr(self, item)
4415 4415 try:
4416 4416 return self.attrs[item]
4417 4417 except KeyError:
4418 4418 raise AttributeError(
4419 4419 '%s object has no attribute %s' % (self, item))
4420 4420
4421 4421 def __repr__(self):
4422 4422 return '<DB:PullRequestDisplay #%s>' % self.attrs.get('pull_request_id')
4423 4423
4424 4424 def versions(self):
4425 4425 return pull_request_obj.versions.order_by(
4426 4426 PullRequestVersion.pull_request_version_id).all()
4427 4427
4428 4428 def is_closed(self):
4429 4429 return pull_request_obj.is_closed()
4430 4430
4431 4431 def is_state_changing(self):
4432 4432 return pull_request_obj.is_state_changing()
4433 4433
4434 4434 @property
4435 4435 def pull_request_version_id(self):
4436 4436 return getattr(pull_request_obj, 'pull_request_version_id', None)
4437 4437
4438 4438 @property
4439 4439 def pull_request_last_version(self):
4440 4440 return pull_request_obj.pull_request_last_version
4441 4441
4442 4442 attrs = StrictAttributeDict(pull_request_obj.get_api_data(with_merge_state=False))
4443 4443
4444 4444 attrs.author = StrictAttributeDict(
4445 4445 pull_request_obj.author.get_api_data())
4446 4446 if pull_request_obj.target_repo:
4447 4447 attrs.target_repo = StrictAttributeDict(
4448 4448 pull_request_obj.target_repo.get_api_data())
4449 4449 attrs.target_repo.clone_url = pull_request_obj.target_repo.clone_url
4450 4450
4451 4451 if pull_request_obj.source_repo:
4452 4452 attrs.source_repo = StrictAttributeDict(
4453 4453 pull_request_obj.source_repo.get_api_data())
4454 4454 attrs.source_repo.clone_url = pull_request_obj.source_repo.clone_url
4455 4455
4456 4456 attrs.source_ref_parts = pull_request_obj.source_ref_parts
4457 4457 attrs.target_ref_parts = pull_request_obj.target_ref_parts
4458 4458 attrs.revisions = pull_request_obj.revisions
4459 4459 attrs.common_ancestor_id = pull_request_obj.common_ancestor_id
4460 4460 attrs.shadow_merge_ref = org_pull_request_obj.shadow_merge_ref
4461 4461 attrs.reviewer_data = org_pull_request_obj.reviewer_data
4462 4462 attrs.reviewer_data_json = org_pull_request_obj.reviewer_data_json
4463 4463
4464 4464 return PullRequestDisplay(attrs, internal=internal_methods)
4465 4465
4466 4466 def is_closed(self):
4467 4467 return self.status == self.STATUS_CLOSED
4468 4468
4469 4469 def is_state_changing(self):
4470 4470 return self.pull_request_state != PullRequest.STATE_CREATED
4471 4471
4472 4472 def __json__(self):
4473 4473 return {
4474 4474 'revisions': self.revisions,
4475 4475 'versions': self.versions_count
4476 4476 }
4477 4477
4478 4478 def calculated_review_status(self):
4479 4479 from rhodecode.model.changeset_status import ChangesetStatusModel
4480 4480 return ChangesetStatusModel().calculated_review_status(self)
4481 4481
4482 def reviewers_statuses(self):
4482 def reviewers_statuses(self, user=None):
4483 4483 from rhodecode.model.changeset_status import ChangesetStatusModel
4484 return ChangesetStatusModel().reviewers_statuses(self)
4484 return ChangesetStatusModel().reviewers_statuses(self, user=user)
4485 4485
4486 4486 def get_pull_request_reviewers(self, role=None):
4487 4487 qry = PullRequestReviewers.query()\
4488 4488 .filter(PullRequestReviewers.pull_request_id == self.pull_request_id)
4489 4489 if role:
4490 4490 qry = qry.filter(PullRequestReviewers.role == role)
4491 4491
4492 4492 return qry.all()
4493 4493
4494 4494 @property
4495 4495 def reviewers_count(self):
4496 4496 qry = PullRequestReviewers.query()\
4497 4497 .filter(PullRequestReviewers.pull_request_id == self.pull_request_id)\
4498 4498 .filter(PullRequestReviewers.role == PullRequestReviewers.ROLE_REVIEWER)
4499 4499 return qry.count()
4500 4500
4501 4501 @property
4502 4502 def observers_count(self):
4503 4503 qry = PullRequestReviewers.query()\
4504 4504 .filter(PullRequestReviewers.pull_request_id == self.pull_request_id)\
4505 4505 .filter(PullRequestReviewers.role == PullRequestReviewers.ROLE_OBSERVER)
4506 4506 return qry.count()
4507 4507
4508 4508 def observers(self):
4509 4509 qry = PullRequestReviewers.query()\
4510 4510 .filter(PullRequestReviewers.pull_request_id == self.pull_request_id)\
4511 4511 .filter(PullRequestReviewers.role == PullRequestReviewers.ROLE_OBSERVER)\
4512 4512 .all()
4513 4513
4514 4514 for entry in qry:
4515 4515 yield entry, entry.user
4516 4516
4517 4517 @property
4518 4518 def workspace_id(self):
4519 4519 from rhodecode.model.pull_request import PullRequestModel
4520 4520 return PullRequestModel()._workspace_id(self)
4521 4521
4522 4522 def get_shadow_repo(self):
4523 4523 workspace_id = self.workspace_id
4524 4524 shadow_repository_path = self.target_repo.get_shadow_repository_path(workspace_id)
4525 4525 if os.path.isdir(shadow_repository_path):
4526 4526 vcs_obj = self.target_repo.scm_instance()
4527 4527 return vcs_obj.get_shadow_instance(shadow_repository_path)
4528 4528
4529 4529 @property
4530 4530 def versions_count(self):
4531 4531 """
4532 4532 return number of versions this PR have, e.g a PR that once been
4533 4533 updated will have 2 versions
4534 4534 """
4535 4535 return self.versions.count() + 1
4536 4536
4537 4537 @property
4538 4538 def pull_request_last_version(self):
4539 4539 return self.versions_count
4540 4540
4541 4541
4542 4542 class PullRequestVersion(Base, _PullRequestBase):
4543 4543 __tablename__ = 'pull_request_versions'
4544 4544 __table_args__ = (
4545 4545 base_table_args,
4546 4546 )
4547 4547
4548 4548 pull_request_version_id = Column(
4549 4549 'pull_request_version_id', Integer(), nullable=False, primary_key=True)
4550 4550 pull_request_id = Column(
4551 4551 'pull_request_id', Integer(),
4552 4552 ForeignKey('pull_requests.pull_request_id'), nullable=False)
4553 4553 pull_request = relationship('PullRequest')
4554 4554
4555 4555 def __repr__(self):
4556 4556 if self.pull_request_version_id:
4557 4557 return '<DB:PullRequestVersion #%s>' % self.pull_request_version_id
4558 4558 else:
4559 4559 return '<DB:PullRequestVersion at %#x>' % id(self)
4560 4560
4561 4561 @property
4562 4562 def reviewers(self):
4563 4563 return self.pull_request.reviewers
4564 4564 @property
4565 4565 def reviewers(self):
4566 4566 return self.pull_request.reviewers
4567 4567
4568 4568 @property
4569 4569 def versions(self):
4570 4570 return self.pull_request.versions
4571 4571
4572 4572 def is_closed(self):
4573 4573 # calculate from original
4574 4574 return self.pull_request.status == self.STATUS_CLOSED
4575 4575
4576 4576 def is_state_changing(self):
4577 4577 return self.pull_request.pull_request_state != PullRequest.STATE_CREATED
4578 4578
4579 4579 def calculated_review_status(self):
4580 4580 return self.pull_request.calculated_review_status()
4581 4581
4582 4582 def reviewers_statuses(self):
4583 4583 return self.pull_request.reviewers_statuses()
4584 4584
4585 4585 def observers(self):
4586 4586 return self.pull_request.observers()
4587 4587
4588 4588
4589 4589 class PullRequestReviewers(Base, BaseModel):
4590 4590 __tablename__ = 'pull_request_reviewers'
4591 4591 __table_args__ = (
4592 4592 base_table_args,
4593 4593 )
4594 4594 ROLE_REVIEWER = u'reviewer'
4595 4595 ROLE_OBSERVER = u'observer'
4596 4596 ROLES = [ROLE_REVIEWER, ROLE_OBSERVER]
4597 4597
4598 4598 @hybrid_property
4599 4599 def reasons(self):
4600 4600 if not self._reasons:
4601 4601 return []
4602 4602 return self._reasons
4603 4603
4604 4604 @reasons.setter
4605 4605 def reasons(self, val):
4606 4606 val = val or []
4607 4607 if any(not isinstance(x, compat.string_types) for x in val):
4608 4608 raise Exception('invalid reasons type, must be list of strings')
4609 4609 self._reasons = val
4610 4610
4611 4611 pull_requests_reviewers_id = Column(
4612 4612 'pull_requests_reviewers_id', Integer(), nullable=False,
4613 4613 primary_key=True)
4614 4614 pull_request_id = Column(
4615 4615 "pull_request_id", Integer(),
4616 4616 ForeignKey('pull_requests.pull_request_id'), nullable=False)
4617 4617 user_id = Column(
4618 4618 "user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
4619 4619 _reasons = Column(
4620 4620 'reason', MutationList.as_mutable(
4621 4621 JsonType('list', dialect_map=dict(mysql=UnicodeText(16384)))))
4622 4622
4623 4623 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
4624 4624 role = Column('role', Unicode(255), nullable=True, default=ROLE_REVIEWER)
4625 4625
4626 4626 user = relationship('User')
4627 4627 pull_request = relationship('PullRequest')
4628 4628
4629 4629 rule_data = Column(
4630 4630 'rule_data_json',
4631 4631 JsonType(dialect_map=dict(mysql=UnicodeText(16384))))
4632 4632
4633 4633 def rule_user_group_data(self):
4634 4634 """
4635 4635 Returns the voting user group rule data for this reviewer
4636 4636 """
4637 4637
4638 4638 if self.rule_data and 'vote_rule' in self.rule_data:
4639 4639 user_group_data = {}
4640 4640 if 'rule_user_group_entry_id' in self.rule_data:
4641 4641 # means a group with voting rules !
4642 4642 user_group_data['id'] = self.rule_data['rule_user_group_entry_id']
4643 4643 user_group_data['name'] = self.rule_data['rule_name']
4644 4644 user_group_data['vote_rule'] = self.rule_data['vote_rule']
4645 4645
4646 4646 return user_group_data
4647 4647
4648 4648 @classmethod
4649 4649 def get_pull_request_reviewers(cls, pull_request_id, role=None):
4650 4650 qry = PullRequestReviewers.query()\
4651 4651 .filter(PullRequestReviewers.pull_request_id == pull_request_id)
4652 4652 if role:
4653 4653 qry = qry.filter(PullRequestReviewers.role == role)
4654 4654
4655 4655 return qry.all()
4656 4656
4657 4657 def __unicode__(self):
4658 4658 return u"<%s('id:%s')>" % (self.__class__.__name__,
4659 4659 self.pull_requests_reviewers_id)
4660 4660
4661 4661
4662 4662 class Notification(Base, BaseModel):
4663 4663 __tablename__ = 'notifications'
4664 4664 __table_args__ = (
4665 4665 Index('notification_type_idx', 'type'),
4666 4666 base_table_args,
4667 4667 )
4668 4668
4669 4669 TYPE_CHANGESET_COMMENT = u'cs_comment'
4670 4670 TYPE_MESSAGE = u'message'
4671 4671 TYPE_MENTION = u'mention'
4672 4672 TYPE_REGISTRATION = u'registration'
4673 4673 TYPE_PULL_REQUEST = u'pull_request'
4674 4674 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
4675 4675 TYPE_PULL_REQUEST_UPDATE = u'pull_request_update'
4676 4676
4677 4677 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
4678 4678 subject = Column('subject', Unicode(512), nullable=True)
4679 4679 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
4680 4680 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
4681 4681 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4682 4682 type_ = Column('type', Unicode(255))
4683 4683
4684 4684 created_by_user = relationship('User')
4685 4685 notifications_to_users = relationship('UserNotification', lazy='joined',
4686 4686 cascade="all, delete-orphan")
4687 4687
4688 4688 @property
4689 4689 def recipients(self):
4690 4690 return [x.user for x in UserNotification.query()\
4691 4691 .filter(UserNotification.notification == self)\
4692 4692 .order_by(UserNotification.user_id.asc()).all()]
4693 4693
4694 4694 @classmethod
4695 4695 def create(cls, created_by, subject, body, recipients, type_=None):
4696 4696 if type_ is None:
4697 4697 type_ = Notification.TYPE_MESSAGE
4698 4698
4699 4699 notification = cls()
4700 4700 notification.created_by_user = created_by
4701 4701 notification.subject = subject
4702 4702 notification.body = body
4703 4703 notification.type_ = type_
4704 4704 notification.created_on = datetime.datetime.now()
4705 4705
4706 4706 # For each recipient link the created notification to his account
4707 4707 for u in recipients:
4708 4708 assoc = UserNotification()
4709 4709 assoc.user_id = u.user_id
4710 4710 assoc.notification = notification
4711 4711
4712 4712 # if created_by is inside recipients mark his notification
4713 4713 # as read
4714 4714 if u.user_id == created_by.user_id:
4715 4715 assoc.read = True
4716 4716 Session().add(assoc)
4717 4717
4718 4718 Session().add(notification)
4719 4719
4720 4720 return notification
4721 4721
4722 4722
4723 4723 class UserNotification(Base, BaseModel):
4724 4724 __tablename__ = 'user_to_notification'
4725 4725 __table_args__ = (
4726 4726 UniqueConstraint('user_id', 'notification_id'),
4727 4727 base_table_args
4728 4728 )
4729 4729
4730 4730 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
4731 4731 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
4732 4732 read = Column('read', Boolean, default=False)
4733 4733 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
4734 4734
4735 4735 user = relationship('User', lazy="joined")
4736 4736 notification = relationship('Notification', lazy="joined",
4737 4737 order_by=lambda: Notification.created_on.desc(),)
4738 4738
4739 4739 def mark_as_read(self):
4740 4740 self.read = True
4741 4741 Session().add(self)
4742 4742
4743 4743
4744 4744 class UserNotice(Base, BaseModel):
4745 4745 __tablename__ = 'user_notices'
4746 4746 __table_args__ = (
4747 4747 base_table_args
4748 4748 )
4749 4749
4750 4750 NOTIFICATION_TYPE_MESSAGE = 'message'
4751 4751 NOTIFICATION_TYPE_NOTICE = 'notice'
4752 4752
4753 4753 NOTIFICATION_LEVEL_INFO = 'info'
4754 4754 NOTIFICATION_LEVEL_WARNING = 'warning'
4755 4755 NOTIFICATION_LEVEL_ERROR = 'error'
4756 4756
4757 4757 user_notice_id = Column('gist_id', Integer(), primary_key=True)
4758 4758
4759 4759 notice_subject = Column('notice_subject', Unicode(512), nullable=True)
4760 4760 notice_body = Column('notice_body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
4761 4761
4762 4762 notice_read = Column('notice_read', Boolean, default=False)
4763 4763
4764 4764 notification_level = Column('notification_level', String(1024), default=NOTIFICATION_LEVEL_INFO)
4765 4765 notification_type = Column('notification_type', String(1024), default=NOTIFICATION_TYPE_NOTICE)
4766 4766
4767 4767 notice_created_by = Column('notice_created_by', Integer(), ForeignKey('users.user_id'), nullable=True)
4768 4768 notice_created_on = Column('notice_created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4769 4769
4770 4770 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'))
4771 4771 user = relationship('User', lazy="joined", primaryjoin='User.user_id==UserNotice.user_id')
4772 4772
4773 4773 @classmethod
4774 4774 def create_for_user(cls, user, subject, body, notice_level=NOTIFICATION_LEVEL_INFO, allow_duplicate=False):
4775 4775
4776 4776 if notice_level not in [cls.NOTIFICATION_LEVEL_ERROR,
4777 4777 cls.NOTIFICATION_LEVEL_WARNING,
4778 4778 cls.NOTIFICATION_LEVEL_INFO]:
4779 4779 return
4780 4780
4781 4781 from rhodecode.model.user import UserModel
4782 4782 user = UserModel().get_user(user)
4783 4783
4784 4784 new_notice = UserNotice()
4785 4785 if not allow_duplicate:
4786 4786 existing_msg = UserNotice().query() \
4787 4787 .filter(UserNotice.user == user) \
4788 4788 .filter(UserNotice.notice_body == body) \
4789 4789 .filter(UserNotice.notice_read == false()) \
4790 4790 .scalar()
4791 4791 if existing_msg:
4792 4792 log.warning('Ignoring duplicate notice for user %s', user)
4793 4793 return
4794 4794
4795 4795 new_notice.user = user
4796 4796 new_notice.notice_subject = subject
4797 4797 new_notice.notice_body = body
4798 4798 new_notice.notification_level = notice_level
4799 4799 Session().add(new_notice)
4800 4800 Session().commit()
4801 4801
4802 4802
4803 4803 class Gist(Base, BaseModel):
4804 4804 __tablename__ = 'gists'
4805 4805 __table_args__ = (
4806 4806 Index('g_gist_access_id_idx', 'gist_access_id'),
4807 4807 Index('g_created_on_idx', 'created_on'),
4808 4808 base_table_args
4809 4809 )
4810 4810
4811 4811 GIST_PUBLIC = u'public'
4812 4812 GIST_PRIVATE = u'private'
4813 4813 DEFAULT_FILENAME = u'gistfile1.txt'
4814 4814
4815 4815 ACL_LEVEL_PUBLIC = u'acl_public'
4816 4816 ACL_LEVEL_PRIVATE = u'acl_private'
4817 4817
4818 4818 gist_id = Column('gist_id', Integer(), primary_key=True)
4819 4819 gist_access_id = Column('gist_access_id', Unicode(250))
4820 4820 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
4821 4821 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
4822 4822 gist_expires = Column('gist_expires', Float(53), nullable=False)
4823 4823 gist_type = Column('gist_type', Unicode(128), nullable=False)
4824 4824 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4825 4825 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
4826 4826 acl_level = Column('acl_level', Unicode(128), nullable=True)
4827 4827
4828 4828 owner = relationship('User')
4829 4829
4830 4830 def __repr__(self):
4831 4831 return '<Gist:[%s]%s>' % (self.gist_type, self.gist_access_id)
4832 4832
4833 4833 @hybrid_property
4834 4834 def description_safe(self):
4835 4835 from rhodecode.lib import helpers as h
4836 4836 return h.escape(self.gist_description)
4837 4837
4838 4838 @classmethod
4839 4839 def get_or_404(cls, id_):
4840 4840 from pyramid.httpexceptions import HTTPNotFound
4841 4841
4842 4842 res = cls.query().filter(cls.gist_access_id == id_).scalar()
4843 4843 if not res:
4844 4844 log.debug('WARN: No DB entry with id %s', id_)
4845 4845 raise HTTPNotFound()
4846 4846 return res
4847 4847
4848 4848 @classmethod
4849 4849 def get_by_access_id(cls, gist_access_id):
4850 4850 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
4851 4851
4852 4852 def gist_url(self):
4853 4853 from rhodecode.model.gist import GistModel
4854 4854 return GistModel().get_url(self)
4855 4855
4856 4856 @classmethod
4857 4857 def base_path(cls):
4858 4858 """
4859 4859 Returns base path when all gists are stored
4860 4860
4861 4861 :param cls:
4862 4862 """
4863 4863 from rhodecode.model.gist import GIST_STORE_LOC
4864 4864 q = Session().query(RhodeCodeUi)\
4865 4865 .filter(RhodeCodeUi.ui_key == URL_SEP)
4866 4866 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
4867 4867 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
4868 4868
4869 4869 def get_api_data(self):
4870 4870 """
4871 4871 Common function for generating gist related data for API
4872 4872 """
4873 4873 gist = self
4874 4874 data = {
4875 4875 'gist_id': gist.gist_id,
4876 4876 'type': gist.gist_type,
4877 4877 'access_id': gist.gist_access_id,
4878 4878 'description': gist.gist_description,
4879 4879 'url': gist.gist_url(),
4880 4880 'expires': gist.gist_expires,
4881 4881 'created_on': gist.created_on,
4882 4882 'modified_at': gist.modified_at,
4883 4883 'content': None,
4884 4884 'acl_level': gist.acl_level,
4885 4885 }
4886 4886 return data
4887 4887
4888 4888 def __json__(self):
4889 4889 data = dict(
4890 4890 )
4891 4891 data.update(self.get_api_data())
4892 4892 return data
4893 4893 # SCM functions
4894 4894
4895 4895 def scm_instance(self, **kwargs):
4896 4896 """
4897 4897 Get an instance of VCS Repository
4898 4898
4899 4899 :param kwargs:
4900 4900 """
4901 4901 from rhodecode.model.gist import GistModel
4902 4902 full_repo_path = os.path.join(self.base_path(), self.gist_access_id)
4903 4903 return get_vcs_instance(
4904 4904 repo_path=safe_str(full_repo_path), create=False,
4905 4905 _vcs_alias=GistModel.vcs_backend)
4906 4906
4907 4907
4908 4908 class ExternalIdentity(Base, BaseModel):
4909 4909 __tablename__ = 'external_identities'
4910 4910 __table_args__ = (
4911 4911 Index('local_user_id_idx', 'local_user_id'),
4912 4912 Index('external_id_idx', 'external_id'),
4913 4913 base_table_args
4914 4914 )
4915 4915
4916 4916 external_id = Column('external_id', Unicode(255), default=u'', primary_key=True)
4917 4917 external_username = Column('external_username', Unicode(1024), default=u'')
4918 4918 local_user_id = Column('local_user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
4919 4919 provider_name = Column('provider_name', Unicode(255), default=u'', primary_key=True)
4920 4920 access_token = Column('access_token', String(1024), default=u'')
4921 4921 alt_token = Column('alt_token', String(1024), default=u'')
4922 4922 token_secret = Column('token_secret', String(1024), default=u'')
4923 4923
4924 4924 @classmethod
4925 4925 def by_external_id_and_provider(cls, external_id, provider_name, local_user_id=None):
4926 4926 """
4927 4927 Returns ExternalIdentity instance based on search params
4928 4928
4929 4929 :param external_id:
4930 4930 :param provider_name:
4931 4931 :return: ExternalIdentity
4932 4932 """
4933 4933 query = cls.query()
4934 4934 query = query.filter(cls.external_id == external_id)
4935 4935 query = query.filter(cls.provider_name == provider_name)
4936 4936 if local_user_id:
4937 4937 query = query.filter(cls.local_user_id == local_user_id)
4938 4938 return query.first()
4939 4939
4940 4940 @classmethod
4941 4941 def user_by_external_id_and_provider(cls, external_id, provider_name):
4942 4942 """
4943 4943 Returns User instance based on search params
4944 4944
4945 4945 :param external_id:
4946 4946 :param provider_name:
4947 4947 :return: User
4948 4948 """
4949 4949 query = User.query()
4950 4950 query = query.filter(cls.external_id == external_id)
4951 4951 query = query.filter(cls.provider_name == provider_name)
4952 4952 query = query.filter(User.user_id == cls.local_user_id)
4953 4953 return query.first()
4954 4954
4955 4955 @classmethod
4956 4956 def by_local_user_id(cls, local_user_id):
4957 4957 """
4958 4958 Returns all tokens for user
4959 4959
4960 4960 :param local_user_id:
4961 4961 :return: ExternalIdentity
4962 4962 """
4963 4963 query = cls.query()
4964 4964 query = query.filter(cls.local_user_id == local_user_id)
4965 4965 return query
4966 4966
4967 4967 @classmethod
4968 4968 def load_provider_plugin(cls, plugin_id):
4969 4969 from rhodecode.authentication.base import loadplugin
4970 4970 _plugin_id = 'egg:rhodecode-enterprise-ee#{}'.format(plugin_id)
4971 4971 auth_plugin = loadplugin(_plugin_id)
4972 4972 return auth_plugin
4973 4973
4974 4974
4975 4975 class Integration(Base, BaseModel):
4976 4976 __tablename__ = 'integrations'
4977 4977 __table_args__ = (
4978 4978 base_table_args
4979 4979 )
4980 4980
4981 4981 integration_id = Column('integration_id', Integer(), primary_key=True)
4982 4982 integration_type = Column('integration_type', String(255))
4983 4983 enabled = Column('enabled', Boolean(), nullable=False)
4984 4984 name = Column('name', String(255), nullable=False)
4985 4985 child_repos_only = Column('child_repos_only', Boolean(), nullable=False,
4986 4986 default=False)
4987 4987
4988 4988 settings = Column(
4989 4989 'settings_json', MutationObj.as_mutable(
4990 4990 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
4991 4991 repo_id = Column(
4992 4992 'repo_id', Integer(), ForeignKey('repositories.repo_id'),
4993 4993 nullable=True, unique=None, default=None)
4994 4994 repo = relationship('Repository', lazy='joined')
4995 4995
4996 4996 repo_group_id = Column(
4997 4997 'repo_group_id', Integer(), ForeignKey('groups.group_id'),
4998 4998 nullable=True, unique=None, default=None)
4999 4999 repo_group = relationship('RepoGroup', lazy='joined')
5000 5000
5001 5001 @property
5002 5002 def scope(self):
5003 5003 if self.repo:
5004 5004 return repr(self.repo)
5005 5005 if self.repo_group:
5006 5006 if self.child_repos_only:
5007 5007 return repr(self.repo_group) + ' (child repos only)'
5008 5008 else:
5009 5009 return repr(self.repo_group) + ' (recursive)'
5010 5010 if self.child_repos_only:
5011 5011 return 'root_repos'
5012 5012 return 'global'
5013 5013
5014 5014 def __repr__(self):
5015 5015 return '<Integration(%r, %r)>' % (self.integration_type, self.scope)
5016 5016
5017 5017
5018 5018 class RepoReviewRuleUser(Base, BaseModel):
5019 5019 __tablename__ = 'repo_review_rules_users'
5020 5020 __table_args__ = (
5021 5021 base_table_args
5022 5022 )
5023 5023 ROLE_REVIEWER = u'reviewer'
5024 5024 ROLE_OBSERVER = u'observer'
5025 5025 ROLES = [ROLE_REVIEWER, ROLE_OBSERVER]
5026 5026
5027 5027 repo_review_rule_user_id = Column('repo_review_rule_user_id', Integer(), primary_key=True)
5028 5028 repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id'))
5029 5029 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False)
5030 5030 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
5031 5031 role = Column('role', Unicode(255), nullable=True, default=ROLE_REVIEWER)
5032 5032 user = relationship('User')
5033 5033
5034 5034 def rule_data(self):
5035 5035 return {
5036 5036 'mandatory': self.mandatory,
5037 5037 'role': self.role,
5038 5038 }
5039 5039
5040 5040
5041 5041 class RepoReviewRuleUserGroup(Base, BaseModel):
5042 5042 __tablename__ = 'repo_review_rules_users_groups'
5043 5043 __table_args__ = (
5044 5044 base_table_args
5045 5045 )
5046 5046
5047 5047 VOTE_RULE_ALL = -1
5048 5048 ROLE_REVIEWER = u'reviewer'
5049 5049 ROLE_OBSERVER = u'observer'
5050 5050 ROLES = [ROLE_REVIEWER, ROLE_OBSERVER]
5051 5051
5052 5052 repo_review_rule_users_group_id = Column('repo_review_rule_users_group_id', Integer(), primary_key=True)
5053 5053 repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id'))
5054 5054 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False)
5055 5055 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
5056 5056 role = Column('role', Unicode(255), nullable=True, default=ROLE_REVIEWER)
5057 5057 vote_rule = Column("vote_rule", Integer(), nullable=True, default=VOTE_RULE_ALL)
5058 5058 users_group = relationship('UserGroup')
5059 5059
5060 5060 def rule_data(self):
5061 5061 return {
5062 5062 'mandatory': self.mandatory,
5063 5063 'role': self.role,
5064 5064 'vote_rule': self.vote_rule
5065 5065 }
5066 5066
5067 5067 @property
5068 5068 def vote_rule_label(self):
5069 5069 if not self.vote_rule or self.vote_rule == self.VOTE_RULE_ALL:
5070 5070 return 'all must vote'
5071 5071 else:
5072 5072 return 'min. vote {}'.format(self.vote_rule)
5073 5073
5074 5074
5075 5075 class RepoReviewRule(Base, BaseModel):
5076 5076 __tablename__ = 'repo_review_rules'
5077 5077 __table_args__ = (
5078 5078 base_table_args
5079 5079 )
5080 5080
5081 5081 repo_review_rule_id = Column(
5082 5082 'repo_review_rule_id', Integer(), primary_key=True)
5083 5083 repo_id = Column(
5084 5084 "repo_id", Integer(), ForeignKey('repositories.repo_id'))
5085 5085 repo = relationship('Repository', backref='review_rules')
5086 5086
5087 5087 review_rule_name = Column('review_rule_name', String(255))
5088 5088 _branch_pattern = Column("branch_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
5089 5089 _target_branch_pattern = Column("target_branch_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
5090 5090 _file_pattern = Column("file_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
5091 5091
5092 5092 use_authors_for_review = Column("use_authors_for_review", Boolean(), nullable=False, default=False)
5093 5093
5094 5094 # Legacy fields, just for backward compat
5095 5095 _forbid_author_to_review = Column("forbid_author_to_review", Boolean(), nullable=False, default=False)
5096 5096 _forbid_commit_author_to_review = Column("forbid_commit_author_to_review", Boolean(), nullable=False, default=False)
5097 5097
5098 5098 pr_author = Column("pr_author", UnicodeText().with_variant(UnicodeText(255), 'mysql'), nullable=True)
5099 5099 commit_author = Column("commit_author", UnicodeText().with_variant(UnicodeText(255), 'mysql'), nullable=True)
5100 5100
5101 5101 forbid_adding_reviewers = Column("forbid_adding_reviewers", Boolean(), nullable=False, default=False)
5102 5102
5103 5103 rule_users = relationship('RepoReviewRuleUser')
5104 5104 rule_user_groups = relationship('RepoReviewRuleUserGroup')
5105 5105
5106 5106 def _validate_pattern(self, value):
5107 5107 re.compile('^' + glob2re(value) + '$')
5108 5108
5109 5109 @hybrid_property
5110 5110 def source_branch_pattern(self):
5111 5111 return self._branch_pattern or '*'
5112 5112
5113 5113 @source_branch_pattern.setter
5114 5114 def source_branch_pattern(self, value):
5115 5115 self._validate_pattern(value)
5116 5116 self._branch_pattern = value or '*'
5117 5117
5118 5118 @hybrid_property
5119 5119 def target_branch_pattern(self):
5120 5120 return self._target_branch_pattern or '*'
5121 5121
5122 5122 @target_branch_pattern.setter
5123 5123 def target_branch_pattern(self, value):
5124 5124 self._validate_pattern(value)
5125 5125 self._target_branch_pattern = value or '*'
5126 5126
5127 5127 @hybrid_property
5128 5128 def file_pattern(self):
5129 5129 return self._file_pattern or '*'
5130 5130
5131 5131 @file_pattern.setter
5132 5132 def file_pattern(self, value):
5133 5133 self._validate_pattern(value)
5134 5134 self._file_pattern = value or '*'
5135 5135
5136 5136 @hybrid_property
5137 5137 def forbid_pr_author_to_review(self):
5138 5138 return self.pr_author == 'forbid_pr_author'
5139 5139
5140 5140 @hybrid_property
5141 5141 def include_pr_author_to_review(self):
5142 5142 return self.pr_author == 'include_pr_author'
5143 5143
5144 5144 @hybrid_property
5145 5145 def forbid_commit_author_to_review(self):
5146 5146 return self.commit_author == 'forbid_commit_author'
5147 5147
5148 5148 @hybrid_property
5149 5149 def include_commit_author_to_review(self):
5150 5150 return self.commit_author == 'include_commit_author'
5151 5151
5152 5152 def matches(self, source_branch, target_branch, files_changed):
5153 5153 """
5154 5154 Check if this review rule matches a branch/files in a pull request
5155 5155
5156 5156 :param source_branch: source branch name for the commit
5157 5157 :param target_branch: target branch name for the commit
5158 5158 :param files_changed: list of file paths changed in the pull request
5159 5159 """
5160 5160
5161 5161 source_branch = source_branch or ''
5162 5162 target_branch = target_branch or ''
5163 5163 files_changed = files_changed or []
5164 5164
5165 5165 branch_matches = True
5166 5166 if source_branch or target_branch:
5167 5167 if self.source_branch_pattern == '*':
5168 5168 source_branch_match = True
5169 5169 else:
5170 5170 if self.source_branch_pattern.startswith('re:'):
5171 5171 source_pattern = self.source_branch_pattern[3:]
5172 5172 else:
5173 5173 source_pattern = '^' + glob2re(self.source_branch_pattern) + '$'
5174 5174 source_branch_regex = re.compile(source_pattern)
5175 5175 source_branch_match = bool(source_branch_regex.search(source_branch))
5176 5176 if self.target_branch_pattern == '*':
5177 5177 target_branch_match = True
5178 5178 else:
5179 5179 if self.target_branch_pattern.startswith('re:'):
5180 5180 target_pattern = self.target_branch_pattern[3:]
5181 5181 else:
5182 5182 target_pattern = '^' + glob2re(self.target_branch_pattern) + '$'
5183 5183 target_branch_regex = re.compile(target_pattern)
5184 5184 target_branch_match = bool(target_branch_regex.search(target_branch))
5185 5185
5186 5186 branch_matches = source_branch_match and target_branch_match
5187 5187
5188 5188 files_matches = True
5189 5189 if self.file_pattern != '*':
5190 5190 files_matches = False
5191 5191 if self.file_pattern.startswith('re:'):
5192 5192 file_pattern = self.file_pattern[3:]
5193 5193 else:
5194 5194 file_pattern = glob2re(self.file_pattern)
5195 5195 file_regex = re.compile(file_pattern)
5196 5196 for file_data in files_changed:
5197 5197 filename = file_data.get('filename')
5198 5198
5199 5199 if file_regex.search(filename):
5200 5200 files_matches = True
5201 5201 break
5202 5202
5203 5203 return branch_matches and files_matches
5204 5204
5205 5205 @property
5206 5206 def review_users(self):
5207 5207 """ Returns the users which this rule applies to """
5208 5208
5209 5209 users = collections.OrderedDict()
5210 5210
5211 5211 for rule_user in self.rule_users:
5212 5212 if rule_user.user.active:
5213 5213 if rule_user.user not in users:
5214 5214 users[rule_user.user.username] = {
5215 5215 'user': rule_user.user,
5216 5216 'source': 'user',
5217 5217 'source_data': {},
5218 5218 'data': rule_user.rule_data()
5219 5219 }
5220 5220
5221 5221 for rule_user_group in self.rule_user_groups:
5222 5222 source_data = {
5223 5223 'user_group_id': rule_user_group.users_group.users_group_id,
5224 5224 'name': rule_user_group.users_group.users_group_name,
5225 5225 'members': len(rule_user_group.users_group.members)
5226 5226 }
5227 5227 for member in rule_user_group.users_group.members:
5228 5228 if member.user.active:
5229 5229 key = member.user.username
5230 5230 if key in users:
5231 5231 # skip this member as we have him already
5232 5232 # this prevents from override the "first" matched
5233 5233 # users with duplicates in multiple groups
5234 5234 continue
5235 5235
5236 5236 users[key] = {
5237 5237 'user': member.user,
5238 5238 'source': 'user_group',
5239 5239 'source_data': source_data,
5240 5240 'data': rule_user_group.rule_data()
5241 5241 }
5242 5242
5243 5243 return users
5244 5244
5245 5245 def user_group_vote_rule(self, user_id):
5246 5246
5247 5247 rules = []
5248 5248 if not self.rule_user_groups:
5249 5249 return rules
5250 5250
5251 5251 for user_group in self.rule_user_groups:
5252 5252 user_group_members = [x.user_id for x in user_group.users_group.members]
5253 5253 if user_id in user_group_members:
5254 5254 rules.append(user_group)
5255 5255 return rules
5256 5256
5257 5257 def __repr__(self):
5258 5258 return '<RepoReviewerRule(id=%r, repo=%r)>' % (
5259 5259 self.repo_review_rule_id, self.repo)
5260 5260
5261 5261
5262 5262 class ScheduleEntry(Base, BaseModel):
5263 5263 __tablename__ = 'schedule_entries'
5264 5264 __table_args__ = (
5265 5265 UniqueConstraint('schedule_name', name='s_schedule_name_idx'),
5266 5266 UniqueConstraint('task_uid', name='s_task_uid_idx'),
5267 5267 base_table_args,
5268 5268 )
5269 5269
5270 5270 schedule_types = ['crontab', 'timedelta', 'integer']
5271 5271 schedule_entry_id = Column('schedule_entry_id', Integer(), primary_key=True)
5272 5272
5273 5273 schedule_name = Column("schedule_name", String(255), nullable=False, unique=None, default=None)
5274 5274 schedule_description = Column("schedule_description", String(10000), nullable=True, unique=None, default=None)
5275 5275 schedule_enabled = Column("schedule_enabled", Boolean(), nullable=False, unique=None, default=True)
5276 5276
5277 5277 _schedule_type = Column("schedule_type", String(255), nullable=False, unique=None, default=None)
5278 5278 schedule_definition = Column('schedule_definition_json', MutationObj.as_mutable(JsonType(default=lambda: "", dialect_map=dict(mysql=LONGTEXT()))))
5279 5279
5280 5280 schedule_last_run = Column('schedule_last_run', DateTime(timezone=False), nullable=True, unique=None, default=None)
5281 5281 schedule_total_run_count = Column('schedule_total_run_count', Integer(), nullable=True, unique=None, default=0)
5282 5282
5283 5283 # task
5284 5284 task_uid = Column("task_uid", String(255), nullable=False, unique=None, default=None)
5285 5285 task_dot_notation = Column("task_dot_notation", String(4096), nullable=False, unique=None, default=None)
5286 5286 task_args = Column('task_args_json', MutationObj.as_mutable(JsonType(default=list, dialect_map=dict(mysql=LONGTEXT()))))
5287 5287 task_kwargs = Column('task_kwargs_json', MutationObj.as_mutable(JsonType(default=dict, dialect_map=dict(mysql=LONGTEXT()))))
5288 5288
5289 5289 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
5290 5290 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=None)
5291 5291
5292 5292 @hybrid_property
5293 5293 def schedule_type(self):
5294 5294 return self._schedule_type
5295 5295
5296 5296 @schedule_type.setter
5297 5297 def schedule_type(self, val):
5298 5298 if val not in self.schedule_types:
5299 5299 raise ValueError('Value must be on of `{}` and got `{}`'.format(
5300 5300 val, self.schedule_type))
5301 5301
5302 5302 self._schedule_type = val
5303 5303
5304 5304 @classmethod
5305 5305 def get_uid(cls, obj):
5306 5306 args = obj.task_args
5307 5307 kwargs = obj.task_kwargs
5308 5308 if isinstance(args, JsonRaw):
5309 5309 try:
5310 5310 args = json.loads(args)
5311 5311 except ValueError:
5312 5312 args = tuple()
5313 5313
5314 5314 if isinstance(kwargs, JsonRaw):
5315 5315 try:
5316 5316 kwargs = json.loads(kwargs)
5317 5317 except ValueError:
5318 5318 kwargs = dict()
5319 5319
5320 5320 dot_notation = obj.task_dot_notation
5321 5321 val = '.'.join(map(safe_str, [
5322 5322 sorted(dot_notation), args, sorted(kwargs.items())]))
5323 5323 return hashlib.sha1(val).hexdigest()
5324 5324
5325 5325 @classmethod
5326 5326 def get_by_schedule_name(cls, schedule_name):
5327 5327 return cls.query().filter(cls.schedule_name == schedule_name).scalar()
5328 5328
5329 5329 @classmethod
5330 5330 def get_by_schedule_id(cls, schedule_id):
5331 5331 return cls.query().filter(cls.schedule_entry_id == schedule_id).scalar()
5332 5332
5333 5333 @property
5334 5334 def task(self):
5335 5335 return self.task_dot_notation
5336 5336
5337 5337 @property
5338 5338 def schedule(self):
5339 5339 from rhodecode.lib.celerylib.utils import raw_2_schedule
5340 5340 schedule = raw_2_schedule(self.schedule_definition, self.schedule_type)
5341 5341 return schedule
5342 5342
5343 5343 @property
5344 5344 def args(self):
5345 5345 try:
5346 5346 return list(self.task_args or [])
5347 5347 except ValueError:
5348 5348 return list()
5349 5349
5350 5350 @property
5351 5351 def kwargs(self):
5352 5352 try:
5353 5353 return dict(self.task_kwargs or {})
5354 5354 except ValueError:
5355 5355 return dict()
5356 5356
5357 5357 def _as_raw(self, val, indent=None):
5358 5358 if hasattr(val, 'de_coerce'):
5359 5359 val = val.de_coerce()
5360 5360 if val:
5361 5361 val = json.dumps(val, indent=indent, sort_keys=True)
5362 5362
5363 5363 return val
5364 5364
5365 5365 @property
5366 5366 def schedule_definition_raw(self):
5367 5367 return self._as_raw(self.schedule_definition)
5368 5368
5369 5369 def args_raw(self, indent=None):
5370 5370 return self._as_raw(self.task_args, indent)
5371 5371
5372 5372 def kwargs_raw(self, indent=None):
5373 5373 return self._as_raw(self.task_kwargs, indent)
5374 5374
5375 5375 def __repr__(self):
5376 5376 return '<DB:ScheduleEntry({}:{})>'.format(
5377 5377 self.schedule_entry_id, self.schedule_name)
5378 5378
5379 5379
5380 5380 @event.listens_for(ScheduleEntry, 'before_update')
5381 5381 def update_task_uid(mapper, connection, target):
5382 5382 target.task_uid = ScheduleEntry.get_uid(target)
5383 5383
5384 5384
5385 5385 @event.listens_for(ScheduleEntry, 'before_insert')
5386 5386 def set_task_uid(mapper, connection, target):
5387 5387 target.task_uid = ScheduleEntry.get_uid(target)
5388 5388
5389 5389
5390 5390 class _BaseBranchPerms(BaseModel):
5391 5391 @classmethod
5392 5392 def compute_hash(cls, value):
5393 5393 return sha1_safe(value)
5394 5394
5395 5395 @hybrid_property
5396 5396 def branch_pattern(self):
5397 5397 return self._branch_pattern or '*'
5398 5398
5399 5399 @hybrid_property
5400 5400 def branch_hash(self):
5401 5401 return self._branch_hash
5402 5402
5403 5403 def _validate_glob(self, value):
5404 5404 re.compile('^' + glob2re(value) + '$')
5405 5405
5406 5406 @branch_pattern.setter
5407 5407 def branch_pattern(self, value):
5408 5408 self._validate_glob(value)
5409 5409 self._branch_pattern = value or '*'
5410 5410 # set the Hash when setting the branch pattern
5411 5411 self._branch_hash = self.compute_hash(self._branch_pattern)
5412 5412
5413 5413 def matches(self, branch):
5414 5414 """
5415 5415 Check if this the branch matches entry
5416 5416
5417 5417 :param branch: branch name for the commit
5418 5418 """
5419 5419
5420 5420 branch = branch or ''
5421 5421
5422 5422 branch_matches = True
5423 5423 if branch:
5424 5424 branch_regex = re.compile('^' + glob2re(self.branch_pattern) + '$')
5425 5425 branch_matches = bool(branch_regex.search(branch))
5426 5426
5427 5427 return branch_matches
5428 5428
5429 5429
5430 5430 class UserToRepoBranchPermission(Base, _BaseBranchPerms):
5431 5431 __tablename__ = 'user_to_repo_branch_permissions'
5432 5432 __table_args__ = (
5433 5433 base_table_args
5434 5434 )
5435 5435
5436 5436 branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True)
5437 5437
5438 5438 repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
5439 5439 repo = relationship('Repository', backref='user_branch_perms')
5440 5440
5441 5441 permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
5442 5442 permission = relationship('Permission')
5443 5443
5444 5444 rule_to_perm_id = Column('rule_to_perm_id', Integer(), ForeignKey('repo_to_perm.repo_to_perm_id'), nullable=False, unique=None, default=None)
5445 5445 user_repo_to_perm = relationship('UserRepoToPerm')
5446 5446
5447 5447 rule_order = Column('rule_order', Integer(), nullable=False)
5448 5448 _branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob
5449 5449 _branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql'))
5450 5450
5451 5451 def __unicode__(self):
5452 5452 return u'<UserBranchPermission(%s => %r)>' % (
5453 5453 self.user_repo_to_perm, self.branch_pattern)
5454 5454
5455 5455
5456 5456 class UserGroupToRepoBranchPermission(Base, _BaseBranchPerms):
5457 5457 __tablename__ = 'user_group_to_repo_branch_permissions'
5458 5458 __table_args__ = (
5459 5459 base_table_args
5460 5460 )
5461 5461
5462 5462 branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True)
5463 5463
5464 5464 repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
5465 5465 repo = relationship('Repository', backref='user_group_branch_perms')
5466 5466
5467 5467 permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
5468 5468 permission = relationship('Permission')
5469 5469
5470 5470 rule_to_perm_id = Column('rule_to_perm_id', Integer(), ForeignKey('users_group_repo_to_perm.users_group_to_perm_id'), nullable=False, unique=None, default=None)
5471 5471 user_group_repo_to_perm = relationship('UserGroupRepoToPerm')
5472 5472
5473 5473 rule_order = Column('rule_order', Integer(), nullable=False)
5474 5474 _branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob
5475 5475 _branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql'))
5476 5476
5477 5477 def __unicode__(self):
5478 5478 return u'<UserBranchPermission(%s => %r)>' % (
5479 5479 self.user_group_repo_to_perm, self.branch_pattern)
5480 5480
5481 5481
5482 5482 class UserBookmark(Base, BaseModel):
5483 5483 __tablename__ = 'user_bookmarks'
5484 5484 __table_args__ = (
5485 5485 UniqueConstraint('user_id', 'bookmark_repo_id'),
5486 5486 UniqueConstraint('user_id', 'bookmark_repo_group_id'),
5487 5487 UniqueConstraint('user_id', 'bookmark_position'),
5488 5488 base_table_args
5489 5489 )
5490 5490
5491 5491 user_bookmark_id = Column("user_bookmark_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
5492 5492 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
5493 5493 position = Column("bookmark_position", Integer(), nullable=False)
5494 5494 title = Column("bookmark_title", String(255), nullable=True, unique=None, default=None)
5495 5495 redirect_url = Column("bookmark_redirect_url", String(10240), nullable=True, unique=None, default=None)
5496 5496 created_on = Column("created_on", DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
5497 5497
5498 5498 bookmark_repo_id = Column("bookmark_repo_id", Integer(), ForeignKey("repositories.repo_id"), nullable=True, unique=None, default=None)
5499 5499 bookmark_repo_group_id = Column("bookmark_repo_group_id", Integer(), ForeignKey("groups.group_id"), nullable=True, unique=None, default=None)
5500 5500
5501 5501 user = relationship("User")
5502 5502
5503 5503 repository = relationship("Repository")
5504 5504 repository_group = relationship("RepoGroup")
5505 5505
5506 5506 @classmethod
5507 5507 def get_by_position_for_user(cls, position, user_id):
5508 5508 return cls.query() \
5509 5509 .filter(UserBookmark.user_id == user_id) \
5510 5510 .filter(UserBookmark.position == position).scalar()
5511 5511
5512 5512 @classmethod
5513 5513 def get_bookmarks_for_user(cls, user_id, cache=True):
5514 5514 bookmarks = cls.query() \
5515 5515 .filter(UserBookmark.user_id == user_id) \
5516 5516 .options(joinedload(UserBookmark.repository)) \
5517 5517 .options(joinedload(UserBookmark.repository_group)) \
5518 5518 .order_by(UserBookmark.position.asc())
5519 5519
5520 5520 if cache:
5521 5521 bookmarks = bookmarks.options(
5522 5522 FromCache("sql_cache_short", "get_user_{}_bookmarks".format(user_id))
5523 5523 )
5524 5524
5525 5525 return bookmarks.all()
5526 5526
5527 5527 def __unicode__(self):
5528 5528 return u'<UserBookmark(%s @ %r)>' % (self.position, self.redirect_url)
5529 5529
5530 5530
5531 5531 class FileStore(Base, BaseModel):
5532 5532 __tablename__ = 'file_store'
5533 5533 __table_args__ = (
5534 5534 base_table_args
5535 5535 )
5536 5536
5537 5537 file_store_id = Column('file_store_id', Integer(), primary_key=True)
5538 5538 file_uid = Column('file_uid', String(1024), nullable=False)
5539 5539 file_display_name = Column('file_display_name', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), nullable=True)
5540 5540 file_description = Column('file_description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
5541 5541 file_org_name = Column('file_org_name', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=False)
5542 5542
5543 5543 # sha256 hash
5544 5544 file_hash = Column('file_hash', String(512), nullable=False)
5545 5545 file_size = Column('file_size', BigInteger(), nullable=False)
5546 5546
5547 5547 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
5548 5548 accessed_on = Column('accessed_on', DateTime(timezone=False), nullable=True)
5549 5549 accessed_count = Column('accessed_count', Integer(), default=0)
5550 5550
5551 5551 enabled = Column('enabled', Boolean(), nullable=False, default=True)
5552 5552
5553 5553 # if repo/repo_group reference is set, check for permissions
5554 5554 check_acl = Column('check_acl', Boolean(), nullable=False, default=True)
5555 5555
5556 5556 # hidden defines an attachment that should be hidden from showing in artifact listing
5557 5557 hidden = Column('hidden', Boolean(), nullable=False, default=False)
5558 5558
5559 5559 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
5560 5560 upload_user = relationship('User', lazy='joined', primaryjoin='User.user_id==FileStore.user_id')
5561 5561
5562 5562 file_metadata = relationship('FileStoreMetadata', lazy='joined')
5563 5563
5564 5564 # scope limited to user, which requester have access to
5565 5565 scope_user_id = Column(
5566 5566 'scope_user_id', Integer(), ForeignKey('users.user_id'),
5567 5567 nullable=True, unique=None, default=None)
5568 5568 user = relationship('User', lazy='joined', primaryjoin='User.user_id==FileStore.scope_user_id')
5569 5569
5570 5570 # scope limited to user group, which requester have access to
5571 5571 scope_user_group_id = Column(
5572 5572 'scope_user_group_id', Integer(), ForeignKey('users_groups.users_group_id'),
5573 5573 nullable=True, unique=None, default=None)
5574 5574 user_group = relationship('UserGroup', lazy='joined')
5575 5575
5576 5576 # scope limited to repo, which requester have access to
5577 5577 scope_repo_id = Column(
5578 5578 'scope_repo_id', Integer(), ForeignKey('repositories.repo_id'),
5579 5579 nullable=True, unique=None, default=None)
5580 5580 repo = relationship('Repository', lazy='joined')
5581 5581
5582 5582 # scope limited to repo group, which requester have access to
5583 5583 scope_repo_group_id = Column(
5584 5584 'scope_repo_group_id', Integer(), ForeignKey('groups.group_id'),
5585 5585 nullable=True, unique=None, default=None)
5586 5586 repo_group = relationship('RepoGroup', lazy='joined')
5587 5587
5588 5588 @classmethod
5589 5589 def get_by_store_uid(cls, file_store_uid, safe=False):
5590 5590 if safe:
5591 5591 return FileStore.query().filter(FileStore.file_uid == file_store_uid).first()
5592 5592 else:
5593 5593 return FileStore.query().filter(FileStore.file_uid == file_store_uid).scalar()
5594 5594
5595 5595 @classmethod
5596 5596 def create(cls, file_uid, filename, file_hash, file_size, file_display_name='',
5597 5597 file_description='', enabled=True, hidden=False, check_acl=True,
5598 5598 user_id=None, scope_user_id=None, scope_repo_id=None, scope_repo_group_id=None):
5599 5599
5600 5600 store_entry = FileStore()
5601 5601 store_entry.file_uid = file_uid
5602 5602 store_entry.file_display_name = file_display_name
5603 5603 store_entry.file_org_name = filename
5604 5604 store_entry.file_size = file_size
5605 5605 store_entry.file_hash = file_hash
5606 5606 store_entry.file_description = file_description
5607 5607
5608 5608 store_entry.check_acl = check_acl
5609 5609 store_entry.enabled = enabled
5610 5610 store_entry.hidden = hidden
5611 5611
5612 5612 store_entry.user_id = user_id
5613 5613 store_entry.scope_user_id = scope_user_id
5614 5614 store_entry.scope_repo_id = scope_repo_id
5615 5615 store_entry.scope_repo_group_id = scope_repo_group_id
5616 5616
5617 5617 return store_entry
5618 5618
5619 5619 @classmethod
5620 5620 def store_metadata(cls, file_store_id, args, commit=True):
5621 5621 file_store = FileStore.get(file_store_id)
5622 5622 if file_store is None:
5623 5623 return
5624 5624
5625 5625 for section, key, value, value_type in args:
5626 5626 has_key = FileStoreMetadata().query() \
5627 5627 .filter(FileStoreMetadata.file_store_id == file_store.file_store_id) \
5628 5628 .filter(FileStoreMetadata.file_store_meta_section == section) \
5629 5629 .filter(FileStoreMetadata.file_store_meta_key == key) \
5630 5630 .scalar()
5631 5631 if has_key:
5632 5632 msg = 'key `{}` already defined under section `{}` for this file.'\
5633 5633 .format(key, section)
5634 5634 raise ArtifactMetadataDuplicate(msg, err_section=section, err_key=key)
5635 5635
5636 5636 # NOTE(marcink): raises ArtifactMetadataBadValueType
5637 5637 FileStoreMetadata.valid_value_type(value_type)
5638 5638
5639 5639 meta_entry = FileStoreMetadata()
5640 5640 meta_entry.file_store = file_store
5641 5641 meta_entry.file_store_meta_section = section
5642 5642 meta_entry.file_store_meta_key = key
5643 5643 meta_entry.file_store_meta_value_type = value_type
5644 5644 meta_entry.file_store_meta_value = value
5645 5645
5646 5646 Session().add(meta_entry)
5647 5647
5648 5648 try:
5649 5649 if commit:
5650 5650 Session().commit()
5651 5651 except IntegrityError:
5652 5652 Session().rollback()
5653 5653 raise ArtifactMetadataDuplicate('Duplicate section/key found for this file.')
5654 5654
5655 5655 @classmethod
5656 5656 def bump_access_counter(cls, file_uid, commit=True):
5657 5657 FileStore().query()\
5658 5658 .filter(FileStore.file_uid == file_uid)\
5659 5659 .update({FileStore.accessed_count: (FileStore.accessed_count + 1),
5660 5660 FileStore.accessed_on: datetime.datetime.now()})
5661 5661 if commit:
5662 5662 Session().commit()
5663 5663
5664 5664 def __json__(self):
5665 5665 data = {
5666 5666 'filename': self.file_display_name,
5667 5667 'filename_org': self.file_org_name,
5668 5668 'file_uid': self.file_uid,
5669 5669 'description': self.file_description,
5670 5670 'hidden': self.hidden,
5671 5671 'size': self.file_size,
5672 5672 'created_on': self.created_on,
5673 5673 'uploaded_by': self.upload_user.get_api_data(details='basic'),
5674 5674 'downloaded_times': self.accessed_count,
5675 5675 'sha256': self.file_hash,
5676 5676 'metadata': self.file_metadata,
5677 5677 }
5678 5678
5679 5679 return data
5680 5680
5681 5681 def __repr__(self):
5682 5682 return '<FileStore({})>'.format(self.file_store_id)
5683 5683
5684 5684
5685 5685 class FileStoreMetadata(Base, BaseModel):
5686 5686 __tablename__ = 'file_store_metadata'
5687 5687 __table_args__ = (
5688 5688 UniqueConstraint('file_store_id', 'file_store_meta_section_hash', 'file_store_meta_key_hash'),
5689 5689 Index('file_store_meta_section_idx', 'file_store_meta_section', mysql_length=255),
5690 5690 Index('file_store_meta_key_idx', 'file_store_meta_key', mysql_length=255),
5691 5691 base_table_args
5692 5692 )
5693 5693 SETTINGS_TYPES = {
5694 5694 'str': safe_str,
5695 5695 'int': safe_int,
5696 5696 'unicode': safe_unicode,
5697 5697 'bool': str2bool,
5698 5698 'list': functools.partial(aslist, sep=',')
5699 5699 }
5700 5700
5701 5701 file_store_meta_id = Column(
5702 5702 "file_store_meta_id", Integer(), nullable=False, unique=True, default=None,
5703 5703 primary_key=True)
5704 5704 _file_store_meta_section = Column(
5705 5705 "file_store_meta_section", UnicodeText().with_variant(UnicodeText(1024), 'mysql'),
5706 5706 nullable=True, unique=None, default=None)
5707 5707 _file_store_meta_section_hash = Column(
5708 5708 "file_store_meta_section_hash", String(255),
5709 5709 nullable=True, unique=None, default=None)
5710 5710 _file_store_meta_key = Column(
5711 5711 "file_store_meta_key", UnicodeText().with_variant(UnicodeText(1024), 'mysql'),
5712 5712 nullable=True, unique=None, default=None)
5713 5713 _file_store_meta_key_hash = Column(
5714 5714 "file_store_meta_key_hash", String(255), nullable=True, unique=None, default=None)
5715 5715 _file_store_meta_value = Column(
5716 5716 "file_store_meta_value", UnicodeText().with_variant(UnicodeText(20480), 'mysql'),
5717 5717 nullable=True, unique=None, default=None)
5718 5718 _file_store_meta_value_type = Column(
5719 5719 "file_store_meta_value_type", String(255), nullable=True, unique=None,
5720 5720 default='unicode')
5721 5721
5722 5722 file_store_id = Column(
5723 5723 'file_store_id', Integer(), ForeignKey('file_store.file_store_id'),
5724 5724 nullable=True, unique=None, default=None)
5725 5725
5726 5726 file_store = relationship('FileStore', lazy='joined')
5727 5727
5728 5728 @classmethod
5729 5729 def valid_value_type(cls, value):
5730 5730 if value.split('.')[0] not in cls.SETTINGS_TYPES:
5731 5731 raise ArtifactMetadataBadValueType(
5732 5732 'value_type must be one of %s got %s' % (cls.SETTINGS_TYPES.keys(), value))
5733 5733
5734 5734 @hybrid_property
5735 5735 def file_store_meta_section(self):
5736 5736 return self._file_store_meta_section
5737 5737
5738 5738 @file_store_meta_section.setter
5739 5739 def file_store_meta_section(self, value):
5740 5740 self._file_store_meta_section = value
5741 5741 self._file_store_meta_section_hash = _hash_key(value)
5742 5742
5743 5743 @hybrid_property
5744 5744 def file_store_meta_key(self):
5745 5745 return self._file_store_meta_key
5746 5746
5747 5747 @file_store_meta_key.setter
5748 5748 def file_store_meta_key(self, value):
5749 5749 self._file_store_meta_key = value
5750 5750 self._file_store_meta_key_hash = _hash_key(value)
5751 5751
5752 5752 @hybrid_property
5753 5753 def file_store_meta_value(self):
5754 5754 val = self._file_store_meta_value
5755 5755
5756 5756 if self._file_store_meta_value_type:
5757 5757 # e.g unicode.encrypted == unicode
5758 5758 _type = self._file_store_meta_value_type.split('.')[0]
5759 5759 # decode the encrypted value if it's encrypted field type
5760 5760 if '.encrypted' in self._file_store_meta_value_type:
5761 5761 cipher = EncryptedTextValue()
5762 5762 val = safe_unicode(cipher.process_result_value(val, None))
5763 5763 # do final type conversion
5764 5764 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
5765 5765 val = converter(val)
5766 5766
5767 5767 return val
5768 5768
5769 5769 @file_store_meta_value.setter
5770 5770 def file_store_meta_value(self, val):
5771 5771 val = safe_unicode(val)
5772 5772 # encode the encrypted value
5773 5773 if '.encrypted' in self.file_store_meta_value_type:
5774 5774 cipher = EncryptedTextValue()
5775 5775 val = safe_unicode(cipher.process_bind_param(val, None))
5776 5776 self._file_store_meta_value = val
5777 5777
5778 5778 @hybrid_property
5779 5779 def file_store_meta_value_type(self):
5780 5780 return self._file_store_meta_value_type
5781 5781
5782 5782 @file_store_meta_value_type.setter
5783 5783 def file_store_meta_value_type(self, val):
5784 5784 # e.g unicode.encrypted
5785 5785 self.valid_value_type(val)
5786 5786 self._file_store_meta_value_type = val
5787 5787
5788 5788 def __json__(self):
5789 5789 data = {
5790 5790 'artifact': self.file_store.file_uid,
5791 5791 'section': self.file_store_meta_section,
5792 5792 'key': self.file_store_meta_key,
5793 5793 'value': self.file_store_meta_value,
5794 5794 }
5795 5795
5796 5796 return data
5797 5797
5798 5798 def __repr__(self):
5799 5799 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.file_store_meta_section,
5800 5800 self.file_store_meta_key, self.file_store_meta_value)
5801 5801
5802 5802
5803 5803 class DbMigrateVersion(Base, BaseModel):
5804 5804 __tablename__ = 'db_migrate_version'
5805 5805 __table_args__ = (
5806 5806 base_table_args,
5807 5807 )
5808 5808
5809 5809 repository_id = Column('repository_id', String(250), primary_key=True)
5810 5810 repository_path = Column('repository_path', Text)
5811 5811 version = Column('version', Integer)
5812 5812
5813 5813 @classmethod
5814 5814 def set_version(cls, version):
5815 5815 """
5816 5816 Helper for forcing a different version, usually for debugging purposes via ishell.
5817 5817 """
5818 5818 ver = DbMigrateVersion.query().first()
5819 5819 ver.version = version
5820 5820 Session().commit()
5821 5821
5822 5822
5823 5823 class DbSession(Base, BaseModel):
5824 5824 __tablename__ = 'db_session'
5825 5825 __table_args__ = (
5826 5826 base_table_args,
5827 5827 )
5828 5828
5829 5829 def __repr__(self):
5830 5830 return '<DB:DbSession({})>'.format(self.id)
5831 5831
5832 5832 id = Column('id', Integer())
5833 5833 namespace = Column('namespace', String(255), primary_key=True)
5834 5834 accessed = Column('accessed', DateTime, nullable=False)
5835 5835 created = Column('created', DateTime, nullable=False)
5836 5836 data = Column('data', PickleType, nullable=False)
@@ -1,2249 +1,2372 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 """
23 23 pull request model for RhodeCode
24 24 """
25 25
26 26
27 27 import json
28 28 import logging
29 29 import os
30 30
31 31 import datetime
32 32 import urllib
33 33 import collections
34 34
35 35 from pyramid import compat
36 36 from pyramid.threadlocal import get_current_request
37 37
38 38 from rhodecode.lib.vcs.nodes import FileNode
39 39 from rhodecode.translation import lazy_ugettext
40 40 from rhodecode.lib import helpers as h, hooks_utils, diffs
41 41 from rhodecode.lib import audit_logger
42 42 from rhodecode.lib.compat import OrderedDict
43 43 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
44 44 from rhodecode.lib.markup_renderer import (
45 45 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
46 46 from rhodecode.lib.utils2 import (
47 47 safe_unicode, safe_str, md5_safe, AttributeDict, safe_int,
48 48 get_current_rhodecode_user)
49 49 from rhodecode.lib.vcs.backends.base import (
50 50 Reference, MergeResponse, MergeFailureReason, UpdateFailureReason,
51 51 TargetRefMissing, SourceRefMissing)
52 52 from rhodecode.lib.vcs.conf import settings as vcs_settings
53 53 from rhodecode.lib.vcs.exceptions import (
54 54 CommitDoesNotExistError, EmptyRepositoryError)
55 55 from rhodecode.model import BaseModel
56 56 from rhodecode.model.changeset_status import ChangesetStatusModel
57 57 from rhodecode.model.comment import CommentsModel
58 58 from rhodecode.model.db import (
59 or_, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
59 aliased, null, lazyload, and_, or_, func, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
60 60 PullRequestVersion, ChangesetComment, Repository, RepoReviewRule, User)
61 61 from rhodecode.model.meta import Session
62 62 from rhodecode.model.notification import NotificationModel, \
63 63 EmailNotificationModel
64 64 from rhodecode.model.scm import ScmModel
65 65 from rhodecode.model.settings import VcsSettingsModel
66 66
67 67
68 68 log = logging.getLogger(__name__)
69 69
70 70
71 71 # Data structure to hold the response data when updating commits during a pull
72 72 # request update.
73 73 class UpdateResponse(object):
74 74
75 75 def __init__(self, executed, reason, new, old, common_ancestor_id,
76 76 commit_changes, source_changed, target_changed):
77 77
78 78 self.executed = executed
79 79 self.reason = reason
80 80 self.new = new
81 81 self.old = old
82 82 self.common_ancestor_id = common_ancestor_id
83 83 self.changes = commit_changes
84 84 self.source_changed = source_changed
85 85 self.target_changed = target_changed
86 86
87 87
88 88 def get_diff_info(
89 89 source_repo, source_ref, target_repo, target_ref, get_authors=False,
90 90 get_commit_authors=True):
91 91 """
92 92 Calculates detailed diff information for usage in preview of creation of a pull-request.
93 93 This is also used for default reviewers logic
94 94 """
95 95
96 96 source_scm = source_repo.scm_instance()
97 97 target_scm = target_repo.scm_instance()
98 98
99 99 ancestor_id = target_scm.get_common_ancestor(target_ref, source_ref, source_scm)
100 100 if not ancestor_id:
101 101 raise ValueError(
102 102 'cannot calculate diff info without a common ancestor. '
103 103 'Make sure both repositories are related, and have a common forking commit.')
104 104
105 105 # case here is that want a simple diff without incoming commits,
106 106 # previewing what will be merged based only on commits in the source.
107 107 log.debug('Using ancestor %s as source_ref instead of %s',
108 108 ancestor_id, source_ref)
109 109
110 110 # source of changes now is the common ancestor
111 111 source_commit = source_scm.get_commit(commit_id=ancestor_id)
112 112 # target commit becomes the source ref as it is the last commit
113 113 # for diff generation this logic gives proper diff
114 114 target_commit = source_scm.get_commit(commit_id=source_ref)
115 115
116 116 vcs_diff = \
117 117 source_scm.get_diff(commit1=source_commit, commit2=target_commit,
118 118 ignore_whitespace=False, context=3)
119 119
120 120 diff_processor = diffs.DiffProcessor(
121 121 vcs_diff, format='newdiff', diff_limit=None,
122 122 file_limit=None, show_full_diff=True)
123 123
124 124 _parsed = diff_processor.prepare()
125 125
126 126 all_files = []
127 127 all_files_changes = []
128 128 changed_lines = {}
129 129 stats = [0, 0]
130 130 for f in _parsed:
131 131 all_files.append(f['filename'])
132 132 all_files_changes.append({
133 133 'filename': f['filename'],
134 134 'stats': f['stats']
135 135 })
136 136 stats[0] += f['stats']['added']
137 137 stats[1] += f['stats']['deleted']
138 138
139 139 changed_lines[f['filename']] = []
140 140 if len(f['chunks']) < 2:
141 141 continue
142 142 # first line is "context" information
143 143 for chunks in f['chunks'][1:]:
144 144 for chunk in chunks['lines']:
145 145 if chunk['action'] not in ('del', 'mod'):
146 146 continue
147 147 changed_lines[f['filename']].append(chunk['old_lineno'])
148 148
149 149 commit_authors = []
150 150 user_counts = {}
151 151 email_counts = {}
152 152 author_counts = {}
153 153 _commit_cache = {}
154 154
155 155 commits = []
156 156 if get_commit_authors:
157 157 log.debug('Obtaining commit authors from set of commits')
158 158 _compare_data = target_scm.compare(
159 159 target_ref, source_ref, source_scm, merge=True,
160 160 pre_load=["author", "date", "message"]
161 161 )
162 162
163 163 for commit in _compare_data:
164 164 # NOTE(marcink): we serialize here, so we don't produce more vcsserver calls on data returned
165 165 # at this function which is later called via JSON serialization
166 166 serialized_commit = dict(
167 167 author=commit.author,
168 168 date=commit.date,
169 169 message=commit.message,
170 170 commit_id=commit.raw_id,
171 171 raw_id=commit.raw_id
172 172 )
173 173 commits.append(serialized_commit)
174 174 user = User.get_from_cs_author(serialized_commit['author'])
175 175 if user and user not in commit_authors:
176 176 commit_authors.append(user)
177 177
178 178 # lines
179 179 if get_authors:
180 180 log.debug('Calculating authors of changed files')
181 181 target_commit = source_repo.get_commit(ancestor_id)
182 182
183 183 for fname, lines in changed_lines.items():
184 184
185 185 try:
186 186 node = target_commit.get_node(fname, pre_load=["is_binary"])
187 187 except Exception:
188 188 log.exception("Failed to load node with path %s", fname)
189 189 continue
190 190
191 191 if not isinstance(node, FileNode):
192 192 continue
193 193
194 194 # NOTE(marcink): for binary node we don't do annotation, just use last author
195 195 if node.is_binary:
196 196 author = node.last_commit.author
197 197 email = node.last_commit.author_email
198 198
199 199 user = User.get_from_cs_author(author)
200 200 if user:
201 201 user_counts[user.user_id] = user_counts.get(user.user_id, 0) + 1
202 202 author_counts[author] = author_counts.get(author, 0) + 1
203 203 email_counts[email] = email_counts.get(email, 0) + 1
204 204
205 205 continue
206 206
207 207 for annotation in node.annotate:
208 208 line_no, commit_id, get_commit_func, line_text = annotation
209 209 if line_no in lines:
210 210 if commit_id not in _commit_cache:
211 211 _commit_cache[commit_id] = get_commit_func()
212 212 commit = _commit_cache[commit_id]
213 213 author = commit.author
214 214 email = commit.author_email
215 215 user = User.get_from_cs_author(author)
216 216 if user:
217 217 user_counts[user.user_id] = user_counts.get(user.user_id, 0) + 1
218 218 author_counts[author] = author_counts.get(author, 0) + 1
219 219 email_counts[email] = email_counts.get(email, 0) + 1
220 220
221 221 log.debug('Default reviewers processing finished')
222 222
223 223 return {
224 224 'commits': commits,
225 225 'files': all_files_changes,
226 226 'stats': stats,
227 227 'ancestor': ancestor_id,
228 228 # original authors of modified files
229 229 'original_authors': {
230 230 'users': user_counts,
231 231 'authors': author_counts,
232 232 'emails': email_counts,
233 233 },
234 234 'commit_authors': commit_authors
235 235 }
236 236
237 237
238 238 class PullRequestModel(BaseModel):
239 239
240 240 cls = PullRequest
241 241
242 242 DIFF_CONTEXT = diffs.DEFAULT_CONTEXT
243 243
244 244 UPDATE_STATUS_MESSAGES = {
245 245 UpdateFailureReason.NONE: lazy_ugettext(
246 246 'Pull request update successful.'),
247 247 UpdateFailureReason.UNKNOWN: lazy_ugettext(
248 248 'Pull request update failed because of an unknown error.'),
249 249 UpdateFailureReason.NO_CHANGE: lazy_ugettext(
250 250 'No update needed because the source and target have not changed.'),
251 251 UpdateFailureReason.WRONG_REF_TYPE: lazy_ugettext(
252 252 'Pull request cannot be updated because the reference type is '
253 253 'not supported for an update. Only Branch, Tag or Bookmark is allowed.'),
254 254 UpdateFailureReason.MISSING_TARGET_REF: lazy_ugettext(
255 255 'This pull request cannot be updated because the target '
256 256 'reference is missing.'),
257 257 UpdateFailureReason.MISSING_SOURCE_REF: lazy_ugettext(
258 258 'This pull request cannot be updated because the source '
259 259 'reference is missing.'),
260 260 }
261 261 REF_TYPES = ['bookmark', 'book', 'tag', 'branch']
262 262 UPDATABLE_REF_TYPES = ['bookmark', 'book', 'branch']
263 263
264 264 def __get_pull_request(self, pull_request):
265 265 return self._get_instance((
266 266 PullRequest, PullRequestVersion), pull_request)
267 267
268 268 def _check_perms(self, perms, pull_request, user, api=False):
269 269 if not api:
270 270 return h.HasRepoPermissionAny(*perms)(
271 271 user=user, repo_name=pull_request.target_repo.repo_name)
272 272 else:
273 273 return h.HasRepoPermissionAnyApi(*perms)(
274 274 user=user, repo_name=pull_request.target_repo.repo_name)
275 275
276 276 def check_user_read(self, pull_request, user, api=False):
277 277 _perms = ('repository.admin', 'repository.write', 'repository.read',)
278 278 return self._check_perms(_perms, pull_request, user, api)
279 279
280 280 def check_user_merge(self, pull_request, user, api=False):
281 281 _perms = ('repository.admin', 'repository.write', 'hg.admin',)
282 282 return self._check_perms(_perms, pull_request, user, api)
283 283
284 284 def check_user_update(self, pull_request, user, api=False):
285 285 owner = user.user_id == pull_request.user_id
286 286 return self.check_user_merge(pull_request, user, api) or owner
287 287
288 288 def check_user_delete(self, pull_request, user):
289 289 owner = user.user_id == pull_request.user_id
290 290 _perms = ('repository.admin',)
291 291 return self._check_perms(_perms, pull_request, user) or owner
292 292
293 293 def is_user_reviewer(self, pull_request, user):
294 294 return user.user_id in [
295 295 x.user_id for x in
296 296 pull_request.get_pull_request_reviewers(PullRequestReviewers.ROLE_REVIEWER)
297 297 if x.user
298 298 ]
299 299
300 300 def check_user_change_status(self, pull_request, user, api=False):
301 301 return self.check_user_update(pull_request, user, api) \
302 302 or self.is_user_reviewer(pull_request, user)
303 303
304 304 def check_user_comment(self, pull_request, user):
305 305 owner = user.user_id == pull_request.user_id
306 306 return self.check_user_read(pull_request, user) or owner
307 307
308 308 def get(self, pull_request):
309 309 return self.__get_pull_request(pull_request)
310 310
311 311 def _prepare_get_all_query(self, repo_name, search_q=None, source=False,
312 312 statuses=None, opened_by=None, order_by=None,
313 313 order_dir='desc', only_created=False):
314 314 repo = None
315 315 if repo_name:
316 316 repo = self._get_repo(repo_name)
317 317
318 318 q = PullRequest.query()
319 319
320 320 if search_q:
321 321 like_expression = u'%{}%'.format(safe_unicode(search_q))
322 q = q.join(User)
322 q = q.join(User, User.user_id == PullRequest.user_id)
323 323 q = q.filter(or_(
324 324 cast(PullRequest.pull_request_id, String).ilike(like_expression),
325 325 User.username.ilike(like_expression),
326 326 PullRequest.title.ilike(like_expression),
327 327 PullRequest.description.ilike(like_expression),
328 328 ))
329 329
330 330 # source or target
331 331 if repo and source:
332 332 q = q.filter(PullRequest.source_repo == repo)
333 333 elif repo:
334 334 q = q.filter(PullRequest.target_repo == repo)
335 335
336 336 # closed,opened
337 337 if statuses:
338 338 q = q.filter(PullRequest.status.in_(statuses))
339 339
340 340 # opened by filter
341 341 if opened_by:
342 342 q = q.filter(PullRequest.user_id.in_(opened_by))
343 343
344 344 # only get those that are in "created" state
345 345 if only_created:
346 346 q = q.filter(PullRequest.pull_request_state == PullRequest.STATE_CREATED)
347 347
348 348 if order_by:
349 349 order_map = {
350 350 'name_raw': PullRequest.pull_request_id,
351 351 'id': PullRequest.pull_request_id,
352 352 'title': PullRequest.title,
353 353 'updated_on_raw': PullRequest.updated_on,
354 354 'target_repo': PullRequest.target_repo_id
355 355 }
356 356 if order_dir == 'asc':
357 357 q = q.order_by(order_map[order_by].asc())
358 358 else:
359 359 q = q.order_by(order_map[order_by].desc())
360 360
361 361 return q
362 362
363 363 def count_all(self, repo_name, search_q=None, source=False, statuses=None,
364 364 opened_by=None):
365 365 """
366 366 Count the number of pull requests for a specific repository.
367 367
368 368 :param repo_name: target or source repo
369 369 :param search_q: filter by text
370 370 :param source: boolean flag to specify if repo_name refers to source
371 371 :param statuses: list of pull request statuses
372 372 :param opened_by: author user of the pull request
373 373 :returns: int number of pull requests
374 374 """
375 375 q = self._prepare_get_all_query(
376 376 repo_name, search_q=search_q, source=source, statuses=statuses,
377 377 opened_by=opened_by)
378 378
379 379 return q.count()
380 380
381 381 def get_all(self, repo_name, search_q=None, source=False, statuses=None,
382 382 opened_by=None, offset=0, length=None, order_by=None, order_dir='desc'):
383 383 """
384 384 Get all pull requests for a specific repository.
385 385
386 386 :param repo_name: target or source repo
387 387 :param search_q: filter by text
388 388 :param source: boolean flag to specify if repo_name refers to source
389 389 :param statuses: list of pull request statuses
390 390 :param opened_by: author user of the pull request
391 391 :param offset: pagination offset
392 392 :param length: length of returned list
393 393 :param order_by: order of the returned list
394 394 :param order_dir: 'asc' or 'desc' ordering direction
395 395 :returns: list of pull requests
396 396 """
397 397 q = self._prepare_get_all_query(
398 398 repo_name, search_q=search_q, source=source, statuses=statuses,
399 399 opened_by=opened_by, order_by=order_by, order_dir=order_dir)
400 400
401 401 if length:
402 402 pull_requests = q.limit(length).offset(offset).all()
403 403 else:
404 404 pull_requests = q.all()
405 405
406 406 return pull_requests
407 407
408 def count_awaiting_review(self, repo_name, search_q=None, source=False, statuses=None,
409 opened_by=None):
408 def count_awaiting_review(self, repo_name, search_q=None, statuses=None):
410 409 """
411 410 Count the number of pull requests for a specific repository that are
412 411 awaiting review.
413 412
414 413 :param repo_name: target or source repo
415 414 :param search_q: filter by text
416 :param source: boolean flag to specify if repo_name refers to source
417 415 :param statuses: list of pull request statuses
418 :param opened_by: author user of the pull request
419 416 :returns: int number of pull requests
420 417 """
421 418 pull_requests = self.get_awaiting_review(
422 repo_name, search_q=search_q, source=source, statuses=statuses, opened_by=opened_by)
419 repo_name, search_q=search_q, statuses=statuses)
423 420
424 421 return len(pull_requests)
425 422
426 def get_awaiting_review(self, repo_name, search_q=None, source=False, statuses=None,
427 opened_by=None, offset=0, length=None,
428 order_by=None, order_dir='desc'):
423 def get_awaiting_review(self, repo_name, search_q=None, statuses=None,
424 offset=0, length=None, order_by=None, order_dir='desc'):
429 425 """
430 426 Get all pull requests for a specific repository that are awaiting
431 427 review.
432 428
433 429 :param repo_name: target or source repo
434 430 :param search_q: filter by text
435 :param source: boolean flag to specify if repo_name refers to source
436 431 :param statuses: list of pull request statuses
437 :param opened_by: author user of the pull request
438 432 :param offset: pagination offset
439 433 :param length: length of returned list
440 434 :param order_by: order of the returned list
441 435 :param order_dir: 'asc' or 'desc' ordering direction
442 436 :returns: list of pull requests
443 437 """
444 438 pull_requests = self.get_all(
445 repo_name, search_q=search_q, source=source, statuses=statuses,
446 opened_by=opened_by, order_by=order_by, order_dir=order_dir)
439 repo_name, search_q=search_q, statuses=statuses,
440 order_by=order_by, order_dir=order_dir)
447 441
448 442 _filtered_pull_requests = []
449 443 for pr in pull_requests:
450 444 status = pr.calculated_review_status()
451 445 if status in [ChangesetStatus.STATUS_NOT_REVIEWED,
452 446 ChangesetStatus.STATUS_UNDER_REVIEW]:
453 447 _filtered_pull_requests.append(pr)
454 448 if length:
455 449 return _filtered_pull_requests[offset:offset+length]
456 450 else:
457 451 return _filtered_pull_requests
458 452
459 def count_awaiting_my_review(self, repo_name, search_q=None, source=False, statuses=None,
460 opened_by=None, user_id=None):
453 def _prepare_awaiting_my_review_review_query(
454 self, repo_name, user_id, search_q=None, statuses=None,
455 order_by=None, order_dir='desc'):
456
457 for_review_statuses = [
458 ChangesetStatus.STATUS_UNDER_REVIEW, ChangesetStatus.STATUS_NOT_REVIEWED
459 ]
460
461 pull_request_alias = aliased(PullRequest)
462 status_alias = aliased(ChangesetStatus)
463 reviewers_alias = aliased(PullRequestReviewers)
464 repo_alias = aliased(Repository)
465
466 last_ver_subq = Session()\
467 .query(func.min(ChangesetStatus.version)) \
468 .filter(ChangesetStatus.pull_request_id == reviewers_alias.pull_request_id)\
469 .filter(ChangesetStatus.user_id == reviewers_alias.user_id) \
470 .subquery()
471
472 q = Session().query(pull_request_alias) \
473 .options(lazyload(pull_request_alias.author)) \
474 .join(reviewers_alias,
475 reviewers_alias.pull_request_id == pull_request_alias.pull_request_id) \
476 .join(repo_alias,
477 repo_alias.repo_id == pull_request_alias.target_repo_id) \
478 .outerjoin(status_alias,
479 and_(status_alias.user_id == reviewers_alias.user_id,
480 status_alias.pull_request_id == reviewers_alias.pull_request_id)) \
481 .filter(or_(status_alias.version == null(),
482 status_alias.version == last_ver_subq)) \
483 .filter(reviewers_alias.user_id == user_id) \
484 .filter(repo_alias.repo_name == repo_name) \
485 .filter(or_(status_alias.status == null(), status_alias.status.in_(for_review_statuses))) \
486 .group_by(pull_request_alias)
487
488 # closed,opened
489 if statuses:
490 q = q.filter(pull_request_alias.status.in_(statuses))
491
492 if search_q:
493 like_expression = u'%{}%'.format(safe_unicode(search_q))
494 q = q.join(User, User.user_id == pull_request_alias.user_id)
495 q = q.filter(or_(
496 cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
497 User.username.ilike(like_expression),
498 pull_request_alias.title.ilike(like_expression),
499 pull_request_alias.description.ilike(like_expression),
500 ))
501
502 if order_by:
503 order_map = {
504 'name_raw': pull_request_alias.pull_request_id,
505 'title': pull_request_alias.title,
506 'updated_on_raw': pull_request_alias.updated_on,
507 'target_repo': pull_request_alias.target_repo_id
508 }
509 if order_dir == 'asc':
510 q = q.order_by(order_map[order_by].asc())
511 else:
512 q = q.order_by(order_map[order_by].desc())
513
514 return q
515
516 def count_awaiting_my_review(self, repo_name, user_id, search_q=None, statuses=None):
461 517 """
462 518 Count the number of pull requests for a specific repository that are
463 519 awaiting review from a specific user.
464 520
465 521 :param repo_name: target or source repo
522 :param user_id: reviewer user of the pull request
466 523 :param search_q: filter by text
467 :param source: boolean flag to specify if repo_name refers to source
468 524 :param statuses: list of pull request statuses
469 :param opened_by: author user of the pull request
470 :param user_id: reviewer user of the pull request
471 525 :returns: int number of pull requests
472 526 """
473 pull_requests = self.get_awaiting_my_review(
474 repo_name, search_q=search_q, source=source, statuses=statuses,
475 opened_by=opened_by, user_id=user_id)
527 q = self._prepare_awaiting_my_review_review_query(
528 repo_name, user_id, search_q=search_q, statuses=statuses)
529 return q.count()
476 530
477 return len(pull_requests)
478
479 def get_awaiting_my_review(self, repo_name, search_q=None, source=False, statuses=None,
480 opened_by=None, user_id=None, offset=0,
481 length=None, order_by=None, order_dir='desc'):
531 def get_awaiting_my_review(self, repo_name, user_id, search_q=None, statuses=None,
532 offset=0, length=None, order_by=None, order_dir='desc'):
482 533 """
483 534 Get all pull requests for a specific repository that are awaiting
484 535 review from a specific user.
485 536
486 537 :param repo_name: target or source repo
538 :param user_id: reviewer user of the pull request
487 539 :param search_q: filter by text
488 :param source: boolean flag to specify if repo_name refers to source
489 540 :param statuses: list of pull request statuses
490 :param opened_by: author user of the pull request
491 :param user_id: reviewer user of the pull request
492 541 :param offset: pagination offset
493 542 :param length: length of returned list
494 543 :param order_by: order of the returned list
495 544 :param order_dir: 'asc' or 'desc' ordering direction
496 545 :returns: list of pull requests
497 546 """
498 pull_requests = self.get_all(
499 repo_name, search_q=search_q, source=source, statuses=statuses,
500 opened_by=opened_by, order_by=order_by, order_dir=order_dir)
501 547
502 _my = PullRequestModel().get_not_reviewed(user_id)
503 my_participation = []
504 for pr in pull_requests:
505 if pr in _my:
506 my_participation.append(pr)
507 _filtered_pull_requests = my_participation
548 q = self._prepare_awaiting_my_review_review_query(
549 repo_name, user_id, search_q=search_q, statuses=statuses,
550 order_by=order_by, order_dir=order_dir)
551
508 552 if length:
509 return _filtered_pull_requests[offset:offset+length]
553 pull_requests = q.limit(length).offset(offset).all()
510 554 else:
511 return _filtered_pull_requests
555 pull_requests = q.all()
556
557 return pull_requests
512 558
513 def get_not_reviewed(self, user_id):
514 return [
515 x.pull_request for x in PullRequestReviewers.query().filter(
516 PullRequestReviewers.user_id == user_id).all()
517 ]
518
519 def _prepare_participating_query(self, user_id=None, statuses=None, query='',
559 def _prepare_im_participating_query(self, user_id=None, statuses=None, query='',
520 560 order_by=None, order_dir='desc'):
561 """
562 return a query of pull-requests user is an creator, or he's added as a reviewer
563 """
521 564 q = PullRequest.query()
522 565 if user_id:
523 566 reviewers_subquery = Session().query(
524 567 PullRequestReviewers.pull_request_id).filter(
525 568 PullRequestReviewers.user_id == user_id).subquery()
526 569 user_filter = or_(
527 570 PullRequest.user_id == user_id,
528 571 PullRequest.pull_request_id.in_(reviewers_subquery)
529 572 )
530 573 q = PullRequest.query().filter(user_filter)
531 574
532 575 # closed,opened
533 576 if statuses:
534 577 q = q.filter(PullRequest.status.in_(statuses))
535 578
536 579 if query:
537 580 like_expression = u'%{}%'.format(safe_unicode(query))
538 q = q.join(User)
581 q = q.join(User, User.user_id == PullRequest.user_id)
539 582 q = q.filter(or_(
540 583 cast(PullRequest.pull_request_id, String).ilike(like_expression),
541 584 User.username.ilike(like_expression),
542 585 PullRequest.title.ilike(like_expression),
543 586 PullRequest.description.ilike(like_expression),
544 587 ))
545 588 if order_by:
546 589 order_map = {
547 590 'name_raw': PullRequest.pull_request_id,
548 591 'title': PullRequest.title,
549 592 'updated_on_raw': PullRequest.updated_on,
550 593 'target_repo': PullRequest.target_repo_id
551 594 }
552 595 if order_dir == 'asc':
553 596 q = q.order_by(order_map[order_by].asc())
554 597 else:
555 598 q = q.order_by(order_map[order_by].desc())
556 599
557 600 return q
558 601
559 602 def count_im_participating_in(self, user_id=None, statuses=None, query=''):
560 q = self._prepare_participating_query(user_id, statuses=statuses, query=query)
603 q = self._prepare_im_participating_query(user_id, statuses=statuses, query=query)
561 604 return q.count()
562 605
563 606 def get_im_participating_in(
564 607 self, user_id=None, statuses=None, query='', offset=0,
565 608 length=None, order_by=None, order_dir='desc'):
566 609 """
567 Get all Pull requests that i'm participating in, or i have opened
610 Get all Pull requests that i'm participating in as a reviewer, or i have opened
568 611 """
569 612
570 q = self._prepare_participating_query(
613 q = self._prepare_im_participating_query(
614 user_id, statuses=statuses, query=query, order_by=order_by,
615 order_dir=order_dir)
616
617 if length:
618 pull_requests = q.limit(length).offset(offset).all()
619 else:
620 pull_requests = q.all()
621
622 return pull_requests
623
624 def _prepare_participating_in_for_review_query(
625 self, user_id, statuses=None, query='', order_by=None, order_dir='desc'):
626
627 for_review_statuses = [
628 ChangesetStatus.STATUS_UNDER_REVIEW, ChangesetStatus.STATUS_NOT_REVIEWED
629 ]
630
631 pull_request_alias = aliased(PullRequest)
632 status_alias = aliased(ChangesetStatus)
633 reviewers_alias = aliased(PullRequestReviewers)
634
635 last_ver_subq = Session()\
636 .query(func.min(ChangesetStatus.version)) \
637 .filter(ChangesetStatus.pull_request_id == reviewers_alias.pull_request_id)\
638 .filter(ChangesetStatus.user_id == reviewers_alias.user_id) \
639 .subquery()
640
641 q = Session().query(pull_request_alias) \
642 .options(lazyload(pull_request_alias.author)) \
643 .join(reviewers_alias,
644 reviewers_alias.pull_request_id == pull_request_alias.pull_request_id) \
645 .outerjoin(status_alias,
646 and_(status_alias.user_id == reviewers_alias.user_id,
647 status_alias.pull_request_id == reviewers_alias.pull_request_id)) \
648 .filter(or_(status_alias.version == null(),
649 status_alias.version == last_ver_subq)) \
650 .filter(reviewers_alias.user_id == user_id) \
651 .filter(or_(status_alias.status == null(), status_alias.status.in_(for_review_statuses))) \
652 .group_by(pull_request_alias)
653
654 # closed,opened
655 if statuses:
656 q = q.filter(pull_request_alias.status.in_(statuses))
657
658 if query:
659 like_expression = u'%{}%'.format(safe_unicode(query))
660 q = q.join(User, User.user_id == pull_request_alias.user_id)
661 q = q.filter(or_(
662 cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
663 User.username.ilike(like_expression),
664 pull_request_alias.title.ilike(like_expression),
665 pull_request_alias.description.ilike(like_expression),
666 ))
667
668 if order_by:
669 order_map = {
670 'name_raw': pull_request_alias.pull_request_id,
671 'title': pull_request_alias.title,
672 'updated_on_raw': pull_request_alias.updated_on,
673 'target_repo': pull_request_alias.target_repo_id
674 }
675 if order_dir == 'asc':
676 q = q.order_by(order_map[order_by].asc())
677 else:
678 q = q.order_by(order_map[order_by].desc())
679
680 return q
681
682 def count_im_participating_in_for_review(self, user_id, statuses=None, query=''):
683 q = self._prepare_participating_in_for_review_query(user_id, statuses=statuses, query=query)
684 return q.count()
685
686 def get_im_participating_in_for_review(
687 self, user_id, statuses=None, query='', offset=0,
688 length=None, order_by=None, order_dir='desc'):
689 """
690 Get all Pull requests that needs user approval or rejection
691 """
692
693 q = self._prepare_participating_in_for_review_query(
571 694 user_id, statuses=statuses, query=query, order_by=order_by,
572 695 order_dir=order_dir)
573 696
574 697 if length:
575 698 pull_requests = q.limit(length).offset(offset).all()
576 699 else:
577 700 pull_requests = q.all()
578 701
579 702 return pull_requests
580 703
581 704 def get_versions(self, pull_request):
582 705 """
583 706 returns version of pull request sorted by ID descending
584 707 """
585 708 return PullRequestVersion.query()\
586 709 .filter(PullRequestVersion.pull_request == pull_request)\
587 710 .order_by(PullRequestVersion.pull_request_version_id.asc())\
588 711 .all()
589 712
590 713 def get_pr_version(self, pull_request_id, version=None):
591 714 at_version = None
592 715
593 716 if version and version == 'latest':
594 717 pull_request_ver = PullRequest.get(pull_request_id)
595 718 pull_request_obj = pull_request_ver
596 719 _org_pull_request_obj = pull_request_obj
597 720 at_version = 'latest'
598 721 elif version:
599 722 pull_request_ver = PullRequestVersion.get_or_404(version)
600 723 pull_request_obj = pull_request_ver
601 724 _org_pull_request_obj = pull_request_ver.pull_request
602 725 at_version = pull_request_ver.pull_request_version_id
603 726 else:
604 727 _org_pull_request_obj = pull_request_obj = PullRequest.get_or_404(
605 728 pull_request_id)
606 729
607 730 pull_request_display_obj = PullRequest.get_pr_display_object(
608 731 pull_request_obj, _org_pull_request_obj)
609 732
610 733 return _org_pull_request_obj, pull_request_obj, \
611 734 pull_request_display_obj, at_version
612 735
613 736 def pr_commits_versions(self, versions):
614 737 """
615 738 Maps the pull-request commits into all known PR versions. This way we can obtain
616 739 each pr version the commit was introduced in.
617 740 """
618 741 commit_versions = collections.defaultdict(list)
619 742 num_versions = [x.pull_request_version_id for x in versions]
620 743 for ver in versions:
621 744 for commit_id in ver.revisions:
622 745 ver_idx = ChangesetComment.get_index_from_version(
623 746 ver.pull_request_version_id, num_versions=num_versions)
624 747 commit_versions[commit_id].append(ver_idx)
625 748 return commit_versions
626 749
627 750 def create(self, created_by, source_repo, source_ref, target_repo,
628 751 target_ref, revisions, reviewers, observers, title, description=None,
629 752 common_ancestor_id=None,
630 753 description_renderer=None,
631 754 reviewer_data=None, translator=None, auth_user=None):
632 755 translator = translator or get_current_request().translate
633 756
634 757 created_by_user = self._get_user(created_by)
635 758 auth_user = auth_user or created_by_user.AuthUser()
636 759 source_repo = self._get_repo(source_repo)
637 760 target_repo = self._get_repo(target_repo)
638 761
639 762 pull_request = PullRequest()
640 763 pull_request.source_repo = source_repo
641 764 pull_request.source_ref = source_ref
642 765 pull_request.target_repo = target_repo
643 766 pull_request.target_ref = target_ref
644 767 pull_request.revisions = revisions
645 768 pull_request.title = title
646 769 pull_request.description = description
647 770 pull_request.description_renderer = description_renderer
648 771 pull_request.author = created_by_user
649 772 pull_request.reviewer_data = reviewer_data
650 773 pull_request.pull_request_state = pull_request.STATE_CREATING
651 774 pull_request.common_ancestor_id = common_ancestor_id
652 775
653 776 Session().add(pull_request)
654 777 Session().flush()
655 778
656 779 reviewer_ids = set()
657 780 # members / reviewers
658 781 for reviewer_object in reviewers:
659 782 user_id, reasons, mandatory, role, rules = reviewer_object
660 783 user = self._get_user(user_id)
661 784
662 785 # skip duplicates
663 786 if user.user_id in reviewer_ids:
664 787 continue
665 788
666 789 reviewer_ids.add(user.user_id)
667 790
668 791 reviewer = PullRequestReviewers()
669 792 reviewer.user = user
670 793 reviewer.pull_request = pull_request
671 794 reviewer.reasons = reasons
672 795 reviewer.mandatory = mandatory
673 796 reviewer.role = role
674 797
675 798 # NOTE(marcink): pick only first rule for now
676 799 rule_id = list(rules)[0] if rules else None
677 800 rule = RepoReviewRule.get(rule_id) if rule_id else None
678 801 if rule:
679 802 review_group = rule.user_group_vote_rule(user_id)
680 803 # we check if this particular reviewer is member of a voting group
681 804 if review_group:
682 805 # NOTE(marcink):
683 806 # can be that user is member of more but we pick the first same,
684 807 # same as default reviewers algo
685 808 review_group = review_group[0]
686 809
687 810 rule_data = {
688 811 'rule_name':
689 812 rule.review_rule_name,
690 813 'rule_user_group_entry_id':
691 814 review_group.repo_review_rule_users_group_id,
692 815 'rule_user_group_name':
693 816 review_group.users_group.users_group_name,
694 817 'rule_user_group_members':
695 818 [x.user.username for x in review_group.users_group.members],
696 819 'rule_user_group_members_id':
697 820 [x.user.user_id for x in review_group.users_group.members],
698 821 }
699 822 # e.g {'vote_rule': -1, 'mandatory': True}
700 823 rule_data.update(review_group.rule_data())
701 824
702 825 reviewer.rule_data = rule_data
703 826
704 827 Session().add(reviewer)
705 828 Session().flush()
706 829
707 830 for observer_object in observers:
708 831 user_id, reasons, mandatory, role, rules = observer_object
709 832 user = self._get_user(user_id)
710 833
711 834 # skip duplicates from reviewers
712 835 if user.user_id in reviewer_ids:
713 836 continue
714 837
715 838 #reviewer_ids.add(user.user_id)
716 839
717 840 observer = PullRequestReviewers()
718 841 observer.user = user
719 842 observer.pull_request = pull_request
720 843 observer.reasons = reasons
721 844 observer.mandatory = mandatory
722 845 observer.role = role
723 846
724 847 # NOTE(marcink): pick only first rule for now
725 848 rule_id = list(rules)[0] if rules else None
726 849 rule = RepoReviewRule.get(rule_id) if rule_id else None
727 850 if rule:
728 851 # TODO(marcink): do we need this for observers ??
729 852 pass
730 853
731 854 Session().add(observer)
732 855 Session().flush()
733 856
734 857 # Set approval status to "Under Review" for all commits which are
735 858 # part of this pull request.
736 859 ChangesetStatusModel().set_status(
737 860 repo=target_repo,
738 861 status=ChangesetStatus.STATUS_UNDER_REVIEW,
739 862 user=created_by_user,
740 863 pull_request=pull_request
741 864 )
742 865 # we commit early at this point. This has to do with a fact
743 866 # that before queries do some row-locking. And because of that
744 867 # we need to commit and finish transaction before below validate call
745 868 # that for large repos could be long resulting in long row locks
746 869 Session().commit()
747 870
748 871 # prepare workspace, and run initial merge simulation. Set state during that
749 872 # operation
750 873 pull_request = PullRequest.get(pull_request.pull_request_id)
751 874
752 875 # set as merging, for merge simulation, and if finished to created so we mark
753 876 # simulation is working fine
754 877 with pull_request.set_state(PullRequest.STATE_MERGING,
755 878 final_state=PullRequest.STATE_CREATED) as state_obj:
756 879 MergeCheck.validate(
757 880 pull_request, auth_user=auth_user, translator=translator)
758 881
759 882 self.notify_reviewers(pull_request, reviewer_ids, created_by_user)
760 883 self.trigger_pull_request_hook(pull_request, created_by_user, 'create')
761 884
762 885 creation_data = pull_request.get_api_data(with_merge_state=False)
763 886 self._log_audit_action(
764 887 'repo.pull_request.create', {'data': creation_data},
765 888 auth_user, pull_request)
766 889
767 890 return pull_request
768 891
769 892 def trigger_pull_request_hook(self, pull_request, user, action, data=None):
770 893 pull_request = self.__get_pull_request(pull_request)
771 894 target_scm = pull_request.target_repo.scm_instance()
772 895 if action == 'create':
773 896 trigger_hook = hooks_utils.trigger_create_pull_request_hook
774 897 elif action == 'merge':
775 898 trigger_hook = hooks_utils.trigger_merge_pull_request_hook
776 899 elif action == 'close':
777 900 trigger_hook = hooks_utils.trigger_close_pull_request_hook
778 901 elif action == 'review_status_change':
779 902 trigger_hook = hooks_utils.trigger_review_pull_request_hook
780 903 elif action == 'update':
781 904 trigger_hook = hooks_utils.trigger_update_pull_request_hook
782 905 elif action == 'comment':
783 906 trigger_hook = hooks_utils.trigger_comment_pull_request_hook
784 907 elif action == 'comment_edit':
785 908 trigger_hook = hooks_utils.trigger_comment_pull_request_edit_hook
786 909 else:
787 910 return
788 911
789 912 log.debug('Handling pull_request %s trigger_pull_request_hook with action %s and hook: %s',
790 913 pull_request, action, trigger_hook)
791 914 trigger_hook(
792 915 username=user.username,
793 916 repo_name=pull_request.target_repo.repo_name,
794 917 repo_type=target_scm.alias,
795 918 pull_request=pull_request,
796 919 data=data)
797 920
798 921 def _get_commit_ids(self, pull_request):
799 922 """
800 923 Return the commit ids of the merged pull request.
801 924
802 925 This method is not dealing correctly yet with the lack of autoupdates
803 926 nor with the implicit target updates.
804 927 For example: if a commit in the source repo is already in the target it
805 928 will be reported anyways.
806 929 """
807 930 merge_rev = pull_request.merge_rev
808 931 if merge_rev is None:
809 932 raise ValueError('This pull request was not merged yet')
810 933
811 934 commit_ids = list(pull_request.revisions)
812 935 if merge_rev not in commit_ids:
813 936 commit_ids.append(merge_rev)
814 937
815 938 return commit_ids
816 939
817 940 def merge_repo(self, pull_request, user, extras):
818 941 log.debug("Merging pull request %s", pull_request.pull_request_id)
819 942 extras['user_agent'] = 'internal-merge'
820 943 merge_state = self._merge_pull_request(pull_request, user, extras)
821 944 if merge_state.executed:
822 945 log.debug("Merge was successful, updating the pull request comments.")
823 946 self._comment_and_close_pr(pull_request, user, merge_state)
824 947
825 948 self._log_audit_action(
826 949 'repo.pull_request.merge',
827 950 {'merge_state': merge_state.__dict__},
828 951 user, pull_request)
829 952
830 953 else:
831 954 log.warn("Merge failed, not updating the pull request.")
832 955 return merge_state
833 956
834 957 def _merge_pull_request(self, pull_request, user, extras, merge_msg=None):
835 958 target_vcs = pull_request.target_repo.scm_instance()
836 959 source_vcs = pull_request.source_repo.scm_instance()
837 960
838 961 message = safe_unicode(merge_msg or vcs_settings.MERGE_MESSAGE_TMPL).format(
839 962 pr_id=pull_request.pull_request_id,
840 963 pr_title=pull_request.title,
841 964 source_repo=source_vcs.name,
842 965 source_ref_name=pull_request.source_ref_parts.name,
843 966 target_repo=target_vcs.name,
844 967 target_ref_name=pull_request.target_ref_parts.name,
845 968 )
846 969
847 970 workspace_id = self._workspace_id(pull_request)
848 971 repo_id = pull_request.target_repo.repo_id
849 972 use_rebase = self._use_rebase_for_merging(pull_request)
850 973 close_branch = self._close_branch_before_merging(pull_request)
851 974 user_name = self._user_name_for_merging(pull_request, user)
852 975
853 976 target_ref = self._refresh_reference(
854 977 pull_request.target_ref_parts, target_vcs)
855 978
856 979 callback_daemon, extras = prepare_callback_daemon(
857 980 extras, protocol=vcs_settings.HOOKS_PROTOCOL,
858 981 host=vcs_settings.HOOKS_HOST,
859 982 use_direct_calls=vcs_settings.HOOKS_DIRECT_CALLS)
860 983
861 984 with callback_daemon:
862 985 # TODO: johbo: Implement a clean way to run a config_override
863 986 # for a single call.
864 987 target_vcs.config.set(
865 988 'rhodecode', 'RC_SCM_DATA', json.dumps(extras))
866 989
867 990 merge_state = target_vcs.merge(
868 991 repo_id, workspace_id, target_ref, source_vcs,
869 992 pull_request.source_ref_parts,
870 993 user_name=user_name, user_email=user.email,
871 994 message=message, use_rebase=use_rebase,
872 995 close_branch=close_branch)
873 996 return merge_state
874 997
875 998 def _comment_and_close_pr(self, pull_request, user, merge_state, close_msg=None):
876 999 pull_request.merge_rev = merge_state.merge_ref.commit_id
877 1000 pull_request.updated_on = datetime.datetime.now()
878 1001 close_msg = close_msg or 'Pull request merged and closed'
879 1002
880 1003 CommentsModel().create(
881 1004 text=safe_unicode(close_msg),
882 1005 repo=pull_request.target_repo.repo_id,
883 1006 user=user.user_id,
884 1007 pull_request=pull_request.pull_request_id,
885 1008 f_path=None,
886 1009 line_no=None,
887 1010 closing_pr=True
888 1011 )
889 1012
890 1013 Session().add(pull_request)
891 1014 Session().flush()
892 1015 # TODO: paris: replace invalidation with less radical solution
893 1016 ScmModel().mark_for_invalidation(
894 1017 pull_request.target_repo.repo_name)
895 1018 self.trigger_pull_request_hook(pull_request, user, 'merge')
896 1019
897 1020 def has_valid_update_type(self, pull_request):
898 1021 source_ref_type = pull_request.source_ref_parts.type
899 1022 return source_ref_type in self.REF_TYPES
900 1023
901 1024 def get_flow_commits(self, pull_request):
902 1025
903 1026 # source repo
904 1027 source_ref_name = pull_request.source_ref_parts.name
905 1028 source_ref_type = pull_request.source_ref_parts.type
906 1029 source_ref_id = pull_request.source_ref_parts.commit_id
907 1030 source_repo = pull_request.source_repo.scm_instance()
908 1031
909 1032 try:
910 1033 if source_ref_type in self.REF_TYPES:
911 1034 source_commit = source_repo.get_commit(
912 1035 source_ref_name, reference_obj=pull_request.source_ref_parts)
913 1036 else:
914 1037 source_commit = source_repo.get_commit(source_ref_id)
915 1038 except CommitDoesNotExistError:
916 1039 raise SourceRefMissing()
917 1040
918 1041 # target repo
919 1042 target_ref_name = pull_request.target_ref_parts.name
920 1043 target_ref_type = pull_request.target_ref_parts.type
921 1044 target_ref_id = pull_request.target_ref_parts.commit_id
922 1045 target_repo = pull_request.target_repo.scm_instance()
923 1046
924 1047 try:
925 1048 if target_ref_type in self.REF_TYPES:
926 1049 target_commit = target_repo.get_commit(
927 1050 target_ref_name, reference_obj=pull_request.target_ref_parts)
928 1051 else:
929 1052 target_commit = target_repo.get_commit(target_ref_id)
930 1053 except CommitDoesNotExistError:
931 1054 raise TargetRefMissing()
932 1055
933 1056 return source_commit, target_commit
934 1057
935 1058 def update_commits(self, pull_request, updating_user):
936 1059 """
937 1060 Get the updated list of commits for the pull request
938 1061 and return the new pull request version and the list
939 1062 of commits processed by this update action
940 1063
941 1064 updating_user is the user_object who triggered the update
942 1065 """
943 1066 pull_request = self.__get_pull_request(pull_request)
944 1067 source_ref_type = pull_request.source_ref_parts.type
945 1068 source_ref_name = pull_request.source_ref_parts.name
946 1069 source_ref_id = pull_request.source_ref_parts.commit_id
947 1070
948 1071 target_ref_type = pull_request.target_ref_parts.type
949 1072 target_ref_name = pull_request.target_ref_parts.name
950 1073 target_ref_id = pull_request.target_ref_parts.commit_id
951 1074
952 1075 if not self.has_valid_update_type(pull_request):
953 1076 log.debug("Skipping update of pull request %s due to ref type: %s",
954 1077 pull_request, source_ref_type)
955 1078 return UpdateResponse(
956 1079 executed=False,
957 1080 reason=UpdateFailureReason.WRONG_REF_TYPE,
958 1081 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
959 1082 source_changed=False, target_changed=False)
960 1083
961 1084 try:
962 1085 source_commit, target_commit = self.get_flow_commits(pull_request)
963 1086 except SourceRefMissing:
964 1087 return UpdateResponse(
965 1088 executed=False,
966 1089 reason=UpdateFailureReason.MISSING_SOURCE_REF,
967 1090 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
968 1091 source_changed=False, target_changed=False)
969 1092 except TargetRefMissing:
970 1093 return UpdateResponse(
971 1094 executed=False,
972 1095 reason=UpdateFailureReason.MISSING_TARGET_REF,
973 1096 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
974 1097 source_changed=False, target_changed=False)
975 1098
976 1099 source_changed = source_ref_id != source_commit.raw_id
977 1100 target_changed = target_ref_id != target_commit.raw_id
978 1101
979 1102 if not (source_changed or target_changed):
980 1103 log.debug("Nothing changed in pull request %s", pull_request)
981 1104 return UpdateResponse(
982 1105 executed=False,
983 1106 reason=UpdateFailureReason.NO_CHANGE,
984 1107 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
985 1108 source_changed=target_changed, target_changed=source_changed)
986 1109
987 1110 change_in_found = 'target repo' if target_changed else 'source repo'
988 1111 log.debug('Updating pull request because of change in %s detected',
989 1112 change_in_found)
990 1113
991 1114 # Finally there is a need for an update, in case of source change
992 1115 # we create a new version, else just an update
993 1116 if source_changed:
994 1117 pull_request_version = self._create_version_from_snapshot(pull_request)
995 1118 self._link_comments_to_version(pull_request_version)
996 1119 else:
997 1120 try:
998 1121 ver = pull_request.versions[-1]
999 1122 except IndexError:
1000 1123 ver = None
1001 1124
1002 1125 pull_request.pull_request_version_id = \
1003 1126 ver.pull_request_version_id if ver else None
1004 1127 pull_request_version = pull_request
1005 1128
1006 1129 source_repo = pull_request.source_repo.scm_instance()
1007 1130 target_repo = pull_request.target_repo.scm_instance()
1008 1131
1009 1132 # re-compute commit ids
1010 1133 old_commit_ids = pull_request.revisions
1011 1134 pre_load = ["author", "date", "message", "branch"]
1012 1135 commit_ranges = target_repo.compare(
1013 1136 target_commit.raw_id, source_commit.raw_id, source_repo, merge=True,
1014 1137 pre_load=pre_load)
1015 1138
1016 1139 target_ref = target_commit.raw_id
1017 1140 source_ref = source_commit.raw_id
1018 1141 ancestor_commit_id = target_repo.get_common_ancestor(
1019 1142 target_ref, source_ref, source_repo)
1020 1143
1021 1144 if not ancestor_commit_id:
1022 1145 raise ValueError(
1023 1146 'cannot calculate diff info without a common ancestor. '
1024 1147 'Make sure both repositories are related, and have a common forking commit.')
1025 1148
1026 1149 pull_request.common_ancestor_id = ancestor_commit_id
1027 1150
1028 1151 pull_request.source_ref = '%s:%s:%s' % (
1029 1152 source_ref_type, source_ref_name, source_commit.raw_id)
1030 1153 pull_request.target_ref = '%s:%s:%s' % (
1031 1154 target_ref_type, target_ref_name, ancestor_commit_id)
1032 1155
1033 1156 pull_request.revisions = [
1034 1157 commit.raw_id for commit in reversed(commit_ranges)]
1035 1158 pull_request.updated_on = datetime.datetime.now()
1036 1159 Session().add(pull_request)
1037 1160 new_commit_ids = pull_request.revisions
1038 1161
1039 1162 old_diff_data, new_diff_data = self._generate_update_diffs(
1040 1163 pull_request, pull_request_version)
1041 1164
1042 1165 # calculate commit and file changes
1043 1166 commit_changes = self._calculate_commit_id_changes(
1044 1167 old_commit_ids, new_commit_ids)
1045 1168 file_changes = self._calculate_file_changes(
1046 1169 old_diff_data, new_diff_data)
1047 1170
1048 1171 # set comments as outdated if DIFFS changed
1049 1172 CommentsModel().outdate_comments(
1050 1173 pull_request, old_diff_data=old_diff_data,
1051 1174 new_diff_data=new_diff_data)
1052 1175
1053 1176 valid_commit_changes = (commit_changes.added or commit_changes.removed)
1054 1177 file_node_changes = (
1055 1178 file_changes.added or file_changes.modified or file_changes.removed)
1056 1179 pr_has_changes = valid_commit_changes or file_node_changes
1057 1180
1058 1181 # Add an automatic comment to the pull request, in case
1059 1182 # anything has changed
1060 1183 if pr_has_changes:
1061 1184 update_comment = CommentsModel().create(
1062 1185 text=self._render_update_message(ancestor_commit_id, commit_changes, file_changes),
1063 1186 repo=pull_request.target_repo,
1064 1187 user=pull_request.author,
1065 1188 pull_request=pull_request,
1066 1189 send_email=False, renderer=DEFAULT_COMMENTS_RENDERER)
1067 1190
1068 1191 # Update status to "Under Review" for added commits
1069 1192 for commit_id in commit_changes.added:
1070 1193 ChangesetStatusModel().set_status(
1071 1194 repo=pull_request.source_repo,
1072 1195 status=ChangesetStatus.STATUS_UNDER_REVIEW,
1073 1196 comment=update_comment,
1074 1197 user=pull_request.author,
1075 1198 pull_request=pull_request,
1076 1199 revision=commit_id)
1077 1200
1078 1201 # send update email to users
1079 1202 try:
1080 1203 self.notify_users(pull_request=pull_request, updating_user=updating_user,
1081 1204 ancestor_commit_id=ancestor_commit_id,
1082 1205 commit_changes=commit_changes,
1083 1206 file_changes=file_changes)
1084 1207 except Exception:
1085 1208 log.exception('Failed to send email notification to users')
1086 1209
1087 1210 log.debug(
1088 1211 'Updated pull request %s, added_ids: %s, common_ids: %s, '
1089 1212 'removed_ids: %s', pull_request.pull_request_id,
1090 1213 commit_changes.added, commit_changes.common, commit_changes.removed)
1091 1214 log.debug(
1092 1215 'Updated pull request with the following file changes: %s',
1093 1216 file_changes)
1094 1217
1095 1218 log.info(
1096 1219 "Updated pull request %s from commit %s to commit %s, "
1097 1220 "stored new version %s of this pull request.",
1098 1221 pull_request.pull_request_id, source_ref_id,
1099 1222 pull_request.source_ref_parts.commit_id,
1100 1223 pull_request_version.pull_request_version_id)
1101 1224 Session().commit()
1102 1225 self.trigger_pull_request_hook(pull_request, pull_request.author, 'update')
1103 1226
1104 1227 return UpdateResponse(
1105 1228 executed=True, reason=UpdateFailureReason.NONE,
1106 1229 old=pull_request, new=pull_request_version,
1107 1230 common_ancestor_id=ancestor_commit_id, commit_changes=commit_changes,
1108 1231 source_changed=source_changed, target_changed=target_changed)
1109 1232
1110 1233 def _create_version_from_snapshot(self, pull_request):
1111 1234 version = PullRequestVersion()
1112 1235 version.title = pull_request.title
1113 1236 version.description = pull_request.description
1114 1237 version.status = pull_request.status
1115 1238 version.pull_request_state = pull_request.pull_request_state
1116 1239 version.created_on = datetime.datetime.now()
1117 1240 version.updated_on = pull_request.updated_on
1118 1241 version.user_id = pull_request.user_id
1119 1242 version.source_repo = pull_request.source_repo
1120 1243 version.source_ref = pull_request.source_ref
1121 1244 version.target_repo = pull_request.target_repo
1122 1245 version.target_ref = pull_request.target_ref
1123 1246
1124 1247 version._last_merge_source_rev = pull_request._last_merge_source_rev
1125 1248 version._last_merge_target_rev = pull_request._last_merge_target_rev
1126 1249 version.last_merge_status = pull_request.last_merge_status
1127 1250 version.last_merge_metadata = pull_request.last_merge_metadata
1128 1251 version.shadow_merge_ref = pull_request.shadow_merge_ref
1129 1252 version.merge_rev = pull_request.merge_rev
1130 1253 version.reviewer_data = pull_request.reviewer_data
1131 1254
1132 1255 version.revisions = pull_request.revisions
1133 1256 version.common_ancestor_id = pull_request.common_ancestor_id
1134 1257 version.pull_request = pull_request
1135 1258 Session().add(version)
1136 1259 Session().flush()
1137 1260
1138 1261 return version
1139 1262
1140 1263 def _generate_update_diffs(self, pull_request, pull_request_version):
1141 1264
1142 1265 diff_context = (
1143 1266 self.DIFF_CONTEXT +
1144 1267 CommentsModel.needed_extra_diff_context())
1145 1268 hide_whitespace_changes = False
1146 1269 source_repo = pull_request_version.source_repo
1147 1270 source_ref_id = pull_request_version.source_ref_parts.commit_id
1148 1271 target_ref_id = pull_request_version.target_ref_parts.commit_id
1149 1272 old_diff = self._get_diff_from_pr_or_version(
1150 1273 source_repo, source_ref_id, target_ref_id,
1151 1274 hide_whitespace_changes=hide_whitespace_changes, diff_context=diff_context)
1152 1275
1153 1276 source_repo = pull_request.source_repo
1154 1277 source_ref_id = pull_request.source_ref_parts.commit_id
1155 1278 target_ref_id = pull_request.target_ref_parts.commit_id
1156 1279
1157 1280 new_diff = self._get_diff_from_pr_or_version(
1158 1281 source_repo, source_ref_id, target_ref_id,
1159 1282 hide_whitespace_changes=hide_whitespace_changes, diff_context=diff_context)
1160 1283
1161 1284 old_diff_data = diffs.DiffProcessor(old_diff)
1162 1285 old_diff_data.prepare()
1163 1286 new_diff_data = diffs.DiffProcessor(new_diff)
1164 1287 new_diff_data.prepare()
1165 1288
1166 1289 return old_diff_data, new_diff_data
1167 1290
1168 1291 def _link_comments_to_version(self, pull_request_version):
1169 1292 """
1170 1293 Link all unlinked comments of this pull request to the given version.
1171 1294
1172 1295 :param pull_request_version: The `PullRequestVersion` to which
1173 1296 the comments shall be linked.
1174 1297
1175 1298 """
1176 1299 pull_request = pull_request_version.pull_request
1177 1300 comments = ChangesetComment.query()\
1178 1301 .filter(
1179 1302 # TODO: johbo: Should we query for the repo at all here?
1180 1303 # Pending decision on how comments of PRs are to be related
1181 1304 # to either the source repo, the target repo or no repo at all.
1182 1305 ChangesetComment.repo_id == pull_request.target_repo.repo_id,
1183 1306 ChangesetComment.pull_request == pull_request,
1184 1307 ChangesetComment.pull_request_version == None)\
1185 1308 .order_by(ChangesetComment.comment_id.asc())
1186 1309
1187 1310 # TODO: johbo: Find out why this breaks if it is done in a bulk
1188 1311 # operation.
1189 1312 for comment in comments:
1190 1313 comment.pull_request_version_id = (
1191 1314 pull_request_version.pull_request_version_id)
1192 1315 Session().add(comment)
1193 1316
1194 1317 def _calculate_commit_id_changes(self, old_ids, new_ids):
1195 1318 added = [x for x in new_ids if x not in old_ids]
1196 1319 common = [x for x in new_ids if x in old_ids]
1197 1320 removed = [x for x in old_ids if x not in new_ids]
1198 1321 total = new_ids
1199 1322 return ChangeTuple(added, common, removed, total)
1200 1323
1201 1324 def _calculate_file_changes(self, old_diff_data, new_diff_data):
1202 1325
1203 1326 old_files = OrderedDict()
1204 1327 for diff_data in old_diff_data.parsed_diff:
1205 1328 old_files[diff_data['filename']] = md5_safe(diff_data['raw_diff'])
1206 1329
1207 1330 added_files = []
1208 1331 modified_files = []
1209 1332 removed_files = []
1210 1333 for diff_data in new_diff_data.parsed_diff:
1211 1334 new_filename = diff_data['filename']
1212 1335 new_hash = md5_safe(diff_data['raw_diff'])
1213 1336
1214 1337 old_hash = old_files.get(new_filename)
1215 1338 if not old_hash:
1216 1339 # file is not present in old diff, we have to figure out from parsed diff
1217 1340 # operation ADD/REMOVE
1218 1341 operations_dict = diff_data['stats']['ops']
1219 1342 if diffs.DEL_FILENODE in operations_dict:
1220 1343 removed_files.append(new_filename)
1221 1344 else:
1222 1345 added_files.append(new_filename)
1223 1346 else:
1224 1347 if new_hash != old_hash:
1225 1348 modified_files.append(new_filename)
1226 1349 # now remove a file from old, since we have seen it already
1227 1350 del old_files[new_filename]
1228 1351
1229 1352 # removed files is when there are present in old, but not in NEW,
1230 1353 # since we remove old files that are present in new diff, left-overs
1231 1354 # if any should be the removed files
1232 1355 removed_files.extend(old_files.keys())
1233 1356
1234 1357 return FileChangeTuple(added_files, modified_files, removed_files)
1235 1358
1236 1359 def _render_update_message(self, ancestor_commit_id, changes, file_changes):
1237 1360 """
1238 1361 render the message using DEFAULT_COMMENTS_RENDERER (RST renderer),
1239 1362 so it's always looking the same disregarding on which default
1240 1363 renderer system is using.
1241 1364
1242 1365 :param ancestor_commit_id: ancestor raw_id
1243 1366 :param changes: changes named tuple
1244 1367 :param file_changes: file changes named tuple
1245 1368
1246 1369 """
1247 1370 new_status = ChangesetStatus.get_status_lbl(
1248 1371 ChangesetStatus.STATUS_UNDER_REVIEW)
1249 1372
1250 1373 changed_files = (
1251 1374 file_changes.added + file_changes.modified + file_changes.removed)
1252 1375
1253 1376 params = {
1254 1377 'under_review_label': new_status,
1255 1378 'added_commits': changes.added,
1256 1379 'removed_commits': changes.removed,
1257 1380 'changed_files': changed_files,
1258 1381 'added_files': file_changes.added,
1259 1382 'modified_files': file_changes.modified,
1260 1383 'removed_files': file_changes.removed,
1261 1384 'ancestor_commit_id': ancestor_commit_id
1262 1385 }
1263 1386 renderer = RstTemplateRenderer()
1264 1387 return renderer.render('pull_request_update.mako', **params)
1265 1388
1266 1389 def edit(self, pull_request, title, description, description_renderer, user):
1267 1390 pull_request = self.__get_pull_request(pull_request)
1268 1391 old_data = pull_request.get_api_data(with_merge_state=False)
1269 1392 if pull_request.is_closed():
1270 1393 raise ValueError('This pull request is closed')
1271 1394 if title:
1272 1395 pull_request.title = title
1273 1396 pull_request.description = description
1274 1397 pull_request.updated_on = datetime.datetime.now()
1275 1398 pull_request.description_renderer = description_renderer
1276 1399 Session().add(pull_request)
1277 1400 self._log_audit_action(
1278 1401 'repo.pull_request.edit', {'old_data': old_data},
1279 1402 user, pull_request)
1280 1403
1281 1404 def update_reviewers(self, pull_request, reviewer_data, user):
1282 1405 """
1283 1406 Update the reviewers in the pull request
1284 1407
1285 1408 :param pull_request: the pr to update
1286 1409 :param reviewer_data: list of tuples
1287 1410 [(user, ['reason1', 'reason2'], mandatory_flag, role, [rules])]
1288 1411 :param user: current use who triggers this action
1289 1412 """
1290 1413
1291 1414 pull_request = self.__get_pull_request(pull_request)
1292 1415 if pull_request.is_closed():
1293 1416 raise ValueError('This pull request is closed')
1294 1417
1295 1418 reviewers = {}
1296 1419 for user_id, reasons, mandatory, role, rules in reviewer_data:
1297 1420 if isinstance(user_id, (int, compat.string_types)):
1298 1421 user_id = self._get_user(user_id).user_id
1299 1422 reviewers[user_id] = {
1300 1423 'reasons': reasons, 'mandatory': mandatory, 'role': role}
1301 1424
1302 1425 reviewers_ids = set(reviewers.keys())
1303 1426 current_reviewers = PullRequestReviewers.get_pull_request_reviewers(
1304 1427 pull_request.pull_request_id, role=PullRequestReviewers.ROLE_REVIEWER)
1305 1428
1306 1429 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
1307 1430
1308 1431 ids_to_add = reviewers_ids.difference(current_reviewers_ids)
1309 1432 ids_to_remove = current_reviewers_ids.difference(reviewers_ids)
1310 1433
1311 1434 log.debug("Adding %s reviewers", ids_to_add)
1312 1435 log.debug("Removing %s reviewers", ids_to_remove)
1313 1436 changed = False
1314 1437 added_audit_reviewers = []
1315 1438 removed_audit_reviewers = []
1316 1439
1317 1440 for uid in ids_to_add:
1318 1441 changed = True
1319 1442 _usr = self._get_user(uid)
1320 1443 reviewer = PullRequestReviewers()
1321 1444 reviewer.user = _usr
1322 1445 reviewer.pull_request = pull_request
1323 1446 reviewer.reasons = reviewers[uid]['reasons']
1324 1447 # NOTE(marcink): mandatory shouldn't be changed now
1325 1448 # reviewer.mandatory = reviewers[uid]['reasons']
1326 1449 # NOTE(marcink): role should be hardcoded, so we won't edit it.
1327 1450 reviewer.role = PullRequestReviewers.ROLE_REVIEWER
1328 1451 Session().add(reviewer)
1329 1452 added_audit_reviewers.append(reviewer.get_dict())
1330 1453
1331 1454 for uid in ids_to_remove:
1332 1455 changed = True
1333 1456 # NOTE(marcink): we fetch "ALL" reviewers objects using .all().
1334 1457 # This is an edge case that handles previous state of having the same reviewer twice.
1335 1458 # this CAN happen due to the lack of DB checks
1336 1459 reviewers = PullRequestReviewers.query()\
1337 1460 .filter(PullRequestReviewers.user_id == uid,
1338 1461 PullRequestReviewers.role == PullRequestReviewers.ROLE_REVIEWER,
1339 1462 PullRequestReviewers.pull_request == pull_request)\
1340 1463 .all()
1341 1464
1342 1465 for obj in reviewers:
1343 1466 added_audit_reviewers.append(obj.get_dict())
1344 1467 Session().delete(obj)
1345 1468
1346 1469 if changed:
1347 1470 Session().expire_all()
1348 1471 pull_request.updated_on = datetime.datetime.now()
1349 1472 Session().add(pull_request)
1350 1473
1351 1474 # finally store audit logs
1352 1475 for user_data in added_audit_reviewers:
1353 1476 self._log_audit_action(
1354 1477 'repo.pull_request.reviewer.add', {'data': user_data},
1355 1478 user, pull_request)
1356 1479 for user_data in removed_audit_reviewers:
1357 1480 self._log_audit_action(
1358 1481 'repo.pull_request.reviewer.delete', {'old_data': user_data},
1359 1482 user, pull_request)
1360 1483
1361 1484 self.notify_reviewers(pull_request, ids_to_add, user)
1362 1485 return ids_to_add, ids_to_remove
1363 1486
1364 1487 def update_observers(self, pull_request, observer_data, user):
1365 1488 """
1366 1489 Update the observers in the pull request
1367 1490
1368 1491 :param pull_request: the pr to update
1369 1492 :param observer_data: list of tuples
1370 1493 [(user, ['reason1', 'reason2'], mandatory_flag, role, [rules])]
1371 1494 :param user: current use who triggers this action
1372 1495 """
1373 1496 pull_request = self.__get_pull_request(pull_request)
1374 1497 if pull_request.is_closed():
1375 1498 raise ValueError('This pull request is closed')
1376 1499
1377 1500 observers = {}
1378 1501 for user_id, reasons, mandatory, role, rules in observer_data:
1379 1502 if isinstance(user_id, (int, compat.string_types)):
1380 1503 user_id = self._get_user(user_id).user_id
1381 1504 observers[user_id] = {
1382 1505 'reasons': reasons, 'observers': mandatory, 'role': role}
1383 1506
1384 1507 observers_ids = set(observers.keys())
1385 1508 current_observers = PullRequestReviewers.get_pull_request_reviewers(
1386 1509 pull_request.pull_request_id, role=PullRequestReviewers.ROLE_OBSERVER)
1387 1510
1388 1511 current_observers_ids = set([x.user.user_id for x in current_observers])
1389 1512
1390 1513 ids_to_add = observers_ids.difference(current_observers_ids)
1391 1514 ids_to_remove = current_observers_ids.difference(observers_ids)
1392 1515
1393 1516 log.debug("Adding %s observer", ids_to_add)
1394 1517 log.debug("Removing %s observer", ids_to_remove)
1395 1518 changed = False
1396 1519 added_audit_observers = []
1397 1520 removed_audit_observers = []
1398 1521
1399 1522 for uid in ids_to_add:
1400 1523 changed = True
1401 1524 _usr = self._get_user(uid)
1402 1525 observer = PullRequestReviewers()
1403 1526 observer.user = _usr
1404 1527 observer.pull_request = pull_request
1405 1528 observer.reasons = observers[uid]['reasons']
1406 1529 # NOTE(marcink): mandatory shouldn't be changed now
1407 1530 # observer.mandatory = observer[uid]['reasons']
1408 1531
1409 1532 # NOTE(marcink): role should be hardcoded, so we won't edit it.
1410 1533 observer.role = PullRequestReviewers.ROLE_OBSERVER
1411 1534 Session().add(observer)
1412 1535 added_audit_observers.append(observer.get_dict())
1413 1536
1414 1537 for uid in ids_to_remove:
1415 1538 changed = True
1416 1539 # NOTE(marcink): we fetch "ALL" reviewers objects using .all().
1417 1540 # This is an edge case that handles previous state of having the same reviewer twice.
1418 1541 # this CAN happen due to the lack of DB checks
1419 1542 observers = PullRequestReviewers.query()\
1420 1543 .filter(PullRequestReviewers.user_id == uid,
1421 1544 PullRequestReviewers.role == PullRequestReviewers.ROLE_OBSERVER,
1422 1545 PullRequestReviewers.pull_request == pull_request)\
1423 1546 .all()
1424 1547
1425 1548 for obj in observers:
1426 1549 added_audit_observers.append(obj.get_dict())
1427 1550 Session().delete(obj)
1428 1551
1429 1552 if changed:
1430 1553 Session().expire_all()
1431 1554 pull_request.updated_on = datetime.datetime.now()
1432 1555 Session().add(pull_request)
1433 1556
1434 1557 # finally store audit logs
1435 1558 for user_data in added_audit_observers:
1436 1559 self._log_audit_action(
1437 1560 'repo.pull_request.observer.add', {'data': user_data},
1438 1561 user, pull_request)
1439 1562 for user_data in removed_audit_observers:
1440 1563 self._log_audit_action(
1441 1564 'repo.pull_request.observer.delete', {'old_data': user_data},
1442 1565 user, pull_request)
1443 1566
1444 1567 self.notify_observers(pull_request, ids_to_add, user)
1445 1568 return ids_to_add, ids_to_remove
1446 1569
1447 1570 def get_url(self, pull_request, request=None, permalink=False):
1448 1571 if not request:
1449 1572 request = get_current_request()
1450 1573
1451 1574 if permalink:
1452 1575 return request.route_url(
1453 1576 'pull_requests_global',
1454 1577 pull_request_id=pull_request.pull_request_id,)
1455 1578 else:
1456 1579 return request.route_url('pullrequest_show',
1457 1580 repo_name=safe_str(pull_request.target_repo.repo_name),
1458 1581 pull_request_id=pull_request.pull_request_id,)
1459 1582
1460 1583 def get_shadow_clone_url(self, pull_request, request=None):
1461 1584 """
1462 1585 Returns qualified url pointing to the shadow repository. If this pull
1463 1586 request is closed there is no shadow repository and ``None`` will be
1464 1587 returned.
1465 1588 """
1466 1589 if pull_request.is_closed():
1467 1590 return None
1468 1591 else:
1469 1592 pr_url = urllib.unquote(self.get_url(pull_request, request=request))
1470 1593 return safe_unicode('{pr_url}/repository'.format(pr_url=pr_url))
1471 1594
1472 1595 def _notify_reviewers(self, pull_request, user_ids, role, user):
1473 1596 # notification to reviewers/observers
1474 1597 if not user_ids:
1475 1598 return
1476 1599
1477 1600 log.debug('Notify following %s users about pull-request %s', role, user_ids)
1478 1601
1479 1602 pull_request_obj = pull_request
1480 1603 # get the current participants of this pull request
1481 1604 recipients = user_ids
1482 1605 notification_type = EmailNotificationModel.TYPE_PULL_REQUEST
1483 1606
1484 1607 pr_source_repo = pull_request_obj.source_repo
1485 1608 pr_target_repo = pull_request_obj.target_repo
1486 1609
1487 1610 pr_url = h.route_url('pullrequest_show',
1488 1611 repo_name=pr_target_repo.repo_name,
1489 1612 pull_request_id=pull_request_obj.pull_request_id,)
1490 1613
1491 1614 # set some variables for email notification
1492 1615 pr_target_repo_url = h.route_url(
1493 1616 'repo_summary', repo_name=pr_target_repo.repo_name)
1494 1617
1495 1618 pr_source_repo_url = h.route_url(
1496 1619 'repo_summary', repo_name=pr_source_repo.repo_name)
1497 1620
1498 1621 # pull request specifics
1499 1622 pull_request_commits = [
1500 1623 (x.raw_id, x.message)
1501 1624 for x in map(pr_source_repo.get_commit, pull_request.revisions)]
1502 1625
1503 1626 current_rhodecode_user = user
1504 1627 kwargs = {
1505 1628 'user': current_rhodecode_user,
1506 1629 'pull_request_author': pull_request.author,
1507 1630 'pull_request': pull_request_obj,
1508 1631 'pull_request_commits': pull_request_commits,
1509 1632
1510 1633 'pull_request_target_repo': pr_target_repo,
1511 1634 'pull_request_target_repo_url': pr_target_repo_url,
1512 1635
1513 1636 'pull_request_source_repo': pr_source_repo,
1514 1637 'pull_request_source_repo_url': pr_source_repo_url,
1515 1638
1516 1639 'pull_request_url': pr_url,
1517 1640 'thread_ids': [pr_url],
1518 1641 'user_role': role
1519 1642 }
1520 1643
1521 1644 # create notification objects, and emails
1522 1645 NotificationModel().create(
1523 1646 created_by=current_rhodecode_user,
1524 1647 notification_subject='', # Filled in based on the notification_type
1525 1648 notification_body='', # Filled in based on the notification_type
1526 1649 notification_type=notification_type,
1527 1650 recipients=recipients,
1528 1651 email_kwargs=kwargs,
1529 1652 )
1530 1653
1531 1654 def notify_reviewers(self, pull_request, reviewers_ids, user):
1532 1655 return self._notify_reviewers(pull_request, reviewers_ids,
1533 1656 PullRequestReviewers.ROLE_REVIEWER, user)
1534 1657
1535 1658 def notify_observers(self, pull_request, observers_ids, user):
1536 1659 return self._notify_reviewers(pull_request, observers_ids,
1537 1660 PullRequestReviewers.ROLE_OBSERVER, user)
1538 1661
1539 1662 def notify_users(self, pull_request, updating_user, ancestor_commit_id,
1540 1663 commit_changes, file_changes):
1541 1664
1542 1665 updating_user_id = updating_user.user_id
1543 1666 reviewers = set([x.user.user_id for x in pull_request.get_pull_request_reviewers()])
1544 1667 # NOTE(marcink): send notification to all other users except to
1545 1668 # person who updated the PR
1546 1669 recipients = reviewers.difference(set([updating_user_id]))
1547 1670
1548 1671 log.debug('Notify following recipients about pull-request update %s', recipients)
1549 1672
1550 1673 pull_request_obj = pull_request
1551 1674
1552 1675 # send email about the update
1553 1676 changed_files = (
1554 1677 file_changes.added + file_changes.modified + file_changes.removed)
1555 1678
1556 1679 pr_source_repo = pull_request_obj.source_repo
1557 1680 pr_target_repo = pull_request_obj.target_repo
1558 1681
1559 1682 pr_url = h.route_url('pullrequest_show',
1560 1683 repo_name=pr_target_repo.repo_name,
1561 1684 pull_request_id=pull_request_obj.pull_request_id,)
1562 1685
1563 1686 # set some variables for email notification
1564 1687 pr_target_repo_url = h.route_url(
1565 1688 'repo_summary', repo_name=pr_target_repo.repo_name)
1566 1689
1567 1690 pr_source_repo_url = h.route_url(
1568 1691 'repo_summary', repo_name=pr_source_repo.repo_name)
1569 1692
1570 1693 email_kwargs = {
1571 1694 'date': datetime.datetime.now(),
1572 1695 'updating_user': updating_user,
1573 1696
1574 1697 'pull_request': pull_request_obj,
1575 1698
1576 1699 'pull_request_target_repo': pr_target_repo,
1577 1700 'pull_request_target_repo_url': pr_target_repo_url,
1578 1701
1579 1702 'pull_request_source_repo': pr_source_repo,
1580 1703 'pull_request_source_repo_url': pr_source_repo_url,
1581 1704
1582 1705 'pull_request_url': pr_url,
1583 1706
1584 1707 'ancestor_commit_id': ancestor_commit_id,
1585 1708 'added_commits': commit_changes.added,
1586 1709 'removed_commits': commit_changes.removed,
1587 1710 'changed_files': changed_files,
1588 1711 'added_files': file_changes.added,
1589 1712 'modified_files': file_changes.modified,
1590 1713 'removed_files': file_changes.removed,
1591 1714 'thread_ids': [pr_url],
1592 1715 }
1593 1716
1594 1717 # create notification objects, and emails
1595 1718 NotificationModel().create(
1596 1719 created_by=updating_user,
1597 1720 notification_subject='', # Filled in based on the notification_type
1598 1721 notification_body='', # Filled in based on the notification_type
1599 1722 notification_type=EmailNotificationModel.TYPE_PULL_REQUEST_UPDATE,
1600 1723 recipients=recipients,
1601 1724 email_kwargs=email_kwargs,
1602 1725 )
1603 1726
1604 1727 def delete(self, pull_request, user=None):
1605 1728 if not user:
1606 1729 user = getattr(get_current_rhodecode_user(), 'username', None)
1607 1730
1608 1731 pull_request = self.__get_pull_request(pull_request)
1609 1732 old_data = pull_request.get_api_data(with_merge_state=False)
1610 1733 self._cleanup_merge_workspace(pull_request)
1611 1734 self._log_audit_action(
1612 1735 'repo.pull_request.delete', {'old_data': old_data},
1613 1736 user, pull_request)
1614 1737 Session().delete(pull_request)
1615 1738
1616 1739 def close_pull_request(self, pull_request, user):
1617 1740 pull_request = self.__get_pull_request(pull_request)
1618 1741 self._cleanup_merge_workspace(pull_request)
1619 1742 pull_request.status = PullRequest.STATUS_CLOSED
1620 1743 pull_request.updated_on = datetime.datetime.now()
1621 1744 Session().add(pull_request)
1622 1745 self.trigger_pull_request_hook(pull_request, pull_request.author, 'close')
1623 1746
1624 1747 pr_data = pull_request.get_api_data(with_merge_state=False)
1625 1748 self._log_audit_action(
1626 1749 'repo.pull_request.close', {'data': pr_data}, user, pull_request)
1627 1750
1628 1751 def close_pull_request_with_comment(
1629 1752 self, pull_request, user, repo, message=None, auth_user=None):
1630 1753
1631 1754 pull_request_review_status = pull_request.calculated_review_status()
1632 1755
1633 1756 if pull_request_review_status == ChangesetStatus.STATUS_APPROVED:
1634 1757 # approved only if we have voting consent
1635 1758 status = ChangesetStatus.STATUS_APPROVED
1636 1759 else:
1637 1760 status = ChangesetStatus.STATUS_REJECTED
1638 1761 status_lbl = ChangesetStatus.get_status_lbl(status)
1639 1762
1640 1763 default_message = (
1641 1764 'Closing with status change {transition_icon} {status}.'
1642 1765 ).format(transition_icon='>', status=status_lbl)
1643 1766 text = message or default_message
1644 1767
1645 1768 # create a comment, and link it to new status
1646 1769 comment = CommentsModel().create(
1647 1770 text=text,
1648 1771 repo=repo.repo_id,
1649 1772 user=user.user_id,
1650 1773 pull_request=pull_request.pull_request_id,
1651 1774 status_change=status_lbl,
1652 1775 status_change_type=status,
1653 1776 closing_pr=True,
1654 1777 auth_user=auth_user,
1655 1778 )
1656 1779
1657 1780 # calculate old status before we change it
1658 1781 old_calculated_status = pull_request.calculated_review_status()
1659 1782 ChangesetStatusModel().set_status(
1660 1783 repo.repo_id,
1661 1784 status,
1662 1785 user.user_id,
1663 1786 comment=comment,
1664 1787 pull_request=pull_request.pull_request_id
1665 1788 )
1666 1789
1667 1790 Session().flush()
1668 1791
1669 1792 self.trigger_pull_request_hook(pull_request, user, 'comment',
1670 1793 data={'comment': comment})
1671 1794
1672 1795 # we now calculate the status of pull request again, and based on that
1673 1796 # calculation trigger status change. This might happen in cases
1674 1797 # that non-reviewer admin closes a pr, which means his vote doesn't
1675 1798 # change the status, while if he's a reviewer this might change it.
1676 1799 calculated_status = pull_request.calculated_review_status()
1677 1800 if old_calculated_status != calculated_status:
1678 1801 self.trigger_pull_request_hook(pull_request, user, 'review_status_change',
1679 1802 data={'status': calculated_status})
1680 1803
1681 1804 # finally close the PR
1682 1805 PullRequestModel().close_pull_request(pull_request.pull_request_id, user)
1683 1806
1684 1807 return comment, status
1685 1808
1686 1809 def merge_status(self, pull_request, translator=None, force_shadow_repo_refresh=False):
1687 1810 _ = translator or get_current_request().translate
1688 1811
1689 1812 if not self._is_merge_enabled(pull_request):
1690 1813 return None, False, _('Server-side pull request merging is disabled.')
1691 1814
1692 1815 if pull_request.is_closed():
1693 1816 return None, False, _('This pull request is closed.')
1694 1817
1695 1818 merge_possible, msg = self._check_repo_requirements(
1696 1819 target=pull_request.target_repo, source=pull_request.source_repo,
1697 1820 translator=_)
1698 1821 if not merge_possible:
1699 1822 return None, merge_possible, msg
1700 1823
1701 1824 try:
1702 1825 merge_response = self._try_merge(
1703 1826 pull_request, force_shadow_repo_refresh=force_shadow_repo_refresh)
1704 1827 log.debug("Merge response: %s", merge_response)
1705 1828 return merge_response, merge_response.possible, merge_response.merge_status_message
1706 1829 except NotImplementedError:
1707 1830 return None, False, _('Pull request merging is not supported.')
1708 1831
1709 1832 def _check_repo_requirements(self, target, source, translator):
1710 1833 """
1711 1834 Check if `target` and `source` have compatible requirements.
1712 1835
1713 1836 Currently this is just checking for largefiles.
1714 1837 """
1715 1838 _ = translator
1716 1839 target_has_largefiles = self._has_largefiles(target)
1717 1840 source_has_largefiles = self._has_largefiles(source)
1718 1841 merge_possible = True
1719 1842 message = u''
1720 1843
1721 1844 if target_has_largefiles != source_has_largefiles:
1722 1845 merge_possible = False
1723 1846 if source_has_largefiles:
1724 1847 message = _(
1725 1848 'Target repository large files support is disabled.')
1726 1849 else:
1727 1850 message = _(
1728 1851 'Source repository large files support is disabled.')
1729 1852
1730 1853 return merge_possible, message
1731 1854
1732 1855 def _has_largefiles(self, repo):
1733 1856 largefiles_ui = VcsSettingsModel(repo=repo).get_ui_settings(
1734 1857 'extensions', 'largefiles')
1735 1858 return largefiles_ui and largefiles_ui[0].active
1736 1859
1737 1860 def _try_merge(self, pull_request, force_shadow_repo_refresh=False):
1738 1861 """
1739 1862 Try to merge the pull request and return the merge status.
1740 1863 """
1741 1864 log.debug(
1742 1865 "Trying out if the pull request %s can be merged. Force_refresh=%s",
1743 1866 pull_request.pull_request_id, force_shadow_repo_refresh)
1744 1867 target_vcs = pull_request.target_repo.scm_instance()
1745 1868 # Refresh the target reference.
1746 1869 try:
1747 1870 target_ref = self._refresh_reference(
1748 1871 pull_request.target_ref_parts, target_vcs)
1749 1872 except CommitDoesNotExistError:
1750 1873 merge_state = MergeResponse(
1751 1874 False, False, None, MergeFailureReason.MISSING_TARGET_REF,
1752 1875 metadata={'target_ref': pull_request.target_ref_parts})
1753 1876 return merge_state
1754 1877
1755 1878 target_locked = pull_request.target_repo.locked
1756 1879 if target_locked and target_locked[0]:
1757 1880 locked_by = 'user:{}'.format(target_locked[0])
1758 1881 log.debug("The target repository is locked by %s.", locked_by)
1759 1882 merge_state = MergeResponse(
1760 1883 False, False, None, MergeFailureReason.TARGET_IS_LOCKED,
1761 1884 metadata={'locked_by': locked_by})
1762 1885 elif force_shadow_repo_refresh or self._needs_merge_state_refresh(
1763 1886 pull_request, target_ref):
1764 1887 log.debug("Refreshing the merge status of the repository.")
1765 1888 merge_state = self._refresh_merge_state(
1766 1889 pull_request, target_vcs, target_ref)
1767 1890 else:
1768 1891 possible = pull_request.last_merge_status == MergeFailureReason.NONE
1769 1892 metadata = {
1770 1893 'unresolved_files': '',
1771 1894 'target_ref': pull_request.target_ref_parts,
1772 1895 'source_ref': pull_request.source_ref_parts,
1773 1896 }
1774 1897 if pull_request.last_merge_metadata:
1775 1898 metadata.update(pull_request.last_merge_metadata_parsed)
1776 1899
1777 1900 if not possible and target_ref.type == 'branch':
1778 1901 # NOTE(marcink): case for mercurial multiple heads on branch
1779 1902 heads = target_vcs._heads(target_ref.name)
1780 1903 if len(heads) != 1:
1781 1904 heads = '\n,'.join(target_vcs._heads(target_ref.name))
1782 1905 metadata.update({
1783 1906 'heads': heads
1784 1907 })
1785 1908
1786 1909 merge_state = MergeResponse(
1787 1910 possible, False, None, pull_request.last_merge_status, metadata=metadata)
1788 1911
1789 1912 return merge_state
1790 1913
1791 1914 def _refresh_reference(self, reference, vcs_repository):
1792 1915 if reference.type in self.UPDATABLE_REF_TYPES:
1793 1916 name_or_id = reference.name
1794 1917 else:
1795 1918 name_or_id = reference.commit_id
1796 1919
1797 1920 refreshed_commit = vcs_repository.get_commit(name_or_id)
1798 1921 refreshed_reference = Reference(
1799 1922 reference.type, reference.name, refreshed_commit.raw_id)
1800 1923 return refreshed_reference
1801 1924
1802 1925 def _needs_merge_state_refresh(self, pull_request, target_reference):
1803 1926 return not(
1804 1927 pull_request.revisions and
1805 1928 pull_request.revisions[0] == pull_request._last_merge_source_rev and
1806 1929 target_reference.commit_id == pull_request._last_merge_target_rev)
1807 1930
1808 1931 def _refresh_merge_state(self, pull_request, target_vcs, target_reference):
1809 1932 workspace_id = self._workspace_id(pull_request)
1810 1933 source_vcs = pull_request.source_repo.scm_instance()
1811 1934 repo_id = pull_request.target_repo.repo_id
1812 1935 use_rebase = self._use_rebase_for_merging(pull_request)
1813 1936 close_branch = self._close_branch_before_merging(pull_request)
1814 1937 merge_state = target_vcs.merge(
1815 1938 repo_id, workspace_id,
1816 1939 target_reference, source_vcs, pull_request.source_ref_parts,
1817 1940 dry_run=True, use_rebase=use_rebase,
1818 1941 close_branch=close_branch)
1819 1942
1820 1943 # Do not store the response if there was an unknown error.
1821 1944 if merge_state.failure_reason != MergeFailureReason.UNKNOWN:
1822 1945 pull_request._last_merge_source_rev = \
1823 1946 pull_request.source_ref_parts.commit_id
1824 1947 pull_request._last_merge_target_rev = target_reference.commit_id
1825 1948 pull_request.last_merge_status = merge_state.failure_reason
1826 1949 pull_request.last_merge_metadata = merge_state.metadata
1827 1950
1828 1951 pull_request.shadow_merge_ref = merge_state.merge_ref
1829 1952 Session().add(pull_request)
1830 1953 Session().commit()
1831 1954
1832 1955 return merge_state
1833 1956
1834 1957 def _workspace_id(self, pull_request):
1835 1958 workspace_id = 'pr-%s' % pull_request.pull_request_id
1836 1959 return workspace_id
1837 1960
1838 1961 def generate_repo_data(self, repo, commit_id=None, branch=None,
1839 1962 bookmark=None, translator=None):
1840 1963 from rhodecode.model.repo import RepoModel
1841 1964
1842 1965 all_refs, selected_ref = \
1843 1966 self._get_repo_pullrequest_sources(
1844 1967 repo.scm_instance(), commit_id=commit_id,
1845 1968 branch=branch, bookmark=bookmark, translator=translator)
1846 1969
1847 1970 refs_select2 = []
1848 1971 for element in all_refs:
1849 1972 children = [{'id': x[0], 'text': x[1]} for x in element[0]]
1850 1973 refs_select2.append({'text': element[1], 'children': children})
1851 1974
1852 1975 return {
1853 1976 'user': {
1854 1977 'user_id': repo.user.user_id,
1855 1978 'username': repo.user.username,
1856 1979 'firstname': repo.user.first_name,
1857 1980 'lastname': repo.user.last_name,
1858 1981 'gravatar_link': h.gravatar_url(repo.user.email, 14),
1859 1982 },
1860 1983 'name': repo.repo_name,
1861 1984 'link': RepoModel().get_url(repo),
1862 1985 'description': h.chop_at_smart(repo.description_safe, '\n'),
1863 1986 'refs': {
1864 1987 'all_refs': all_refs,
1865 1988 'selected_ref': selected_ref,
1866 1989 'select2_refs': refs_select2
1867 1990 }
1868 1991 }
1869 1992
1870 1993 def generate_pullrequest_title(self, source, source_ref, target):
1871 1994 return u'{source}#{at_ref} to {target}'.format(
1872 1995 source=source,
1873 1996 at_ref=source_ref,
1874 1997 target=target,
1875 1998 )
1876 1999
1877 2000 def _cleanup_merge_workspace(self, pull_request):
1878 2001 # Merging related cleanup
1879 2002 repo_id = pull_request.target_repo.repo_id
1880 2003 target_scm = pull_request.target_repo.scm_instance()
1881 2004 workspace_id = self._workspace_id(pull_request)
1882 2005
1883 2006 try:
1884 2007 target_scm.cleanup_merge_workspace(repo_id, workspace_id)
1885 2008 except NotImplementedError:
1886 2009 pass
1887 2010
1888 2011 def _get_repo_pullrequest_sources(
1889 2012 self, repo, commit_id=None, branch=None, bookmark=None,
1890 2013 translator=None):
1891 2014 """
1892 2015 Return a structure with repo's interesting commits, suitable for
1893 2016 the selectors in pullrequest controller
1894 2017
1895 2018 :param commit_id: a commit that must be in the list somehow
1896 2019 and selected by default
1897 2020 :param branch: a branch that must be in the list and selected
1898 2021 by default - even if closed
1899 2022 :param bookmark: a bookmark that must be in the list and selected
1900 2023 """
1901 2024 _ = translator or get_current_request().translate
1902 2025
1903 2026 commit_id = safe_str(commit_id) if commit_id else None
1904 2027 branch = safe_unicode(branch) if branch else None
1905 2028 bookmark = safe_unicode(bookmark) if bookmark else None
1906 2029
1907 2030 selected = None
1908 2031
1909 2032 # order matters: first source that has commit_id in it will be selected
1910 2033 sources = []
1911 2034 sources.append(('book', repo.bookmarks.items(), _('Bookmarks'), bookmark))
1912 2035 sources.append(('branch', repo.branches.items(), _('Branches'), branch))
1913 2036
1914 2037 if commit_id:
1915 2038 ref_commit = (h.short_id(commit_id), commit_id)
1916 2039 sources.append(('rev', [ref_commit], _('Commit IDs'), commit_id))
1917 2040
1918 2041 sources.append(
1919 2042 ('branch', repo.branches_closed.items(), _('Closed Branches'), branch),
1920 2043 )
1921 2044
1922 2045 groups = []
1923 2046
1924 2047 for group_key, ref_list, group_name, match in sources:
1925 2048 group_refs = []
1926 2049 for ref_name, ref_id in ref_list:
1927 2050 ref_key = u'{}:{}:{}'.format(group_key, ref_name, ref_id)
1928 2051 group_refs.append((ref_key, ref_name))
1929 2052
1930 2053 if not selected:
1931 2054 if set([commit_id, match]) & set([ref_id, ref_name]):
1932 2055 selected = ref_key
1933 2056
1934 2057 if group_refs:
1935 2058 groups.append((group_refs, group_name))
1936 2059
1937 2060 if not selected:
1938 2061 ref = commit_id or branch or bookmark
1939 2062 if ref:
1940 2063 raise CommitDoesNotExistError(
1941 2064 u'No commit refs could be found matching: {}'.format(ref))
1942 2065 elif repo.DEFAULT_BRANCH_NAME in repo.branches:
1943 2066 selected = u'branch:{}:{}'.format(
1944 2067 safe_unicode(repo.DEFAULT_BRANCH_NAME),
1945 2068 safe_unicode(repo.branches[repo.DEFAULT_BRANCH_NAME])
1946 2069 )
1947 2070 elif repo.commit_ids:
1948 2071 # make the user select in this case
1949 2072 selected = None
1950 2073 else:
1951 2074 raise EmptyRepositoryError()
1952 2075 return groups, selected
1953 2076
1954 2077 def get_diff(self, source_repo, source_ref_id, target_ref_id,
1955 2078 hide_whitespace_changes, diff_context):
1956 2079
1957 2080 return self._get_diff_from_pr_or_version(
1958 2081 source_repo, source_ref_id, target_ref_id,
1959 2082 hide_whitespace_changes=hide_whitespace_changes, diff_context=diff_context)
1960 2083
1961 2084 def _get_diff_from_pr_or_version(
1962 2085 self, source_repo, source_ref_id, target_ref_id,
1963 2086 hide_whitespace_changes, diff_context):
1964 2087
1965 2088 target_commit = source_repo.get_commit(
1966 2089 commit_id=safe_str(target_ref_id))
1967 2090 source_commit = source_repo.get_commit(
1968 2091 commit_id=safe_str(source_ref_id), maybe_unreachable=True)
1969 2092 if isinstance(source_repo, Repository):
1970 2093 vcs_repo = source_repo.scm_instance()
1971 2094 else:
1972 2095 vcs_repo = source_repo
1973 2096
1974 2097 # TODO: johbo: In the context of an update, we cannot reach
1975 2098 # the old commit anymore with our normal mechanisms. It needs
1976 2099 # some sort of special support in the vcs layer to avoid this
1977 2100 # workaround.
1978 2101 if (source_commit.raw_id == vcs_repo.EMPTY_COMMIT_ID and
1979 2102 vcs_repo.alias == 'git'):
1980 2103 source_commit.raw_id = safe_str(source_ref_id)
1981 2104
1982 2105 log.debug('calculating diff between '
1983 2106 'source_ref:%s and target_ref:%s for repo `%s`',
1984 2107 target_ref_id, source_ref_id,
1985 2108 safe_unicode(vcs_repo.path))
1986 2109
1987 2110 vcs_diff = vcs_repo.get_diff(
1988 2111 commit1=target_commit, commit2=source_commit,
1989 2112 ignore_whitespace=hide_whitespace_changes, context=diff_context)
1990 2113 return vcs_diff
1991 2114
1992 2115 def _is_merge_enabled(self, pull_request):
1993 2116 return self._get_general_setting(
1994 2117 pull_request, 'rhodecode_pr_merge_enabled')
1995 2118
1996 2119 def _use_rebase_for_merging(self, pull_request):
1997 2120 repo_type = pull_request.target_repo.repo_type
1998 2121 if repo_type == 'hg':
1999 2122 return self._get_general_setting(
2000 2123 pull_request, 'rhodecode_hg_use_rebase_for_merging')
2001 2124 elif repo_type == 'git':
2002 2125 return self._get_general_setting(
2003 2126 pull_request, 'rhodecode_git_use_rebase_for_merging')
2004 2127
2005 2128 return False
2006 2129
2007 2130 def _user_name_for_merging(self, pull_request, user):
2008 2131 env_user_name_attr = os.environ.get('RC_MERGE_USER_NAME_ATTR', '')
2009 2132 if env_user_name_attr and hasattr(user, env_user_name_attr):
2010 2133 user_name_attr = env_user_name_attr
2011 2134 else:
2012 2135 user_name_attr = 'short_contact'
2013 2136
2014 2137 user_name = getattr(user, user_name_attr)
2015 2138 return user_name
2016 2139
2017 2140 def _close_branch_before_merging(self, pull_request):
2018 2141 repo_type = pull_request.target_repo.repo_type
2019 2142 if repo_type == 'hg':
2020 2143 return self._get_general_setting(
2021 2144 pull_request, 'rhodecode_hg_close_branch_before_merging')
2022 2145 elif repo_type == 'git':
2023 2146 return self._get_general_setting(
2024 2147 pull_request, 'rhodecode_git_close_branch_before_merging')
2025 2148
2026 2149 return False
2027 2150
2028 2151 def _get_general_setting(self, pull_request, settings_key, default=False):
2029 2152 settings_model = VcsSettingsModel(repo=pull_request.target_repo)
2030 2153 settings = settings_model.get_general_settings()
2031 2154 return settings.get(settings_key, default)
2032 2155
2033 2156 def _log_audit_action(self, action, action_data, user, pull_request):
2034 2157 audit_logger.store(
2035 2158 action=action,
2036 2159 action_data=action_data,
2037 2160 user=user,
2038 2161 repo=pull_request.target_repo)
2039 2162
2040 2163 def get_reviewer_functions(self):
2041 2164 """
2042 2165 Fetches functions for validation and fetching default reviewers.
2043 2166 If available we use the EE package, else we fallback to CE
2044 2167 package functions
2045 2168 """
2046 2169 try:
2047 2170 from rc_reviewers.utils import get_default_reviewers_data
2048 2171 from rc_reviewers.utils import validate_default_reviewers
2049 2172 from rc_reviewers.utils import validate_observers
2050 2173 except ImportError:
2051 2174 from rhodecode.apps.repository.utils import get_default_reviewers_data
2052 2175 from rhodecode.apps.repository.utils import validate_default_reviewers
2053 2176 from rhodecode.apps.repository.utils import validate_observers
2054 2177
2055 2178 return get_default_reviewers_data, validate_default_reviewers, validate_observers
2056 2179
2057 2180
2058 2181 class MergeCheck(object):
2059 2182 """
2060 2183 Perform Merge Checks and returns a check object which stores information
2061 2184 about merge errors, and merge conditions
2062 2185 """
2063 2186 TODO_CHECK = 'todo'
2064 2187 PERM_CHECK = 'perm'
2065 2188 REVIEW_CHECK = 'review'
2066 2189 MERGE_CHECK = 'merge'
2067 2190 WIP_CHECK = 'wip'
2068 2191
2069 2192 def __init__(self):
2070 2193 self.review_status = None
2071 2194 self.merge_possible = None
2072 2195 self.merge_msg = ''
2073 2196 self.merge_response = None
2074 2197 self.failed = None
2075 2198 self.errors = []
2076 2199 self.error_details = OrderedDict()
2077 2200 self.source_commit = AttributeDict()
2078 2201 self.target_commit = AttributeDict()
2079 2202 self.reviewers_count = 0
2080 2203 self.observers_count = 0
2081 2204
2082 2205 def __repr__(self):
2083 2206 return '<MergeCheck(possible:{}, failed:{}, errors:{})>'.format(
2084 2207 self.merge_possible, self.failed, self.errors)
2085 2208
2086 2209 def push_error(self, error_type, message, error_key, details):
2087 2210 self.failed = True
2088 2211 self.errors.append([error_type, message])
2089 2212 self.error_details[error_key] = dict(
2090 2213 details=details,
2091 2214 error_type=error_type,
2092 2215 message=message
2093 2216 )
2094 2217
2095 2218 @classmethod
2096 2219 def validate(cls, pull_request, auth_user, translator, fail_early=False,
2097 2220 force_shadow_repo_refresh=False):
2098 2221 _ = translator
2099 2222 merge_check = cls()
2100 2223
2101 2224 # title has WIP:
2102 2225 if pull_request.work_in_progress:
2103 2226 log.debug("MergeCheck: cannot merge, title has wip: marker.")
2104 2227
2105 2228 msg = _('WIP marker in title prevents from accidental merge.')
2106 2229 merge_check.push_error('error', msg, cls.WIP_CHECK, pull_request.title)
2107 2230 if fail_early:
2108 2231 return merge_check
2109 2232
2110 2233 # permissions to merge
2111 2234 user_allowed_to_merge = PullRequestModel().check_user_merge(pull_request, auth_user)
2112 2235 if not user_allowed_to_merge:
2113 2236 log.debug("MergeCheck: cannot merge, approval is pending.")
2114 2237
2115 2238 msg = _('User `{}` not allowed to perform merge.').format(auth_user.username)
2116 2239 merge_check.push_error('error', msg, cls.PERM_CHECK, auth_user.username)
2117 2240 if fail_early:
2118 2241 return merge_check
2119 2242
2120 2243 # permission to merge into the target branch
2121 2244 target_commit_id = pull_request.target_ref_parts.commit_id
2122 2245 if pull_request.target_ref_parts.type == 'branch':
2123 2246 branch_name = pull_request.target_ref_parts.name
2124 2247 else:
2125 2248 # for mercurial we can always figure out the branch from the commit
2126 2249 # in case of bookmark
2127 2250 target_commit = pull_request.target_repo.get_commit(target_commit_id)
2128 2251 branch_name = target_commit.branch
2129 2252
2130 2253 rule, branch_perm = auth_user.get_rule_and_branch_permission(
2131 2254 pull_request.target_repo.repo_name, branch_name)
2132 2255 if branch_perm and branch_perm == 'branch.none':
2133 2256 msg = _('Target branch `{}` changes rejected by rule {}.').format(
2134 2257 branch_name, rule)
2135 2258 merge_check.push_error('error', msg, cls.PERM_CHECK, auth_user.username)
2136 2259 if fail_early:
2137 2260 return merge_check
2138 2261
2139 2262 # review status, must be always present
2140 2263 review_status = pull_request.calculated_review_status()
2141 2264 merge_check.review_status = review_status
2142 2265 merge_check.reviewers_count = pull_request.reviewers_count
2143 2266 merge_check.observers_count = pull_request.observers_count
2144 2267
2145 2268 status_approved = review_status == ChangesetStatus.STATUS_APPROVED
2146 2269 if not status_approved and merge_check.reviewers_count:
2147 2270 log.debug("MergeCheck: cannot merge, approval is pending.")
2148 2271 msg = _('Pull request reviewer approval is pending.')
2149 2272
2150 2273 merge_check.push_error('warning', msg, cls.REVIEW_CHECK, review_status)
2151 2274
2152 2275 if fail_early:
2153 2276 return merge_check
2154 2277
2155 2278 # left over TODOs
2156 2279 todos = CommentsModel().get_pull_request_unresolved_todos(pull_request)
2157 2280 if todos:
2158 2281 log.debug("MergeCheck: cannot merge, {} "
2159 2282 "unresolved TODOs left.".format(len(todos)))
2160 2283
2161 2284 if len(todos) == 1:
2162 2285 msg = _('Cannot merge, {} TODO still not resolved.').format(
2163 2286 len(todos))
2164 2287 else:
2165 2288 msg = _('Cannot merge, {} TODOs still not resolved.').format(
2166 2289 len(todos))
2167 2290
2168 2291 merge_check.push_error('warning', msg, cls.TODO_CHECK, todos)
2169 2292
2170 2293 if fail_early:
2171 2294 return merge_check
2172 2295
2173 2296 # merge possible, here is the filesystem simulation + shadow repo
2174 2297 merge_response, merge_status, msg = PullRequestModel().merge_status(
2175 2298 pull_request, translator=translator,
2176 2299 force_shadow_repo_refresh=force_shadow_repo_refresh)
2177 2300
2178 2301 merge_check.merge_possible = merge_status
2179 2302 merge_check.merge_msg = msg
2180 2303 merge_check.merge_response = merge_response
2181 2304
2182 2305 source_ref_id = pull_request.source_ref_parts.commit_id
2183 2306 target_ref_id = pull_request.target_ref_parts.commit_id
2184 2307
2185 2308 try:
2186 2309 source_commit, target_commit = PullRequestModel().get_flow_commits(pull_request)
2187 2310 merge_check.source_commit.changed = source_ref_id != source_commit.raw_id
2188 2311 merge_check.source_commit.ref_spec = pull_request.source_ref_parts
2189 2312 merge_check.source_commit.current_raw_id = source_commit.raw_id
2190 2313 merge_check.source_commit.previous_raw_id = source_ref_id
2191 2314
2192 2315 merge_check.target_commit.changed = target_ref_id != target_commit.raw_id
2193 2316 merge_check.target_commit.ref_spec = pull_request.target_ref_parts
2194 2317 merge_check.target_commit.current_raw_id = target_commit.raw_id
2195 2318 merge_check.target_commit.previous_raw_id = target_ref_id
2196 2319 except (SourceRefMissing, TargetRefMissing):
2197 2320 pass
2198 2321
2199 2322 if not merge_status:
2200 2323 log.debug("MergeCheck: cannot merge, pull request merge not possible.")
2201 2324 merge_check.push_error('warning', msg, cls.MERGE_CHECK, None)
2202 2325
2203 2326 if fail_early:
2204 2327 return merge_check
2205 2328
2206 2329 log.debug('MergeCheck: is failed: %s', merge_check.failed)
2207 2330 return merge_check
2208 2331
2209 2332 @classmethod
2210 2333 def get_merge_conditions(cls, pull_request, translator):
2211 2334 _ = translator
2212 2335 merge_details = {}
2213 2336
2214 2337 model = PullRequestModel()
2215 2338 use_rebase = model._use_rebase_for_merging(pull_request)
2216 2339
2217 2340 if use_rebase:
2218 2341 merge_details['merge_strategy'] = dict(
2219 2342 details={},
2220 2343 message=_('Merge strategy: rebase')
2221 2344 )
2222 2345 else:
2223 2346 merge_details['merge_strategy'] = dict(
2224 2347 details={},
2225 2348 message=_('Merge strategy: explicit merge commit')
2226 2349 )
2227 2350
2228 2351 close_branch = model._close_branch_before_merging(pull_request)
2229 2352 if close_branch:
2230 2353 repo_type = pull_request.target_repo.repo_type
2231 2354 close_msg = ''
2232 2355 if repo_type == 'hg':
2233 2356 close_msg = _('Source branch will be closed before the merge.')
2234 2357 elif repo_type == 'git':
2235 2358 close_msg = _('Source branch will be deleted after the merge.')
2236 2359
2237 2360 merge_details['close_branch'] = dict(
2238 2361 details={},
2239 2362 message=close_msg
2240 2363 )
2241 2364
2242 2365 return merge_details
2243 2366
2244 2367
2245 2368 ChangeTuple = collections.namedtuple(
2246 2369 'ChangeTuple', ['added', 'common', 'removed', 'total'])
2247 2370
2248 2371 FileChangeTuple = collections.namedtuple(
2249 2372 'FileChangeTuple', ['added', 'modified', 'removed'])
@@ -1,630 +1,630 b''
1 1
2 2
3 3 //BUTTONS
4 4 button,
5 5 .btn,
6 6 input[type="button"] {
7 7 -webkit-appearance: none;
8 8 display: inline-block;
9 9 margin: 0 @padding/3 0 0;
10 10 padding: @button-padding;
11 11 text-align: center;
12 12 font-size: @basefontsize;
13 13 line-height: 1em;
14 14 font-family: @text-light;
15 15 text-decoration: none;
16 16 text-shadow: none;
17 17 color: @grey2;
18 18 background-color: white;
19 19 background-image: none;
20 20 border: none;
21 21 .border ( @border-thickness-buttons, @grey5 );
22 22 .border-radius (@border-radius);
23 23 cursor: pointer;
24 24 white-space: nowrap;
25 25 -webkit-transition: background .3s,color .3s;
26 26 -moz-transition: background .3s,color .3s;
27 27 -o-transition: background .3s,color .3s;
28 28 transition: background .3s,color .3s;
29 29 box-shadow: @button-shadow;
30 30 -webkit-box-shadow: @button-shadow;
31 31
32 32
33 33
34 34 a {
35 35 display: block;
36 36 margin: 0;
37 37 padding: 0;
38 38 color: inherit;
39 39 text-decoration: none;
40 40
41 41 &:hover {
42 42 text-decoration: none;
43 43 }
44 44 }
45 45
46 46 &:focus,
47 47 &:active {
48 48 outline:none;
49 49 }
50 50
51 51 &:hover {
52 52 color: @rcdarkblue;
53 53 background-color: @grey6;
54 54
55 55 }
56 56
57 57 &.btn-active {
58 58 color: @rcdarkblue;
59 59 background-color: @grey6;
60 60 }
61 61
62 62 .icon-remove {
63 63 display: none;
64 64 }
65 65
66 66 //disabled buttons
67 67 //last; overrides any other styles
68 68 &:disabled {
69 69 opacity: .7;
70 70 cursor: auto;
71 71 background-color: white;
72 72 color: @grey4;
73 73 text-shadow: none;
74 74 }
75 75
76 76 &.no-margin {
77 77 margin: 0 0 0 0;
78 78 }
79 79
80 80
81 81
82 82 }
83 83
84 84
85 85 .btn-default {
86 86 border: @border-thickness solid @grey5;
87 87 background-image: none;
88 88 color: @grey2;
89 89
90 90 a {
91 91 color: @grey2;
92 92 }
93 93
94 94 &:hover,
95 95 &.active {
96 96 color: @rcdarkblue;
97 97 background-color: @white;
98 98 .border ( @border-thickness, @grey4 );
99 99
100 100 a {
101 101 color: @grey2;
102 102 }
103 103 }
104 104 &:disabled {
105 105 .border ( @border-thickness-buttons, @grey5 );
106 106 background-color: transparent;
107 107 }
108 108 &.btn-active {
109 109 color: @rcdarkblue;
110 110 background-color: @white;
111 111 .border ( @border-thickness, @rcdarkblue );
112 112 }
113 113 }
114 114
115 115 .btn-primary,
116 116 .btn-small, /* TODO: anderson: remove .btn-small to not mix with the new btn-sm */
117 117 .btn-success {
118 118 .border ( @border-thickness, @rcblue );
119 119 background-color: @rcblue;
120 120 color: white;
121 121
122 122 a {
123 123 color: white;
124 124 }
125 125
126 126 &:hover,
127 127 &.active {
128 128 .border ( @border-thickness, @rcdarkblue );
129 129 color: white;
130 130 background-color: @rcdarkblue;
131 131
132 132 a {
133 133 color: white;
134 134 }
135 135 }
136 136 &:disabled {
137 137 background-color: @rcblue;
138 138 }
139 139 }
140 140
141 141 .btn-secondary {
142 142 &:extend(.btn-default);
143 143
144 144 background-color: white;
145 145
146 146 &:focus {
147 147 outline: 0;
148 148 }
149 149
150 150 &:hover {
151 151 &:extend(.btn-default:hover);
152 152 }
153 153
154 154 &.btn-link {
155 155 &:extend(.btn-link);
156 156 color: @rcblue;
157 157 }
158 158
159 159 &:disabled {
160 160 color: @rcblue;
161 161 background-color: white;
162 162 }
163 163 }
164 164
165 165 .btn-danger,
166 166 .revoke_perm,
167 167 .btn-x,
168 168 .form .action_button.btn-x {
169 169 .border ( @border-thickness, @alert2 );
170 170 background-color: white;
171 171 color: @alert2;
172 172
173 173 a {
174 174 color: @alert2;
175 175 }
176 176
177 177 &:hover,
178 178 &.active {
179 179 .border ( @border-thickness, @alert2 );
180 180 color: white;
181 181 background-color: @alert2;
182 182
183 183 a {
184 184 color: white;
185 185 }
186 186 }
187 187
188 188 i {
189 189 display:none;
190 190 }
191 191
192 192 &:disabled {
193 193 background-color: white;
194 194 color: @alert2;
195 195 }
196 196 }
197 197
198 198 .btn-warning {
199 199 .border ( @border-thickness, @alert3 );
200 200 background-color: white;
201 201 color: @alert3;
202 202
203 203 a {
204 204 color: @alert3;
205 205 }
206 206
207 207 &:hover,
208 208 &.active {
209 209 .border ( @border-thickness, @alert3 );
210 210 color: white;
211 211 background-color: @alert3;
212 212
213 213 a {
214 214 color: white;
215 215 }
216 216 }
217 217
218 218 i {
219 219 display:none;
220 220 }
221 221
222 222 &:disabled {
223 223 background-color: white;
224 224 color: @alert3;
225 225 }
226 226 }
227 227
228 228
229 229 .btn-approved-status {
230 230 .border ( @border-thickness, @alert1 );
231 231 background-color: white;
232 232 color: @alert1;
233 233
234 234 }
235 235
236 236 .btn-rejected-status {
237 237 .border ( @border-thickness, @alert2 );
238 238 background-color: white;
239 239 color: @alert2;
240 240 }
241 241
242 242 .btn-sm,
243 243 .btn-mini,
244 244 .field-sm .btn {
245 245 padding: @padding/3;
246 246 }
247 247
248 248 .btn-xs {
249 249 padding: @padding/4;
250 250 }
251 251
252 252 .btn-lg {
253 253 padding: @padding * 1.2;
254 254 }
255 255
256 256 .btn-group {
257 257 display: inline-block;
258 258 .btn {
259 259 float: left;
260 260 margin: 0 0 0 0;
261 261 // first item
262 262 &:first-of-type:not(:last-of-type) {
263 263 border-radius: @border-radius 0 0 @border-radius;
264 264
265 265 }
266 266 // 2nd, if only 2 elements are there
267 267 &:nth-of-type(2) {
268 268 border-left-width: 0;
269 269 }
270 270 // middle elements
271 271 &:not(:first-of-type):not(:last-of-type) {
272 272 border-radius: 0;
273 273 border-left-width: 0;
274 274 border-right-width: 0;
275 275 }
276 276 // last item
277 277 &:last-of-type:not(:first-of-type) {
278 278 border-radius: 0 @border-radius @border-radius 0;
279 279 }
280 280
281 281 &:only-child {
282 282 border-radius: @border-radius;
283 283 }
284 284 }
285 285
286 286 }
287 287
288 288
289 289 .btn-group-actions {
290 290 position: relative;
291 291 z-index: 50;
292 292
293 293 &:not(.open) .btn-action-switcher-container {
294 294 display: none;
295 295 }
296 296
297 297 .btn-more-option {
298 298 margin-left: -1px;
299 299 padding-left: 2px;
300 300 padding-right: 2px;
301 301 }
302 302 }
303 303
304 304
305 305 .btn-action-switcher-container {
306 306 position: absolute;
307 307 top: 100%;
308 308
309 309 &.left-align {
310 310 left: 0;
311 311 }
312 312 &.right-align {
313 313 right: 0;
314 314 }
315 315
316 316 }
317 317
318 318 .btn-action-switcher {
319 319 display: block;
320 320 position: relative;
321 321 z-index: 300;
322 322 max-width: 600px;
323 323 margin-top: 4px;
324 324 margin-bottom: 24px;
325 325 font-size: 14px;
326 326 font-weight: 400;
327 327 padding: 8px 0;
328 328 background-color: #fff;
329 329 border: 1px solid @grey4;
330 330 border-radius: 3px;
331 331 box-shadow: @dropdown-shadow;
332 332 overflow: auto;
333 333
334 334 li {
335 335 display: block;
336 336 text-align: left;
337 337 list-style: none;
338 338 padding: 5px 10px;
339 339 }
340 340
341 341 li .action-help-block {
342 342 font-size: 10px;
343 343 line-height: normal;
344 344 color: @grey4;
345 345 }
346 346
347 347 }
348 348
349 349 .btn-link {
350 350 background: transparent;
351 351 border: none;
352 352 padding: 0;
353 353 color: @rcblue;
354 354
355 355 &:hover {
356 356 background: transparent;
357 357 border: none;
358 358 color: @rcdarkblue;
359 359 }
360 360
361 361 //disabled buttons
362 362 //last; overrides any other styles
363 363 &:disabled {
364 364 opacity: .7;
365 365 cursor: auto;
366 366 background-color: white;
367 367 color: @grey4;
368 368 text-shadow: none;
369 369 }
370 370
371 371 // TODO: johbo: Check if we can avoid this, indicates that the structure
372 372 // is not yet good.
373 373 // lisa: The button CSS reflects the button HTML; both need a cleanup.
374 374 &.btn-danger {
375 375 color: @alert2;
376 376
377 377 &:hover {
378 378 color: darken(@alert2, 30%);
379 379 }
380 380
381 381 &:disabled {
382 382 color: @alert2;
383 383 }
384 384 }
385 385 }
386 386
387 387 .btn-social {
388 388 &:extend(.btn-default);
389 389 margin: 5px 5px 5px 0px;
390 390 min-width: 160px;
391 391 }
392 392
393 393 // TODO: johbo: check these exceptions
394 394
395 395 .links {
396 396
397 397 .btn + .btn {
398 398 margin-top: @padding;
399 399 }
400 400 }
401 401
402 402
403 403 .action_button {
404 404 display:inline;
405 405 margin: 0;
406 406 padding: 0 1em 0 0;
407 407 font-size: inherit;
408 408 color: @rcblue;
409 409 border: none;
410 410 border-radius: 0;
411 411 background-color: transparent;
412 412
413 413 &.last-item {
414 414 border: none;
415 415 padding: 0 0 0 0;
416 416 }
417 417
418 418 &:last-child {
419 419 border: none;
420 420 padding: 0 0 0 0;
421 421 }
422 422
423 423 &:hover {
424 424 color: @rcdarkblue;
425 425 background-color: transparent;
426 426 border: none;
427 427 }
428 428 .noselect
429 429 }
430 430
431 431 .grid_delete {
432 432 .action_button {
433 433 border: none;
434 434 }
435 435 }
436 436
437 437 input[type="submit"].btn-draft {
438 438 .border ( @border-thickness, @rcblue );
439 439 background-color: white;
440 440 color: @rcblue;
441 441
442 442 a {
443 443 color: @rcblue;
444 444 }
445 445
446 446 &:hover,
447 447 &.active {
448 448 .border ( @border-thickness, @rcdarkblue );
449 449 background-color: white;
450 450 color: @rcdarkblue;
451 451
452 452 a {
453 453 color: @rcdarkblue;
454 454 }
455 455 }
456 456
457 457 &:disabled {
458 458 background-color: white;
459 459 color: @rcblue;
460 460 }
461 461 }
462 462
463 463 input[type="submit"].btn-warning {
464 464 &:extend(.btn-warning);
465 465
466 466 &:focus {
467 467 outline: 0;
468 468 }
469 469
470 470 &:hover {
471 471 &:extend(.btn-warning:hover);
472 472 }
473 473
474 474 &.btn-link {
475 475 &:extend(.btn-link);
476 476 color: @alert3;
477 477
478 478 &:disabled {
479 479 color: @alert3;
480 480 background-color: transparent;
481 481 }
482 482 }
483 483
484 484 &:disabled {
485 485 .border ( @border-thickness-buttons, @alert3 );
486 486 background-color: white;
487 487 color: @alert3;
488 488 opacity: 0.5;
489 489 }
490 490 }
491 491
492 492
493 493
494 494 // TODO: johbo: Form button tweaks, check if we can use the classes instead
495 495 input[type="submit"] {
496 496 &:extend(.btn-primary);
497 497
498 498 &:focus {
499 499 outline: 0;
500 500 }
501 501
502 502 &:hover {
503 503 &:extend(.btn-primary:hover);
504 504 }
505 505
506 506 &.btn-link {
507 507 &:extend(.btn-link);
508 508 color: @rcblue;
509 509
510 510 &:disabled {
511 511 color: @rcblue;
512 512 background-color: transparent;
513 513 }
514 514 }
515 515
516 516 &:disabled {
517 517 .border ( @border-thickness-buttons, @rcblue );
518 518 background-color: @rcblue;
519 519 color: white;
520 520 opacity: 0.5;
521 521 }
522 522 }
523 523
524 524 input[type="reset"] {
525 525 &:extend(.btn-default);
526 526
527 527 // TODO: johbo: Check if this tweak can be avoided.
528 528 background: transparent;
529 529
530 530 &:focus {
531 531 outline: 0;
532 532 }
533 533
534 534 &:hover {
535 535 &:extend(.btn-default:hover);
536 536 }
537 537
538 538 &.btn-link {
539 539 &:extend(.btn-link);
540 540 color: @rcblue;
541 541
542 542 &:disabled {
543 543 border: none;
544 544 }
545 545 }
546 546
547 547 &:disabled {
548 548 .border ( @border-thickness-buttons, @rcblue );
549 549 background-color: white;
550 550 color: @rcblue;
551 551 }
552 552 }
553 553
554 554 input[type="submit"],
555 555 input[type="reset"] {
556 556 &.btn-danger {
557 557 &:extend(.btn-danger);
558 558
559 559 &:focus {
560 560 outline: 0;
561 561 }
562 562
563 563 &:hover {
564 564 &:extend(.btn-danger:hover);
565 565 }
566 566
567 567 &.btn-link {
568 568 &:extend(.btn-link);
569 569 color: @alert2;
570 570
571 571 &:hover {
572 572 color: darken(@alert2,30%);
573 573 }
574 574 }
575 575
576 576 &:disabled {
577 577 color: @alert2;
578 578 background-color: white;
579 579 }
580 580 }
581 581 &.btn-danger-action {
582 582 .border ( @border-thickness, @alert2 );
583 583 background-color: @alert2;
584 584 color: white;
585 585
586 586 a {
587 587 color: white;
588 588 }
589 589
590 590 &:hover {
591 591 background-color: darken(@alert2,20%);
592 592 }
593 593
594 594 &.active {
595 595 .border ( @border-thickness, @alert2 );
596 596 color: white;
597 597 background-color: @alert2;
598 598
599 599 a {
600 600 color: white;
601 601 }
602 602 }
603 603
604 604 &:disabled {
605 605 background-color: white;
606 606 color: @alert2;
607 607 }
608 608 }
609 609 }
610 610
611 611
612 612 .button-links {
613 613 float: left;
614 614 display: inline;
615 615 margin: 0;
616 616 padding-left: 0;
617 617 list-style: none;
618 618 text-align: right;
619 619
620 620 li {
621
622
621 list-style: none;
622 text-align: right;
623 display: inline-block;
623 624 }
624 625
625 li.active {
626 background-color: @grey6;
627 .border ( @border-thickness, @grey4 );
626 a.active {
627 border: 2px solid @rcblue;
628 628 }
629 629
630 630 }
@@ -1,85 +1,85 b''
1 1 // See panels-bootstrap.less
2 2 // These provide overrides for custom styling of Bootstrap panels
3 3
4 4 .panel {
5 5 &:extend(.clearfix);
6 6
7 7 width: 100%;
8 8 margin: 0 0 25px 0;
9 9 .border-radius(@border-radius);
10 10 .box-shadow(none);
11 11
12 12 .permalink {
13 13 visibility: hidden;
14 14 }
15 15
16 16 &:hover .permalink {
17 17 visibility: visible;
18 18 color: @rcblue;
19 19 }
20 20
21 21 .panel-heading {
22 22 position: relative;
23 23 min-height: 1em;
24 24 padding: @padding @panel-padding;
25 25 border-bottom: none;
26 26
27 27 .panel-title,
28 28 h3.panel-title {
29 29 float: left;
30 30 padding: 0 @padding 0 0;
31 31 line-height: 1;
32 32 font-size: @panel-title;
33 33 color: @grey1;
34 34 }
35 35
36 36 .panel-edit {
37 37 float: right;
38 38 line-height: 1;
39 39 font-size: @panel-title;
40 40 }
41 41 }
42 42
43 43 .panel-body {
44 44 padding: @panel-padding;
45 45
46 46 &.panel-body-min-height {
47 min-height: 150px
47 min-height: 200px
48 48 }
49 49 }
50 50
51 51 .panel-footer {
52 52 background-color: white;
53 53 padding: .65em @panel-padding .5em;
54 54 font-size: @panel-footer;
55 55 color: @text-muted;
56 56 }
57 57
58 58 .q_filter_box {
59 59 min-width: 40%;
60 60 }
61 61
62 62 // special cases
63 63 &.user-profile {
64 64 float: left;
65 65
66 66 .panel-body {
67 67 &:extend(.clearfix);
68 68 }
69 69 }
70 70 }
71 71
72 72 .main-content h3.panel-title {
73 73 font-size: @panel-title;
74 74 color: @grey1;
75 75 }
76 76
77 77 .panel-body-title-text {
78 78 margin: 0 0 20px 0;
79 79 }
80 80
81 81 // play nice with the current form and field css
82 82 .field.panel-default,
83 83 .form.panel-default {
84 84 width: auto;
85 85 } No newline at end of file
@@ -1,1639 +1,1644 b''
1 1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 2 // #
3 3 // # This program is free software: you can redistribute it and/or modify
4 4 // # it under the terms of the GNU Affero General Public License, version 3
5 5 // # (only), as published by the Free Software Foundation.
6 6 // #
7 7 // # This program is distributed in the hope that it will be useful,
8 8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 // # GNU General Public License for more details.
11 11 // #
12 12 // # You should have received a copy of the GNU Affero General Public License
13 13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 // #
15 15 // # This program is dual-licensed. If you wish to learn more about the
16 16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 var firefoxAnchorFix = function() {
20 20 // hack to make anchor links behave properly on firefox, in our inline
21 21 // comments generation when comments are injected firefox is misbehaving
22 22 // when jumping to anchor links
23 23 if (location.href.indexOf('#') > -1) {
24 24 location.href += '';
25 25 }
26 26 };
27 27
28 28
29 29 var linkifyComments = function(comments) {
30 30 var firstCommentId = null;
31 31 if (comments) {
32 32 firstCommentId = $(comments[0]).data('comment-id');
33 33 }
34 34
35 35 if (firstCommentId){
36 36 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
37 37 }
38 38 };
39 39
40 40
41 41 var bindToggleButtons = function() {
42 42 $('.comment-toggle').on('click', function() {
43 43 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
44 44 });
45 45 };
46 46
47 47
48 48 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
49 49 failHandler = failHandler || function() {};
50 50 postData = toQueryString(postData);
51 51 var request = $.ajax({
52 52 url: url,
53 53 type: 'POST',
54 54 data: postData,
55 55 headers: {'X-PARTIAL-XHR': true}
56 56 })
57 57 .done(function (data) {
58 58 successHandler(data);
59 59 })
60 60 .fail(function (data, textStatus, errorThrown) {
61 61 failHandler(data, textStatus, errorThrown)
62 62 });
63 63 return request;
64 64 };
65 65
66 66
67 67 /* Comment form for main and inline comments */
68 68 (function(mod) {
69 69
70 70 if (typeof exports == "object" && typeof module == "object") {
71 71 // CommonJS
72 72 module.exports = mod();
73 73 }
74 74 else {
75 75 // Plain browser env
76 76 (this || window).CommentForm = mod();
77 77 }
78 78
79 79 })(function() {
80 80 "use strict";
81 81
82 82 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id) {
83 83
84 84 if (!(this instanceof CommentForm)) {
85 85 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id);
86 86 }
87 87
88 88 // bind the element instance to our Form
89 89 $(formElement).get(0).CommentForm = this;
90 90
91 91 this.withLineNo = function(selector) {
92 92 var lineNo = this.lineNo;
93 93 if (lineNo === undefined) {
94 94 return selector
95 95 } else {
96 96 return selector + '_' + lineNo;
97 97 }
98 98 };
99 99
100 100 this.commitId = commitId;
101 101 this.pullRequestId = pullRequestId;
102 102 this.lineNo = lineNo;
103 103 this.initAutocompleteActions = initAutocompleteActions;
104 104
105 105 this.previewButton = this.withLineNo('#preview-btn');
106 106 this.previewContainer = this.withLineNo('#preview-container');
107 107
108 108 this.previewBoxSelector = this.withLineNo('#preview-box');
109 109
110 110 this.editButton = this.withLineNo('#edit-btn');
111 111 this.editContainer = this.withLineNo('#edit-container');
112 112 this.cancelButton = this.withLineNo('#cancel-btn');
113 113 this.commentType = this.withLineNo('#comment_type');
114 114
115 115 this.resolvesId = null;
116 116 this.resolvesActionId = null;
117 117
118 118 this.closesPr = '#close_pull_request';
119 119
120 120 this.cmBox = this.withLineNo('#text');
121 121 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
122 122
123 123 this.statusChange = this.withLineNo('#change_status');
124 124
125 125 this.submitForm = formElement;
126 126
127 127 this.submitButton = $(this.submitForm).find('.submit-comment-action');
128 128 this.submitButtonText = this.submitButton.val();
129 129
130 130 this.submitDraftButton = $(this.submitForm).find('.submit-draft-action');
131 131 this.submitDraftButtonText = this.submitDraftButton.val();
132 132
133 133 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
134 134 {'repo_name': templateContext.repo_name,
135 135 'commit_id': templateContext.commit_data.commit_id});
136 136
137 137 if (edit){
138 138 this.submitDraftButton.hide();
139 139 this.submitButtonText = _gettext('Update Comment');
140 140 $(this.commentType).prop('disabled', true);
141 141 $(this.commentType).addClass('disabled');
142 142 var editInfo =
143 143 '';
144 144 $(editInfo).insertBefore($(this.editButton).parent());
145 145 }
146 146
147 147 if (resolvesCommentId){
148 148 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
149 149 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
150 150 $(this.commentType).prop('disabled', true);
151 151 $(this.commentType).addClass('disabled');
152 152
153 153 // disable select
154 154 setTimeout(function() {
155 155 $(self.statusChange).select2('readonly', true);
156 156 }, 10);
157 157
158 158 var resolvedInfo = (
159 159 '<li class="resolve-action">' +
160 160 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
161 161 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
162 162 '</li>'
163 163 ).format(resolvesCommentId, _gettext('resolve comment'));
164 164 $(resolvedInfo).insertAfter($(this.commentType).parent());
165 165 }
166 166
167 167 // based on commitId, or pullRequestId decide where do we submit
168 168 // out data
169 169 if (this.commitId){
170 170 var pyurl = 'repo_commit_comment_create';
171 171 if(edit){
172 172 pyurl = 'repo_commit_comment_edit';
173 173 }
174 174 this.submitUrl = pyroutes.url(pyurl,
175 175 {'repo_name': templateContext.repo_name,
176 176 'commit_id': this.commitId,
177 177 'comment_id': comment_id});
178 178 this.selfUrl = pyroutes.url('repo_commit',
179 179 {'repo_name': templateContext.repo_name,
180 180 'commit_id': this.commitId});
181 181
182 182 } else if (this.pullRequestId) {
183 183 var pyurl = 'pullrequest_comment_create';
184 184 if(edit){
185 185 pyurl = 'pullrequest_comment_edit';
186 186 }
187 187 this.submitUrl = pyroutes.url(pyurl,
188 188 {'repo_name': templateContext.repo_name,
189 189 'pull_request_id': this.pullRequestId,
190 190 'comment_id': comment_id});
191 191 this.selfUrl = pyroutes.url('pullrequest_show',
192 192 {'repo_name': templateContext.repo_name,
193 193 'pull_request_id': this.pullRequestId});
194 194
195 195 } else {
196 196 throw new Error(
197 197 'CommentForm requires pullRequestId, or commitId to be specified.')
198 198 }
199 199
200 200 // FUNCTIONS and helpers
201 201 var self = this;
202 202
203 203 this.isInline = function(){
204 204 return this.lineNo && this.lineNo != 'general';
205 205 };
206 206
207 207 this.getCmInstance = function(){
208 208 return this.cm
209 209 };
210 210
211 211 this.setPlaceholder = function(placeholder) {
212 212 var cm = this.getCmInstance();
213 213 if (cm){
214 214 cm.setOption('placeholder', placeholder);
215 215 }
216 216 };
217 217
218 218 this.getCommentStatus = function() {
219 219 return $(this.submitForm).find(this.statusChange).val();
220 220 };
221 221
222 222 this.getCommentType = function() {
223 223 return $(this.submitForm).find(this.commentType).val();
224 224 };
225 225
226 226 this.getDraftState = function () {
227 227 var submitterElem = $(this.submitForm).find('input[type="submit"].submitter');
228 228 var data = $(submitterElem).data('isDraft');
229 229 return data
230 230 }
231 231
232 232 this.getResolvesId = function() {
233 233 return $(this.submitForm).find(this.resolvesId).val() || null;
234 234 };
235 235
236 236 this.getClosePr = function() {
237 237 return $(this.submitForm).find(this.closesPr).val() || null;
238 238 };
239 239
240 240 this.markCommentResolved = function(resolvedCommentId){
241 241 Rhodecode.comments.markCommentResolved(resolvedCommentId)
242 242 };
243 243
244 244 this.isAllowedToSubmit = function() {
245 245 var commentDisabled = $(this.submitButton).prop('disabled');
246 246 var draftDisabled = $(this.submitDraftButton).prop('disabled');
247 247 return !commentDisabled && !draftDisabled;
248 248 };
249 249
250 250 this.initStatusChangeSelector = function(){
251 251 var formatChangeStatus = function(state, escapeMarkup) {
252 252 var originalOption = state.element;
253 253 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
254 254 return tmpl
255 255 };
256 256 var formatResult = function(result, container, query, escapeMarkup) {
257 257 return formatChangeStatus(result, escapeMarkup);
258 258 };
259 259
260 260 var formatSelection = function(data, container, escapeMarkup) {
261 261 return formatChangeStatus(data, escapeMarkup);
262 262 };
263 263
264 264 $(this.submitForm).find(this.statusChange).select2({
265 265 placeholder: _gettext('Status Review'),
266 266 formatResult: formatResult,
267 267 formatSelection: formatSelection,
268 268 containerCssClass: "drop-menu status_box_menu",
269 269 dropdownCssClass: "drop-menu-dropdown",
270 270 dropdownAutoWidth: true,
271 271 minimumResultsForSearch: -1
272 272 });
273 273
274 274 $(this.submitForm).find(this.statusChange).on('change', function() {
275 275 var status = self.getCommentStatus();
276 276
277 277 if (status && !self.isInline()) {
278 278 $(self.submitButton).prop('disabled', false);
279 279 $(self.submitDraftButton).prop('disabled', false);
280 280 }
281 281
282 282 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
283 283 self.setPlaceholder(placeholderText)
284 284 })
285 285 };
286 286
287 287 // reset the comment form into it's original state
288 288 this.resetCommentFormState = function(content) {
289 289 content = content || '';
290 290
291 291 $(this.editContainer).show();
292 292 $(this.editButton).parent().addClass('active');
293 293
294 294 $(this.previewContainer).hide();
295 295 $(this.previewButton).parent().removeClass('active');
296 296
297 297 this.setActionButtonsDisabled(true);
298 298 self.cm.setValue(content);
299 299 self.cm.setOption("readOnly", false);
300 300
301 301 if (this.resolvesId) {
302 302 // destroy the resolve action
303 303 $(this.resolvesId).parent().remove();
304 304 }
305 305 // reset closingPR flag
306 306 $('.close-pr-input').remove();
307 307
308 308 $(this.statusChange).select2('readonly', false);
309 309 };
310 310
311 311 this.globalSubmitSuccessCallback = function(comment){
312 312 // default behaviour is to call GLOBAL hook, if it's registered.
313 313 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
314 314 commentFormGlobalSubmitSuccessCallback(comment);
315 315 }
316 316 };
317 317
318 318 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
319 319 return _submitAjaxPOST(url, postData, successHandler, failHandler);
320 320 };
321 321
322 322 // overwrite a submitHandler, we need to do it for inline comments
323 323 this.setHandleFormSubmit = function(callback) {
324 324 this.handleFormSubmit = callback;
325 325 };
326 326
327 327 // overwrite a submitSuccessHandler
328 328 this.setGlobalSubmitSuccessCallback = function(callback) {
329 329 this.globalSubmitSuccessCallback = callback;
330 330 };
331 331
332 332 // default handler for for submit for main comments
333 333 this.handleFormSubmit = function() {
334 334 var text = self.cm.getValue();
335 335 var status = self.getCommentStatus();
336 336 var commentType = self.getCommentType();
337 337 var isDraft = self.getDraftState();
338 338 var resolvesCommentId = self.getResolvesId();
339 339 var closePullRequest = self.getClosePr();
340 340
341 341 if (text === "" && !status) {
342 342 return;
343 343 }
344 344
345 345 var excludeCancelBtn = false;
346 346 var submitEvent = true;
347 347 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
348 348 self.cm.setOption("readOnly", true);
349 349
350 350 var postData = {
351 351 'text': text,
352 352 'changeset_status': status,
353 353 'comment_type': commentType,
354 354 'csrf_token': CSRF_TOKEN
355 355 };
356 356
357 357 if (resolvesCommentId) {
358 358 postData['resolves_comment_id'] = resolvesCommentId;
359 359 }
360 360
361 361 if (closePullRequest) {
362 362 postData['close_pull_request'] = true;
363 363 }
364 364
365 365 // submitSuccess for general comments
366 366 var submitSuccessCallback = function(json_data) {
367 367 // reload page if we change status for single commit.
368 368 if (status && self.commitId) {
369 369 location.reload(true);
370 370 } else {
371 371 // inject newly created comments, json_data is {<comment_id>: {}}
372 372 Rhodecode.comments.attachGeneralComment(json_data)
373 373
374 374 self.resetCommentFormState();
375 375 timeagoActivate();
376 376 tooltipActivate();
377 377
378 378 // mark visually which comment was resolved
379 379 if (resolvesCommentId) {
380 380 self.markCommentResolved(resolvesCommentId);
381 381 }
382 382 }
383 383
384 384 // run global callback on submit
385 385 self.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
386 386
387 387 };
388 388 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
389 389 var prefix = "Error while submitting comment.\n"
390 390 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
391 391 ajaxErrorSwal(message);
392 392 self.resetCommentFormState(text);
393 393 };
394 394 self.submitAjaxPOST(
395 395 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
396 396 };
397 397
398 398 this.previewSuccessCallback = function(o) {
399 399 $(self.previewBoxSelector).html(o);
400 400 $(self.previewBoxSelector).removeClass('unloaded');
401 401
402 402 // swap buttons, making preview active
403 403 $(self.previewButton).parent().addClass('active');
404 404 $(self.editButton).parent().removeClass('active');
405 405
406 406 // unlock buttons
407 407 self.setActionButtonsDisabled(false);
408 408 };
409 409
410 410 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
411 411 excludeCancelBtn = excludeCancelBtn || false;
412 412 submitEvent = submitEvent || false;
413 413
414 414 $(this.editButton).prop('disabled', state);
415 415 $(this.previewButton).prop('disabled', state);
416 416
417 417 if (!excludeCancelBtn) {
418 418 $(this.cancelButton).prop('disabled', state);
419 419 }
420 420
421 421 var submitState = state;
422 422 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
423 423 // if the value of commit review status is set, we allow
424 424 // submit button, but only on Main form, isInline means inline
425 425 submitState = false
426 426 }
427 427
428 428 $(this.submitButton).prop('disabled', submitState);
429 429 $(this.submitDraftButton).prop('disabled', submitState);
430 430
431 431 if (submitEvent) {
432 432 var isDraft = self.getDraftState();
433 433
434 434 if (isDraft) {
435 435 $(this.submitDraftButton).val(_gettext('Saving Draft...'));
436 436 } else {
437 437 $(this.submitButton).val(_gettext('Submitting...'));
438 438 }
439 439
440 440 } else {
441 441 $(this.submitButton).val(this.submitButtonText);
442 442 $(this.submitDraftButton).val(this.submitDraftButtonText);
443 443 }
444 444
445 445 };
446 446
447 447 // lock preview/edit/submit buttons on load, but exclude cancel button
448 448 var excludeCancelBtn = true;
449 449 this.setActionButtonsDisabled(true, excludeCancelBtn);
450 450
451 451 // anonymous users don't have access to initialized CM instance
452 452 if (this.cm !== undefined){
453 453 this.cm.on('change', function(cMirror) {
454 454 if (cMirror.getValue() === "") {
455 455 self.setActionButtonsDisabled(true, excludeCancelBtn)
456 456 } else {
457 457 self.setActionButtonsDisabled(false, excludeCancelBtn)
458 458 }
459 459 });
460 460 }
461 461
462 462 $(this.editButton).on('click', function(e) {
463 463 e.preventDefault();
464 464
465 465 $(self.previewButton).parent().removeClass('active');
466 466 $(self.previewContainer).hide();
467 467
468 468 $(self.editButton).parent().addClass('active');
469 469 $(self.editContainer).show();
470 470
471 471 });
472 472
473 473 $(this.previewButton).on('click', function(e) {
474 474 e.preventDefault();
475 475 var text = self.cm.getValue();
476 476
477 477 if (text === "") {
478 478 return;
479 479 }
480 480
481 481 var postData = {
482 482 'text': text,
483 483 'renderer': templateContext.visual.default_renderer,
484 484 'csrf_token': CSRF_TOKEN
485 485 };
486 486
487 487 // lock ALL buttons on preview
488 488 self.setActionButtonsDisabled(true);
489 489
490 490 $(self.previewBoxSelector).addClass('unloaded');
491 491 $(self.previewBoxSelector).html(_gettext('Loading ...'));
492 492
493 493 $(self.editContainer).hide();
494 494 $(self.previewContainer).show();
495 495
496 496 // by default we reset state of comment preserving the text
497 497 var previewFailCallback = function(jqXHR, textStatus, errorThrown) {
498 498 var prefix = "Error while preview of comment.\n"
499 499 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
500 500 ajaxErrorSwal(message);
501 501
502 502 self.resetCommentFormState(text)
503 503 };
504 504 self.submitAjaxPOST(
505 505 self.previewUrl, postData, self.previewSuccessCallback,
506 506 previewFailCallback);
507 507
508 508 $(self.previewButton).parent().addClass('active');
509 509 $(self.editButton).parent().removeClass('active');
510 510 });
511 511
512 512 $(this.submitForm).submit(function(e) {
513 513 e.preventDefault();
514 514 var allowedToSubmit = self.isAllowedToSubmit();
515 515 if (!allowedToSubmit){
516 516 return false;
517 517 }
518 518
519 519 self.handleFormSubmit();
520 520 });
521 521
522 522 }
523 523
524 524 return CommentForm;
525 525 });
526 526
527 527 /* selector for comment versions */
528 528 var initVersionSelector = function(selector, initialData) {
529 529
530 530 var formatResult = function(result, container, query, escapeMarkup) {
531 531
532 532 return renderTemplate('commentVersion', {
533 533 show_disabled: true,
534 534 version: result.comment_version,
535 535 user_name: result.comment_author_username,
536 536 gravatar_url: result.comment_author_gravatar,
537 537 size: 16,
538 538 timeago_component: result.comment_created_on,
539 539 })
540 540 };
541 541
542 542 $(selector).select2({
543 543 placeholder: "Edited",
544 544 containerCssClass: "drop-menu-comment-history",
545 545 dropdownCssClass: "drop-menu-dropdown",
546 546 dropdownAutoWidth: true,
547 547 minimumResultsForSearch: -1,
548 548 data: initialData,
549 549 formatResult: formatResult,
550 550 });
551 551
552 552 $(selector).on('select2-selecting', function (e) {
553 553 // hide the mast as we later do preventDefault()
554 554 $("#select2-drop-mask").click();
555 555 e.preventDefault();
556 556 e.choice.action();
557 557 });
558 558
559 559 $(selector).on("select2-open", function() {
560 560 timeagoActivate();
561 561 });
562 562 };
563 563
564 564 /* comments controller */
565 565 var CommentsController = function() {
566 566 var mainComment = '#text';
567 567 var self = this;
568 568
569 569 this.showVersion = function (comment_id, comment_history_id) {
570 570
571 571 var historyViewUrl = pyroutes.url(
572 572 'repo_commit_comment_history_view',
573 573 {
574 574 'repo_name': templateContext.repo_name,
575 575 'commit_id': comment_id,
576 576 'comment_history_id': comment_history_id,
577 577 }
578 578 );
579 579 successRenderCommit = function (data) {
580 580 SwalNoAnimation.fire({
581 581 html: data,
582 582 title: '',
583 583 });
584 584 };
585 585 failRenderCommit = function () {
586 586 SwalNoAnimation.fire({
587 587 html: 'Error while loading comment history',
588 588 title: '',
589 589 });
590 590 };
591 591 _submitAjaxPOST(
592 592 historyViewUrl, {'csrf_token': CSRF_TOKEN},
593 593 successRenderCommit,
594 594 failRenderCommit
595 595 );
596 596 };
597 597
598 598 this.getLineNumber = function(node) {
599 599 var $node = $(node);
600 600 var lineNo = $node.closest('td').attr('data-line-no');
601 601 if (lineNo === undefined && $node.data('commentInline')){
602 602 lineNo = $node.data('commentLineNo')
603 603 }
604 604
605 605 return lineNo
606 606 };
607 607
608 608 this.scrollToComment = function(node, offset, outdated) {
609 609 if (offset === undefined) {
610 610 offset = 0;
611 611 }
612 612 var outdated = outdated || false;
613 613 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
614 614
615 615 if (!node) {
616 616 node = $('.comment-selected');
617 617 if (!node.length) {
618 618 node = $('comment-current')
619 619 }
620 620 }
621 621
622 622 $wrapper = $(node).closest('div.comment');
623 623
624 624 // show hidden comment when referenced.
625 625 if (!$wrapper.is(':visible')){
626 626 $wrapper.show();
627 627 }
628 628
629 629 $comment = $(node).closest(klass);
630 630 $comments = $(klass);
631 631
632 632 $('.comment-selected').removeClass('comment-selected');
633 633
634 634 var nextIdx = $(klass).index($comment) + offset;
635 635 if (nextIdx >= $comments.length) {
636 636 nextIdx = 0;
637 637 }
638 638 var $next = $(klass).eq(nextIdx);
639 639
640 640 var $cb = $next.closest('.cb');
641 641 $cb.removeClass('cb-collapsed');
642 642
643 643 var $filediffCollapseState = $cb.closest('.filediff').prev();
644 644 $filediffCollapseState.prop('checked', false);
645 645 $next.addClass('comment-selected');
646 646 scrollToElement($next);
647 647 return false;
648 648 };
649 649
650 650 this.nextComment = function(node) {
651 651 return self.scrollToComment(node, 1);
652 652 };
653 653
654 654 this.prevComment = function(node) {
655 655 return self.scrollToComment(node, -1);
656 656 };
657 657
658 658 this.nextOutdatedComment = function(node) {
659 659 return self.scrollToComment(node, 1, true);
660 660 };
661 661
662 662 this.prevOutdatedComment = function(node) {
663 663 return self.scrollToComment(node, -1, true);
664 664 };
665 665
666 666 this.cancelComment = function (node) {
667 667 var $node = $(node);
668 668 var edit = $(this).attr('edit');
669 669 var $inlineComments = $node.closest('div.inline-comments');
670 670
671 671 if (edit) {
672 672 var $general_comments = null;
673 673 if (!$inlineComments.length) {
674 674 $general_comments = $('#comments');
675 675 var $comment = $general_comments.parent().find('div.comment:hidden');
676 676 // show hidden general comment form
677 677 $('#cb-comment-general-form-placeholder').show();
678 678 } else {
679 679 var $comment = $inlineComments.find('div.comment:hidden');
680 680 }
681 681 $comment.show();
682 682 }
683 683 var $replyWrapper = $node.closest('.comment-inline-form').closest('.reply-thread-container-wrapper')
684 684 $replyWrapper.removeClass('comment-form-active');
685 685
686 686 var lastComment = $inlineComments.find('.comment-inline').last();
687 687 if ($(lastComment).hasClass('comment-outdated')) {
688 688 $replyWrapper.hide();
689 689 }
690 690
691 691 $node.closest('.comment-inline-form').remove();
692 692 return false;
693 693 };
694 694
695 695 this._deleteComment = function(node) {
696 696 var $node = $(node);
697 697 var $td = $node.closest('td');
698 698 var $comment = $node.closest('.comment');
699 699 var comment_id = $($comment).data('commentId');
700 700 var isDraft = $($comment).data('commentDraft');
701 701
702 702 var pullRequestId = templateContext.pull_request_data.pull_request_id;
703 703 var commitId = templateContext.commit_data.commit_id;
704 704
705 705 if (pullRequestId) {
706 706 var url = pyroutes.url('pullrequest_comment_delete', {"comment_id": comment_id, "repo_name": templateContext.repo_name, "pull_request_id": pullRequestId})
707 707 } else if (commitId) {
708 708 var url = pyroutes.url('repo_commit_comment_delete', {"comment_id": comment_id, "repo_name": templateContext.repo_name, "commit_id": commitId})
709 709 }
710 710
711 711 var postData = {
712 712 'csrf_token': CSRF_TOKEN
713 713 };
714 714
715 715 $comment.addClass('comment-deleting');
716 716 $comment.hide('fast');
717 717
718 718 var success = function(response) {
719 719 $comment.remove();
720 720
721 721 if (window.updateSticky !== undefined) {
722 722 // potentially our comments change the active window size, so we
723 723 // notify sticky elements
724 724 updateSticky()
725 725 }
726 726
727 727 if (window.refreshAllComments !== undefined && !isDraft) {
728 728 // if we have this handler, run it, and refresh all comments boxes
729 729 refreshAllComments()
730 730 }
731 731 else if (window.refreshDraftComments !== undefined && isDraft) {
732 732 // if we have this handler, run it, and refresh all comments boxes
733 733 refreshDraftComments();
734 734 }
735 735 return false;
736 736 };
737 737
738 738 var failure = function(jqXHR, textStatus, errorThrown) {
739 739 var prefix = "Error while deleting this comment.\n"
740 740 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
741 741 ajaxErrorSwal(message);
742 742
743 743 $comment.show('fast');
744 744 $comment.removeClass('comment-deleting');
745 745 return false;
746 746 };
747 747 ajaxPOST(url, postData, success, failure);
748 748
749 749 }
750 750
751 751 this.deleteComment = function(node) {
752 752 var $comment = $(node).closest('.comment');
753 753 var comment_id = $comment.attr('data-comment-id');
754 754
755 755 SwalNoAnimation.fire({
756 756 title: 'Delete this comment?',
757 757 icon: 'warning',
758 758 showCancelButton: true,
759 759 confirmButtonText: _gettext('Yes, delete comment #{0}!').format(comment_id),
760 760
761 761 }).then(function(result) {
762 762 if (result.value) {
763 763 self._deleteComment(node);
764 764 }
765 765 })
766 766 };
767 767
768 768 this._finalizeDrafts = function(commentIds) {
769 769
770 770 var pullRequestId = templateContext.pull_request_data.pull_request_id;
771 771 var commitId = templateContext.commit_data.commit_id;
772 772
773 773 if (pullRequestId) {
774 774 var url = pyroutes.url('pullrequest_draft_comments_submit', {"repo_name": templateContext.repo_name, "pull_request_id": pullRequestId})
775 775 } else if (commitId) {
776 776 var url = pyroutes.url('commit_draft_comments_submit', {"repo_name": templateContext.repo_name, "commit_id": commitId})
777 777 }
778 778
779 779 // remove the drafts so we can lock them before submit.
780 780 $.each(commentIds, function(idx, val){
781 781 $('#comment-{0}'.format(val)).remove();
782 782 })
783 783
784 784 var postData = {'comments': commentIds, 'csrf_token': CSRF_TOKEN};
785 785
786 786 var submitSuccessCallback = function(json_data) {
787 787 self.attachInlineComment(json_data);
788 788
789 789 if (window.refreshDraftComments !== undefined) {
790 790 // if we have this handler, run it, and refresh all comments boxes
791 791 refreshDraftComments()
792 792 }
793 793
794 if (window.refreshAllComments !== undefined) {
795 // if we have this handler, run it, and refresh all comments boxes
796 refreshAllComments()
797 }
798
794 799 return false;
795 800 };
796 801
797 802 ajaxPOST(url, postData, submitSuccessCallback)
798 803
799 804 }
800 805
801 806 this.finalizeDrafts = function(commentIds, callback) {
802 807
803 808 SwalNoAnimation.fire({
804 809 title: _ngettext('Submit {0} draft comment.', 'Submit {0} draft comments.', commentIds.length).format(commentIds.length),
805 810 icon: 'warning',
806 811 showCancelButton: true,
807 812 confirmButtonText: _gettext('Yes'),
808 813
809 814 }).then(function(result) {
810 815 if (result.value) {
811 816 if (callback !== undefined) {
812 817 callback(result)
813 818 }
814 819 self._finalizeDrafts(commentIds);
815 820 }
816 821 })
817 822 };
818 823
819 824 this.toggleWideMode = function (node) {
820 825
821 826 if ($('#content').hasClass('wrapper')) {
822 827 $('#content').removeClass("wrapper");
823 828 $('#content').addClass("wide-mode-wrapper");
824 829 $(node).addClass('btn-success');
825 830 return true
826 831 } else {
827 832 $('#content').removeClass("wide-mode-wrapper");
828 833 $('#content').addClass("wrapper");
829 834 $(node).removeClass('btn-success');
830 835 return false
831 836 }
832 837
833 838 };
834 839
835 840 /**
836 841 * Turn off/on all comments in file diff
837 842 */
838 843 this.toggleDiffComments = function(node) {
839 844 // Find closes filediff container
840 845 var $filediff = $(node).closest('.filediff');
841 846 if ($(node).hasClass('toggle-on')) {
842 847 var show = false;
843 848 } else if ($(node).hasClass('toggle-off')) {
844 849 var show = true;
845 850 }
846 851
847 852 // Toggle each individual comment block, so we can un-toggle single ones
848 853 $.each($filediff.find('.toggle-comment-action'), function(idx, val) {
849 854 self.toggleLineComments($(val), show)
850 855 })
851 856
852 857 // since we change the height of the diff container that has anchor points for upper
853 858 // sticky header, we need to tell it to re-calculate those
854 859 if (window.updateSticky !== undefined) {
855 860 // potentially our comments change the active window size, so we
856 861 // notify sticky elements
857 862 updateSticky()
858 863 }
859 864
860 865 return false;
861 866 }
862 867
863 868 this.toggleLineComments = function(node, show) {
864 869
865 870 var trElem = $(node).closest('tr')
866 871
867 872 if (show === true) {
868 873 // mark outdated comments as visible before the toggle;
869 874 $(trElem).find('.comment-outdated').show();
870 875 $(trElem).removeClass('hide-line-comments');
871 876 } else if (show === false) {
872 877 $(trElem).find('.comment-outdated').hide();
873 878 $(trElem).addClass('hide-line-comments');
874 879 } else {
875 880 // mark outdated comments as visible before the toggle;
876 881 $(trElem).find('.comment-outdated').show();
877 882 $(trElem).toggleClass('hide-line-comments');
878 883 }
879 884
880 885 // since we change the height of the diff container that has anchor points for upper
881 886 // sticky header, we need to tell it to re-calculate those
882 887 if (window.updateSticky !== undefined) {
883 888 // potentially our comments change the active window size, so we
884 889 // notify sticky elements
885 890 updateSticky()
886 891 }
887 892
888 893 };
889 894
890 895 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId, edit, comment_id){
891 896 var pullRequestId = templateContext.pull_request_data.pull_request_id;
892 897 var commitId = templateContext.commit_data.commit_id;
893 898
894 899 var commentForm = new CommentForm(
895 900 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId, edit, comment_id);
896 901 var cm = commentForm.getCmInstance();
897 902
898 903 if (resolvesCommentId){
899 904 placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
900 905 }
901 906
902 907 setTimeout(function() {
903 908 // callbacks
904 909 if (cm !== undefined) {
905 910 commentForm.setPlaceholder(placeholderText);
906 911 if (commentForm.isInline()) {
907 912 cm.focus();
908 913 cm.refresh();
909 914 }
910 915 }
911 916 }, 10);
912 917
913 918 // trigger scrolldown to the resolve comment, since it might be away
914 919 // from the clicked
915 920 if (resolvesCommentId){
916 921 var actionNode = $(commentForm.resolvesActionId).offset();
917 922
918 923 setTimeout(function() {
919 924 if (actionNode) {
920 925 $('body, html').animate({scrollTop: actionNode.top}, 10);
921 926 }
922 927 }, 100);
923 928 }
924 929
925 930 // add dropzone support
926 931 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
927 932 var renderer = templateContext.visual.default_renderer;
928 933 if (renderer == 'rst') {
929 934 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
930 935 if (isRendered){
931 936 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
932 937 }
933 938 } else if (renderer == 'markdown') {
934 939 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
935 940 if (isRendered){
936 941 attachmentUrl = '!' + attachmentUrl;
937 942 }
938 943 } else {
939 944 var attachmentUrl = '{}'.format(attachmentStoreUrl);
940 945 }
941 946 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
942 947
943 948 return false;
944 949 };
945 950
946 951 //see: https://www.dropzonejs.com/#configuration
947 952 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
948 953 {'repo_name': templateContext.repo_name,
949 954 'commit_id': templateContext.commit_data.commit_id})
950 955
951 956 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0);
952 957 if (previewTmpl !== undefined){
953 958 var selectLink = $(formElement).find('.pick-attachment').get(0);
954 959 $(formElement).find('.comment-attachment-uploader').dropzone({
955 960 url: storeUrl,
956 961 headers: {"X-CSRF-Token": CSRF_TOKEN},
957 962 paramName: function () {
958 963 return "attachment"
959 964 }, // The name that will be used to transfer the file
960 965 clickable: selectLink,
961 966 parallelUploads: 1,
962 967 maxFiles: 10,
963 968 maxFilesize: templateContext.attachment_store.max_file_size_mb,
964 969 uploadMultiple: false,
965 970 autoProcessQueue: true, // if false queue will not be processed automatically.
966 971 createImageThumbnails: false,
967 972 previewTemplate: previewTmpl.innerHTML,
968 973
969 974 accept: function (file, done) {
970 975 done();
971 976 },
972 977 init: function () {
973 978
974 979 this.on("sending", function (file, xhr, formData) {
975 980 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
976 981 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
977 982 });
978 983
979 984 this.on("success", function (file, response) {
980 985 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
981 986 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
982 987
983 988 var isRendered = false;
984 989 var ext = file.name.split('.').pop();
985 990 var imageExts = templateContext.attachment_store.image_ext;
986 991 if (imageExts.indexOf(ext) !== -1){
987 992 isRendered = true;
988 993 }
989 994
990 995 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
991 996 });
992 997
993 998 this.on("error", function (file, errorMessage, xhr) {
994 999 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
995 1000
996 1001 var error = null;
997 1002
998 1003 if (xhr !== undefined){
999 1004 var httpStatus = xhr.status + " " + xhr.statusText;
1000 1005 if (xhr !== undefined && xhr.status >= 500) {
1001 1006 error = httpStatus;
1002 1007 }
1003 1008 }
1004 1009
1005 1010 if (error === null) {
1006 1011 error = errorMessage.error || errorMessage || httpStatus;
1007 1012 }
1008 1013 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
1009 1014
1010 1015 });
1011 1016 }
1012 1017 });
1013 1018 }
1014 1019 return commentForm;
1015 1020 };
1016 1021
1017 1022 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
1018 1023
1019 1024 var tmpl = $('#cb-comment-general-form-template').html();
1020 1025 tmpl = tmpl.format(null, 'general');
1021 1026 var $form = $(tmpl);
1022 1027
1023 1028 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
1024 1029 var curForm = $formPlaceholder.find('form');
1025 1030 if (curForm){
1026 1031 curForm.remove();
1027 1032 }
1028 1033 $formPlaceholder.append($form);
1029 1034
1030 1035 var _form = $($form[0]);
1031 1036 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
1032 1037 var edit = false;
1033 1038 var comment_id = null;
1034 1039 var commentForm = this.createCommentForm(
1035 1040 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId, edit, comment_id);
1036 1041 commentForm.initStatusChangeSelector();
1037 1042
1038 1043 return commentForm;
1039 1044 };
1040 1045
1041 1046 this.editComment = function(node, line_no, f_path) {
1042 1047 self.edit = true;
1043 1048 var $node = $(node);
1044 1049 var $td = $node.closest('td');
1045 1050
1046 1051 var $comment = $(node).closest('.comment');
1047 1052 var comment_id = $($comment).data('commentId');
1048 1053 var isDraft = $($comment).data('commentDraft');
1049 1054 var $editForm = null
1050 1055
1051 1056 var $comments = $node.closest('div.inline-comments');
1052 1057 var $general_comments = null;
1053 1058
1054 1059 if($comments.length){
1055 1060 // inline comments setup
1056 1061 $editForm = $comments.find('.comment-inline-form');
1057 1062 line_no = self.getLineNumber(node)
1058 1063 }
1059 1064 else{
1060 1065 // general comments setup
1061 1066 $comments = $('#comments');
1062 1067 $editForm = $comments.find('.comment-inline-form');
1063 1068 line_no = $comment[0].id
1064 1069 $('#cb-comment-general-form-placeholder').hide();
1065 1070 }
1066 1071
1067 1072 if ($editForm.length === 0) {
1068 1073
1069 1074 // unhide all comments if they are hidden for a proper REPLY mode
1070 1075 var $filediff = $node.closest('.filediff');
1071 1076 $filediff.removeClass('hide-comments');
1072 1077
1073 1078 $editForm = self.createNewFormWrapper(f_path, line_no);
1074 1079 if(f_path && line_no) {
1075 1080 $editForm.addClass('comment-inline-form-edit')
1076 1081 }
1077 1082
1078 1083 $comment.after($editForm)
1079 1084
1080 1085 var _form = $($editForm[0]).find('form');
1081 1086 var autocompleteActions = ['as_note',];
1082 1087 var commentForm = this.createCommentForm(
1083 1088 _form, line_no, '', autocompleteActions, resolvesCommentId,
1084 1089 this.edit, comment_id);
1085 1090 var old_comment_text_binary = $comment.attr('data-comment-text');
1086 1091 var old_comment_text = b64DecodeUnicode(old_comment_text_binary);
1087 1092 commentForm.cm.setValue(old_comment_text);
1088 1093 $comment.hide();
1089 1094 tooltipActivate();
1090 1095
1091 1096 // set a CUSTOM submit handler for inline comment edit action.
1092 1097 commentForm.setHandleFormSubmit(function(o) {
1093 1098 var text = commentForm.cm.getValue();
1094 1099 var commentType = commentForm.getCommentType();
1095 1100
1096 1101 if (text === "") {
1097 1102 return;
1098 1103 }
1099 1104
1100 1105 if (old_comment_text == text) {
1101 1106 SwalNoAnimation.fire({
1102 1107 title: 'Unable to edit comment',
1103 1108 html: _gettext('Comment body was not changed.'),
1104 1109 });
1105 1110 return;
1106 1111 }
1107 1112 var excludeCancelBtn = false;
1108 1113 var submitEvent = true;
1109 1114 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1110 1115 commentForm.cm.setOption("readOnly", true);
1111 1116
1112 1117 // Read last version known
1113 1118 var versionSelector = $('#comment_versions_{0}'.format(comment_id));
1114 1119 var version = versionSelector.data('lastVersion');
1115 1120
1116 1121 if (!version) {
1117 1122 version = 0;
1118 1123 }
1119 1124
1120 1125 var postData = {
1121 1126 'text': text,
1122 1127 'f_path': f_path,
1123 1128 'line': line_no,
1124 1129 'comment_type': commentType,
1125 1130 'draft': isDraft,
1126 1131 'version': version,
1127 1132 'csrf_token': CSRF_TOKEN
1128 1133 };
1129 1134
1130 1135 var submitSuccessCallback = function(json_data) {
1131 1136 $editForm.remove();
1132 1137 $comment.show();
1133 1138 var postData = {
1134 1139 'text': text,
1135 1140 'renderer': $comment.attr('data-comment-renderer'),
1136 1141 'csrf_token': CSRF_TOKEN
1137 1142 };
1138 1143
1139 1144 /* Inject new edited version selector */
1140 1145 var updateCommentVersionDropDown = function () {
1141 1146 var versionSelectId = '#comment_versions_'+comment_id;
1142 1147 var preLoadVersionData = [
1143 1148 {
1144 1149 id: json_data['comment_version'],
1145 1150 text: "v{0}".format(json_data['comment_version']),
1146 1151 action: function () {
1147 1152 Rhodecode.comments.showVersion(
1148 1153 json_data['comment_id'],
1149 1154 json_data['comment_history_id']
1150 1155 )
1151 1156 },
1152 1157 comment_version: json_data['comment_version'],
1153 1158 comment_author_username: json_data['comment_author_username'],
1154 1159 comment_author_gravatar: json_data['comment_author_gravatar'],
1155 1160 comment_created_on: json_data['comment_created_on'],
1156 1161 },
1157 1162 ]
1158 1163
1159 1164
1160 1165 if ($(versionSelectId).data('select2')) {
1161 1166 var oldData = $(versionSelectId).data('select2').opts.data.results;
1162 1167 $(versionSelectId).select2("destroy");
1163 1168 preLoadVersionData = oldData.concat(preLoadVersionData)
1164 1169 }
1165 1170
1166 1171 initVersionSelector(versionSelectId, {results: preLoadVersionData});
1167 1172
1168 1173 $comment.attr('data-comment-text', utf8ToB64(text));
1169 1174
1170 1175 var versionSelector = $('#comment_versions_'+comment_id);
1171 1176
1172 1177 // set lastVersion so we know our last edit version
1173 1178 versionSelector.data('lastVersion', json_data['comment_version'])
1174 1179 versionSelector.parent().show();
1175 1180 }
1176 1181 updateCommentVersionDropDown();
1177 1182
1178 1183 // by default we reset state of comment preserving the text
1179 1184 var failRenderCommit = function(jqXHR, textStatus, errorThrown) {
1180 1185 var prefix = "Error while editing this comment.\n"
1181 1186 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1182 1187 ajaxErrorSwal(message);
1183 1188 };
1184 1189
1185 1190 var successRenderCommit = function(o){
1186 1191 $comment.show();
1187 1192 $comment[0].lastElementChild.innerHTML = o;
1188 1193 };
1189 1194
1190 1195 var previewUrl = pyroutes.url(
1191 1196 'repo_commit_comment_preview',
1192 1197 {'repo_name': templateContext.repo_name,
1193 1198 'commit_id': templateContext.commit_data.commit_id});
1194 1199
1195 1200 _submitAjaxPOST(
1196 1201 previewUrl, postData, successRenderCommit, failRenderCommit
1197 1202 );
1198 1203
1199 1204 try {
1200 1205 var html = json_data.rendered_text;
1201 1206 var lineno = json_data.line_no;
1202 1207 var target_id = json_data.target_id;
1203 1208
1204 1209 $comments.find('.cb-comment-add-button').before(html);
1205 1210
1206 1211 // run global callback on submit
1207 1212 commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
1208 1213
1209 1214 } catch (e) {
1210 1215 console.error(e);
1211 1216 }
1212 1217
1213 1218 // re trigger the linkification of next/prev navigation
1214 1219 linkifyComments($('.inline-comment-injected'));
1215 1220 timeagoActivate();
1216 1221 tooltipActivate();
1217 1222
1218 1223 if (window.updateSticky !== undefined) {
1219 1224 // potentially our comments change the active window size, so we
1220 1225 // notify sticky elements
1221 1226 updateSticky()
1222 1227 }
1223 1228
1224 1229 if (window.refreshAllComments !== undefined && !isDraft) {
1225 1230 // if we have this handler, run it, and refresh all comments boxes
1226 1231 refreshAllComments()
1227 1232 }
1228 1233 else if (window.refreshDraftComments !== undefined && isDraft) {
1229 1234 // if we have this handler, run it, and refresh all comments boxes
1230 1235 refreshDraftComments();
1231 1236 }
1232 1237
1233 1238 commentForm.setActionButtonsDisabled(false);
1234 1239
1235 1240 };
1236 1241
1237 1242 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1238 1243 var prefix = "Error while editing comment.\n"
1239 1244 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1240 1245 if (jqXHR.status == 409){
1241 1246 message = 'This comment was probably changed somewhere else. Please reload the content of this comment.'
1242 1247 ajaxErrorSwal(message, 'Comment version mismatch.');
1243 1248 } else {
1244 1249 ajaxErrorSwal(message);
1245 1250 }
1246 1251
1247 1252 commentForm.resetCommentFormState(text)
1248 1253 };
1249 1254 commentForm.submitAjaxPOST(
1250 1255 commentForm.submitUrl, postData,
1251 1256 submitSuccessCallback,
1252 1257 submitFailCallback);
1253 1258 });
1254 1259 }
1255 1260
1256 1261 $editForm.addClass('comment-inline-form-open');
1257 1262 };
1258 1263
1259 1264 this.attachComment = function(json_data) {
1260 1265 var self = this;
1261 1266 $.each(json_data, function(idx, val) {
1262 1267 var json_data_elem = [val]
1263 1268 var isInline = val.comment_f_path && val.comment_lineno
1264 1269
1265 1270 if (isInline) {
1266 1271 self.attachInlineComment(json_data_elem)
1267 1272 } else {
1268 1273 self.attachGeneralComment(json_data_elem)
1269 1274 }
1270 1275 })
1271 1276
1272 1277 }
1273 1278
1274 1279 this.attachGeneralComment = function(json_data) {
1275 1280 $.each(json_data, function(idx, val) {
1276 1281 $('#injected_page_comments').append(val.rendered_text);
1277 1282 })
1278 1283 }
1279 1284
1280 1285 this.attachInlineComment = function(json_data) {
1281 1286
1282 1287 $.each(json_data, function (idx, val) {
1283 1288 var line_qry = '*[data-line-no="{0}"]'.format(val.line_no);
1284 1289 var html = val.rendered_text;
1285 1290 var $inlineComments = $('#' + val.target_id)
1286 1291 .find(line_qry)
1287 1292 .find('.inline-comments');
1288 1293
1289 1294 var lastComment = $inlineComments.find('.comment-inline').last();
1290 1295
1291 1296 if (lastComment.length === 0) {
1292 1297 // first comment, we append simply
1293 1298 $inlineComments.find('.reply-thread-container-wrapper').before(html);
1294 1299 } else {
1295 1300 $(lastComment).after(html)
1296 1301 }
1297 1302
1298 1303 })
1299 1304
1300 1305 };
1301 1306
1302 1307 this.createNewFormWrapper = function(f_path, line_no) {
1303 1308 // create a new reply HTML form from template
1304 1309 var tmpl = $('#cb-comment-inline-form-template').html();
1305 1310 tmpl = tmpl.format(escapeHtml(f_path), line_no);
1306 1311 return $(tmpl);
1307 1312 }
1308 1313
1309 1314 this.markCommentResolved = function(commentId) {
1310 1315 $('#comment-label-{0}'.format(commentId)).find('.resolved').show();
1311 1316 $('#comment-label-{0}'.format(commentId)).find('.resolve').hide();
1312 1317 };
1313 1318
1314 1319 this.createComment = function(node, f_path, line_no, resolutionComment) {
1315 1320 self.edit = false;
1316 1321 var $node = $(node);
1317 1322 var $td = $node.closest('td');
1318 1323 var resolvesCommentId = resolutionComment || null;
1319 1324
1320 1325 var $replyForm = $td.find('.comment-inline-form');
1321 1326
1322 1327 // if form isn't existing, we're generating a new one and injecting it.
1323 1328 if ($replyForm.length === 0) {
1324 1329
1325 1330 // unhide/expand all comments if they are hidden for a proper REPLY mode
1326 1331 self.toggleLineComments($node, true);
1327 1332
1328 1333 $replyForm = self.createNewFormWrapper(f_path, line_no);
1329 1334
1330 1335 var $comments = $td.find('.inline-comments');
1331 1336
1332 1337 // There aren't any comments, we init the `.inline-comments` with `reply-thread-container` first
1333 1338 if ($comments.length===0) {
1334 1339 var replBtn = '<button class="cb-comment-add-button" onclick="return Rhodecode.comments.createComment(this, \'{0}\', \'{1}\', null)">Reply...</button>'.format(escapeHtml(f_path), line_no)
1335 1340 var $reply_container = $('#cb-comments-inline-container-template')
1336 1341 $reply_container.find('button.cb-comment-add-button').replaceWith(replBtn);
1337 1342 $td.append($($reply_container).html());
1338 1343 }
1339 1344
1340 1345 // default comment button exists, so we prepend the form for leaving initial comment
1341 1346 $td.find('.cb-comment-add-button').before($replyForm);
1342 1347 // set marker, that we have a open form
1343 1348 var $replyWrapper = $td.find('.reply-thread-container-wrapper')
1344 1349 $replyWrapper.addClass('comment-form-active');
1345 1350
1346 1351 var lastComment = $comments.find('.comment-inline').last();
1347 1352 if ($(lastComment).hasClass('comment-outdated')) {
1348 1353 $replyWrapper.show();
1349 1354 }
1350 1355
1351 1356 var _form = $($replyForm[0]).find('form');
1352 1357 var autocompleteActions = ['as_note', 'as_todo'];
1353 1358 var comment_id=null;
1354 1359 var placeholderText = _gettext('Leave a comment on file {0} line {1}.').format(f_path, line_no);
1355 1360 var commentForm = self.createCommentForm(
1356 1361 _form, line_no, placeholderText, autocompleteActions, resolvesCommentId,
1357 1362 self.edit, comment_id);
1358 1363
1359 1364 // set a CUSTOM submit handler for inline comments.
1360 1365 commentForm.setHandleFormSubmit(function(o) {
1361 1366 var text = commentForm.cm.getValue();
1362 1367 var commentType = commentForm.getCommentType();
1363 1368 var resolvesCommentId = commentForm.getResolvesId();
1364 1369 var isDraft = commentForm.getDraftState();
1365 1370
1366 1371 if (text === "") {
1367 1372 return;
1368 1373 }
1369 1374
1370 1375 if (line_no === undefined) {
1371 1376 alert('Error: unable to fetch line number for this inline comment !');
1372 1377 return;
1373 1378 }
1374 1379
1375 1380 if (f_path === undefined) {
1376 1381 alert('Error: unable to fetch file path for this inline comment !');
1377 1382 return;
1378 1383 }
1379 1384
1380 1385 var excludeCancelBtn = false;
1381 1386 var submitEvent = true;
1382 1387 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1383 1388 commentForm.cm.setOption("readOnly", true);
1384 1389 var postData = {
1385 1390 'text': text,
1386 1391 'f_path': f_path,
1387 1392 'line': line_no,
1388 1393 'comment_type': commentType,
1389 1394 'draft': isDraft,
1390 1395 'csrf_token': CSRF_TOKEN
1391 1396 };
1392 1397 if (resolvesCommentId){
1393 1398 postData['resolves_comment_id'] = resolvesCommentId;
1394 1399 }
1395 1400
1396 1401 // submitSuccess for inline commits
1397 1402 var submitSuccessCallback = function(json_data) {
1398 1403
1399 1404 $replyForm.remove();
1400 1405 $td.find('.reply-thread-container-wrapper').removeClass('comment-form-active');
1401 1406
1402 1407 try {
1403 1408
1404 1409 // inject newly created comments, json_data is {<comment_id>: {}}
1405 1410 self.attachInlineComment(json_data)
1406 1411
1407 1412 //mark visually which comment was resolved
1408 1413 if (resolvesCommentId) {
1409 1414 self.markCommentResolved(resolvesCommentId);
1410 1415 }
1411 1416
1412 1417 // run global callback on submit
1413 1418 commentForm.globalSubmitSuccessCallback({
1414 1419 draft: isDraft,
1415 1420 comment_id: comment_id
1416 1421 });
1417 1422
1418 1423 } catch (e) {
1419 1424 console.error(e);
1420 1425 }
1421 1426
1422 1427 if (window.updateSticky !== undefined) {
1423 1428 // potentially our comments change the active window size, so we
1424 1429 // notify sticky elements
1425 1430 updateSticky()
1426 1431 }
1427 1432
1428 1433 if (window.refreshAllComments !== undefined && !isDraft) {
1429 1434 // if we have this handler, run it, and refresh all comments boxes
1430 1435 refreshAllComments()
1431 1436 }
1432 1437 else if (window.refreshDraftComments !== undefined && isDraft) {
1433 1438 // if we have this handler, run it, and refresh all comments boxes
1434 1439 refreshDraftComments();
1435 1440 }
1436 1441
1437 1442 commentForm.setActionButtonsDisabled(false);
1438 1443
1439 1444 // re trigger the linkification of next/prev navigation
1440 1445 linkifyComments($('.inline-comment-injected'));
1441 1446 timeagoActivate();
1442 1447 tooltipActivate();
1443 1448 };
1444 1449
1445 1450 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1446 1451 var prefix = "Error while submitting comment.\n"
1447 1452 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1448 1453 ajaxErrorSwal(message);
1449 1454 commentForm.resetCommentFormState(text)
1450 1455 };
1451 1456
1452 1457 commentForm.submitAjaxPOST(
1453 1458 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
1454 1459 });
1455 1460 }
1456 1461
1457 1462 // Finally "open" our reply form, since we know there are comments and we have the "attached" old form
1458 1463 $replyForm.addClass('comment-inline-form-open');
1459 1464 tooltipActivate();
1460 1465 };
1461 1466
1462 1467 this.createResolutionComment = function(commentId){
1463 1468 // hide the trigger text
1464 1469 $('#resolve-comment-{0}'.format(commentId)).hide();
1465 1470
1466 1471 var comment = $('#comment-'+commentId);
1467 1472 var commentData = comment.data();
1468 1473
1469 1474 if (commentData.commentInline) {
1470 1475 var f_path = commentData.commentFPath;
1471 1476 var line_no = commentData.commentLineNo;
1472 1477 this.createComment(comment, f_path, line_no, commentId)
1473 1478 } else {
1474 1479 this.createGeneralComment('general', "$placeholder", commentId)
1475 1480 }
1476 1481
1477 1482 return false;
1478 1483 };
1479 1484
1480 1485 this.submitResolution = function(commentId){
1481 1486 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
1482 1487 var commentForm = form.get(0).CommentForm;
1483 1488
1484 1489 var cm = commentForm.getCmInstance();
1485 1490 var renderer = templateContext.visual.default_renderer;
1486 1491 if (renderer == 'rst'){
1487 1492 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
1488 1493 } else if (renderer == 'markdown') {
1489 1494 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
1490 1495 } else {
1491 1496 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
1492 1497 }
1493 1498
1494 1499 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
1495 1500 form.submit();
1496 1501 return false;
1497 1502 };
1498 1503
1499 1504 this.resolveTodo = function (elem, todoId) {
1500 1505 var commentId = todoId;
1501 1506
1502 1507 SwalNoAnimation.fire({
1503 1508 title: 'Resolve TODO {0}'.format(todoId),
1504 1509 showCancelButton: true,
1505 1510 confirmButtonText: _gettext('Yes'),
1506 1511 showLoaderOnConfirm: true,
1507 1512
1508 1513 allowOutsideClick: function () {
1509 1514 !Swal.isLoading()
1510 1515 },
1511 1516 preConfirm: function () {
1512 1517 var comment = $('#comment-' + commentId);
1513 1518 var commentData = comment.data();
1514 1519
1515 1520 var f_path = null
1516 1521 var line_no = null
1517 1522 if (commentData.commentInline) {
1518 1523 f_path = commentData.commentFPath;
1519 1524 line_no = commentData.commentLineNo;
1520 1525 }
1521 1526
1522 1527 var renderer = templateContext.visual.default_renderer;
1523 1528 var commentBoxUrl = '{1}#comment-{0}'.format(commentId);
1524 1529
1525 1530 // Pull request case
1526 1531 if (templateContext.pull_request_data.pull_request_id !== null) {
1527 1532 var commentUrl = pyroutes.url('pullrequest_comment_create',
1528 1533 {
1529 1534 'repo_name': templateContext.repo_name,
1530 1535 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1531 1536 'comment_id': commentId
1532 1537 });
1533 1538 } else {
1534 1539 var commentUrl = pyroutes.url('repo_commit_comment_create',
1535 1540 {
1536 1541 'repo_name': templateContext.repo_name,
1537 1542 'commit_id': templateContext.commit_data.commit_id,
1538 1543 'comment_id': commentId
1539 1544 });
1540 1545 }
1541 1546
1542 1547 if (renderer === 'rst') {
1543 1548 commentBoxUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentUrl);
1544 1549 } else if (renderer === 'markdown') {
1545 1550 commentBoxUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentUrl);
1546 1551 }
1547 1552 var resolveText = _gettext('TODO from comment {0} was fixed.').format(commentBoxUrl);
1548 1553
1549 1554 var postData = {
1550 1555 text: resolveText,
1551 1556 comment_type: 'note',
1552 1557 draft: false,
1553 1558 csrf_token: CSRF_TOKEN,
1554 1559 resolves_comment_id: commentId
1555 1560 }
1556 1561 if (commentData.commentInline) {
1557 1562 postData['f_path'] = f_path;
1558 1563 postData['line'] = line_no;
1559 1564 }
1560 1565
1561 1566 return new Promise(function (resolve, reject) {
1562 1567 $.ajax({
1563 1568 type: 'POST',
1564 1569 data: postData,
1565 1570 url: commentUrl,
1566 1571 headers: {'X-PARTIAL-XHR': true}
1567 1572 })
1568 1573 .done(function (data) {
1569 1574 resolve(data);
1570 1575 })
1571 1576 .fail(function (jqXHR, textStatus, errorThrown) {
1572 1577 var prefix = "Error while resolving TODO.\n"
1573 1578 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1574 1579 ajaxErrorSwal(message);
1575 1580 });
1576 1581 })
1577 1582 }
1578 1583
1579 1584 })
1580 1585 .then(function (result) {
1581 1586 var success = function (json_data) {
1582 1587 resolvesCommentId = commentId;
1583 1588 var commentResolved = json_data[Object.keys(json_data)[0]]
1584 1589
1585 1590 try {
1586 1591
1587 1592 if (commentResolved.f_path) {
1588 1593 // inject newly created comments, json_data is {<comment_id>: {}}
1589 1594 self.attachInlineComment(json_data)
1590 1595 } else {
1591 1596 self.attachGeneralComment(json_data)
1592 1597 }
1593 1598
1594 1599 //mark visually which comment was resolved
1595 1600 if (resolvesCommentId) {
1596 1601 self.markCommentResolved(resolvesCommentId);
1597 1602 }
1598 1603
1599 1604 // run global callback on submit
1600 1605 if (window.commentFormGlobalSubmitSuccessCallback !== undefined) {
1601 1606 commentFormGlobalSubmitSuccessCallback({
1602 1607 draft: false,
1603 1608 comment_id: commentId
1604 1609 });
1605 1610 }
1606 1611
1607 1612 } catch (e) {
1608 1613 console.error(e);
1609 1614 }
1610 1615
1611 1616 if (window.updateSticky !== undefined) {
1612 1617 // potentially our comments change the active window size, so we
1613 1618 // notify sticky elements
1614 1619 updateSticky()
1615 1620 }
1616 1621
1617 1622 if (window.refreshAllComments !== undefined) {
1618 1623 // if we have this handler, run it, and refresh all comments boxes
1619 1624 refreshAllComments()
1620 1625 }
1621 1626 // re trigger the linkification of next/prev navigation
1622 1627 linkifyComments($('.inline-comment-injected'));
1623 1628 timeagoActivate();
1624 1629 tooltipActivate();
1625 1630 };
1626 1631
1627 1632 if (result.value) {
1628 1633 $(elem).remove();
1629 1634 success(result.value)
1630 1635 }
1631 1636 })
1632 1637 };
1633 1638
1634 1639 };
1635 1640
1636 1641 window.commentHelp = function(renderer) {
1637 1642 var funcData = {'renderer': renderer}
1638 1643 return renderTemplate('commentHelpHovercard', funcData)
1639 1644 }
@@ -1,147 +1,147 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 3
4 4 <%def name="title()">
5 5 %if c.show_private:
6 6 ${_('Private Gists for user {}').format(c.rhodecode_user.username)}
7 7 %elif c.show_public:
8 8 ${_('Public Gists for user {}').format(c.rhodecode_user.username)}
9 9 %else:
10 10 ${_('Public Gists')}
11 11 %endif
12 12 %if c.rhodecode_name:
13 13 &middot; ${h.branding(c.rhodecode_name)}
14 14 %endif
15 15 </%def>
16 16
17 17 <%def name="breadcrumbs_links()"></%def>
18 18
19 19 <%def name="menu_bar_nav()">
20 20 ${self.menu_items(active='gists')}
21 21 </%def>
22 22
23 23 <%def name="main()">
24 24
25 25 <div class="box">
26 26 <div class="title">
27 27
28 28 <ul class="button-links">
29 29 % if c.is_super_admin:
30 <li class="btn ${h.is_active('all', c.active)}"><a href="${h.route_path('gists_show', _query={'all': 1})}">${_('All gists')}</a></li>
30 <li><a class="btn ${h.is_active('all', c.active)}" href="${h.route_path('gists_show', _query={'all': 1})}">${_('All gists')}</a></li>
31 31 %endif
32 <li class="btn ${h.is_active('public', c.active)}"><a href="${h.route_path('gists_show')}">${_('All public')}</a></li>
32 <li><a class="btn ${h.is_active('public', c.active)}" href="${h.route_path('gists_show')}">${_('All public')}</a></li>
33 33 %if c.rhodecode_user.username != h.DEFAULT_USER:
34 <li class="btn ${h.is_active('my_all', c.active)}"><a href="${h.route_path('gists_show', _query={'public':1, 'private': 1})}">${_('My gists')}</a></li>
35 <li class="btn ${h.is_active('my_private', c.active)}"><a href="${h.route_path('gists_show', _query={'private': 1})}">${_('My private')}</a></li>
36 <li class="btn ${h.is_active('my_public', c.active)}"><a href="${h.route_path('gists_show', _query={'public': 1})}">${_('My public')}</a></li>
34 <li><a class="btn ${h.is_active('my_all', c.active)}" href="${h.route_path('gists_show', _query={'public':1, 'private': 1})}">${_('My gists')}</a></li>
35 <li><a class="btn ${h.is_active('my_private', c.active)}" href="${h.route_path('gists_show', _query={'private': 1})}">${_('My private')}</a></li>
36 <li><a class="btn ${h.is_active('my_public', c.active)}" href="${h.route_path('gists_show', _query={'public': 1})}">${_('My public')}</a></li>
37 37 %endif
38 38 </ul>
39 39
40 40 % if c.rhodecode_user.username != h.DEFAULT_USER:
41 41 <div class="pull-right">
42 42 <a class="btn btn-primary" href="${h.route_path('gists_new')}" >
43 43 ${_(u'Create New Gist')}
44 44 </a>
45 45 </div>
46 46 % endif
47 47
48 48 <div class="grid-quick-filter">
49 49 <ul class="grid-filter-box">
50 50 <li class="grid-filter-box-icon">
51 51 <i class="icon-search"></i>
52 52 </li>
53 53 <li class="grid-filter-box-input">
54 54 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
55 55 </li>
56 56 </ul>
57 57 </div>
58 58
59 59 </div>
60 60
61 61 <div class="main-content-full-width">
62 62 <div id="repos_list_wrap">
63 63 <table id="gist_list_table" class="display"></table>
64 64 </div>
65 65 </div>
66 66
67 67 </div>
68 68
69 69 <script type="text/javascript">
70 70 $(document).ready(function() {
71 71
72 72 var get_datatable_count = function(){
73 73 var api = $('#gist_list_table').dataTable().api();
74 74 $('#gists_count').text(api.page.info().recordsDisplay);
75 75 };
76 76
77 77
78 78 // custom filter that filters by access_id, description or author
79 79 $.fn.dataTable.ext.search.push(
80 80 function( settings, data, dataIndex ) {
81 81 var query = $('#q_filter').val();
82 82 var author = data[0].strip();
83 83 var access_id = data[2].strip();
84 84 var description = data[3].strip();
85 85
86 86 var query_str = (access_id + " " + author + " " + description).toLowerCase();
87 87
88 88 if(query_str.indexOf(query.toLowerCase()) !== -1){
89 89 return true;
90 90 }
91 91 return false;
92 92 }
93 93 );
94 94
95 95 // gists list
96 96 $('#gist_list_table').DataTable({
97 97 data: ${c.data|n},
98 98 dom: 'rtp',
99 99 pageLength: ${c.visual.dashboard_items},
100 100 order: [[ 4, "desc" ]],
101 101 columns: [
102 102 { data: {"_": "author",
103 103 "sort": "author_raw"}, title: "${_("Author")}", width: "250px", className: "td-user" },
104 104 { data: {"_": "type",
105 105 "sort": "type"}, title: "${_("Type")}", width: "100px", className: "td-gist-type" },
106 106 { data: {"_": "access_id",
107 107 "sort": "access_id"}, title: "${_("Name")}", width:"150px", className: "td-componentname" },
108 108 { data: {"_": "description",
109 109 "sort": "description"}, title: "${_("Description")}", width: "250px", className: "td-description" },
110 110 { data: {"_": "created_on",
111 111 "sort": "created_on_raw"}, title: "${_("Created on")}", className: "td-time" },
112 112 { data: {"_": "expires",
113 113 "sort": "expires"}, title: "${_("Expires")}", width: "200px", className: "td-expire" }
114 114 ],
115 115 language: {
116 116 paginate: DEFAULT_GRID_PAGINATION,
117 117 emptyTable: _gettext("No gists available yet.")
118 118 },
119 119 "initComplete": function( settings, json ) {
120 120 timeagoActivate();
121 121 tooltipActivate();
122 122 get_datatable_count();
123 123 }
124 124 });
125 125
126 126 // update the counter when things change
127 127 $('#gist_list_table').on('draw.dt', function() {
128 128 timeagoActivate();
129 129 tooltipActivate();
130 130 get_datatable_count();
131 131 });
132 132
133 133 // filter, filter both grids
134 134 $('#q_filter').on( 'keyup', function () {
135 135 var repo_api = $('#gist_list_table').dataTable().api();
136 136 repo_api
137 137 .draw();
138 138 });
139 139
140 140 // refilter table if page load via back button
141 141 $("#q_filter").trigger('keyup');
142 142
143 143 });
144 144
145 145 </script>
146 146 </%def>
147 147
@@ -1,103 +1,112 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 3
4 4 <%def name="title()">
5 5 ${_('New Gist')}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}
8 8 %endif
9 9 </%def>
10 10
11 11 <%def name="breadcrumbs_links()"></%def>
12 12
13 13 <%def name="menu_bar_nav()">
14 14 ${self.menu_items(active='gists')}
15 15 </%def>
16 16
17 17 <%def name="main()">
18 18 <div class="box">
19 19 <!-- box / title -->
20 20 <div class="title">
21 21
22 22 </div>
23 23
24 24 <div class="table">
25 25 <div id="files_data">
26 26 ${h.secure_form(h.route_path('gists_create'), id='eform', request=request)}
27 27 <div>
28 28 <span class="gist-gravatar">
29 29 ${self.gravatar(c.rhodecode_user.email, 30)}
30 30 </span>
31 31 <label for='gistid'>${_('Gist id')}</label>
32 32 ${h.text('gistid', placeholder=_('Auto generated'))}
33 33
34 34 <label for='lifetime'>${_('Gist lifetime')}</label>
35 35 ${h.dropdownmenu('lifetime', '', c.lifetime_options)}
36 36
37 37 <label for='acl_level'>${_('Private Gist access level')}</label>
38 38 ${h.dropdownmenu('gist_acl_level', '', c.acl_options)}
39 39
40 40 <textarea style="margin-top: 5px; border-color: #dbd9da" id="description" name="description" placeholder="${_('Gist description ...')}"></textarea>
41 41 </div>
42 42
43 43 <div id="codeblock" class="codeblock">
44 44 <div class="code-header">
45 45 <div class="form">
46 46 <div class="fields">
47 47 ${h.text('filename', size=30, placeholder=_('name gist file...'))}
48 48 ${h.dropdownmenu('mimetype','plain',[('plain',_('plain'))],enable_filter=True)}
49 49 </div>
50 50 </div>
51 51 </div>
52 52
53 53 <div id="editor_container">
54 54 <div id="editor_pre"></div>
55 55 <textarea id="editor" name="content" ></textarea>
56 56 </div>
57 57 </div>
58 58
59 59 <div class="pull-left">
60 60 <div class="pull-right">
61 61 ${h.submit('create',_('Create Gist'),class_="btn")}
62 62 </div>
63 63 <div class="rcform-element pull-right">
64 64 <div class="fields gist-type-fields">
65 65 <fieldset>
66 66 <div class="gist-type-fields-wrapper">
67 67
68 <input type="radio" id="private_gist" checked="" name="gist_type" value="private">
68 <input type="radio" id="private_gist" checked="" name="gist_type" value="private" onchange="setGistId('private')">
69 69 <label for="private_gist">${_('Private Gist')}</label>
70 70 <span class="tooltip label" title="${_('Private Gists are not listed and only accessible through their secret url.')}">${_('Private Gist')}</span>
71 71
72 <input type="radio" id="public_gist" name="gist_type" value="public">
72 <input type="radio" id="public_gist" name="gist_type" value="public" onchange="setGistId('public')">
73 73 <label for="public_gist">${_('Public Gist')}</label>
74 74 <span class="tooltip label" title="${_('Public Gists are accessible to anyone and listed in Gists page.')}">${_('Public Gist')}</span>
75 75 </div>
76 76 </fieldset>
77 77 </div>
78 78 </div>
79 79
80 80 </div>
81 81 ${h.end_form()}
82 82 </div>
83 83 </div>
84 84
85 85 </div>
86 86
87 87 <script type="text/javascript">
88 88 var myCodeMirror = initCodeMirror('editor', '');
89 89
90 90 var modes_select = $('#mimetype');
91 91 fillCodeMirrorOptions(modes_select);
92 92
93 93 var filename_selector = '#filename';
94 94 // on change of select field set mode
95 95 setCodeMirrorModeFromSelect(
96 96 modes_select, filename_selector, myCodeMirror, null);
97 97
98 98 // on entering the new filename set mode, from given extension
99 99 setCodeMirrorModeFromInput(
100 100 modes_select, filename_selector, myCodeMirror, null);
101 101
102 setGistId = function(gistType) {
103 if (gistType === 'private') {
104 $('#gistid').removeAttr('disabled');
105 }
106 else {
107 $('#gistid').val('');
108 $('#gistid').attr('disabled', 'disabled')
109 }
110 }
102 111 </script>
103 112 </%def>
@@ -1,226 +1,217 b''
1 1 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
2 2
3 3 <%def name="form_item(position=None, title=None, redirect_url=None, repo=None, repo_group=None)">
4 4 <tr>
5 5 <td class="td-align-top" >
6 6 <div class="label">
7 7 <label for="position">${_('Position')}:</label>
8 8 </div>
9 9 <div class="input">
10 10 <input type="text" name="position" value="${position}" style="width: 40px"/>
11 11 ${h.hidden('cur_position', position)}
12 12 </div>
13 13 </td>
14 14
15 15 <td>
16 16 <div class="label">
17 17 <label for="title">${_('Bookmark title (max 30 characters, optional)')}:</label>
18 18 </div>
19 19 <div class="input">
20 20 <input type="text" name="title" value="${title}" style="width: 300px" maxlength="30"/>
21 21
22 22 <div class="field pull-right">
23 23 <div>
24 24 <label class="btn-link btn-danger">${_('Clear')}:</label>
25 25 ${h.checkbox('remove', value=True)}
26 26 </div>
27 27 </div>
28 28 </div>
29 <p class="help-block help-block-inline" >
30 ${_('Server URL is available as ${server_url} variable. E.g. Redirect url: ${server_url}/_admin/exception_tracker')}
31 </p>
32 29
33 <div class="label">
30 <div class="label" style="margin-top:10px">
34 31 <label for="redirect_url">${_('Redirect URL')}:</label>
35 32 </div>
36 33 <div class="input">
37 34 <input type="text" name="redirect_url" value="${redirect_url}" style="width: 600px"/>
38 35 </div>
39
36 <p class="help-block help-block-inline">
37 ${_('Server URL is available as ${server_url} variable. E.g. Redirect url: ${server_url}/_admin/exception_tracker')}
38 </p>
40 39
41 <div class="select">
40 <div class="select" style="margin-top:5px">
41 <div class="label">
42 <label for="redirect_url">${_('Templates')}:</label>
43 </div>
44
42 45 % if repo:
43 <div class="label">
44 <label for="redirect_url">${_('Repository template')}:</label>
45 </div>
46 46 ${dt.repo_name(name=repo.repo_name, rtype=repo.repo_type,rstate=None,private=None,archived=False,fork_of=False)}
47 47 ${h.hidden('bookmark_repo', repo.repo_id)}
48 48 % elif repo_group:
49 <div class="label">
50 <label for="redirect_url">${_('Repository group template')}:</label>
51 </div>
52 49 ${dt.repo_group_name(repo_group.group_name)}
53 50 ${h.hidden('bookmark_repo_group', repo_group.group_id)}
54 51 % else:
55 <div class="label">
56 <label for="redirect_url">${_('Template Repository or Repository group')}:</label>
52 <div>
53 ${h.hidden('bookmark_repo', class_='bookmark_repo')}
54 <p class="help-block help-block-inline">${_('Available as ${repo_url} e.g. Redirect url: ${repo_url}/changelog')}</p>
57 55 </div>
58 ${h.hidden('bookmark_repo', class_='bookmark_repo')}
59 <span style="padding-right:15px">OR</span>
56 <div style="margin-top:5px">
60 57 ${h.hidden('bookmark_repo_group', class_='bookmark_repo_group')}
58 <p class="help-block help-block-inline">${_('Available as ${repo_group_url} e.g. Redirect url: ${repo_group_url}')}</p>
59 </div>
60
61 61 % endif
62 62 </div>
63 63
64 <p class="help-block help-block-inline" >
65 % if repo:
66 ${_('Available as ${repo_url} e.g. Redirect url: ${repo_url}/changelog')}
67 % elif repo_group:
68 ${_('Available as ${repo_group_url} e.g. Redirect url: ${repo_group_url}')}
69 % else:
70 ${_('Available as full url variables in redirect url. i.e: ${repo_url}, ${repo_group_url}.')}
71 % endif
72 </p>
73 64 </td>
74 65
75 66 </tr>
76 67 </%def>
77 68
78 69 <div class="panel panel-default">
79 70 <div class="panel-heading">
80 71 <h3 class="panel-title">${_('Your Bookmarks')}</h3>
81 72 </div>
82 73
83 74 <div class="panel-body">
84 75 <p>
85 76 ${_('Store upto 10 bookmark links to favorite repositories, external issue tracker or CI server. ')}
86 77 <br/>
87 78 ${_('Bookmarks are accessible from your username dropdown or by keyboard shortcut `g 0-9`')}
88 79 </p>
89 80
90 81 ${h.secure_form(h.route_path('my_account_bookmarks_update'), request=request)}
91 82 <div class="form-vertical">
92 83 <table class="rctable">
93 84 ## generate always 10 entries
94 85 <input type="hidden" name="__start__" value="bookmarks:sequence"/>
95 86 % for item in (c.bookmark_items + [None for i in range(10)])[:10]:
96 87 <input type="hidden" name="__start__" value="bookmark:mapping"/>
97 88 % if item is None:
98 89 ## empty placehodlder
99 90 ${form_item()}
100 91 % else:
101 92 ## actual entry
102 93 ${form_item(position=item.position, title=item.title, redirect_url=item.redirect_url, repo=item.repository, repo_group=item.repository_group)}
103 94 % endif
104 95 <input type="hidden" name="__end__" value="bookmark:mapping"/>
105 96 % endfor
106 97 <input type="hidden" name="__end__" value="bookmarks:sequence"/>
107 98 </table>
108 99 <div class="buttons">
109 100 ${h.submit('save',_('Save'),class_="btn")}
110 101 </div>
111 102 </div>
112 103 ${h.end_form()}
113 104 </div>
114 105 </div>
115 106
116 107 <script>
117 108 $(document).ready(function(){
118 109
119 110
120 111 var repoFilter = function (data) {
121 112 var results = [];
122 113
123 114 if (!data.results[0]) {
124 115 return data
125 116 }
126 117
127 118 $.each(data.results[0].children, function () {
128 119 // replace name to ID for submision
129 120 this.id = this.repo_id;
130 121 results.push(this);
131 122 });
132 123
133 124 data.results[0].children = results;
134 125 return data;
135 126 };
136 127
137 128
138 129 $(".bookmark_repo").select2({
139 130 cachedDataSource: {},
140 131 minimumInputLength: 2,
141 132 placeholder: "${_('repository')}",
142 133 dropdownAutoWidth: true,
143 134 containerCssClass: "drop-menu",
144 135 dropdownCssClass: "drop-menu-dropdown",
145 136 formatResult: formatRepoResult,
146 137 query: $.debounce(250, function (query) {
147 138 self = this;
148 139 var cacheKey = query.term;
149 140 var cachedData = self.cachedDataSource[cacheKey];
150 141
151 142 if (cachedData) {
152 143 query.callback({results: cachedData.results});
153 144 } else {
154 145 $.ajax({
155 146 url: pyroutes.url('repo_list_data'),
156 147 data: {'query': query.term},
157 148 dataType: 'json',
158 149 type: 'GET',
159 150 success: function (data) {
160 151 data = repoFilter(data);
161 152 self.cachedDataSource[cacheKey] = data;
162 153 query.callback({results: data.results});
163 154 },
164 155 error: function (data, textStatus, errorThrown) {
165 156 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
166 157 }
167 158 })
168 159 }
169 160 }),
170 161 });
171 162
172 163 var repoGroupFilter = function (data) {
173 164 var results = [];
174 165
175 166 if (!data.results[0]) {
176 167 return data
177 168 }
178 169
179 170 $.each(data.results[0].children, function () {
180 171 // replace name to ID for submision
181 172 this.id = this.repo_group_id;
182 173 results.push(this);
183 174 });
184 175
185 176 data.results[0].children = results;
186 177 return data;
187 178 };
188 179
189 180 $(".bookmark_repo_group").select2({
190 181 cachedDataSource: {},
191 182 minimumInputLength: 2,
192 183 placeholder: "${_('repository group')}",
193 184 dropdownAutoWidth: true,
194 185 containerCssClass: "drop-menu",
195 186 dropdownCssClass: "drop-menu-dropdown",
196 187 formatResult: formatRepoGroupResult,
197 188 query: $.debounce(250, function (query) {
198 189 self = this;
199 190 var cacheKey = query.term;
200 191 var cachedData = self.cachedDataSource[cacheKey];
201 192
202 193 if (cachedData) {
203 194 query.callback({results: cachedData.results});
204 195 } else {
205 196 $.ajax({
206 197 url: pyroutes.url('repo_group_list_data'),
207 198 data: {'query': query.term},
208 199 dataType: 'json',
209 200 type: 'GET',
210 201 success: function (data) {
211 202 data = repoGroupFilter(data);
212 203 self.cachedDataSource[cacheKey] = data;
213 204 query.callback({results: data.results});
214 205 },
215 206 error: function (data, textStatus, errorThrown) {
216 207 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
217 208 }
218 209 })
219 210 }
220 211 })
221 212 });
222 213
223 214
224 215 });
225 216
226 217 </script>
@@ -1,151 +1,164 b''
1 1 <%namespace name="base" file="/base/base.mako"/>
2 2
3 3 <div class="panel panel-default">
4 <div class="panel-body">
5 <div style="height: 35px">
6 <%
7 selected_filter = 'all'
8 if c.closed:
9 selected_filter = 'all_closed'
10 %>
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Pull Requests You Participate In')}</h3>
6 </div>
11 7
8 <div class="panel-body panel-body-min-height">
9 <div class="title">
12 10 <ul class="button-links">
13 <li class="btn ${h.is_active('all', selected_filter)}"><a href="${h.route_path('my_account_pullrequests')}">${_('All')}</a></li>
14 <li class="btn ${h.is_active('all_closed', selected_filter)}"><a href="${h.route_path('my_account_pullrequests', _query={'pr_show_closed':1})}">${_('All + Closed')}</a></li>
11 <li><a class="btn ${h.is_active('all', c.selected_filter)}"
12 href="${h.route_path('my_account_pullrequests', _query={})}">
13 ${_('Open')}
14 </a>
15 </li>
16 <li><a class="btn ${h.is_active('all_closed', c.selected_filter)}"
17 href="${h.route_path('my_account_pullrequests', _query={'closed':1})}">
18 ${_('All + Closed')}
19 </a>
20 </li>
21 <li><a class="btn ${h.is_active('awaiting_my_review', c.selected_filter)}"
22 href="${h.route_path('my_account_pullrequests', _query={'awaiting_my_review':1})}">
23
24 ${_('Awaiting my review')}
25 </a>
26 </li>
15 27 </ul>
16 28
17 29 <div class="grid-quick-filter">
18 30 <ul class="grid-filter-box">
19 31 <li class="grid-filter-box-icon">
20 32 <i class="icon-search"></i>
21 33 </li>
22 34 <li class="grid-filter-box-input">
23 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
35 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter"
36 placeholder="${_('quick filter...')}" value=""/>
24 37 </li>
25 38 </ul>
26 39 </div>
27 40 </div>
28 </div>
29 </div>
30 41
31 <div class="panel panel-default">
32 <div class="panel-heading">
33 <h3 class="panel-title">${_('Pull Requests You Participate In')}</h3>
34 </div>
35 <div class="panel-body panel-body-min-height">
36 42 <table id="pull_request_list_table" class="rctable table-bordered"></table>
37 43 </div>
38 44 </div>
39 45
40 46 <script type="text/javascript">
41 47 $(document).ready(function () {
42 48
43 49 var $pullRequestListTable = $('#pull_request_list_table');
44 50
45 51 // participating object list
46 52 $pullRequestListTable.DataTable({
47 53 processing: true,
48 54 serverSide: true,
49 55 stateSave: true,
50 56 stateDuration: -1,
51 57 ajax: {
52 58 "url": "${h.route_path('my_account_pullrequests_data')}",
53 59 "data": function (d) {
54 60 d.closed = "${c.closed}";
61 d.awaiting_my_review = "${c.awaiting_my_review}";
55 62 },
56 63 "dataSrc": function (json) {
57 64 return json.data;
58 65 }
59 66 },
60 67
61 68 dom: 'rtp',
62 69 pageLength: ${c.visual.dashboard_items},
63 order: [[1, "desc"]],
70 order: [[2, "desc"]],
64 71 columns: [
65 72 {
66 73 data: {
67 74 "_": "status",
68 75 "sort": "status"
69 }, title: "", className: "td-status", orderable: false
76 }, title: "PR", className: "td-status", orderable: false
77 },
78 {
79 data: {
80 "_": "my_status",
81 "sort": "status"
82 }, title: "You", className: "td-status", orderable: false
70 83 },
71 84 {
72 85 data: {
73 86 "_": "name",
74 87 "sort": "name_raw"
75 88 }, title: "${_('Id')}", className: "td-componentname", "type": "num"
76 89 },
77 90 {
78 91 data: {
79 92 "_": "title",
80 93 "sort": "title"
81 94 }, title: "${_('Title')}", className: "td-description"
82 95 },
83 96 {
84 97 data: {
85 98 "_": "author",
86 99 "sort": "author_raw"
87 100 }, title: "${_('Author')}", className: "td-user", orderable: false
88 101 },
89 102 {
90 103 data: {
91 104 "_": "comments",
92 105 "sort": "comments_raw"
93 106 }, title: "", className: "td-comments", orderable: false
94 107 },
95 108 {
96 109 data: {
97 110 "_": "updated_on",
98 111 "sort": "updated_on_raw"
99 112 }, title: "${_('Last Update')}", className: "td-time"
100 113 },
101 114 {
102 115 data: {
103 116 "_": "target_repo",
104 117 "sort": "target_repo"
105 118 }, title: "${_('Target Repo')}", className: "td-targetrepo", orderable: false
106 119 },
107 120 ],
108 121 language: {
109 122 paginate: DEFAULT_GRID_PAGINATION,
110 123 sProcessing: _gettext('loading...'),
111 124 emptyTable: _gettext("There are currently no open pull requests requiring your participation.")
112 125 },
113 126 "drawCallback": function (settings, json) {
114 127 timeagoActivate();
115 128 tooltipActivate();
116 129 },
117 130 "createdRow": function (row, data, index) {
118 131 if (data['closed']) {
119 132 $(row).addClass('closed');
120 133 }
121 134 if (data['owned']) {
122 135 $(row).addClass('owned');
123 136 }
124 137 },
125 138 "stateSaveParams": function (settings, data) {
126 139 data.search.search = ""; // Don't save search
127 140 data.start = 0; // don't save pagination
128 141 }
129 142 });
130 143 $pullRequestListTable.on('xhr.dt', function (e, settings, json, xhr) {
131 144 $pullRequestListTable.css('opacity', 1);
132 145 });
133 146
134 147 $pullRequestListTable.on('preXhr.dt', function (e, settings, data) {
135 148 $pullRequestListTable.css('opacity', 0.3);
136 149 });
137 150
138 151
139 152 // filter
140 153 $('#q_filter').on('keyup',
141 154 $.debounce(250, function () {
142 155 $pullRequestListTable.DataTable().search(
143 156 $('#q_filter').val()
144 157 ).draw();
145 158 })
146 159 );
147 160
148 161 });
149 162
150 163
151 164 </script>
@@ -1,304 +1,307 b''
1 1 <%namespace name="base" file="/base/base.mako"/>
2 2
3 3 <%
4 4 elems = [
5 5 (_('Repository ID'), c.rhodecode_db_repo.repo_id, '', ''),
6 6 (_('Owner'), lambda:base.gravatar_with_user(c.rhodecode_db_repo.user.email, tooltip=True), '', ''),
7 7 (_('Created on'), h.format_date(c.rhodecode_db_repo.created_on), '', ''),
8 8 (_('Updated on'), h.format_date(c.rhodecode_db_repo.updated_on), '', ''),
9 9 (_('Cached Commit id'), lambda: h.link_to(c.rhodecode_db_repo.changeset_cache.get('short_id'), h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.changeset_cache.get('raw_id'))), '', ''),
10 10 (_('Cached Commit date'), c.rhodecode_db_repo.changeset_cache.get('date'), '', ''),
11 11 (_('Cached Commit data'), lambda: h.link_to('refresh now', h.current_route_path(request, update_commit_cache=1)), '', ''),
12 12 (_('Attached scoped tokens'), len(c.rhodecode_db_repo.scoped_tokens), '', [x.user for x in c.rhodecode_db_repo.scoped_tokens]),
13 13 (_('Pull requests source'), len(c.rhodecode_db_repo.pull_requests_source), '', ['pr_id:{}, repo:{}'.format(x.pull_request_id,x.source_repo.repo_name) for x in c.rhodecode_db_repo.pull_requests_source]),
14 14 (_('Pull requests target'), len(c.rhodecode_db_repo.pull_requests_target), '', ['pr_id:{}, repo:{}'.format(x.pull_request_id,x.target_repo.repo_name) for x in c.rhodecode_db_repo.pull_requests_target]),
15 15 (_('Attached Artifacts'), len(c.rhodecode_db_repo.artifacts), '', ''),
16 16 ]
17 17 %>
18 18
19 19 <div class="panel panel-default">
20 20 <div class="panel-heading" id="advanced-info" >
21 21 <h3 class="panel-title">${_('Repository: %s') % c.rhodecode_db_repo.repo_name} <a class="permalink" href="#advanced-info"></a></h3>
22 22 </div>
23 23 <div class="panel-body">
24 24 ${base.dt_info_panel(elems)}
25 25 </div>
26 26 </div>
27 27
28 28
29 29 <div class="panel panel-default">
30 30 <div class="panel-heading" id="advanced-fork">
31 31 <h3 class="panel-title">${_('Fork Reference')} <a class="permalink" href="#advanced-fork"></a></h3>
32 32 </div>
33 33 <div class="panel-body">
34 34 ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
35 35
36 36 % if c.rhodecode_db_repo.fork:
37 37 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.rhodecode_db_repo.fork.repo_name, h.route_path('repo_summary', repo_name=c.rhodecode_db_repo.fork.repo_name))})}
38 38 | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div>
39 39 % endif
40 40
41 41 <div class="field">
42 42 ${h.hidden('id_fork_of')}
43 43 ${h.submit('set_as_fork_%s' % c.rhodecode_db_repo.repo_name,_('Set'),class_="btn btn-small",)}
44 44 </div>
45 45 <div class="field">
46 46 <span class="help-block">${_('Manually set this repository as a fork of another from the list')}</span>
47 47 </div>
48 48 ${h.end_form()}
49 49 </div>
50 50 </div>
51 51
52 52
53 53 <div class="panel panel-default">
54 54 <div class="panel-heading" id="advanced-journal">
55 55 <h3 class="panel-title">${_('Public Journal Visibility')} <a class="permalink" href="#advanced-journal"></a></h3>
56 56 </div>
57 57 <div class="panel-body">
58 58 ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
59 59 <div class="field">
60 60 %if c.in_public_journal:
61 61 <button class="btn btn-small" type="submit">
62 62 ${_('Remove from Public Journal')}
63 63 </button>
64 64 %else:
65 65 <button class="btn btn-small" type="submit">
66 66 ${_('Add to Public Journal')}
67 67 </button>
68 68 %endif
69 69 </div>
70 70 <div class="field" >
71 71 <span class="help-block">${_('All actions made on this repository will be visible to everyone following the public journal.')}</span>
72 72 </div>
73 73 ${h.end_form()}
74 74 </div>
75 75 </div>
76 76
77 77
78 78 <div class="panel panel-default">
79 79 <div class="panel-heading" id="advanced-locking">
80 80 <h3 class="panel-title">${_('Locking state')} <a class="permalink" href="#advanced-locking"></a></h3>
81 81 </div>
82 82 <div class="panel-body">
83 83 ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
84 84
85 85 %if c.rhodecode_db_repo.locked[0]:
86 86 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.rhodecode_db_repo.locked[0]),
87 87 h.format_date(h. time_to_datetime(c.rhodecode_db_repo.locked[1])), c.rhodecode_db_repo.locked[2])}</div>
88 88 %else:
89 89 <div class="panel-body-title-text">${_('This Repository is not currently locked.')}</div>
90 90 %endif
91 91
92 92 <div class="field" >
93 93 %if c.rhodecode_db_repo.locked[0]:
94 94 ${h.hidden('set_unlock', '1')}
95 95 <button class="btn btn-small" type="submit"
96 96 onclick="submitConfirm(event, this, _gettext('Confirm to unlock this repository'), _gettext('Unlock'), '${c.rhodecode_db_repo.repo_name}')"
97 97 >
98 98 <i class="icon-unlock"></i>
99 99 ${_('Unlock repository')}
100 100 </button>
101 101 %else:
102 102 ${h.hidden('set_lock', '1')}
103 103 <button class="btn btn-small" type="submit"
104 104 onclick="submitConfirm(event, this, _gettext('Confirm to lock this repository'), _gettext('lock'), '${c.rhodecode_db_repo.repo_name}')"
105 105 >
106 106 <i class="icon-lock"></i>
107 107 ${_('Lock repository')}
108 108 </button>
109 109 %endif
110 110 </div>
111 111 <div class="field" >
112 112 <span class="help-block">
113 113 ${_('Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again.')}
114 114 </span>
115 115 </div>
116 116 ${h.end_form()}
117 117 </div>
118 118 </div>
119 119
120 120
121 121 <div class="panel panel-default">
122 122 <div class="panel-heading" id="advanced-hooks">
123 123 <h3 class="panel-title">${_('Hooks')} <a class="permalink" href="#advanced-hooks"></a></h3>
124 124 </div>
125 125 <div class="panel-body">
126 126 <table class="rctable">
127 127 <th>${_('Hook type')}</th>
128 128 <th>${_('Hook version')}</th>
129 129 <th>${_('Current version')}</th>
130 130 % if c.ver_info_dict:
131 131 <tr>
132 132 <td>${_('PRE HOOK')}</td>
133 133 <td>${c.ver_info_dict['pre_version']}</td>
134 134 <td>${c.rhodecode_version}</td>
135 135 </tr>
136 136 <tr>
137 137 <td>${_('POST HOOK')}</td>
138 138 <td>${c.ver_info_dict['post_version']}</td>
139 139 <td>${c.rhodecode_version}</td>
140 140 </tr>
141 141 % else:
142 142 <tr>
143 143 <td>${_('Unable to read hook information from VCS Server')}</td>
144 144 </tr>
145 145 % endif
146 146 </table>
147 147
148 148 <a class="btn btn-primary" href="${h.route_path('edit_repo_advanced_hooks', repo_name=c.repo_name)}"
149 149 onclick="return confirm('${_('Confirm to reinstall hooks for this repository.')}');">
150 150 ${_('Update Hooks')}
151 151 </a>
152 152 % if c.hooks_outdated:
153 153 <span class="alert-error" style="padding: 10px">
154 154 ${_('Outdated hooks detected, please update hooks using `Update Hooks` action.')}
155 155 </span>
156 156 % endif
157 157 </div>
158 158 </div>
159 159
160 160 <div class="panel panel-warning">
161 161 <div class="panel-heading" id="advanced-archive">
162 162 <h3 class="panel-title">${_('Archive repository')} <a class="permalink" href="#advanced-archive"></a></h3>
163 163 </div>
164 164 <div class="panel-body">
165 165 ${h.secure_form(h.route_path('edit_repo_advanced_archive', repo_name=c.repo_name), request=request)}
166 166
167 167 <div style="margin: 0 0 20px 0" class="fake-space"></div>
168 168
169 169 <div class="field">
170 170 % if c.rhodecode_db_repo.archived:
171 171 This repository is already archived. Only super-admin users can un-archive this repository.
172 172 % else:
173 173 <button class="btn btn-small btn-warning" type="submit"
174 onclick="submitConfirm(event, this, _gettext('Confirm to archive this repository'), _gettext('Archive'), '${c.rhodecode_db_repo.repo_name}')"
174 onclick="submitConfirm(event, this, _gettext('Confirm to archive this repository. <br/>This action is irreversible !'), _gettext('Archive'), '${c.rhodecode_db_repo.repo_name}')"
175 175 >
176 176 ${_('Archive this repository')}
177 177 </button>
178 178 % endif
179 179
180 180 </div>
181 181 <div class="field">
182 182 <span class="help-block">
183 <strong>
184 ${_('This action is irreversible')} !
185 </strong><br/>
183 186 ${_('Archiving the repository will make it entirely read-only. The repository cannot be committed to.'
184 187 'It is hidden from the search results and dashboard. ')}
185 188 </span>
186 189 </div>
187 190
188 191 ${h.end_form()}
189 192 </div>
190 193 </div>
191 194
192 195
193 196 <div class="panel panel-danger">
194 197 <div class="panel-heading" id="advanced-delete">
195 198 <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"></a></h3>
196 199 </div>
197 200 <div class="panel-body">
198 201 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), request=request)}
199 202 <table class="display">
200 203 <tr>
201 204 <td>
202 205 ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.rhodecode_db_repo.forks.count()) % c.rhodecode_db_repo.forks.count()}
203 206 </td>
204 207 <td>
205 208 %if c.rhodecode_db_repo.forks.count():
206 209 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
207 210 %endif
208 211 </td>
209 212 <td>
210 213 %if c.rhodecode_db_repo.forks.count():
211 214 <input type="radio" name="forks" value="delete_forks"/> <label for="forks">${_('Delete forks')}</label>
212 215 %endif
213 216 </td>
214 217 </tr>
215 218 <% attached_prs = len(c.rhodecode_db_repo.pull_requests_source + c.rhodecode_db_repo.pull_requests_target) %>
216 219 % if c.rhodecode_db_repo.pull_requests_source or c.rhodecode_db_repo.pull_requests_target:
217 220 <tr>
218 221 <td>
219 222 ${_ungettext('This repository has %s attached pull request.', 'This repository has %s attached pull requests.', attached_prs) % attached_prs}
220 223 <br/>
221 224 ${_('Consider to archive this repository instead.')}
222 225 </td>
223 226 <td></td>
224 227 <td></td>
225 228 </tr>
226 229 % endif
227 230 </table>
228 231 <div style="margin: 0 0 20px 0" class="fake-space"></div>
229 232
230 233 <div class="field">
231 234 <button class="btn btn-small btn-danger" type="submit"
232 235 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${c.rhodecode_db_repo.repo_name}')"
233 236 >
234 237 ${_('Delete this repository')}
235 238 </button>
236 239 </div>
237 240 <div class="field">
238 241 <span class="help-block">
239 242 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')}
240 243 </span>
241 244 </div>
242 245
243 246 ${h.end_form()}
244 247 </div>
245 248 </div>
246 249
247 250
248 251 <script>
249 252
250 253 var currentRepoId = ${c.rhodecode_db_repo.repo_id};
251 254
252 255 var repoTypeFilter = function(data) {
253 256 var results = [];
254 257
255 258 if (!data.results[0]) {
256 259 return data
257 260 }
258 261
259 262 $.each(data.results[0].children, function() {
260 263 // filter out the SAME repo, it cannot be used as fork of itself
261 264 if (this.repo_id != currentRepoId) {
262 265 this.id = this.repo_id;
263 266 results.push(this)
264 267 }
265 268 });
266 269 data.results[0].children = results;
267 270 return data;
268 271 };
269 272
270 273 $("#id_fork_of").select2({
271 274 cachedDataSource: {},
272 275 minimumInputLength: 2,
273 276 placeholder: "${_('Change repository') if c.rhodecode_db_repo.fork else _('Pick repository')}",
274 277 dropdownAutoWidth: true,
275 278 containerCssClass: "drop-menu",
276 279 dropdownCssClass: "drop-menu-dropdown",
277 280 formatResult: formatRepoResult,
278 281 query: $.debounce(250, function(query){
279 282 self = this;
280 283 var cacheKey = query.term;
281 284 var cachedData = self.cachedDataSource[cacheKey];
282 285
283 286 if (cachedData) {
284 287 query.callback({results: cachedData.results});
285 288 } else {
286 289 $.ajax({
287 290 url: pyroutes.url('repo_list_data'),
288 291 data: {'query': query.term, repo_type: '${c.rhodecode_db_repo.repo_type}'},
289 292 dataType: 'json',
290 293 type: 'GET',
291 294 success: function(data) {
292 295 data = repoTypeFilter(data);
293 296 self.cachedDataSource[cacheKey] = data;
294 297 query.callback({results: data.results});
295 298 },
296 299 error: function(data, textStatus, errorThrown) {
297 300 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
298 301 }
299 302 })
300 303 }
301 304 })
302 305 });
303 306 </script>
304 307
@@ -1,88 +1,88 b''
1 1 ## Changesets table !
2 2 <%namespace name="base" file="/base/base.mako"/>
3 3
4 4 %if c.ancestor:
5 5 <div class="ancestor">${_('Compare was calculated based on this common ancestor commit')}:
6 6 <a href="${h.route_path('repo_commit', repo_name=c.repo_name, commit_id=c.ancestor)}">${h.short_id(c.ancestor)}</a>
7 7 <input id="common_ancestor" type="hidden" name="common_ancestor" value="${c.ancestor}">
8 8 </div>
9 9 %endif
10 10
11 11 <div class="container">
12 12 <input type="hidden" name="__start__" value="revisions:sequence">
13 13 <table class="rctable compare_view_commits">
14 14 <tr>
15 15 % if hasattr(c, 'commit_versions'):
16 16 <th>ver</th>
17 17 % endif
18 18 <th>${_('Time')}</th>
19 19 <th>${_('Author')}</th>
20 20 <th>${_('Commit')}</th>
21 21 <th></th>
22 22 <th>${_('Description')}</th>
23 23 </tr>
24 24 ## to speed up lookups cache some functions before the loop
25 25 <%
26 26 active_patterns = h.get_active_pattern_entries(c.repo_name)
27 27 urlify_commit_message = h.partial(h.urlify_commit_message, active_pattern_entries=active_patterns)
28 28 %>
29 29
30 30 %for commit in c.commit_ranges:
31 31 <tr id="row-${commit.raw_id}"
32 32 commit_id="${commit.raw_id}"
33 33 class="compare_select"
34 34 style="${'display: none' if c.collapse_all_commits else ''}"
35 35 >
36 36 % if hasattr(c, 'commit_versions'):
37 37 <td class="tooltip" title="${_('Pull request version this commit was introduced')}">
38 38 <code>${('v{}'.format(c.commit_versions[commit.raw_id][0]) if c.commit_versions[commit.raw_id] else 'latest')}</code>
39 39 </td>
40 40 % endif
41 41 <td class="td-time">
42 42 ${h.age_component(commit.date)}
43 43 </td>
44 44 <td class="td-user">
45 45 ${base.gravatar_with_user(commit.author, 16, tooltip=True)}
46 46 </td>
47 47 <td class="td-hash">
48 48 <code>
49 49 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
50 50 r${commit.idx}:${h.short_id(commit.raw_id)}
51 51 </a>
52 52 ${h.hidden('revisions',commit.raw_id)}
53 53 </code>
54 54 </td>
55 55 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
56 56 <i class="icon-expand-linked"></i>
57 57 </td>
58 58 <td class="mid td-description">
59 59 <div class="log-container truncate-wrap">
60 60 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">${urlify_commit_message(commit.message, c.repo_name, issues_container_callback=getattr(c, 'referenced_commit_issues', h.IssuesRegistry())(commit.serialize()))}</div>
61 61 </div>
62 62 </td>
63 63 </tr>
64 64 %endfor
65 65 <tr class="compare_select_hidden" style="${('' if c.collapse_all_commits else 'display: none')}">
66 <td colspan="5">
66 <td colspan="7">
67 67 ${_ungettext('{} commit hidden, click expand to show them.', '{} commits hidden, click expand to show them.', len(c.commit_ranges)).format(len(c.commit_ranges))}
68 68 </td>
69 69 </tr>
70 70 % if not c.commit_ranges:
71 71 <tr class="compare_select">
72 72 <td colspan="5">
73 73 ${_('No commits in this compare')}
74 74 </td>
75 75 </tr>
76 76 % endif
77 77 </table>
78 78 <input type="hidden" name="__end__" value="revisions:sequence">
79 79
80 80 </div>
81 81
82 82 <script>
83 83 commitsController = new CommitsController();
84 84 $('.compare_select').on('click',function(e){
85 85 var cid = $(this).attr('commit_id');
86 86 $('#row-'+cid).toggleClass('hl', !$('#row-'+cid).hasClass('hl'));
87 87 });
88 88 </script>
@@ -1,205 +1,205 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base.mako"/>
3 3 <%namespace name="base" file="base.mako"/>
4 4
5 5 ## EMAIL SUBJECT
6 6 <%def name="subject()" filter="n,trim,whitespace_filter">
7 7 <%
8 8 data = {
9 9 'user': '@'+h.person(user),
10 10 'repo_name': repo_name,
11 11 'status': status_change,
12 12 'comment_file': comment_file,
13 13 'comment_line': comment_line,
14 14 'comment_type': comment_type,
15 15 'comment_id': comment_id,
16 16
17 17 'pr_title': pull_request.title_safe,
18 18 'pr_id': pull_request.pull_request_id,
19 19 'mention_prefix': '[mention] ' if mention else '',
20 20 }
21 21
22 22 if comment_file:
23 23 subject_template = email_pr_comment_file_subject_template or \
24 24 _('{mention_prefix}{user} left a {comment_type} on file `{comment_file}` in pull request !{pr_id}: "{pr_title}"').format(**data)
25 25 else:
26 26 if status_change:
27 27 subject_template = email_pr_comment_status_change_subject_template or \
28 28 _('{mention_prefix}[status: {status}] {user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data)
29 29 else:
30 30 subject_template = email_pr_comment_subject_template or \
31 31 _('{mention_prefix}{user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data)
32 32 %>
33 33
34 34 ${subject_template.format(**data) |n}
35 35 </%def>
36 36
37 37 ## PLAINTEXT VERSION OF BODY
38 38 <%def name="body_plaintext()" filter="n,trim">
39 39 <%
40 40 data = {
41 41 'user': h.person(user),
42 42 'repo_name': repo_name,
43 43 'status': status_change,
44 44 'comment_file': comment_file,
45 45 'comment_line': comment_line,
46 46 'comment_type': comment_type,
47 47 'comment_id': comment_id,
48 48
49 49 'pr_title': pull_request.title_safe,
50 50 'pr_id': pull_request.pull_request_id,
51 51 'source_ref_type': pull_request.source_ref_parts.type,
52 52 'source_ref_name': pull_request.source_ref_parts.name,
53 53 'target_ref_type': pull_request.target_ref_parts.type,
54 54 'target_ref_name': pull_request.target_ref_parts.name,
55 55 'source_repo': pull_request_source_repo.repo_name,
56 56 'target_repo': pull_request_target_repo.repo_name,
57 57 'source_repo_url': pull_request_source_repo_url,
58 58 'target_repo_url': pull_request_target_repo_url,
59 59 }
60 60 %>
61 61
62 62 * ${_('Comment link')}: ${pr_comment_url}
63 63
64 64 * ${_('Pull Request')}: !${pull_request.pull_request_id}
65 65
66 66 * ${h.literal(_('Commit flow: {source_ref_type}:{source_ref_name} of {source_repo_url} into {target_ref_type}:{target_ref_name} of {target_repo_url}').format(**data))}
67 67
68 68 %if status_change and not closing_pr:
69 69 * ${_('{user} submitted pull request !{pr_id} status: *{status}*').format(**data)}
70 70
71 71 %elif status_change and closing_pr:
72 72 * ${_('{user} submitted pull request !{pr_id} status: *{status} and closed*').format(**data)}
73 73
74 74 %endif
75 75 %if comment_file:
76 76 * ${_('File: {comment_file} on line {comment_line}').format(**data)}
77 77
78 78 %endif
79 79 % if comment_type == 'todo':
80 80 ${('Inline' if comment_file else 'General')} ${_('`TODO` number')} ${comment_id}:
81 81 % else:
82 82 ${('Inline' if comment_file else 'General')} ${_('`Note` number')} ${comment_id}:
83 83 % endif
84 84
85 85 ${comment_body |n, trim}
86 86
87 87 ---
88 88 ${self.plaintext_footer()}
89 89 </%def>
90 90
91 91
92 92 <%
93 93 data = {
94 94 'user': h.person(user),
95 95 'comment_file': comment_file,
96 96 'comment_line': comment_line,
97 97 'comment_type': comment_type,
98 98 'comment_id': comment_id,
99 99 'renderer_type': renderer_type or 'plain',
100 100
101 101 'pr_title': pull_request.title_safe,
102 102 'pr_id': pull_request.pull_request_id,
103 103 'status': status_change,
104 104 'source_ref_type': pull_request.source_ref_parts.type,
105 105 'source_ref_name': pull_request.source_ref_parts.name,
106 106 'target_ref_type': pull_request.target_ref_parts.type,
107 107 'target_ref_name': pull_request.target_ref_parts.name,
108 108 'source_repo': pull_request_source_repo.repo_name,
109 109 'target_repo': pull_request_target_repo.repo_name,
110 110 'source_repo_url': h.link_to(pull_request_source_repo.repo_name, pull_request_source_repo_url),
111 111 'target_repo_url': h.link_to(pull_request_target_repo.repo_name, pull_request_target_repo_url),
112 112 }
113 113 %>
114 114
115 115 ## header
116 116 <table style="text-align:left;vertical-align:middle;width: 100%">
117 117 <tr>
118 118 <td style="width:100%;border-bottom:1px solid #dbd9da;">
119 119
120 120 <div style="margin: 0; font-weight: bold">
121 121 <div class="clear-both" style="margin-bottom: 4px">
122 122 <span style="color:#7E7F7F">@${h.person(user.username)}</span>
123 123 ${_('left a')}
124 124 <a href="${pr_comment_url}" style="${base.link_css()}">
125 125 % if comment_file:
126 126 ${_('{comment_type} on file `{comment_file}` in pull request.').format(**data)}
127 127 % else:
128 128 ${_('{comment_type} on pull request.').format(**data) |n}
129 129 % endif
130 130 </a>
131 131 </div>
132 132 <div style="margin-top: 10px"></div>
133 133 ${_('Pull request')} <code>!${data['pr_id']}: ${data['pr_title']}</code>
134 134 </div>
135 135
136 136 </td>
137 137 </tr>
138 138
139 139 </table>
140 140 <div class="clear-both"></div>
141 141 ## main body
142 142 <table style="text-align:left;vertical-align:middle;width: 100%">
143 143
144 144 ## spacing def
145 145 <tr>
146 146 <td style="width: 130px"></td>
147 147 <td></td>
148 148 </tr>
149 149
150 150 % if status_change:
151 151 <tr>
152 152 <td style="padding-right:20px;">${_('Review Status')}:</td>
153 153 <td>
154 154 % if closing_pr:
155 155 ${_('Closed pull request with status')}: ${base.status_text(status_change, tag_type=status_change_type)}
156 156 % else:
157 157 ${_('Submitted review status')}: ${base.status_text(status_change, tag_type=status_change_type)}
158 158 % endif
159 159 </td>
160 160 </tr>
161 161 % endif
162 162 <tr>
163 163 <td style="padding-right:20px;">${_('Pull request')}:</td>
164 164 <td>
165 165 <a href="${pull_request_url}" style="${base.link_css()}">
166 166 !${pull_request.pull_request_id}
167 167 </a>
168 168 </td>
169 169 </tr>
170 170
171 171 <tr>
172 172 <td style="padding-right:20px;line-height:20px;">${_('Commit Flow')}:</td>
173 173 <td style="line-height:20px;">
174 <code>${'{}:{}'.format(data['source_ref_type'], pull_request.source_ref_parts.name)}</code> ${_('of')} ${data['source_repo_url']}
174 <code>${data['source_ref_type']}:${pull_request.source_ref_parts.name}</code> ${_('of')} ${data['source_repo_url']}
175 175 &rarr;
176 <code>${'{}:{}'.format(data['target_ref_type'], pull_request.target_ref_parts.name)}</code> ${_('of')} ${data['target_repo_url']}
176 <code>${data['target_ref_type']}:${pull_request.target_ref_parts.name}</code> ${_('of')} ${data['target_repo_url']}
177 177 </td>
178 178 </tr>
179 179
180 180 % if comment_file:
181 181 <tr>
182 182 <td style="padding-right:20px;">${_('File')}:</td>
183 183 <td><a href="${pr_comment_url}" style="${base.link_css()}">${_('`{comment_file}` on line {comment_line}').format(**data)}</a></td>
184 184 </tr>
185 185 % endif
186 186
187 187 <tr style="border-bottom:1px solid #dbd9da;">
188 188 <td colspan="2" style="padding-right:20px;">
189 189 % if comment_type == 'todo':
190 190 ${('Inline' if comment_file else 'General')} ${_('`TODO` number')} ${comment_id}:
191 191 % else:
192 192 ${('Inline' if comment_file else 'General')} ${_('`Note` number')} ${comment_id}:
193 193 % endif
194 194 </td>
195 195 </tr>
196 196
197 197 <tr>
198 198 <td colspan="2" style="background: #F7F7F7">${h.render(comment_body, renderer=data['renderer_type'], mentions=True)}</td>
199 199 </tr>
200 200
201 201 <tr>
202 202 <td><a href="${pr_comment_reply_url}">${_('Reply')}</a></td>
203 203 <td></td>
204 204 </tr>
205 205 </table>
@@ -1,154 +1,154 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base.mako"/>
3 3 <%namespace name="base" file="base.mako"/>
4 4
5 5 ## EMAIL SUBJECT
6 6 <%def name="subject()" filter="n,trim,whitespace_filter">
7 7 <%
8 8 data = {
9 9 'user': '@'+h.person(user),
10 10 'pr_id': pull_request.pull_request_id,
11 11 'pr_title': pull_request.title_safe,
12 12 }
13 13
14 14 if user_role == 'observer':
15 15 subject_template = email_pr_review_subject_template or _('{user} added you as observer to pull request. !{pr_id}: "{pr_title}"')
16 16 else:
17 17 subject_template = email_pr_review_subject_template or _('{user} requested a pull request review. !{pr_id}: "{pr_title}"')
18 18 %>
19 19
20 20 ${subject_template.format(**data) |n}
21 21 </%def>
22 22
23 23 ## PLAINTEXT VERSION OF BODY
24 24 <%def name="body_plaintext()" filter="n,trim">
25 25 <%
26 26 data = {
27 27 'user': h.person(user),
28 28 'pr_id': pull_request.pull_request_id,
29 29 'pr_title': pull_request.title_safe,
30 30 'source_ref_type': pull_request.source_ref_parts.type,
31 31 'source_ref_name': pull_request.source_ref_parts.name,
32 32 'target_ref_type': pull_request.target_ref_parts.type,
33 33 'target_ref_name': pull_request.target_ref_parts.name,
34 34 'repo_url': pull_request_source_repo_url,
35 35 'source_repo': pull_request_source_repo.repo_name,
36 36 'target_repo': pull_request_target_repo.repo_name,
37 37 'source_repo_url': pull_request_source_repo_url,
38 38 'target_repo_url': pull_request_target_repo_url,
39 39 }
40 40
41 41 %>
42 42
43 43 * ${_('Pull Request link')}: ${pull_request_url}
44 44
45 45 * ${h.literal(_('Commit flow: {source_ref_type}:{source_ref_name} of {source_repo_url} into {target_ref_type}:{target_ref_name} of {target_repo_url}').format(**data))}
46 46
47 47 * ${_('Title')}: ${pull_request.title}
48 48
49 49 * ${_('Description')}:
50 50
51 51 ${pull_request.description | trim}
52 52
53 53
54 54 * ${_ungettext('Commit (%(num)s)', 'Commits (%(num)s)', len(pull_request_commits) ) % {'num': len(pull_request_commits)}}:
55 55
56 56 % for commit_id, message in pull_request_commits:
57 57 - ${h.short_id(commit_id)}
58 58 ${h.chop_at_smart(message.lstrip(), '\n', suffix_if_chopped='...')}
59 59
60 60 % endfor
61 61
62 62 ---
63 63 ${self.plaintext_footer()}
64 64 </%def>
65 65 <%
66 66 data = {
67 67 'user': h.person(user),
68 68 'pr_id': pull_request.pull_request_id,
69 69 'pr_title': pull_request.title_safe,
70 70 'source_ref_type': pull_request.source_ref_parts.type,
71 71 'source_ref_name': pull_request.source_ref_parts.name,
72 72 'target_ref_type': pull_request.target_ref_parts.type,
73 73 'target_ref_name': pull_request.target_ref_parts.name,
74 74 'repo_url': pull_request_source_repo_url,
75 75 'source_repo': pull_request_source_repo.repo_name,
76 76 'target_repo': pull_request_target_repo.repo_name,
77 77 'source_repo_url': h.link_to(pull_request_source_repo.repo_name, pull_request_source_repo_url),
78 78 'target_repo_url': h.link_to(pull_request_target_repo.repo_name, pull_request_target_repo_url),
79 79 }
80 80 %>
81 81 ## header
82 82 <table style="text-align:left;vertical-align:middle;width: 100%">
83 83 <tr>
84 84 <td style="width:100%;border-bottom:1px solid #dbd9da;">
85 85 <div style="margin: 0; font-weight: bold">
86 86 % if user_role == 'observer':
87 87 <div class="clear-both" class="clear-both" style="margin-bottom: 4px">
88 88 <span style="color:#7E7F7F">@${h.person(user.username)}</span>
89 89 ${_('added you as observer to')}
90 90 <a href="${pull_request_url}" style="${base.link_css()}">pull request</a>.
91 91 </div>
92 92 % else:
93 93 <div class="clear-both" class="clear-both" style="margin-bottom: 4px">
94 94 <span style="color:#7E7F7F">@${h.person(user.username)}</span>
95 95 ${_('requested a')}
96 96 <a href="${pull_request_url}" style="${base.link_css()}">pull request</a> review.
97 97 </div>
98 98 % endif
99 99 <div style="margin-top: 10px"></div>
100 100 ${_('Pull request')} <code>!${data['pr_id']}: ${data['pr_title']}</code>
101 101 </div>
102 102 </td>
103 103 </tr>
104 104
105 105 </table>
106 106 <div class="clear-both"></div>
107 107 ## main body
108 108 <table style="text-align:left;vertical-align:middle;width: 100%">
109 109 ## spacing def
110 110 <tr>
111 111 <td style="width: 130px"></td>
112 112 <td></td>
113 113 </tr>
114 114
115 115 <tr>
116 116 <td style="padding-right:20px;">${_('Pull request')}:</td>
117 117 <td>
118 118 <a href="${pull_request_url}" style="${base.link_css()}">
119 119 !${pull_request.pull_request_id}
120 120 </a>
121 121 </td>
122 122 </tr>
123 123
124 124 <tr>
125 125 <td style="padding-right:20px;line-height:20px;">${_('Commit Flow')}:</td>
126 126 <td style="line-height:20px;">
127 <code>${'{}:{}'.format(data['source_ref_type'], pull_request.source_ref_parts.name)}</code> ${_('of')} ${data['source_repo_url']}
127 <code>${data['source_ref_type']}:${pull_request.source_ref_parts.name}</code> ${_('of')} ${data['source_repo_url']}
128 128 &rarr;
129 <code>${'{}:{}'.format(data['target_ref_type'], pull_request.target_ref_parts.name)}</code> ${_('of')} ${data['target_repo_url']}
129 <code>${data['target_ref_type']}:${pull_request.target_ref_parts.name}</code> ${_('of')} ${data['target_repo_url']}
130 130 </td>
131 131 </tr>
132 132
133 133 <tr>
134 134 <td style="padding-right:20px;">${_('Description')}:</td>
135 135 <td style="white-space:pre-wrap"><code>${pull_request.description | trim}</code></td>
136 136 </tr>
137 137 <tr>
138 138 <td style="padding-right:20px;">${_ungettext('Commit (%(num)s)', 'Commits (%(num)s)', len(pull_request_commits)) % {'num': len(pull_request_commits)}}:</td>
139 139 <td></td>
140 140 </tr>
141 141
142 142 <tr>
143 143 <td colspan="2">
144 144 <ol style="margin:0 0 0 1em;padding:0;text-align:left;">
145 145 % for commit_id, message in pull_request_commits:
146 146 <li style="margin:0 0 1em;">
147 147 <pre style="margin:0 0 .5em"><a href="${h.route_path('repo_commit', repo_name=pull_request_source_repo.repo_name, commit_id=commit_id)}" style="${base.link_css()}">${h.short_id(commit_id)}</a></pre>
148 148 ${h.chop_at_smart(message, '\n', suffix_if_chopped='...')}
149 149 </li>
150 150 % endfor
151 151 </ol>
152 152 </td>
153 153 </tr>
154 154 </table>
@@ -1,172 +1,172 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base.mako"/>
3 3 <%namespace name="base" file="base.mako"/>
4 4
5 5 ## EMAIL SUBJECT
6 6 <%def name="subject()" filter="n,trim,whitespace_filter">
7 7 <%
8 8 data = {
9 9 'updating_user': '@'+h.person(updating_user),
10 10 'pr_id': pull_request.pull_request_id,
11 11 'pr_title': pull_request.title_safe,
12 12 }
13 13
14 14 subject_template = email_pr_update_subject_template or _('{updating_user} updated pull request. !{pr_id}: "{pr_title}"')
15 15 %>
16 16
17 17 ${subject_template.format(**data) |n}
18 18 </%def>
19 19
20 20 ## PLAINTEXT VERSION OF BODY
21 21 <%def name="body_plaintext()" filter="n,trim">
22 22 <%
23 23 data = {
24 24 'updating_user': h.person(updating_user),
25 25 'pr_id': pull_request.pull_request_id,
26 26 'pr_title': pull_request.title_safe,
27 27 'source_ref_type': pull_request.source_ref_parts.type,
28 28 'source_ref_name': pull_request.source_ref_parts.name,
29 29 'target_ref_type': pull_request.target_ref_parts.type,
30 30 'target_ref_name': pull_request.target_ref_parts.name,
31 31 'repo_url': pull_request_source_repo_url,
32 32 'source_repo': pull_request_source_repo.repo_name,
33 33 'target_repo': pull_request_target_repo.repo_name,
34 34 'source_repo_url': pull_request_source_repo_url,
35 35 'target_repo_url': pull_request_target_repo_url,
36 36 }
37 37 %>
38 38
39 39 * ${_('Pull Request link')}: ${pull_request_url}
40 40
41 41 * ${h.literal(_('Commit flow: {source_ref_type}:{source_ref_name} of {source_repo_url} into {target_ref_type}:{target_ref_name} of {target_repo_url}').format(**data))}
42 42
43 43 * ${_('Title')}: ${pull_request.title}
44 44
45 45 * ${_('Description')}:
46 46
47 47 ${pull_request.description | trim}
48 48
49 49 * Changed commits:
50 50
51 51 - Added: ${len(added_commits)}
52 52 - Removed: ${len(removed_commits)}
53 53
54 54 * Changed files:
55 55
56 56 %if not changed_files:
57 57 No file changes found
58 58 %else:
59 59 %for file_name in added_files:
60 60 - A `${file_name}`
61 61 %endfor
62 62 %for file_name in modified_files:
63 63 - M `${file_name}`
64 64 %endfor
65 65 %for file_name in removed_files:
66 66 - R `${file_name}`
67 67 %endfor
68 68 %endif
69 69
70 70 ---
71 71 ${self.plaintext_footer()}
72 72 </%def>
73 73 <%
74 74 data = {
75 75 'updating_user': h.person(updating_user),
76 76 'pr_id': pull_request.pull_request_id,
77 77 'pr_title': pull_request.title_safe,
78 78 'source_ref_type': pull_request.source_ref_parts.type,
79 79 'source_ref_name': pull_request.source_ref_parts.name,
80 80 'target_ref_type': pull_request.target_ref_parts.type,
81 81 'target_ref_name': pull_request.target_ref_parts.name,
82 82 'repo_url': pull_request_source_repo_url,
83 83 'source_repo': pull_request_source_repo.repo_name,
84 84 'target_repo': pull_request_target_repo.repo_name,
85 85 'source_repo_url': h.link_to(pull_request_source_repo.repo_name, pull_request_source_repo_url),
86 86 'target_repo_url': h.link_to(pull_request_target_repo.repo_name, pull_request_target_repo_url),
87 87 }
88 88 %>
89 89
90 90 ## header
91 91 <table style="text-align:left;vertical-align:middle;width: 100%">
92 92 <tr>
93 93 <td style="width:100%;border-bottom:1px solid #dbd9da;">
94 94
95 95 <div style="margin: 0; font-weight: bold">
96 96 <div class="clear-both" style="margin-bottom: 4px">
97 97 <span style="color:#7E7F7F">@${h.person(updating_user.username)}</span>
98 98 ${_('updated')}
99 99 <a href="${pull_request_url}" style="${base.link_css()}">
100 100 ${_('pull request.').format(**data) }
101 101 </a>
102 102 </div>
103 103 <div style="margin-top: 10px"></div>
104 104 ${_('Pull request')} <code>!${data['pr_id']}: ${data['pr_title']}</code>
105 105 </div>
106 106
107 107 </td>
108 108 </tr>
109 109
110 110 </table>
111 111 <div class="clear-both"></div>
112 112 ## main body
113 113 <table style="text-align:left;vertical-align:middle;width: 100%">
114 114 ## spacing def
115 115 <tr>
116 116 <td style="width: 130px"></td>
117 117 <td></td>
118 118 </tr>
119 119
120 120 <tr>
121 121 <td style="padding-right:20px;">${_('Pull request')}:</td>
122 122 <td>
123 123 <a href="${pull_request_url}" style="${base.link_css()}">
124 124 !${pull_request.pull_request_id}
125 125 </a>
126 126 </td>
127 127 </tr>
128 128
129 129 <tr>
130 130 <td style="padding-right:20px;line-height:20px;">${_('Commit Flow')}:</td>
131 131 <td style="line-height:20px;">
132 <code>${'{}:{}'.format(data['source_ref_type'], pull_request.source_ref_parts.name)}</code> ${_('of')} ${data['source_repo_url']}
132 <code>${data['source_ref_type']}:${pull_request.source_ref_parts.name}</code> ${_('of')} ${data['source_repo_url']}
133 133 &rarr;
134 <code>${'{}:{}'.format(data['target_ref_type'], pull_request.target_ref_parts.name)}</code> ${_('of')} ${data['target_repo_url']}
134 <code>${data['target_ref_type']}:${pull_request.target_ref_parts.name}</code> ${_('of')} ${data['target_repo_url']}
135 135 </td>
136 136 </tr>
137 137
138 138 <tr>
139 139 <td style="padding-right:20px;">${_('Description')}:</td>
140 140 <td style="white-space:pre-wrap"><code>${pull_request.description | trim}</code></td>
141 141 </tr>
142 142 <tr>
143 143 <td style="padding-right:20px;">${_('Changes')}:</td>
144 144 <td>
145 145 <strong>Changed commits:</strong>
146 146 <ul class="changes-ul">
147 147 <li>- Added: ${len(added_commits)}</li>
148 148 <li>- Removed: ${len(removed_commits)}</li>
149 149 </ul>
150 150
151 151 <strong>Changed files:</strong>
152 152 <ul class="changes-ul">
153 153
154 154 %if not changed_files:
155 155 <li>No file changes found</li>
156 156 %else:
157 157 %for file_name in added_files:
158 158 <li>- A <a href="${pull_request_url + '#a_' + h.FID(ancestor_commit_id, file_name)}">${file_name}</a></li>
159 159 %endfor
160 160 %for file_name in modified_files:
161 161 <li>- M <a href="${pull_request_url + '#a_' + h.FID(ancestor_commit_id, file_name)}">${file_name}</a></li>
162 162 %endfor
163 163 %for file_name in removed_files:
164 164 <li>- R <a href="${pull_request_url + '#a_' + h.FID(ancestor_commit_id, file_name)}">${file_name}</a></li>
165 165 %endfor
166 166 %endif
167 167
168 168 </ul>
169 169 </td>
170 170 </tr>
171 171
172 172 </table>
@@ -1,1055 +1,1060 b''
1 1 <%inherit file="/base/base.mako"/>
2 2 <%namespace name="base" file="/base/base.mako"/>
3 3 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 4 <%namespace name="sidebar" file="/base/sidebar.mako"/>
5 5
6 6
7 7 <%def name="title()">
8 8 ${_('{} Pull Request !{}').format(c.repo_name, c.pull_request.pull_request_id)}
9 9 %if c.rhodecode_name:
10 10 &middot; ${h.branding(c.rhodecode_name)}
11 11 %endif
12 12 </%def>
13 13
14 14 <%def name="breadcrumbs_links()">
15 15
16 16 </%def>
17 17
18 18 <%def name="menu_bar_nav()">
19 19 ${self.menu_items(active='repositories')}
20 20 </%def>
21 21
22 22 <%def name="menu_bar_subnav()">
23 23 ${self.repo_menu(active='showpullrequest')}
24 24 </%def>
25 25
26 26
27 27 <%def name="main()">
28 28 ## Container to gather extracted Tickets
29 29 <%
30 30 c.referenced_commit_issues = h.IssuesRegistry()
31 31 c.referenced_desc_issues = h.IssuesRegistry()
32 32 %>
33 33
34 34 <script type="text/javascript">
35 35 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
36 36 templateContext.pull_request_data.pull_request_version = '${request.GET.get('version', '')}';
37 37 </script>
38 38
39 39 <div class="box">
40 40
41 41 <div class="box pr-summary">
42 42
43 43 <div class="summary-details block-left">
44 44 <div id="pr-title">
45 45 % if c.pull_request.is_closed():
46 46 <span class="pr-title-closed-tag tag">${_('Closed')}</span>
47 47 % endif
48 48 <input class="pr-title-input large disabled" disabled="disabled" name="pullrequest_title" type="text" value="${c.pull_request.title}">
49 49 </div>
50 50 <div id="pr-title-edit" class="input" style="display: none;">
51 51 <input class="pr-title-input large" id="pr-title-input" name="pullrequest_title" type="text" value="${c.pull_request.title}">
52 52 </div>
53 53
54 54 <% summary = lambda n:{False:'summary-short'}.get(n) %>
55 55 <div class="pr-details-title">
56 56 <div class="pull-left">
57 57 <a href="${h.route_path('pull_requests_global', pull_request_id=c.pull_request.pull_request_id)}">${_('Pull request !{}').format(c.pull_request.pull_request_id)}</a>
58 58 ${_('Created on')}
59 59 <span class="tooltip" title="${_('Last updated on')} ${h.format_date(c.pull_request.updated_on)}">${h.format_date(c.pull_request.created_on)},</span>
60 60 <span class="pr-details-title-author-pref">${_('by')}</span>
61 61 </div>
62 62
63 63 <div class="pull-left">
64 64 ${self.gravatar_with_user(c.pull_request.author.email, 16, tooltip=True)}
65 65 </div>
66 66
67 67 %if c.allowed_to_update:
68 68 <div class="pull-right">
69 69 <div id="edit_pull_request" class="action_button pr-save" style="display: none;">${_('Update title & description')}</div>
70 70 <div id="delete_pullrequest" class="action_button pr-save ${('' if c.allowed_to_delete else 'disabled' )}" style="display: none;">
71 71 % if c.allowed_to_delete:
72 72 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), request=request)}
73 73 <input class="btn btn-link btn-danger no-margin" id="remove_${c.pull_request.pull_request_id}" name="remove_${c.pull_request.pull_request_id}"
74 74 onclick="submitConfirm(event, this, _gettext('Confirm to delete this pull request'), _gettext('Delete'), '${'!{}'.format(c.pull_request.pull_request_id)}')"
75 75 type="submit" value="${_('Delete pull request')}">
76 76 ${h.end_form()}
77 77 % else:
78 78 <span class="tooltip" title="${_('Not allowed to delete this pull request')}">${_('Delete pull request')}</span>
79 79 % endif
80 80 </div>
81 81 <div id="open_edit_pullrequest" class="action_button">${_('Edit')}</div>
82 82 <div id="close_edit_pullrequest" class="action_button" style="display: none;">${_('Cancel')}</div>
83 83 </div>
84 84
85 85 %endif
86 86 </div>
87 87
88 88 <div id="pr-desc" class="input" title="${_('Rendered using {} renderer').format(c.renderer)}">
89 89 ${h.render(c.pull_request.description, renderer=c.renderer, repo_name=c.repo_name, issues_container_callback=c.referenced_desc_issues())}
90 90 </div>
91 91
92 92 <div id="pr-desc-edit" class="input textarea" style="display: none;">
93 93 <input id="pr-renderer-input" type="hidden" name="description_renderer" value="${c.visual.default_renderer}">
94 94 ${dt.markup_form('pr-description-input', form_text=c.pull_request.description)}
95 95 </div>
96 96
97 97 <div id="summary" class="fields pr-details-content">
98 98
99 99 ## source
100 100 <div class="field">
101 101 <div class="label-pr-detail">
102 102 <label>${_('Commit flow')}:</label>
103 103 </div>
104 104 <div class="input">
105 105 <div class="pr-commit-flow">
106 106 ## Source
107 107 %if c.pull_request.source_ref_parts.type == 'branch':
108 108 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.source_repo.repo_name, _query=dict(branch=c.pull_request.source_ref_parts.name))}"><code class="pr-source-info">${c.pull_request.source_ref_parts.type}:${c.pull_request.source_ref_parts.name}</code></a>
109 109 %else:
110 110 <code class="pr-source-info">${'{}:{}'.format(c.pull_request.source_ref_parts.type, c.pull_request.source_ref_parts.name)}</code>
111 111 %endif
112 112 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.repo_name}</a>
113 113 &rarr;
114 114 ## Target
115 115 %if c.pull_request.target_ref_parts.type == 'branch':
116 116 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.target_repo.repo_name, _query=dict(branch=c.pull_request.target_ref_parts.name))}"><code class="pr-target-info">${c.pull_request.target_ref_parts.type}:${c.pull_request.target_ref_parts.name}</code></a>
117 117 %else:
118 118 <code class="pr-target-info">${'{}:{}'.format(c.pull_request.target_ref_parts.type, c.pull_request.target_ref_parts.name)}</code>
119 119 %endif
120 120
121 121 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.repo_name}</a>
122 122
123 123 <a class="source-details-action" href="#expand-source-details" onclick="return toggleElement(this, '.source-details')" data-toggle-on='<i class="icon-angle-down">more details</i>' data-toggle-off='<i class="icon-angle-up">less details</i>'>
124 124 <i class="icon-angle-down">more details</i>
125 125 </a>
126 126
127 127 </div>
128 128
129 129 <div class="source-details" style="display: none">
130 130
131 131 <ul>
132 132
133 133 ## common ancestor
134 134 <li>
135 135 ${_('Common ancestor')}:
136 136 % if c.ancestor_commit:
137 137 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=c.ancestor_commit.raw_id)}">${h.show_id(c.ancestor_commit)}</a>
138 138 % else:
139 139 ${_('not available')}
140 140 % endif
141 141 </li>
142 142
143 143 ## pull url
144 144 <li>
145 145 %if h.is_hg(c.pull_request.source_repo):
146 <% clone_url = 'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
146 <% clone_url = u'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
147 147 %elif h.is_git(c.pull_request.source_repo):
148 <% clone_url = 'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
148 <% clone_url = u'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
149 149 %endif
150 150
151 151 <span>${_('Pull changes from source')}</span>: <input type="text" class="input-monospace pr-pullinfo" value="${clone_url}" readonly="readonly">
152 152 <i class="tooltip icon-clipboard clipboard-action pull-right pr-pullinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the pull url')}"></i>
153 153 </li>
154 154
155 155 ## Shadow repo
156 156 <li>
157 157 % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref:
158 158 %if h.is_hg(c.pull_request.target_repo):
159 159 <% clone_url = 'hg clone --update {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
160 160 %elif h.is_git(c.pull_request.target_repo):
161 161 <% clone_url = 'git clone --branch {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
162 162 %endif
163 163
164 164 <span class="tooltip" title="${_('Clone repository in its merged state using shadow repository')}">${_('Clone from shadow repository')}</span>: <input type="text" class="input-monospace pr-mergeinfo" value="${clone_url}" readonly="readonly">
165 165 <i class="tooltip icon-clipboard clipboard-action pull-right pr-mergeinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the clone url')}"></i>
166 166
167 167 % else:
168 168 <div class="">
169 169 ${_('Shadow repository data not available')}.
170 170 </div>
171 171 % endif
172 172 </li>
173 173
174 174 </ul>
175 175
176 176 </div>
177 177
178 178 </div>
179 179
180 180 </div>
181 181
182 182 ## versions
183 183 <div class="field">
184 184 <div class="label-pr-detail">
185 185 <label>${_('Versions')}:</label>
186 186 </div>
187 187
188 188 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
189 189 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
190 190
191 191 <div class="pr-versions">
192 192 % if c.show_version_changes:
193 193 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
194 194 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
195 195 ${_ungettext('{} version available for this pull request, ', '{} versions available for this pull request, ', len(c.versions)).format(len(c.versions))}
196 196 <a id="show-pr-versions" onclick="return versionController.toggleVersionView(this)" href="#show-pr-versions"
197 197 data-toggle-on="${_('show versions')}."
198 198 data-toggle-off="${_('hide versions')}.">
199 199 ${_('show versions')}.
200 200 </a>
201 201 <table>
202 202 ## SHOW ALL VERSIONS OF PR
203 203 <% ver_pr = None %>
204 204
205 205 % for data in reversed(list(enumerate(c.versions, 1))):
206 206 <% ver_pos = data[0] %>
207 207 <% ver = data[1] %>
208 208 <% ver_pr = ver.pull_request_version_id %>
209 209 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
210 210
211 211 <tr class="version-pr" style="display: ${display_row}">
212 212 <td>
213 213 <code>
214 214 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
215 215 </code>
216 216 </td>
217 217 <td>
218 218 <input ${('checked="checked"' if c.from_version_index == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_source" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
219 219 <input ${('checked="checked"' if c.at_version_num == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_target" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
220 220 </td>
221 221 <td>
222 222 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
223 223 <i class="tooltip icon-circle review-status-${review_status}" title="${_('Your review status at this version')}"></i>
224 224
225 225 </td>
226 226 <td>
227 227 % if c.at_version_num != ver_pr:
228 228 <i class="tooltip icon-comment" title="${_('Comments from pull request version v{0}').format(ver_pos)}"></i>
229 229 <code>
230 230 General:${len(c.comment_versions[ver_pr]['at'])} / Inline:${len(c.inline_versions[ver_pr]['at'])}
231 231 </code>
232 232 % endif
233 233 </td>
234 234 <td>
235 235 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
236 236 </td>
237 237 <td>
238 238 <code>${h.age_component(ver.updated_on, time_is_local=True, tooltip=False)}</code>
239 239 </td>
240 240 </tr>
241 241 % endfor
242 242
243 243 <tr>
244 244 <td colspan="6">
245 245 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none"
246 246 data-label-text-locked="${_('select versions to show changes')}"
247 247 data-label-text-diff="${_('show changes between versions')}"
248 248 data-label-text-show="${_('show pull request for this version')}"
249 249 >
250 250 ${_('select versions to show changes')}
251 251 </button>
252 252 </td>
253 253 </tr>
254 254 </table>
255 255 % else:
256 256 <div>
257 257 ${_('Pull request versions not available')}.
258 258 </div>
259 259 % endif
260 260 </div>
261 261 </div>
262 262
263 263 </div>
264 264
265 265 </div>
266 266
267 267
268 268 </div>
269 269
270 270 </div>
271 271
272 272 <div class="box">
273 273
274 274 % if c.state_progressing:
275 275
276 276 <h2 style="text-align: center">
277 277 ${_('Cannot show diff when pull request state is changing. Current progress state')}: <span class="tag tag-merge-state-${c.pull_request.state}">${c.pull_request.state}</span>
278 278
279 279 % if c.is_super_admin:
280 280 <br/>
281 281 If you think this is an error try <a href="${h.current_route_path(request, force_state='created')}">forced state reset</a> to <span class="tag tag-merge-state-created">created</span> state.
282 282 % endif
283 283 </h2>
284 284
285 285 % else:
286 286
287 287 ## Diffs rendered here
288 288 <div class="table" >
289 289 <div id="changeset_compare_view_content">
290 290 ##CS
291 291 % if c.missing_requirements:
292 292 <div class="box">
293 293 <div class="alert alert-warning">
294 294 <div>
295 295 <strong>${_('Missing requirements:')}</strong>
296 296 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
297 297 </div>
298 298 </div>
299 299 </div>
300 300 % elif c.missing_commits:
301 301 <div class="box">
302 302 <div class="alert alert-warning">
303 303 <div>
304 304 <strong>${_('Missing commits')}:</strong>
305 305 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}<br/>
306 306 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}<br/>
307 307 ${_('Consider doing a `force update commits` in case you think this is an error.')}
308 308 </div>
309 309 </div>
310 310 </div>
311 311 % elif c.pr_merge_source_commit.changed and not c.pull_request.is_closed():
312 312 <div class="box">
313 313 <div class="alert alert-info">
314 314 <div>
315 315 <strong>${_('There are new changes for `{}:{}` in source repository, please consider updating this pull request.').format(c.pr_merge_source_commit.ref_spec.type, c.pr_merge_source_commit.ref_spec.name)}</strong>
316 316 </div>
317 317 </div>
318 318 </div>
319 319 % endif
320 320
321 321 <div class="compare_view_commits_title">
322 322 % if not c.compare_mode:
323 323
324 324 % if c.at_version_index:
325 325 <h4>
326 326 ${_('Showing changes at v{}, commenting is disabled.').format(c.at_version_index)}
327 327 </h4>
328 328 % endif
329 329
330 330 <div class="pull-left">
331 331 <div class="btn-group">
332 332 <a class="${('collapsed' if c.collapse_all_commits else '')}" href="#expand-commits" onclick="toggleCommitExpand(this); return false" data-toggle-commits-cnt=${len(c.commit_ranges)} >
333 333 % if c.collapse_all_commits:
334 334 <i class="icon-plus-squared-alt icon-no-margin"></i>
335 335 ${_ungettext('Expand {} commit', 'Expand {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
336 336 % else:
337 337 <i class="icon-minus-squared-alt icon-no-margin"></i>
338 338 ${_ungettext('Collapse {} commit', 'Collapse {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
339 339 % endif
340 340 </a>
341 341 </div>
342 342 </div>
343 343
344 344 <div class="pull-right">
345 345 % if c.allowed_to_update and not c.pull_request.is_closed():
346 346
347 347 <div class="btn-group btn-group-actions">
348 348 <a id="update_commits" class="btn btn-primary no-margin" onclick="updateController.updateCommits(this); return false">
349 349 ${_('Update commits')}
350 350 </a>
351 351
352 352 <a id="update_commits_switcher" class="tooltip btn btn-primary btn-more-option" data-toggle="dropdown" aria-pressed="false" role="button" title="${_('more update options')}">
353 353 <i class="icon-down"></i>
354 354 </a>
355 355
356 356 <div class="btn-action-switcher-container right-align" id="update-commits-switcher">
357 357 <ul class="btn-action-switcher" role="menu" style="min-width: 300px;">
358 358 <li>
359 359 <a href="#forceUpdate" onclick="updateController.forceUpdateCommits(this); return false">
360 360 ${_('Force update commits')}
361 361 </a>
362 362 <div class="action-help-block">
363 363 ${_('Update commits and force refresh this pull request.')}
364 364 </div>
365 365 </li>
366 366 </ul>
367 367 </div>
368 368 </div>
369 369
370 370 % else:
371 371 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
372 372 % endif
373 373
374 374 </div>
375 375 % endif
376 376 </div>
377 377
378 378 % if not c.missing_commits:
379 379 ## COMPARE RANGE DIFF MODE
380 380 % if c.compare_mode:
381 381 % if c.at_version:
382 382 <h4>
383 383 ${_('Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled').format(ver_from=c.from_version_index, ver_to=c.at_version_index if c.at_version_index else 'latest')}:
384 384 </h4>
385 385
386 386 <div class="subtitle-compare">
387 387 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
388 388 </div>
389 389
390 390 <div class="container">
391 391 <table class="rctable compare_view_commits">
392 392 <tr>
393 393 <th></th>
394 394 <th>${_('Time')}</th>
395 395 <th>${_('Author')}</th>
396 396 <th>${_('Commit')}</th>
397 397 <th></th>
398 398 <th>${_('Description')}</th>
399 399 </tr>
400 400
401 401 % for c_type, commit in c.commit_changes:
402 402 % if c_type in ['a', 'r']:
403 403 <%
404 404 if c_type == 'a':
405 405 cc_title = _('Commit added in displayed changes')
406 406 elif c_type == 'r':
407 407 cc_title = _('Commit removed in displayed changes')
408 408 else:
409 409 cc_title = ''
410 410 %>
411 411 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
412 412 <td>
413 413 <div class="commit-change-indicator color-${c_type}-border">
414 414 <div class="commit-change-content color-${c_type} tooltip" title="${h.tooltip(cc_title)}">
415 415 ${c_type.upper()}
416 416 </div>
417 417 </div>
418 418 </td>
419 419 <td class="td-time">
420 420 ${h.age_component(commit.date)}
421 421 </td>
422 422 <td class="td-user">
423 423 ${base.gravatar_with_user(commit.author, 16, tooltip=True)}
424 424 </td>
425 425 <td class="td-hash">
426 426 <code>
427 427 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
428 428 r${commit.idx}:${h.short_id(commit.raw_id)}
429 429 </a>
430 430 ${h.hidden('revisions', commit.raw_id)}
431 431 </code>
432 432 </td>
433 433 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
434 434 <i class="icon-expand-linked"></i>
435 435 </td>
436 436 <td class="mid td-description">
437 437 <div class="log-container truncate-wrap">
438 438 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">${h.urlify_commit_message(commit.message, c.repo_name, issues_container_callback=c.referenced_commit_issues(commit.serialize()))}</div>
439 439 </div>
440 440 </td>
441 441 </tr>
442 442 % endif
443 443 % endfor
444 444 </table>
445 445 </div>
446 446
447 447 % endif
448 448
449 449 ## Regular DIFF
450 450 % else:
451 451 <%include file="/compare/compare_commits.mako" />
452 452 % endif
453 453
454 454 <div class="cs_files">
455 455 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
456 456
457 457 <%
458 458 pr_menu_data = {
459 459 'outdated_comm_count_ver': outdated_comm_count_ver,
460 460 'pull_request': c.pull_request
461 461 }
462 462 %>
463 463
464 464 ${cbdiffs.render_diffset_menu(c.diffset, range_diff_on=c.range_diff_on, pull_request_menu=pr_menu_data)}
465 465
466 466 % if c.range_diff_on:
467 467 % for commit in c.commit_ranges:
468 468 ${cbdiffs.render_diffset(
469 469 c.changes[commit.raw_id],
470 470 commit=commit, use_comments=True,
471 471 collapse_when_files_over=5,
472 472 disable_new_comments=True,
473 473 deleted_files_comments=c.deleted_files_comments,
474 474 inline_comments=c.inline_comments,
475 475 pull_request_menu=pr_menu_data, show_todos=False)}
476 476 % endfor
477 477 % else:
478 478 ${cbdiffs.render_diffset(
479 479 c.diffset, use_comments=True,
480 480 collapse_when_files_over=30,
481 481 disable_new_comments=not c.allowed_to_comment,
482 482 deleted_files_comments=c.deleted_files_comments,
483 483 inline_comments=c.inline_comments,
484 484 pull_request_menu=pr_menu_data, show_todos=False)}
485 485 % endif
486 486
487 487 </div>
488 488 % else:
489 489 ## skipping commits we need to clear the view for missing commits
490 490 <div style="clear:both;"></div>
491 491 % endif
492 492
493 493 </div>
494 494 </div>
495 495
496 496 ## template for inline comment form
497 497 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
498 498
499 499 ## comments heading with count
500 500 <div class="comments-heading">
501 501 <i class="icon-comment"></i>
502 502 ${_('General Comments')} ${len(c.comments)}
503 503 </div>
504 504
505 505 ## render general comments
506 506 <div id="comment-tr-show">
507 507 % if general_outdated_comm_count_ver:
508 508 <div class="info-box">
509 509 % if general_outdated_comm_count_ver == 1:
510 510 ${_('there is {num} general comment from older versions').format(num=general_outdated_comm_count_ver)},
511 511 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show it')}</a>
512 512 % else:
513 513 ${_('there are {num} general comments from older versions').format(num=general_outdated_comm_count_ver)},
514 514 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show them')}</a>
515 515 % endif
516 516 </div>
517 517 % endif
518 518 </div>
519 519
520 520 ${comment.generate_comments(c.comments, include_pull_request=True, is_pull_request=True)}
521 521
522 522 % if not c.pull_request.is_closed():
523 523 ## main comment form and it status
524 524 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
525 525 pull_request_id=c.pull_request.pull_request_id),
526 526 c.pull_request_review_status,
527 527 is_pull_request=True, change_status=c.allowed_to_change_status)}
528 528
529 529 ## merge status, and merge action
530 530 <div class="pull-request-merge">
531 531 <%include file="/pullrequests/pullrequest_merge_checks.mako"/>
532 532 </div>
533 533
534 534 %endif
535 535
536 536 % endif
537 537 </div>
538 538
539 539
540 540 ### NAV SIDEBAR
541 541 <aside class="right-sidebar right-sidebar-expanded" id="pr-nav-sticky" style="display: none">
542 542 <div class="sidenav navbar__inner" >
543 543 ## TOGGLE
544 544 <div class="sidebar-toggle" onclick="toggleSidebar(); return false">
545 545 <a href="#toggleSidebar" class="grey-link-action">
546 546
547 547 </a>
548 548 </div>
549 549
550 550 ## CONTENT
551 551 <div class="sidebar-content">
552 552
553 553 ## Drafts
554 554 % if c.rhodecode_edition_id == 'EE':
555 555 <div id="draftsTable" class="sidebar-element clear-both" style="display: ${'block' if c.draft_comments else 'none'}">
556 556 <div class="tooltip right-sidebar-collapsed-state" style="display: none;" onclick="toggleSidebar(); return false" title="${_('Drafts')}">
557 557 <i class="icon-comment icon-draft"></i>
558 558 <span id="drafts-count">${len(c.draft_comments)}</span>
559 559 </div>
560 560
561 561 <div class="right-sidebar-expanded-state pr-details-title">
562 562 <span style="padding-left: 2px">
563 <input name="select_all_drafts" type="checkbox" onclick="$('[name=submit_draft]').prop('checked', !$('[name=submit_draft]').prop('checked'))">
563 <input name="select_all_drafts" type="checkbox" onclick="selectDraftComments(event)">
564 564 </span>
565 565 <span class="sidebar-heading noselect" onclick="refreshDraftComments(); return false">
566 566 <i class="icon-comment icon-draft"></i>
567 567 ${_('Drafts')}
568 568 </span>
569 569 <span class="block-right action_button last-item" onclick="submitDrafts(event)">${_('Submit')}</span>
570 570 </div>
571 571
572 572 <div id="drafts" class="right-sidebar-expanded-state pr-details-content reviewers">
573 573 % if c.draft_comments:
574 574 ${sidebar.comments_table(c.draft_comments, len(c.draft_comments), draft_comments=True)}
575 575 % else:
576 576 <table class="drafts-content-table">
577 577 <tr>
578 578 <td>
579 579 ${_('No TODOs yet')}
580 580 </td>
581 581 </tr>
582 582 </table>
583 583 % endif
584 584 </div>
585 585
586 586 </div>
587 587 % endif
588 588
589 589 ## RULES SUMMARY/RULES
590 590 <div class="sidebar-element clear-both">
591 591 <% vote_title = _ungettext(
592 592 'Status calculated based on votes from {} reviewer',
593 593 'Status calculated based on votes from {} reviewers', c.reviewers_count).format(c.reviewers_count)
594 594 %>
595 595
596 596 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
597 597 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
598 598 ${c.reviewers_count}
599 599 </div>
600 600
601 601 ## REVIEWERS
602 602 <div class="right-sidebar-expanded-state pr-details-title">
603 603 <span class="tooltip sidebar-heading" title="${vote_title}">
604 604 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
605 605 ${_('Reviewers')}
606 606 </span>
607 607
608 608 %if c.allowed_to_update:
609 609 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
610 610 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
611 611 %else:
612 612 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Show rules')}</span>
613 613 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
614 614 %endif
615 615 </div>
616 616
617 617 <div id="reviewers" class="right-sidebar-expanded-state pr-details-content reviewers">
618 618
619 619 <div id="review_rules" style="display: none" class="">
620 620
621 621 <strong>${_('Reviewer rules')}</strong>
622 622 <div class="pr-reviewer-rules">
623 623 ## review rules will be appended here, by default reviewers logic
624 624 </div>
625 625 <input id="review_data" type="hidden" name="review_data" value="">
626 626 </div>
627 627
628 628 ## members redering block
629 629 <input type="hidden" name="__start__" value="review_members:sequence">
630 630
631 631 <table id="review_members" class="group_members">
632 632 ## This content is loaded via JS and ReviewersPanel
633 633 </table>
634 634
635 635 <input type="hidden" name="__end__" value="review_members:sequence">
636 636 ## end members redering block
637 637
638 638 %if not c.pull_request.is_closed():
639 639 <div id="add_reviewer" class="ac" style="display: none;">
640 640 %if c.allowed_to_update:
641 641 % if not c.forbid_adding_reviewers:
642 642 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px">
643 643 <input class="ac-input" id="user" name="user" placeholder="${_('Add reviewer or reviewer group')}" type="text" autocomplete="off">
644 644 <div id="reviewers_container"></div>
645 645 </div>
646 646 % endif
647 647 <div class="pull-right" style="margin-bottom: 15px">
648 648 <button data-role="reviewer" id="update_reviewers" class="btn btn-sm no-margin">${_('Save Changes')}</button>
649 649 </div>
650 650 %endif
651 651 </div>
652 652 %endif
653 653 </div>
654 654 </div>
655 655
656 656 ## OBSERVERS
657 657 % if c.rhodecode_edition_id == 'EE':
658 658 <div class="sidebar-element clear-both">
659 659 <% vote_title = _ungettext(
660 660 '{} observer without voting right.',
661 661 '{} observers without voting right.', c.observers_count).format(c.observers_count)
662 662 %>
663 663
664 664 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
665 665 <i class="icon-circle-thin"></i>
666 666 ${c.observers_count}
667 667 </div>
668 668
669 669 <div class="right-sidebar-expanded-state pr-details-title">
670 670 <span class="tooltip sidebar-heading" title="${vote_title}">
671 671 <i class="icon-circle-thin"></i>
672 672 ${_('Observers')}
673 673 </span>
674 674 %if c.allowed_to_update:
675 675 <span id="open_edit_observers" class="block-right action_button last-item">${_('Edit')}</span>
676 676 <span id="close_edit_observers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
677 677 %endif
678 678 </div>
679 679
680 680 <div id="observers" class="right-sidebar-expanded-state pr-details-content reviewers">
681 681 ## members redering block
682 682 <input type="hidden" name="__start__" value="observer_members:sequence">
683 683
684 684 <table id="observer_members" class="group_members">
685 685 ## This content is loaded via JS and ReviewersPanel
686 686 </table>
687 687
688 688 <input type="hidden" name="__end__" value="observer_members:sequence">
689 689 ## end members redering block
690 690
691 691 %if not c.pull_request.is_closed():
692 692 <div id="add_observer" class="ac" style="display: none;">
693 693 %if c.allowed_to_update:
694 694 % if not c.forbid_adding_reviewers or 1:
695 695 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px" >
696 696 <input class="ac-input" id="observer" name="observer" placeholder="${_('Add observer or observer group')}" type="text" autocomplete="off">
697 697 <div id="observers_container"></div>
698 698 </div>
699 699 % endif
700 700 <div class="pull-right" style="margin-bottom: 15px">
701 701 <button data-role="observer" id="update_observers" class="btn btn-sm no-margin">${_('Save Changes')}</button>
702 702 </div>
703 703 %endif
704 704 </div>
705 705 %endif
706 706 </div>
707 707 </div>
708 708 % endif
709 709
710 710 ## TODOs
711 711 <div id="todosTable" class="sidebar-element clear-both">
712 712 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="TODOs">
713 713 <i class="icon-flag-filled"></i>
714 714 <span id="todos-count">${len(c.unresolved_comments)}</span>
715 715 </div>
716 716
717 717 <div class="right-sidebar-expanded-state pr-details-title">
718 718 ## Only show unresolved, that is only what matters
719 719 <span class="sidebar-heading noselect" onclick="refreshTODOs(); return false">
720 720 <i class="icon-flag-filled"></i>
721 721 TODOs
722 722 </span>
723 723
724 724 % if not c.at_version:
725 725 % if c.resolved_comments:
726 726 <span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
727 727 % else:
728 728 <span class="block-right last-item noselect">Show resolved</span>
729 729 % endif
730 730 % endif
731 731 </div>
732 732
733 733 <div class="right-sidebar-expanded-state pr-details-content">
734 734
735 735 % if c.at_version:
736 736 <table>
737 737 <tr>
738 738 <td class="unresolved-todo-text">${_('TODOs unavailable when browsing versions')}.</td>
739 739 </tr>
740 740 </table>
741 741 % else:
742 742 % if c.unresolved_comments + c.resolved_comments:
743 743 ${sidebar.comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True)}
744 744 % else:
745 745 <table class="todos-content-table">
746 746 <tr>
747 747 <td>
748 748 ${_('No TODOs yet')}
749 749 </td>
750 750 </tr>
751 751 </table>
752 752 % endif
753 753 % endif
754 754 </div>
755 755 </div>
756 756
757 757 ## COMMENTS
758 758 <div id="commentsTable" class="sidebar-element clear-both">
759 759 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
760 760 <i class="icon-comment" style="color: #949494"></i>
761 761 <span id="comments-count">${len(c.inline_comments_flat+c.comments)}</span>
762 762 <span class="display-none" id="general-comments-count">${len(c.comments)}</span>
763 763 <span class="display-none" id="inline-comments-count">${len(c.inline_comments_flat)}</span>
764 764 </div>
765 765
766 766 <div class="right-sidebar-expanded-state pr-details-title">
767 767 <span class="sidebar-heading noselect" onclick="refreshComments(); return false">
768 768 <i class="icon-comment" style="color: #949494"></i>
769 769 ${_('Comments')}
770 770
771 771 ## % if outdated_comm_count_ver:
772 772 ## <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
773 773 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
774 774 ## </a>
775 775 ## <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
776 776 ## <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
777 777
778 778 ## % else:
779 779 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
780 780 ## % endif
781 781
782 782 </span>
783 783
784 784 % if outdated_comm_count_ver:
785 785 <span class="block-right action_button last-item noselect" onclick="return toggleElement(this, '.hidden-comment');" data-toggle-on="Show outdated" data-toggle-off="Hide outdated">Show outdated</span>
786 786 % else:
787 787 <span class="block-right last-item noselect">Show hidden</span>
788 788 % endif
789 789
790 790 </div>
791 791
792 792 <div class="right-sidebar-expanded-state pr-details-content">
793 793 % if c.inline_comments_flat + c.comments:
794 794 ${sidebar.comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments))}
795 795 % else:
796 796 <table class="comments-content-table">
797 797 <tr>
798 798 <td>
799 799 ${_('No Comments yet')}
800 800 </td>
801 801 </tr>
802 802 </table>
803 803 % endif
804 804 </div>
805 805
806 806 </div>
807 807
808 808 ## Referenced Tickets
809 809 <div class="sidebar-element clear-both">
810 810 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Referenced Tickets')}">
811 811 <i class="icon-info-circled"></i>
812 812 ${(c.referenced_desc_issues.issues_unique_count + c.referenced_commit_issues.issues_unique_count)}
813 813 </div>
814 814
815 815 <div class="right-sidebar-expanded-state pr-details-title">
816 816 <span class="sidebar-heading">
817 817 <i class="icon-info-circled"></i>
818 818 ${_('Referenced Tickets')}
819 819 </span>
820 820 </div>
821 821 <div class="right-sidebar-expanded-state pr-details-content">
822 822 <table>
823 823
824 824 <tr><td><code>${_('In pull request description')}:</code></td></tr>
825 825 % if c.referenced_desc_issues.issues:
826 826
827 827 % for ticket_id, ticket_dict in c.referenced_desc_issues.unique_issues.items():
828 828 <tr>
829 829 <td>
830 830 <a href="${ticket_dict[0].get('url')}">
831 831 ${ticket_id}
832 832 </a>
833 833 </td>
834 834 </tr>
835 835
836 836 % endfor
837 837 % else:
838 838 <tr>
839 839 <td>
840 840 ${_('No Ticket data found.')}
841 841 </td>
842 842 </tr>
843 843 % endif
844 844
845 845 <tr><td style="padding-top: 10px"><code>${_('In commit messages')}:</code></td></tr>
846 846 % if c.referenced_commit_issues.issues:
847 847 % for ticket_id, ticket_dict in c.referenced_commit_issues.unique_issues.items():
848 848 <tr>
849 849 <td>
850 850 <a href="${ticket_dict[0].get('url')}">
851 851 ${ticket_id}
852 852 </a>
853 853 - ${_ungettext('in %s commit', 'in %s commits', len(ticket_dict)) % (len(ticket_dict))}
854 854 </td>
855 855 </tr>
856 856 % endfor
857 857 % else:
858 858 <tr>
859 859 <td>
860 860 ${_('No Ticket data found.')}
861 861 </td>
862 862 </tr>
863 863 % endif
864 864 </table>
865 865
866 866 </div>
867 867 </div>
868 868
869 869 </div>
870 870
871 871 </div>
872 872 </aside>
873 873
874 874 ## This JS needs to be at the end
875 875 <script type="text/javascript">
876 876
877 877 versionController = new VersionController();
878 878 versionController.init();
879 879
880 880 reviewersController = new ReviewersController();
881 881 commitsController = new CommitsController();
882 882 commentsController = new CommentsController();
883 883
884 884 updateController = new UpdatePrController();
885 885
886 886 window.reviewerRulesData = ${c.pull_request_default_reviewers_data_json | n};
887 887 window.setReviewersData = ${c.pull_request_set_reviewers_data_json | n};
888 888 window.setObserversData = ${c.pull_request_set_observers_data_json | n};
889 889
890 890 (function () {
891 891 "use strict";
892 892
893 893 // custom code mirror
894 894 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
895 895
896 896 PRDetails.init();
897 897 ReviewersPanel.init(reviewersController, reviewerRulesData, setReviewersData);
898 898 ObserversPanel.init(reviewersController, reviewerRulesData, setObserversData);
899 899
900 900 window.showOutdated = function (self) {
901 901 $('.comment-inline.comment-outdated').show();
902 902 $('.filediff-outdated').show();
903 903 $('.showOutdatedComments').hide();
904 904 $('.hideOutdatedComments').show();
905 905 };
906 906
907 907 window.hideOutdated = function (self) {
908 908 $('.comment-inline.comment-outdated').hide();
909 909 $('.filediff-outdated').hide();
910 910 $('.hideOutdatedComments').hide();
911 911 $('.showOutdatedComments').show();
912 912 };
913 913
914 914 window.refreshMergeChecks = function () {
915 915 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
916 916 $('.pull-request-merge').css('opacity', 0.3);
917 917 $('.action-buttons-extra').css('opacity', 0.3);
918 918
919 919 $('.pull-request-merge').load(
920 920 loadUrl, function () {
921 921 $('.pull-request-merge').css('opacity', 1);
922 922
923 923 $('.action-buttons-extra').css('opacity', 1);
924 924 }
925 925 );
926 926 };
927 927
928 928 window.submitDrafts = function (event) {
929 929 var target = $(event.currentTarget);
930 930 var callback = function (result) {
931 931 target.removeAttr('onclick').html('saving...');
932 932 }
933 933 var draftIds = [];
934 934 $.each($('[name=submit_draft]:checked'), function (idx, val) {
935 935 draftIds.push(parseInt($(val).val()));
936 936 })
937 937 if (draftIds.length > 0) {
938 938 Rhodecode.comments.finalizeDrafts(draftIds, callback);
939 939 }
940 940 else {
941 941
942 942 }
943 943 }
944 944
945 window.selectDraftComments = function (event) {
946 var $target = $(event.currentTarget);
947 $('[name=submit_draft]').prop('checked', $target.prop('checked'))
948 }
949
945 950 window.closePullRequest = function (status) {
946 951 if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
947 952 return false;
948 953 }
949 954 // inject closing flag
950 955 $('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
951 956 $(generalCommentForm.statusChange).select2("val", status).trigger('change');
952 957 $(generalCommentForm.submitForm).submit();
953 958 };
954 959
955 960 //TODO this functionality is now missing
956 961 $('#show-outdated-comments').on('click', function (e) {
957 962 var button = $(this);
958 963 var outdated = $('.comment-outdated');
959 964
960 965 if (button.html() === "(Show)") {
961 966 button.html("(Hide)");
962 967 outdated.show();
963 968 } else {
964 969 button.html("(Show)");
965 970 outdated.hide();
966 971 }
967 972 });
968 973
969 974 $('#merge_pull_request_form').submit(function () {
970 975 if (!$('#merge_pull_request').attr('disabled')) {
971 976 $('#merge_pull_request').attr('disabled', 'disabled');
972 977 }
973 978 return true;
974 979 });
975 980
976 981 $('#edit_pull_request').on('click', function (e) {
977 982 var title = $('#pr-title-input').val();
978 983 var description = codeMirrorInstance.getValue();
979 984 var renderer = $('#pr-renderer-input').val();
980 985 editPullRequest(
981 986 "${c.repo_name}", "${c.pull_request.pull_request_id}",
982 987 title, description, renderer);
983 988 });
984 989
985 990 var $updateButtons = $('#update_reviewers,#update_observers');
986 991 $updateButtons.on('click', function (e) {
987 992 var role = $(this).data('role');
988 993 $updateButtons.attr('disabled', 'disabled');
989 994 $updateButtons.addClass('disabled');
990 995 $updateButtons.html(_gettext('Saving...'));
991 996 reviewersController.updateReviewers(
992 997 templateContext.repo_name,
993 998 templateContext.pull_request_data.pull_request_id,
994 999 role
995 1000 );
996 1001 });
997 1002
998 1003 // fixing issue with caches on firefox
999 1004 $('#update_commits').removeAttr("disabled");
1000 1005
1001 1006 $('.show-inline-comments').on('click', function (e) {
1002 1007 var boxid = $(this).attr('data-comment-id');
1003 1008 var button = $(this);
1004 1009
1005 1010 if (button.hasClass("comments-visible")) {
1006 1011 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
1007 1012 $(this).hide();
1008 1013 });
1009 1014 button.removeClass("comments-visible");
1010 1015 } else {
1011 1016 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
1012 1017 $(this).show();
1013 1018 });
1014 1019 button.addClass("comments-visible");
1015 1020 }
1016 1021 });
1017 1022
1018 1023 $('.show-inline-comments').on('change', function (e) {
1019 1024 var show = 'none';
1020 1025 var target = e.currentTarget;
1021 1026 if (target.checked) {
1022 1027 show = ''
1023 1028 }
1024 1029 var boxid = $(target).attr('id_for');
1025 1030 var comments = $('#{0} .inline-comments'.format(boxid));
1026 1031 var fn_display = function (idx) {
1027 1032 $(this).css('display', show);
1028 1033 };
1029 1034 $(comments).each(fn_display);
1030 1035 var btns = $('#{0} .inline-comments-button'.format(boxid));
1031 1036 $(btns).each(fn_display);
1032 1037 });
1033 1038
1034 1039 // register submit callback on commentForm form to track TODOs, and refresh mergeChecks conditions
1035 1040 window.commentFormGlobalSubmitSuccessCallback = function (comment) {
1036 1041 if (!comment.draft) {
1037 1042 refreshMergeChecks();
1038 1043 }
1039 1044 };
1040 1045
1041 1046 ReviewerAutoComplete('#user', reviewersController);
1042 1047 ObserverAutoComplete('#observer', reviewersController);
1043 1048
1044 1049 })();
1045 1050
1046 1051 $(document).ready(function () {
1047 1052
1048 1053 var channel = '${c.pr_broadcast_channel}';
1049 1054 new ReviewerPresenceController(channel)
1050 1055 // register globally so inject comment logic can re-use it.
1051 1056 window.commentsController = commentsController;
1052 1057 })
1053 1058 </script>
1054 1059
1055 1060 </%def>
@@ -1,146 +1,177 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3 3 <%def name="title()">
4 4 ${_('{} Pull Requests').format(c.repo_name)}
5 5 %if c.rhodecode_name:
6 6 &middot; ${h.branding(c.rhodecode_name)}
7 7 %endif
8 8 </%def>
9 9
10 10 <%def name="breadcrumbs_links()"></%def>
11 11
12 12 <%def name="menu_bar_nav()">
13 13 ${self.menu_items(active='repositories')}
14 14 </%def>
15 15
16 16
17 17 <%def name="menu_bar_subnav()">
18 18 ${self.repo_menu(active='showpullrequest')}
19 19 </%def>
20 20
21 21
22 22 <%def name="main()">
23 23
24 24 <div class="box">
25 25 <div class="title">
26
26 27 <ul class="button-links">
27 <li class="btn ${h.is_active('open', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0})}">${_('Opened')}</a></li>
28 <li class="btn ${h.is_active('my', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'my':1})}">${_('Opened by me')}</a></li>
29 <li class="btn ${h.is_active('awaiting', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_review':1})}">${_('Awaiting review')}</a></li>
30 <li class="btn ${h.is_active('awaiting_my', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_my_review':1})}">${_('Awaiting my review')}</a></li>
31 <li class="btn ${h.is_active('closed', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'closed':1})}">${_('Closed')}</a></li>
32 <li class="btn ${h.is_active('source', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':1})}">${_('From this repo')}</a></li>
28 <li><a class="btn ${h.is_active('open', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'open':1})}">${_('Open')}</a></li>
29 <li><a class="btn ${h.is_active('my', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'my':1})}">${_('Created by me')}</a></li>
30 <li><a class="btn ${h.is_active('awaiting', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_review':1})}">${_('Awaiting review')}</a></li>
31 <li><a class="btn ${h.is_active('awaiting_my', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_my_review':1})}">${_('Awaiting my review')}</a></li>
32 <li><a class="btn ${h.is_active('closed', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'closed':1})}">${_('Closed')}</a></li>
33 <li><a class="btn ${h.is_active('source', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':1})}">${_('From this repo')}</a></li>
33 34 </ul>
34 35
35 36 <ul class="links">
36 37 % if c.rhodecode_user.username != h.DEFAULT_USER:
37 38 <li>
38 39 <span>
39 40 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">
40 41 ${_('Open new Pull Request')}
41 42 </a>
42 43 </span>
43 44 </li>
44 45 % endif
45 46
46 47 <li>
47 48 <div class="grid-quick-filter">
48 49 <ul class="grid-filter-box">
49 50 <li class="grid-filter-box-icon">
50 51 <i class="icon-search"></i>
51 52 </li>
52 53 <li class="grid-filter-box-input">
53 54 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
54 55 </li>
55 56 </ul>
56 57 </div>
57 58 </li>
58 59
59 60 </ul>
60 61
61 62 </div>
62 63
63 64 <div class="main-content-full-width">
64 65 <table id="pull_request_list_table" class="rctable table-bordered"></table>
65 66 </div>
66 67
67 68 </div>
68 69
69 70 <script type="text/javascript">
70 71 $(document).ready(function() {
71 72 var $pullRequestListTable = $('#pull_request_list_table');
72 73
73 74 // object list
74 75 $pullRequestListTable.DataTable({
75 76 processing: true,
76 77 serverSide: true,
77 78 stateSave: true,
78 79 stateDuration: -1,
79 80 ajax: {
80 81 "url": "${h.route_path('pullrequest_show_all_data', repo_name=c.repo_name)}",
81 82 "data": function (d) {
82 83 d.source = "${c.source}";
83 84 d.closed = "${c.closed}";
84 85 d.my = "${c.my}";
85 86 d.awaiting_review = "${c.awaiting_review}";
86 87 d.awaiting_my_review = "${c.awaiting_my_review}";
87 88 }
88 89 },
89 90 dom: 'rtp',
90 91 pageLength: ${c.visual.dashboard_items},
91 order: [[ 1, "desc" ]],
92 order: [[ 2, "desc" ]],
92 93 columns: [
93 { data: {"_": "status",
94 "sort": "status"}, title: "", className: "td-status", orderable: false},
95 { data: {"_": "name",
96 "sort": "name_raw"}, title: "${_('Id')}", className: "td-componentname", "type": "num" },
97 { data: {"_": "title",
98 "sort": "title"}, title: "${_('Title')}", className: "td-description" },
99 { data: {"_": "author",
100 "sort": "author_raw"}, title: "${_('Author')}", className: "td-user", orderable: false },
101 { data: {"_": "comments",
102 "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
103 { data: {"_": "updated_on",
104 "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
94 {
95 data: {
96 "_": "status",
97 "sort": "status"
98 }, title: "PR", className: "td-status", orderable: false
99 },
100 {
101 data: {
102 "_": "my_status",
103 "sort": "status"
104 }, title: "You", className: "td-status", orderable: false
105 },
106 {
107 data: {
108 "_": "name",
109 "sort": "name_raw"
110 }, title: "${_('Id')}", className: "td-componentname", "type": "num"
111 },
112 {
113 data: {
114 "_": "title",
115 "sort": "title"
116 }, title: "${_('Title')}", className: "td-description"
117 },
118 {
119 data: {
120 "_": "author",
121 "sort": "author_raw"
122 }, title: "${_('Author')}", className: "td-user", orderable: false
123 },
124 {
125 data: {
126 "_": "comments",
127 "sort": "comments_raw"
128 }, title: "", className: "td-comments", orderable: false
129 },
130 {
131 data: {
132 "_": "updated_on",
133 "sort": "updated_on_raw"
134 }, title: "${_('Last Update')}", className: "td-time"
135 }
105 136 ],
106 137 language: {
107 138 paginate: DEFAULT_GRID_PAGINATION,
108 139 sProcessing: _gettext('loading...'),
109 140 emptyTable: _gettext("No pull requests available yet.")
110 141 },
111 142 "drawCallback": function( settings, json ) {
112 143 timeagoActivate();
113 144 tooltipActivate();
114 145 },
115 146 "createdRow": function ( row, data, index ) {
116 147 if (data['closed']) {
117 148 $(row).addClass('closed');
118 149 }
119 150 },
120 151 "stateSaveParams": function (settings, data) {
121 152 data.search.search = ""; // Don't save search
122 153 data.start = 0; // don't save pagination
123 154 }
124 155 });
125 156
126 157 $pullRequestListTable.on('xhr.dt', function(e, settings, json, xhr){
127 158 $pullRequestListTable.css('opacity', 1);
128 159 });
129 160
130 161 $pullRequestListTable.on('preXhr.dt', function(e, settings, data){
131 162 $pullRequestListTable.css('opacity', 0.3);
132 163 });
133 164
134 165 // filter
135 166 $('#q_filter').on('keyup',
136 167 $.debounce(250, function() {
137 168 $pullRequestListTable.DataTable().search(
138 169 $('#q_filter').val()
139 170 ).draw();
140 171 })
141 172 );
142 173
143 174 });
144 175
145 176 </script>
146 177 </%def>
@@ -1,167 +1,169 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%namespace name="base" file="/base/base.mako"/>
3 3 %if c.repo_commits:
4 4 <table class="rctable table-bordered">
5 5 <tr>
6 6
7 7 <th class="status"></th>
8 8 <th></th>
9 9 <th>${_('Commit message')}</th>
10 10 <th>${_('Age')}</th>
11 11 <th>${_('Author')}</th>
12 12 <th colspan="2">${_('Refs')}</th>
13 13 </tr>
14 14
15 15 ## to speed up lookups cache some functions before the loop
16 16 <%
17 17 active_patterns = h.get_active_pattern_entries(c.repo_name)
18 18 urlify_commit_message = h.partial(h.urlify_commit_message, active_pattern_entries=active_patterns)
19 19 %>
20 20 %for cnt,cs in enumerate(c.repo_commits):
21 21 <tr class="parity${cnt%2}">
22 22
23 23 <td class="td-status">
24 24 %if c.statuses.get(cs.raw_id):
25 25 <div class="changeset-status-ico shortlog">
26 26 %if c.statuses.get(cs.raw_id)[2]:
27 27 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (c.statuses.get(cs.raw_id)[0], c.statuses.get(cs.raw_id)[2])}" href="${h.route_path('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
28 28 <i class="icon-circle review-status-${c.statuses.get(cs.raw_id)[0]}"></i>
29 29 </a>
30 30 %else:
31 31 <a class="tooltip" title="${_('Commit status: {}').format(h.commit_status_lbl(c.statuses.get(cs.raw_id)[0]))}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=cs.raw_id,_anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
32 32 <i class="icon-circle review-status-${c.statuses.get(cs.raw_id)[0]}"></i>
33 33 </a>
34 34 %endif
35 35 </div>
36 36 %else:
37 37 <i class="icon-circle review-status-not_reviewed" title="${_('Commit status: Not Reviewed')}"></i>
38 38 %endif
39 39 </td>
40 40 <td class="td-hash">
41 41 <code>
42 42 <a href="${h.route_path('repo_commit', repo_name=c.repo_name, commit_id=cs.raw_id)}">${h.show_id(cs)}</a>
43 43 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${cs.raw_id}" title="${_('Copy the full commit id')}"></i>
44 44 </code>
45 45 </td>
46 46
47 47 <td class="td-description mid">
48 48 <div class="log-container truncate-wrap">
49 49 <div class="message truncate" id="c-${cs.raw_id}">${urlify_commit_message(cs.message, c.repo_name)}</div>
50 50 </div>
51 51 </td>
52 52
53 53 <td class="td-time">
54 54 ${h.age_component(cs.date)}
55 55 </td>
56 56 <td class="td-user author">
57 57 ${base.gravatar_with_user(cs.author, tooltip=True)}
58 58 </td>
59 59
60 60 <td class="td-tags">
61 61 <div class="autoexpand">
62 62 %if h.is_hg(c.rhodecode_repo):
63 63 %for book in cs.bookmarks:
64 64 <span class="booktag tag" title="${h.tooltip(_('Bookmark %s') % book)}">
65 65 <a href="${h.route_path('repo_files:default_path',repo_name=c.repo_name,commit_id=cs.raw_id, _query=dict(at=book))}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
66 66 </span>
67 67 %endfor
68 68 %endif
69 69 ## tags
70 70 %for tag in cs.tags:
71 71 <span class="tagtag tag" title="${h.tooltip(_('Tag %s') % tag)}">
72 72 <a href="${h.route_path('repo_files:default_path',repo_name=c.repo_name,commit_id=cs.raw_id, _query=dict(at=tag))}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
73 73 </span>
74 74 %endfor
75 75
76 76 ## branch
77 77 %if cs.branch:
78 78 <span class="branchtag tag" title="${h.tooltip(_('Branch %s') % cs.branch)}">
79 79 <a href="${h.route_path('repo_commits',repo_name=c.repo_name,_query=dict(branch=cs.branch))}"><i class="icon-code-fork"></i>${h.shorter(cs.branch)}</a>
80 80 </span>
81 81 %endif
82 82 </div>
83 83 </td>
84 84 <td class="td-comments">
85 85 <% cs_comments = c.comments.get(cs.raw_id,[]) %>
86 86 % if cs_comments:
87 87 <a title="${_('Commit has comments')}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=cs.raw_id,_anchor='comment-%s' % cs_comments[0].comment_id)}">
88 88 <i class="icon-comment"></i> ${len(cs_comments)}
89 89 </a>
90 90 % else:
91 91 <i class="icon-comment"></i> ${len(cs_comments)}
92 92 % endif
93 93 </td>
94 94 </tr>
95 95 %endfor
96 96
97 97 </table>
98 98
99 99 <script type="text/javascript">
100 100 $(document).pjax('#shortlog_data .pager_link','#shortlog_data', {timeout: 5000, scrollTo: false, push: false});
101 101 $(document).on('pjax:success', function(){ timeagoActivate(); tooltipActivate();});
102 102 $(document).on('pjax:timeout', function(event) {
103 103 // Prevent default timeout redirection behavior
104 104 event.preventDefault()
105 105 })
106 106
107 107 </script>
108 108
109 109 <div class="pagination-wh pagination-left">
110 110 ${c.repo_commits.render()}
111 111 </div>
112 112 %else:
113 113
114 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
114
115 115 <div class="quick_start">
116 116 <div class="fieldset">
117 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
117 118 <p><b>${_('Add or upload files directly via RhodeCode:')}</b></p>
118 119 <div class="pull-left">
119 120 <a href="${h.route_path('repo_files_add_file',repo_name=c.repo_name,commit_id=0, f_path='')}" class="btn btn-default">${_('Add New File')}</a>
120 121 </div>
121 122 <div class="pull-left">
122 123 <a href="${h.route_path('repo_files_upload_file',repo_name=c.repo_name,commit_id=0, f_path='')}" class="btn btn-default">${_('Upload New File')}</a>
123 124 </div>
124 125 %endif
125 126 </div>
126 127
128
127 129 <div class="fieldset">
128 130 <p><b>${_('Push new repo:')}</b></p>
129 131 <pre>
130 132 %if h.is_git(c.rhodecode_repo):
131 133 git clone ${c.clone_repo_url}
132 134 git add README # add first file
133 135 git commit -m "Initial commit" # commit with message
134 136 git remote add origin ${c.clone_repo_url}
135 137 git push -u origin master # push changes back to default master branch
136 138 %elif h.is_hg(c.rhodecode_repo):
137 139 hg clone ${c.clone_repo_url}
138 140 hg add README # add first file
139 141 hg commit -m "Initial commit" # commit with message
140 142 hg push ${c.clone_repo_url}
141 143 %elif h.is_svn(c.rhodecode_repo):
142 144 svn co ${c.clone_repo_url}
143 145 svn add README # add first file
144 146 svn commit -m "Initial commit"
145 147 svn commit # send changes back to the server
146 148 %endif
147 149 </pre>
148 150 </div>
149 151
150 152 <div class="fieldset">
151 153 <p><b>${_('Existing repository?')}</b></p>
152 154 <pre>
153 155 %if h.is_git(c.rhodecode_repo):
154 156 git remote add origin ${c.clone_repo_url}
155 157 git push -u origin master
156 158 %elif h.is_hg(c.rhodecode_repo):
157 159 hg push ${c.clone_repo_url}
158 160 %elif h.is_svn(c.rhodecode_repo):
159 161 svn co ${c.clone_repo_url}
160 162 %endif
161 163 </pre>
162 164
163 165 </div>
164 166
165 167
166 168 </div>
167 169 %endif
General Comments 1
Under Review
author

Auto status change to "Under Review"

You need to be logged in to leave comments. Login now

Merge is not currently possible because of below failed checks.

  • - User `default` not allowed to perform merge.
  • - Pull request reviewer approval is pending.