From ac7e9a61460554aa0183c677bb15d1f473519f55 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 7 Dec 2012 08:24:43 -0500 Subject: Harden metrics from polluted data (issue-176) --- src/com/gitblit/models/Activity.java | 4 ++-- src/com/gitblit/utils/MetricUtils.java | 1 + src/com/gitblit/utils/StringUtils.java | 14 ++++++++++++++ src/com/gitblit/wicket/pages/RepositoryPage.java | 2 ++ 4 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/models/Activity.java b/src/com/gitblit/models/Activity.java index 771c8a1a..7e0cb4b3 100644 --- a/src/com/gitblit/models/Activity.java +++ b/src/com/gitblit/models/Activity.java @@ -28,6 +28,7 @@ import java.util.Set; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.revwalk.RevCommit; +import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; /** @@ -93,8 +94,7 @@ public class Activity implements Serializable, Comparable { } repositoryMetrics.get(repository).count++; - String author = commit.getAuthorIdent().getEmailAddress() - .toLowerCase(); + String author = StringUtils.removeNewlines(commit.getAuthorIdent().getEmailAddress()).toLowerCase(); if (!authorMetrics.containsKey(author)) { authorMetrics.put(author, new Metric(author)); } diff --git a/src/com/gitblit/utils/MetricUtils.java b/src/com/gitblit/utils/MetricUtils.java index e9e1fa52..26e4581c 100644 --- a/src/com/gitblit/utils/MetricUtils.java +++ b/src/com/gitblit/utils/MetricUtils.java @@ -210,6 +210,7 @@ public class MetricUtils { p = rev.getAuthorIdent().getEmailAddress().toLowerCase(); } } + p = p.replace('\n',' ').replace('\r', ' ').trim(); if (!metricMap.containsKey(p)) { metricMap.put(p, new Metric(p)); } diff --git a/src/com/gitblit/utils/StringUtils.java b/src/com/gitblit/utils/StringUtils.java index 86840048..86823db5 100644 --- a/src/com/gitblit/utils/StringUtils.java +++ b/src/com/gitblit/utils/StringUtils.java @@ -719,4 +719,18 @@ public class StringUtils { Matcher m = p.matcher(input); return m.matches(); } + + /** + * Removes new line and carriage return chars from a string. + * If input value is null an empty string is returned. + * + * @param input + * @return a sanitized or empty string + */ + public static String removeNewlines(String input) { + if (input == null) { + return ""; + } + return input.replace('\n',' ').replace('\r', ' ').trim(); + } } \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java index b4e1a0e3..897e2001 100644 --- a/src/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/com/gitblit/wicket/pages/RepositoryPage.java @@ -450,6 +450,8 @@ public abstract class RepositoryPage extends BasePage { Constants.SearchType searchType) { String name = identity == null ? "" : identity.getName(); String address = identity == null ? "" : identity.getEmailAddress(); + name = StringUtils.removeNewlines(name); + address = StringUtils.removeNewlines(address); boolean showEmail = GitBlit.getBoolean(Keys.web.showEmailAddresses, false); if (!showEmail || StringUtils.isEmpty(name) || StringUtils.isEmpty(address)) { String value = name; -- cgit v1.2.3 From 55037b1691c736b3cf95eadf583a8be526b9a3a2 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 7 Dec 2012 08:46:52 -0500 Subject: Support languagecode-countrycode Markdown resources --- src/com/gitblit/wicket/pages/BasePage.java | 4 ++ src/com/gitblit/wicket/pages/ProjectsPage.java | 64 +++++++++++---------- src/com/gitblit/wicket/pages/RepositoriesPage.java | 65 +++++++++++++--------- 3 files changed, 78 insertions(+), 55 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/pages/BasePage.java b/src/com/gitblit/wicket/pages/BasePage.java index d1ee2710..9d469083 100644 --- a/src/com/gitblit/wicket/pages/BasePage.java +++ b/src/com/gitblit/wicket/pages/BasePage.java @@ -98,6 +98,10 @@ public abstract class BasePage extends WebPage { return GitBlitWebSession.get().getLocale().getLanguage(); } + protected String getCountryCode() { + return GitBlitWebSession.get().getLocale().getCountry().toLowerCase(); + } + protected TimeUtils getTimeUtils() { if (timeUtils == null) { ResourceBundle bundle; diff --git a/src/com/gitblit/wicket/pages/ProjectsPage.java b/src/com/gitblit/wicket/pages/ProjectsPage.java index 7161d0f3..4e3e6309 100644 --- a/src/com/gitblit/wicket/pages/ProjectsPage.java +++ b/src/com/gitblit/wicket/pages/ProjectsPage.java @@ -194,39 +194,47 @@ public class ProjectsPage extends RootPage { } private String readDefaultMarkdown(String file) { - String content = readDefaultMarkdown(file, getLanguageCode()); - if (StringUtils.isEmpty(content)) { - content = readDefaultMarkdown(file, null); - } - return content; - } + String base = file.substring(0, file.lastIndexOf('.')); + String ext = file.substring(file.lastIndexOf('.')); + String lc = getLanguageCode(); + String cc = getCountryCode(); - private String readDefaultMarkdown(String file, String lc) { + // try to read file_en-us.ext, file_en.ext, file.ext + List files = new ArrayList(); if (!StringUtils.isEmpty(lc)) { - // convert to file_lc.mkd - file = file.substring(0, file.lastIndexOf('.')) + "_" + lc - + file.substring(file.lastIndexOf('.')); + if (!StringUtils.isEmpty(cc)) { + files.add(base + "_" + lc + "-" + cc + ext); + files.add(base + "_" + lc + "_" + cc + ext); + } + files.add(base + "_" + lc + ext); } - String message; - try { - ContextRelativeResource res = WicketUtils.getResource(file); - InputStream is = res.getResourceStream().getInputStream(); - InputStreamReader reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING); - message = MarkdownUtils.transformMarkdown(reader); - reader.close(); - } catch (ResourceStreamNotFoundException t) { - if (lc == null) { - // could not find default language resource + files.add(file); + + for (String name : files) { + String message; + InputStreamReader reader = null; + try { + ContextRelativeResource res = WicketUtils.getResource(name); + InputStream is = res.getResourceStream().getInputStream(); + reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING); + message = MarkdownUtils.transformMarkdown(reader); + reader.close(); + return message; + } catch (ResourceStreamNotFoundException t) { + continue; + } catch (Throwable t) { message = MessageFormat.format(getString("gb.failedToReadMessage"), file); error(message, t, false); - } else { - // ignore so we can try default language resource - message = null; - } - } catch (Throwable t) { - message = MessageFormat.format(getString("gb.failedToReadMessage"), file); - error(message, t, false); + return message; + } finally { + if (reader != null) { + try { + reader.close(); + } catch (Exception e) { + } + } + } } - return message; + return MessageFormat.format(getString("gb.failedToReadMessage"), file); } } diff --git a/src/com/gitblit/wicket/pages/RepositoriesPage.java b/src/com/gitblit/wicket/pages/RepositoriesPage.java index 97c6aa23..4816d45c 100644 --- a/src/com/gitblit/wicket/pages/RepositoriesPage.java +++ b/src/com/gitblit/wicket/pages/RepositoriesPage.java @@ -20,6 +20,7 @@ import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.List; import org.apache.wicket.Component; @@ -139,37 +140,47 @@ public class RepositoriesPage extends RootPage { } private String readDefaultMarkdown(String file) { - String content = readDefaultMarkdown(file, getLanguageCode()); - if (StringUtils.isEmpty(content)) { - content = readDefaultMarkdown(file, null); - } - return content; - } - - private String readDefaultMarkdown(String file, String lc) { + String base = file.substring(0, file.lastIndexOf('.')); + String ext = file.substring(file.lastIndexOf('.')); + String lc = getLanguageCode(); + String cc = getCountryCode(); + + // try to read file_en-us.ext, file_en.ext, file.ext + List files = new ArrayList(); if (!StringUtils.isEmpty(lc)) { - // convert to file_lc.mkd - file = file.substring(0, file.lastIndexOf('.')) + "_" + lc + file.substring(file.lastIndexOf('.')); + if (!StringUtils.isEmpty(cc)) { + files.add(base + "_" + lc + "-" + cc + ext); + files.add(base + "_" + lc + "_" + cc + ext); + } + files.add(base + "_" + lc + ext); } - String message; - try { - InputStream is = GitBlit.self().getResourceAsStream(file); - InputStreamReader reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING); - message = MarkdownUtils.transformMarkdown(reader); - reader.close(); - } catch (ResourceStreamNotFoundException t) { - if (lc == null) { - // could not find default language resource + files.add(file); + + for (String name : files) { + String message; + InputStreamReader reader = null; + try { + ContextRelativeResource res = WicketUtils.getResource(name); + InputStream is = res.getResourceStream().getInputStream(); + reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING); + message = MarkdownUtils.transformMarkdown(reader); + reader.close(); + return message; + } catch (ResourceStreamNotFoundException t) { + continue; + } catch (Throwable t) { message = MessageFormat.format(getString("gb.failedToReadMessage"), file); error(message, t, false); - } else { - // ignore so we can try default language resource - message = null; - } - } catch (Throwable t) { - message = MessageFormat.format(getString("gb.failedToReadMessage"), file); - error(message, t, false); + return message; + } finally { + if (reader != null) { + try { + reader.close(); + } catch (Exception e) { + } + } + } } - return message; + return MessageFormat.format(getString("gb.failedToReadMessage"), file); } } -- cgit v1.2.3 From ff4a7a48ed25a891368d8082218fb5904a7df1a3 Mon Sep 17 00:00:00 2001 From: Mrbytes Date: Tue, 18 Dec 2012 19:26:30 +0100 Subject: GitBlitWebApp_es.properties: Up-to-date. Some minor changes in syntax for better compression. Signed-off-by: Mrbytes --- src/com/gitblit/wicket/GitBlitWebApp_es.properties | 101 ++++++++++++++++++++- 1 file changed, 98 insertions(+), 3 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/GitBlitWebApp_es.properties b/src/com/gitblit/wicket/GitBlitWebApp_es.properties index d83fcef3..e819503e 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp_es.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp_es.properties @@ -163,7 +163,7 @@ gb.manage = Administrar gb.lastLogin = \u00DAltimo acceso gb.skipSizeCalculation = Saltar comprobaciones de tama\u00F1o gb.skipSizeCalculationDescription = No calcular el tama\u00F1o del repositorio (Reduce tiempo de carga de la p\u00E1gina) -gb.skipSummaryMetrics = Saltar el resumen de estad\u00EDsticas +gb.skipSummaryMetrics = Saltar resumen de estad\u00EDsticas gb.skipSummaryMetricsDescription = No calcular estad\u00EDsticas (Reduce tiempo de carga de la p\u00E1gina) gb.accessLevel = Nivel de acceso gb.default = Predeterminado @@ -341,8 +341,103 @@ gb.isFork = Es bifurcado gb.canCreate = Puede crear gb.canCreateDescription = Puede crear repositorios personales gb.illegalPersonalRepositoryLocation = Tu repositorio personal debe estar ubicado en \"{0}\" -gb.verifyCommitter = Acreditar consignador -gb.verifyCommitterDescription = Require que la acreditaci\u00F3n del consignador coincida con la de la cuenta del usuario en Gitblt (es necesario "--no-ff" al empujar para que consignador se acredite) +gb.verifyCommitter = Consignador acreditado +gb.verifyCommitterDescription = Require que la acreditaci\u00F3n del consignador coincida con la de la cuenta del usuario en Gitblt +gb.verifyCommitterNote = es obligatorio "--no-ff" al empujar para que el consignador se acredite gb.repositoryPermissions = Permisos del repositorio gb.userPermissions = Permisos de usuarios gb.teamPermissions = Permisos de equipos +gb.add = A\u00F1adir +gb.noPermission = BORRAR ESTE PERMISO +gb.excludePermission = {0} (excluir) +gb.viewPermission = {0} (ver) +gb.clonePermission = {0} (clonar) +gb.pushPermission = {0} (empujar) +gb.createPermission = {0} (empujar, ref creaci\u00F3n) +gb.deletePermission = {0} (empujar, ref creaci\u00F3n+borrado) +gb.rewindPermission = {0} (empujar, ref creaci\u00F3n+borrado+supresi\u00F3n) +gb.permission = Permisos +gb.regexPermission = Estos permisos se ajustan desde la expresi\u00F3n regulare \"{0}\" +gb.accessDenied = Acceso denegado +gb.busyCollectingGarbage = Perd\u00F3n, Gitblit est\u00E1 ocupado quitando basura de {0} +gb.gcPeriod = Periodo para GC +gb.gcPeriodDescription = Duraci\u00F3n entre periodos de limpieza +gb.gcThreshold = L\u00EDmites para GC +gb.gcThresholdDescription = Tama\u00F1o m\u00EDnimo total de objetos sueltos para activar la recolecci\u00F3n inmediata de basura +gb.ownerPermission = Propietario del repositorio +gb.administrator = Admin +gb.administratorPermission = Administrador de Gitblit +gb.team = Equipo +gb.teamPermission = Permisos ajustados para \"{0}\" mienbros de equipo +gb.missing = \u00A1Omitido! +gb.missingPermission = \u00A1Falta el repositorio de este permiso! +gb.mutable = Alterables +gb.specified = Espec\u00EDficos +gb.effective = Efectivos +gb.organizationalUnit = Unidad de organizaci\u00F3n +gb.organization = Organizaci\u00F3n +gb.locality = Localidad +gb.stateProvince = Estado o provincia +gb.countryCode = C\u00F3digo postal +gb.properties = Propiedades +gb.issued = Publicado +gb.expires = Expira +gb.expired = Expirado +gb.expiring = Concluido +gb.revoked = Revocado +gb.serialNumber = N\u00FAmero de serie +gb.certificates = Certificados +gb.newCertificate = Nuevo certificado +gb.revokeCertificate = Revocar certificado +gb.sendEmail = Enviar correo +gb.passwordHint = Recordatorio de contrase\u00F1a +gb.ok = ok +gb.invalidExpirationDate = \u00A1La fecha de expiraci\u00F3n no es v\u00E1lida! +gb.passwordHintRequired = \u00A1Se requiere una pista para la contrase\u00F1a! +gb.viewCertificate = Ver certificado +gb.subject = Asunto +gb.issuer = Emisor +gb.validFrom = V\u00E1lido desde +gb.validUntil = V\u00E1lido hasta +gb.publicKey = Clave p\u00FAblica +gb.signatureAlgorithm = Algoritmo de firma +gb.sha1FingerPrint = Huella digital SHA-1 +gb.md5FingerPrint = Huella digital MD5 +gb.reason = Motivo +gb.revokeCertificateReason = Por favor, selecciona un motivo por el que revocas el certificado +gb.unspecified = Sin especificar +gb.keyCompromise = Clave de compromiso +gb.caCompromise = Compromiso CA +gb.affiliationChanged = Afiliaci\u00F3n cambiada +gb.superseded = Sustituida +gb.cessationOfOperation = Cese de operaci\u00F3n +gb.privilegeWithdrawn = Privilegios retirados +gb.time.inMinutes = en {0} mints +gb.time.inHours = en {0} horas +gb.time.inDays = en {0} d\u00EDas +gb.hostname = Nombre de host +gb.hostnameRequired = Por favor, introduzca un nombre de host +gb.newSSLCertificate = Nuevo certificado SSL del servidor +gb.newCertificateDefaults = Nuevo certificado predeterminado +gb.duration = Duraci\u00F3n +gb.certificateRevoked = El cretificado {0,n\u00FAmero,0} ha sido revocado +gb.clientCertificateGenerated = Nuevo certificado de cliente generado correctamente para {0} +gb.sslCertificateGenerated = Nuevo certificado de SSL generado correctamente para {0} +gb.newClientCertificateMessage = AVISO:\nLa 'contrase\u00F1a' no es la contrase\u00F1a del usuario, es la contrase\u00F1a para proteger el almac\u00E9n de claves del usuario. Esta contrase\u00F1a no se guarda por lo que tambi\u00E9n debe introducirse una "pista" que ser\u00E1 incluida en las instrucciones LEEME del usuario. +gb.certificate = Certificado +gb.emailCertificateBundle = Correo del cliente para el paquete del certificado +gb.pleaseGenerateClientCertificate = Por favor, genera un certificado de cliente para {0} +gb.clientCertificateBundleSent = Paquete de certificado de cliente {0} enviado +gb.enterKeystorePassword = Por favor, introduzca la contrase\u00F1a del almacén de claves de Gitblit +gb.warning = Advertencia +gb.jceWarning = Tu entorno de trabajo JAVA no contiene los archivos \"JCE Unlimited Strength Jurisdiction Policy\".\nEsto limita la longitud de la contrase\u00F1a que puedes usuar para cifrar el almac\u00E9n de claves a 7 caracteres.\nEstos archivos opcionales puedes de descargarlos desde Oracle.\n\n\u00BFQuieres continuar y generar la infraestructura de certificados de todos modos?\n\nSi respondes No tu navegador te dirigir\u00E1 a la p\u00E1gina de descarga de Oracle para que pueda descargar dichos archivos. +gb.maxActivityCommits = Actividad m\u00E1xima de consignas +gb.maxActivityCommitsDescription = N\u00FAmero m\u00E1ximo de consignas a incluir en la p\u00E1gina de actividad +gb.noMaximum = Sin m\u00E1ximos +gb.attributes = Atributos +gb.serveCertificate = Servidor https con este certificado +gb.sslCertificateGeneratedRestart = Certificado SSL generado correctamente para {0}.\nDebes reiniciar Gitblit para usar el nuevo certificado.\n\nSi lo has iniciado con la opci\u00F3n '--alias' deber\u00E1s ajustar dicha opci\u00F3n a ''--alias {0}''. +gb.validity = Vigencia +gb.siteName = Nombre del sitio +gb.siteNameDescription = Nombre corto y descriptivo de tu servidor +gb.excludeFromActivity = Excluir de la p\u00E1gina de actividad \ No newline at end of file -- cgit v1.2.3 From 34148fc36e5357525705d8ce1b4169fb8acf2103 Mon Sep 17 00:00:00 2001 From: Rafael Cavazin Date: Tue, 18 Dec 2012 16:40:18 -0500 Subject: Merged Brazilian Portuguese translation --- docs/01_features.mkd | 1 + docs/04_releases.mkd | 1 + resources/login_pt_br.mkd | 3 + resources/welcome_pt_br.mkd | 3 + .../gitblit/wicket/GitBlitWebApp_pt_br.properties | 442 +++++++++++++++++++++ .../wicket/pages/EmptyRepositoryPage_pt_br.html | 53 +++ 6 files changed, 503 insertions(+) create mode 100644 resources/login_pt_br.mkd create mode 100644 resources/welcome_pt_br.mkd create mode 100644 src/com/gitblit/wicket/GitBlitWebApp_pt_br.properties create mode 100644 src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_br.html (limited to 'src/com/gitblit') diff --git a/docs/01_features.mkd b/docs/01_features.mkd index 4676f755..a28b97a9 100644 --- a/docs/01_features.mkd +++ b/docs/01_features.mkd @@ -56,6 +56,7 @@ - Spanish - Polish - Korean + - Brazilian Portuguese ## Gitblit GO Features - Out-of-the-box integrated stack requiring minimal configuration diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index d42152f9..485215b6 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -72,6 +72,7 @@ This is extreme and should be considered carefully since it affects every https - Added HTML sendmail hook script and Gitblit.sendHtmlMail method (github/sauthieg) - Added RedmineUserService (github/mallowlabs) - Support for committer verification. Requires use of *--no-ff* when merging branches or pull requests. See setup page for details. +- Added Brazilian Portuguese translation (github/rafaelcavazin) #### changes diff --git a/resources/login_pt_br.mkd b/resources/login_pt_br.mkd new file mode 100644 index 00000000..6a4a8570 --- /dev/null +++ b/resources/login_pt_br.mkd @@ -0,0 +1,3 @@ +## Faça o Login + +Por favor, entre com suas credenciais para acessar o site Gitblit. diff --git a/resources/welcome_pt_br.mkd b/resources/welcome_pt_br.mkd new file mode 100644 index 00000000..6b30bbcd --- /dev/null +++ b/resources/welcome_pt_br.mkd @@ -0,0 +1,3 @@ +## Bem-Vindo ao Gitblit + +Uma maneira fácil e rápida de armazenar e visualizar o seus próprios repositórios [Git](http://www.git-scm.com). diff --git a/src/com/gitblit/wicket/GitBlitWebApp_pt_br.properties b/src/com/gitblit/wicket/GitBlitWebApp_pt_br.properties new file mode 100644 index 00000000..469d2055 --- /dev/null +++ b/src/com/gitblit/wicket/GitBlitWebApp_pt_br.properties @@ -0,0 +1,442 @@ +gb.repository = repositório +gb.owner = proprietário +gb.description = descrição +gb.lastChange = última alteração +gb.refs = refs +gb.tag = tag +gb.tags = tags +gb.author = autor +gb.committer = committer +gb.commit = commit +gb.tree = árvore +gb.parent = parent +gb.url = URL +gb.history = histórico +gb.raw = raw +gb.object = object +gb.ticketId = ticket id +gb.ticketAssigned = atribuído +gb.ticketOpenDate = data da abertura +gb.ticketState = estado +gb.ticketComments = comentários +gb.view = visualizar +gb.local = local +gb.remote = remote +gb.branches = branches +gb.patch = patch +gb.diff = diff +gb.log = log +gb.moreLogs = mais commits... +gb.allTags = todas as tags... +gb.allBranches = todos os branches... +gb.summary = resumo +gb.ticket = ticket +gb.newRepository = novo repositório +gb.newUser = novo usuário +gb.commitdiff = commitdiff +gb.tickets = tickets +gb.pageFirst = primeira +gb.pagePrevious anterior +gb.pageNext = próxima +gb.head = HEAD +gb.blame = blame +gb.login = login +gb.logout = logout +gb.username = username +gb.password = password +gb.tagger = tagger +gb.moreHistory = mais histórico... +gb.difftocurrent = diff para a atual +gb.search = pesquisar +gb.searchForAuthor = Procurar por commits cujo autor é +gb.searchForCommitter = Procurar por commits commitados por é +gb.addition = adicionados +gb.modification = modificados +gb.deletion = apagados +gb.rename = renomear +gb.metrics = métricas +gb.stats = estatísticas +gb.markdown = markdown +gb.changedFiles = arquivos alterados +gb.filesAdded = {0} arquivos adicionados +gb.filesModified = {0} arquivos modificados +gb.filesDeleted = {0} arquivos deletados +gb.filesCopied = {0} arquivos copiados +gb.filesRenamed = {0} arquivos renomeados +gb.missingUsername = Username desconhecido +gb.edit = editar +gb.searchTypeTooltip = Selecione o Tipo de Pesquisa +gb.searchTooltip = Pesquisar {0} +gb.delete = deletar +gb.docs = documentos +gb.accessRestriction = restrição de acesso +gb.name = nome +gb.enableTickets = ativar tickets +gb.enableDocs = ativar documentação +gb.save = salvar +gb.showRemoteBranches = mostrar branches remotos +gb.editUsers = editar usuários +gb.confirmPassword = confirmar password +gb.restrictedRepositories = repositórios restritos +gb.canAdmin = pode administrar +gb.notRestricted = visualização anônima, clone, & push +gb.pushRestricted = push autênticado +gb.cloneRestricted = clone & push autênticados +gb.viewRestricted = view, clone, & push autênticados +gb.useTicketsDescription = somente leitura, issues do Ticgit distribuídos +gb.useDocsDescription = enumerar documentação Markdown no repositório +gb.showRemoteBranchesDescription = mostrar branches remotos +gb.canAdminDescription = pode administrar o server Gitblit +gb.permittedUsers = usuários autorizados +gb.isFrozen = congelar +gb.isFrozenDescription = proibir fazer push +gb.zip = zip +gb.showReadme = mostrar readme +gb.showReadmeDescription = mostrar um arquivo \"leia-me\" na página de resumo +gb.nameDescription = usar '/' para agrupar repositórios. e.g. libraries/mycoollib.git +gb.ownerDescription = o proprietário pode editar configurações do repositório +gb.blob = blob +gb.commitActivityTrend = tendência dos commits +gb.commitActivityDOW = commits diários +gb.commitActivityAuthors = principais committers +gb.feed = feed +gb.cancel = cancelar +gb.changePassword = alterar password +gb.isFederated = está federado +gb.federateThis = federar este repositório +gb.federateOrigin = federar o origin +gb.excludeFromFederation = excluir da federação +gb.excludeFromFederationDescription = bloquear instâncias federadas do GitBlit de fazer pull desta conta +gb.tokens = tokens de federação +gb.tokenAllDescription = todos repositórios, usuários & configurações +gb.tokenUnrDescription = todos repositórios & usuários +gb.tokenJurDescription = todos repositórios +gb.federatedRepositoryDefinitions = definições de repositório +gb.federatedUserDefinitions = definições de usuários +gb.federatedSettingDefinitions = definições de configurações +gb.proposals = propostas de federações +gb.received = recebidos +gb.type = tipo +gb.token = token +gb.repositories = repositórios +gb.proposal = propostas +gb.frequency = frequência +gb.folder = pasta +gb.lastPull = último pull +gb.nextPull = próximo pull +gb.inclusions = inclusões +gb.exclusions = excluões +gb.registration = cadastro +gb.registrations = cadastro de federações +gb.sendProposal = enviar proposta +gb.status = status +gb.origin = origin +gb.headRef = default branch (HEAD) +gb.headRefDescription = alterar a ref o qual a HEAD aponta. e.g. refs/heads/master +gb.federationStrategy = estratégia de federação +gb.federationRegistration = cadastro de federações +gb.federationResults = resultados dos pulls de federações +gb.federationSets = ajustes de federações +gb.message = mensagem +gb.myUrlDescription = a url de acesso público para a instância Gitblit +gb.destinationUrl = enviar para +gb.destinationUrlDescription = a url da intância do Gitblit para enviar sua proposta +gb.users = usuários +gb.federation = federação +gb.error = erro +gb.refresh = atualizar +gb.browse = navegar +gb.clone = clonar +gb.filter = filtrar +gb.create = criar +gb.servers = servidores +gb.recent = recente +gb.available = disponível +gb.selected = selecionado +gb.size = tamanho +gb.downloading = downloading +gb.loading = loading +gb.starting = inciando +gb.general = geral +gb.settings = configurações +gb.manage = administrar +gb.lastLogin = último login +gb.skipSizeCalculation = ignorar cálculo do tamanho +gb.skipSizeCalculationDescription = não calcular o tamanho do repositório (reduz o tempo de load da página) +gb.skipSummaryMetrics = ignorar resumo das métricas +gb.skipSummaryMetricsDescription = não calcular métricas na página de resumo +gb.accessLevel = acesso +gb.default = default +gb.setDefault = tornar default +gb.since = desde +gb.status = status +gb.bootDate = data do boot +gb.servletContainer = servlet container +gb.heapMaximum = heap máximo +gb.heapAllocated = alocar heap +gb.heapUsed = usar heap +gb.free = free +gb.version = versão +gb.releaseDate = data de release +gb.date = data +gb.activity = atividade +gb.subscribe = inscrever +gb.branch = branch +gb.maxHits = hits máximos +gb.recentActivity = atividade recente +gb.recentActivityStats = últimos {0} dias / {1} commits por {2} autores +gb.recentActivityNone = últimos {0} dias / nenhum +gb.dailyActivity = atividade diária +gb.activeRepositories = repositórios ativos +gb.activeAuthors = autores ativos +gb.commits = commits +gb.teams = equipes +gb.teamName = nome da equipe +gb.teamMembers = membros +gb.teamMemberships = filiações em equipes +gb.newTeam = nova equipe +gb.permittedTeams = equipes permitidas +gb.emptyRepository = repositório vazio +gb.repositoryUrl = url do repositório +gb.mailingLists = listas de e-mails +gb.preReceiveScripts = pre-receive scripts +gb.postReceiveScripts = post-receive scripts +gb.hookScripts = hook scripts +gb.customFields = campos customizados +gb.customFieldsDescription = campos customizados disponíveis para Groovy hooks +gb.accessPermissions = permissões de acesso +gb.filters = filtros +gb.generalDescription = configurações comuns +gb.accessPermissionsDescription = restringir acesso por usuários e equipes +gb.accessPermissionsForUserDescription = ajustar filiações em equipes ou garantir acesso a repositórios específicos +gb.accessPermissionsForTeamDescription = ajustar membros da equipe e garantir acesso a repositórios específicos +gb.federationRepositoryDescription = compartilhar este repositório com outros servidores Gitblit +gb.hookScriptsDescription = rodar scripts Groovy nos pushes pushes para este servidor Gitblit +gb.reset = reset +gb.pages = páginas +gb.workingCopy = working copy +gb.workingCopyWarning = this repository has a working copy and can not receive pushes +gb.query = query +gb.queryHelp = Standard query syntax é suportada.

Por favor veja Lucene Query Parser Syntax para mais detalhes. +gb.queryResults = resultados {0} - {1} ({2} hits) +gb.noHits = sem hits +gb.authored = foi autor do +gb.committed = committed +gb.indexedBranches = branches indexados +gb.indexedBranchesDescription = selecione os branches para incluir nos seus índices Lucene +gb.noIndexedRepositoriesWarning = nenhum dos seus repositórios foram configurados para indexação do Lucene +gb.undefinedQueryWarning = a query não foi definida! +gb.noSelectedRepositoriesWarning = por favor selecione um ou mais repositórios! +gb.luceneDisabled = indexação do Lucene está desabilitada +gb.failedtoRead = leitura falhou +gb.isNotValidFile = não é um arquivo válido +gb.failedToReadMessage = Falhou em ler mensagens default de {0}! +gb.passwordsDoNotMatch = Passwords não conferem! +gb.passwordTooShort = Password é muito curto. Tamanho mínimo são {0} caracteres. +gb.passwordChanged = Password alterado com sucesso. +gb.passwordChangeAborted = alteração do Password foi abortada. +gb.pleaseSetRepositoryName = Por favor ajuste o nome do repositório! +gb.illegalLeadingSlash = Referências a diretórios raiz começando com (/) são proibidas. +gb.illegalRelativeSlash = Referências a diretórios relativos (../) são proibidas. +gb.illegalCharacterRepositoryName = Caractere ilegal ''{0}'' no nome do repositório! +gb.selectAccessRestriction = Please select access restriction! +gb.selectFederationStrategy = Por favor selecione a estratégia de federação! +gb.pleaseSetTeamName = Por favor insira um nome de equipe! +gb.teamNameUnavailable = O nome de equipe ''{0}'' está indisponível. +gb.teamMustSpecifyRepository = Uma equipe deve especificar pelo menos um repositório. +gb.teamCreated = Nova equipe ''{0}'' criada com sucesso. +gb.pleaseSetUsername = Por favor entre com um username! +gb.usernameUnavailable = Username ''{0}'' está indisponível. +gb.combinedMd5Rename = Gitblit está configurado para usar um hash combinado-md5. Você deve inserir um novo password ao renamear a conta. +gb.userCreated = Novo usuário ''{0}'' criado com sucesso. +gb.couldNotFindFederationRegistration = Não foi possível localizar o registro da federação! +gb.failedToFindGravatarProfile = Falhou em localizar um perfil Gravatar para {0} +gb.branchStats = {0} commits e {1} tags em {2} +gb.repositoryNotSpecified = Repositório não específicado! +gb.repositoryNotSpecifiedFor = Repositório não específicado para {0}! +gb.canNotLoadRepository = Não foi possível carregar o repositório +gb.commitIsNull = Commit está nulo +gb.unauthorizedAccessForRepository = Acesso não autorizado para o repositório +gb.failedToFindCommit = Não foi possível achar o commit \"{0}\" em {1} para {2} página! +gb.couldNotFindFederationProposal = Não foi possível localizar propostas de federação! +gb.invalidUsernameOrPassword = username ou password inválido! +gb.OneProposalToReview = Existe uma proposta de federação aguardando revisão. +gb.nFederationProposalsToReview = Existem {0} propostas de federação aguardando revisão. +gb.couldNotFindTag = Não foi possível localizar a tag {0} +gb.couldNotCreateFederationProposal = Não foi possível criar uma proposta de federation! +gb.pleaseSetGitblitUrl = Por favor insira sua url do Gitblit! +gb.pleaseSetDestinationUrl = Por favor insira a url de destino para sua proposta! +gb.proposalReceived = Proposta recebida com sucesso por {0}. +gb.noGitblitFound = Desculpe, {0} não localizou uma instância do Gitblit em {1}. +gb.noProposals = Desculpe, {0} não está aceitando propostas agora. +gb.noFederation = Desculpe, {0} não está configurado com nenhuma intância do Gitblit. +gb.proposalFailed = Desculpe, {0} não recebeu nenhum dado de proposta! +gb.proposalError = Desculpe, {0} reportou que um erro inesperado ocorreu! +gb.failedToSendProposal = Não foi possível enviar a proposta! +gb.userServiceDoesNotPermitAddUser = {0} não permite adicionar uma conta de usuário! +gb.userServiceDoesNotPermitPasswordChanges = {0} não permite alterações no password! +gb.displayName = nome +gb.emailAddress = e-mail +gb.errorAdminLoginRequired = Administração requer um login +gb.errorOnlyAdminMayCreateRepository = Somente umadministrador pode criar um repositório +gb.errorOnlyAdminOrOwnerMayEditRepository = Somente umadministrador pode editar um repositório +gb.errorAdministrationDisabled = Administração está desabilitada +gb.lastNDays = últimos {0} dias +gb.completeGravatarProfile = Profile completo em Gravatar.com +gb.none = nenhum +gb.line = linha +gb.content = conteúdo +gb.empty = vazio +gb.inherited = herdado +gb.deleteRepository = Deletar repositório \"{0}\"? +gb.repositoryDeleted = Repositório ''{0}'' deletado. +gb.repositoryDeleteFailed = Não foi possível apagar o repositório ''{0}''! +gb.deleteUser = Deletar usuário \"{0}\"? +gb.userDeleted = Usuário ''{0}'' deletado. +gb.userDeleteFailed = Não foi possível apagar o usuário ''{0}''! +gb.time.justNow = agora mesmo +gb.time.today = hoje +gb.time.yesterday = ontem +gb.time.minsAgo = há {0} minutos +gb.time.hoursAgo = há {0} horas +gb.time.daysAgo = há {0} dias +gb.time.weeksAgo = há {0} semanas +gb.time.monthsAgo = há {0} meses +gb.time.oneYearAgo = há 1 ano +gb.time.yearsAgo = há {0} anos +gb.duration.oneDay = 1 dia +gb.duration.days = {0} dias +gb.duration.oneMonth = 1 mês +gb.duration.months = {0} meses +gb.duration.oneYear = 1 ano +gb.duration.years = {0} anos +gb.authorizationControl = controle de autorização +gb.allowAuthenticatedDescription = conceder permissão RW+ para todos os usuários autênticados +gb.allowNamedDescription = conceder permissões refinadas para usuários escolhidos ou equipes +gb.markdownFailure = Não foi possível converter conteúdo Markdown! +gb.clearCache = limpar o cache +gb.projects = projetos +gb.project = projeto +gb.allProjects = todos projetos +gb.copyToClipboard = copiar para o clipboard +gb.fork = fork +gb.forks = forks +gb.forkRepository = fork {0}? +gb.repositoryForked = fork feito em {0} +gb.repositoryForkFailed= não foi possível fazer fork +gb.personalRepositories = repositórios pessoais +gb.allowForks = permitir forks +gb.allowForksDescription = permitir usuários autorizados a fazer fork deste repositório +gb.forkedFrom = forked de +gb.canFork = pode fazer fork +gb.canForkDescription = pode fazer fork de repositórios autorizados para repositórios pessoais +gb.myFork = visualizar meu fork +gb.forksProhibited = forks proibidos +gb.forksProhibitedWarning = este repositório proíbe forks +gb.noForks = {0} não possui forks +gb.forkNotAuthorized = desculpe, você não está autorizado a fazer fork de {0} +gb.forkInProgress = fork em progresso +gb.preparingFork = preparando seu fork... +gb.isFork = é fork +gb.canCreate = pode criar +gb.canCreateDescription = pode criar repositórios pessoais +gb.illegalPersonalRepositoryLocation = seu repositório pessoal deve estar localizado em \"{0}\" +gb.verifyCommitter = verificar committer +gb.verifyCommitterDescription = requer a identidade do committer para combinar com uma conta do Gitblt +gb.verifyCommitterNote = todos os merges requerem "--no-ff" para impor a identidade do committer +gb.repositoryPermissions = permissões de repositório +gb.userPermissions = permissões de usuário +gb.teamPermissions = permissões de equipe +gb.add = add +gb.noPermission = APAGAR ESTA PERMISSÃO +gb.excludePermission = {0} (excluir) +gb.viewPermission = {0} (visualizar) +gb.clonePermission = {0} (clonar) +gb.pushPermission = {0} (push) +gb.createPermission = {0} (push, ref creation) +gb.deletePermission = {0} (push, ref creation+deletion) +gb.rewindPermission = {0} (push, ref creation+deletion+rewind) +gb.permission = permissão +gb.regexPermission = esta permissão foi configurada através da expressão regular \"{0}\" +gb.accessDenied = acesso negado +gb.busyCollectingGarbage = desculpe, o Gitblit está ocupado coletando lixo em {0} +gb.gcPeriod = período do GC +gb.gcPeriodDescription = duração entre as coletas de lixo +gb.gcThreshold = limite do GC +gb.gcThresholdDescription = tamanho total mínimo de objetos \"soltos\" que ativam a coleta de lixo +gb.ownerPermission = proprietário do repositório +gb.administrator = administrador +gb.administratorPermission = administrador do Gitblit +gb.team = equipe +gb.teamPermission = permissão concedida pela filiação a equipe \"{0}\" +gb.missing = faltando! +gb.missingPermission = o repositório para esta permissão está faltando! +gb.mutable = mutável +gb.specified = específico +gb.effective = efetivo +gb.organizationalUnit = unidade organizacional +gb.organization = organização +gb.locality = localidade +gb.stateProvince = estado ou província +gb.countryCode = código do país +gb.properties = propriedades +gb.issued = emitido +gb.expires = expira +gb.expired = expirado +gb.expiring = expirando +gb.revoked = revogado +gb.serialNumber = número serial +gb.certificates = certificados +gb.newCertificate = novo certificado +gb.revokeCertificate = revogar certificado +gb.sendEmail = enviar email +gb.passwordHint = dica de password +gb.ok = ok +gb.invalidExpirationDate = data de expiração inválida! +gb.passwordHintRequired = dica de password requerida! +gb.viewCertificate = visualizar certificado +gb.subject = assunto +gb.issuer = emissor +gb.validFrom = válido a partir de +gb.validUntil = válido até +gb.publicKey = chave pública +gb.signatureAlgorithm = algoritmo de assinatura +gb.sha1FingerPrint = digital SHA-1 +gb.md5FingerPrint = digital MD5 +gb.reason = razão +gb.revokeCertificateReason = Por selecione a razão da revogação do certificado +gb.unspecified = não específico +gb.keyCompromise = comprometimento de chave +gb.caCompromise = compromisso CA +gb.affiliationChanged = afiliação foi alterada +gb.superseded = substituídas +gb.cessationOfOperation = cessação de funcionamento +gb.privilegeWithdrawn = privilégio retirado +gb.time.inMinutes = em {0} minutos +gb.time.inHours = em {0} horas +gb.time.inDays = em {0} dias +gb.hostname = hostname +gb.hostnameRequired = Por favor insira um hostname +gb.newSSLCertificate = novo servidor de certificado SSL +gb.newCertificateDefaults = novos padrões de certificação +gb.duration = duração +gb.certificateRevoked = Certificado {0, número, 0} foi revogado +gb.clientCertificateGenerated = Novo certificado cliente para {0} foi gerado com sucesso +gb.sslCertificateGenerated = Novo servidor de certificado SSL gerado com sucesso para {0} +gb.newClientCertificateMessage = OBSERVAÇÃO:\nO 'password' não é o password do usuário mas sim o password usado para proteger a keystore. Este password não será salvo então você também inserir uma dica que será incluída nas instruções de LEIA-ME do usuário. +gb.certificate = certificado +gb.emailCertificateBundle = pacote certificado de cliente de email +gb.pleaseGenerateClientCertificate = Por favor gere um certificado cliente para {0} +gb.clientCertificateBundleSent = Pacote de certificado de cliente para {0} enviada +gb.enterKeystorePassword = Por favor insira uma chave para keystore do Gitblit +gb.warning = warning +gb.jceWarning = Seu Java Runtime Environment não tem os arquivos \"JCE Unlimited Strength Jurisdiction Policy\".\nIsto irá limitar o tamanho dos passwords que você usará para encriptar suas keystores para 7 caracteres.\nEstes arquivos de políticas são um download opcional da Oracle.\n\nVocê gostaria de continuar e gerar os certificados de infraestrutura de qualquer forma?\n\nRespondendo "Não" irá redirecionar o seu browser para a página de downloads da Oracle, de onde você poderá fazer download desses arquivos. +gb.maxActivityCommits = limitar exibição de commits +gb.maxActivityCommitsDescription = quantidade máxima de commits para contribuir para a página de atividade +gb.noMaximum = ilimitado +gb.attributes = atributos +gb.serveCertificate = servir https com este certificado +gb.sslCertificateGeneratedRestart = Novo certificado SSL de servidor gerado com sucesso para {0}.\nVocê deve reiniciar o Gitblit para usar o novo certificado.\n\nSe você estiver executando com o parâmetro '--alias', você precisará alterá-lo para ''--alias {0}''. +gb.validity = validade +gb.siteName = nome do site +gb.siteNameDescription = breve mas um nome descritivo para seu servidor \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_br.html b/src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_br.html new file mode 100644 index 00000000..351ef879 --- /dev/null +++ b/src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_br.html @@ -0,0 +1,53 @@ + + + + + + +

Repositório Vazio

+

+
+
+
+ [repository] é um repositório vazio e não pode ser visualizado pelo Gitblit. +

+ Por favor faça o push de alguns commits para +

+
+ Depois de ter feito push você poderá atualizar esta página para visualizar seu repositório. +
+
+
+ +

Sintaxe dos comandos do Git

+ Se você ainda não tem um repositório local do Git, então você deve primeiro clonar este repositório, fazer commit de alguns arquivos e então fazer push desses commits para o Gitblit. +

+

+		

+ Se você já tem um repositório Git local com alguns commits, então você deve adicionar este repositório como uma referência remota e então fazer push. +

+

+		

+

Aprenda Git

+ Se você estiver com dúvidas sobre como ultilizar essas informações, uma sugestão seria dar uma olhada no livro Git Community Book ou Pro Git para entender melhor como usar o Git. +

+

Alguns clients do Git que são Open Source

+
    +
  • Git - o Git oficial através de linhas de comando
  • +
  • TortoiseGit - Faz integração do Explorer do Windows com o Git (por isso requer o Git Oficial)
  • +
  • Eclipse/EGit - Git para a IDE Eclipse (baseada no JGit, como o Gitblit)
  • +
  • Git Extensions - Interface (em C#) para o Git cuja a característica é a integração com o Windows Explorer e o Visual Studio
  • +
  • GitX (L) - um Cliente do Git para Mac OS X
  • +
+

+

Clients do Git proprietários ou com Código Fechado

+
    +
  • SmartGit - Aplicação Client (em Java) para Git, Mercurial, e SVN (por isso requer o Git Oficial)
  • +
  • SourceTree - Client gratuito para o Mac que suporta Git, Mercurial e SVN
  • +
+ + + \ No newline at end of file -- cgit v1.2.3 From e56a821d5ec9a2606f44f72dd24b7bf2f00e714d Mon Sep 17 00:00:00 2001 From: Mrbytes Date: Wed, 19 Dec 2012 08:07:06 -0500 Subject: Spanish translation tweaks --- src/com/gitblit/wicket/GitBlitWebApp_es.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/GitBlitWebApp_es.properties b/src/com/gitblit/wicket/GitBlitWebApp_es.properties index e819503e..64c9ca13 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp_es.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp_es.properties @@ -428,9 +428,9 @@ gb.certificate = Certificado gb.emailCertificateBundle = Correo del cliente para el paquete del certificado gb.pleaseGenerateClientCertificate = Por favor, genera un certificado de cliente para {0} gb.clientCertificateBundleSent = Paquete de certificado de cliente {0} enviado -gb.enterKeystorePassword = Por favor, introduzca la contrase\u00F1a del almacén de claves de Gitblit +gb.enterKeystorePassword = Por favor, introduzca la contrase\u00F1a del almac\u00E9n de claves de Gitblit gb.warning = Advertencia -gb.jceWarning = Tu entorno de trabajo JAVA no contiene los archivos \"JCE Unlimited Strength Jurisdiction Policy\".\nEsto limita la longitud de la contrase\u00F1a que puedes usuar para cifrar el almac\u00E9n de claves a 7 caracteres.\nEstos archivos opcionales puedes de descargarlos desde Oracle.\n\n\u00BFQuieres continuar y generar la infraestructura de certificados de todos modos?\n\nSi respondes No tu navegador te dirigir\u00E1 a la p\u00E1gina de descarga de Oracle para que pueda descargar dichos archivos. +gb.jceWarning = Tu entorno de trabajo JAVA no contiene los archivos \"JCE Unlimited Strength Jurisdiction Policy\".\nEsto limita la longitud de la contrase\u00F1a que puedes usuar para cifrar el almac\u00E9n de claves a 7 caracteres.\nEstos archivos opcionales puedes descargarlos desde Oracle.\n\n\u00BFQuieres continuar y generar la infraestructura de certificados de todos modos?\n\nSi respondes No tu navegador te dirigir\u00E1 a la p\u00E1gina de descarga de Oracle para que pueda descargar dichos archivos. gb.maxActivityCommits = Actividad m\u00E1xima de consignas gb.maxActivityCommitsDescription = N\u00FAmero m\u00E1ximo de consignas a incluir en la p\u00E1gina de actividad gb.noMaximum = Sin m\u00E1ximos -- cgit v1.2.3 From 2d85d43d61518d26be33a0e7759a3d4f4a627452 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 21 Dec 2012 16:47:46 -0500 Subject: Fixed regression in repository cache wrt repo names with case (issue-172) --- src/com/gitblit/GitBlit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 46b0d406..782da63e 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1339,7 +1339,7 @@ public class GitBlit implements ServletContextListener { } // check for updates - Repository r = getRepository(repositoryName); + Repository r = getRepository(model.name); if (r == null) { // repository is missing removeFromCachedRepositoryList(repositoryName); -- cgit v1.2.3 From 6f8ad9b1068e57062a5f79da83c10ee715d412ef Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 21 Dec 2012 16:49:04 -0500 Subject: Fixed cache bug wrt create/edit repository with existing repo name (issue-172) --- src/com/gitblit/GitBlit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 782da63e..54ca6d20 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1937,7 +1937,7 @@ public class GitBlit implements ServletContextListener { if (!repository.name.toLowerCase().endsWith(org.eclipse.jgit.lib.Constants.DOT_GIT_EXT)) { repository.name += org.eclipse.jgit.lib.Constants.DOT_GIT_EXT; } - if (new File(repositoriesFolder, repository.name).exists()) { + if (hasRepository(repository.name)) { throw new GitBlitException(MessageFormat.format( "Can not create repository ''{0}'' because it already exists.", repository.name)); -- cgit v1.2.3 From 7b0f3069ec3011650c9628b6e17c01d5e845ca46 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 21 Dec 2012 17:14:24 -0500 Subject: Added case-sensitive check for repository --- src/com/gitblit/GitBlit.java | 13 ++++++++++++- src/com/gitblit/wicket/pages/RepositoryPage.java | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 54ca6d20..076bb765 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1652,7 +1652,18 @@ public class GitBlit implements ServletContextListener { * @return true if the repository exists */ public boolean hasRepository(String repositoryName) { - if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { + return hasRepository(repositoryName, false); + } + + /** + * Determines if this server has the requested repository. + * + * @param name + * @param caseInsensitive + * @return true if the repository exists + */ + public boolean hasRepository(String repositoryName, boolean caseSensitiveCheck) { + if (!caseSensitiveCheck && settings.getBoolean(Keys.git.cacheRepositoryList, true)) { // if we are caching use the cache to determine availability // otherwise we end up adding a phantom repository to the cache return repositoryListCache.containsKey(repositoryName.toLowerCase()); diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java index 897e2001..aac527d7 100644 --- a/src/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/com/gitblit/wicket/pages/RepositoryPage.java @@ -326,7 +326,7 @@ public abstract class RepositoryPage extends BasePage { RepositoryModel model = GitBlit.self().getRepositoryModel( GitBlitWebSession.get().getUser(), repositoryName); if (model == null) { - if (GitBlit.self().hasRepository(repositoryName)) { + if (GitBlit.self().hasRepository(repositoryName, true)) { // has repository, but unauthorized authenticationError(getString("gb.unauthorizedAccessForRepository") + " " + repositoryName); } else { -- cgit v1.2.3 From b2d0287b3f424c7f9ada44834365bf909db62a50 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 21 Dec 2012 17:35:18 -0500 Subject: Fixed regression in isFrozen (issue 181) --- docs/04_releases.mkd | 35 +++++++++++++------------ src/com/gitblit/GitServlet.java | 4 +++ tests/com/gitblit/tests/GitServletTest.java | 40 +++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 17 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index 485215b6..e685ffc8 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -12,6 +12,7 @@ The permissions model has changed in this release. #### fixes +- Fixed regression in *isFrozen* (issue 181) - Author metrics can be broken by newlines in email addresses from converted repositories (issue 176) - Set subjectAlternativeName on generated SSL cert if CN is an ip address (issue 170) - Fixed incorrect links on history page for files not in the current/active commit (issue 166) @@ -28,7 +29,7 @@ The permissions model has changed in this release. #### additions -- Implemented discrete repository permissions (issue 36) +- Implemented discrete repository permissions (issue 36) - V (view in web ui, RSS feeds, download zip) - R (clone) - RW (clone and push) @@ -36,34 +37,34 @@ The permissions model has changed in this release. - RWD (clone and push with ref creation, deletion) - RW+ (clone and push with ref creation, deletion, rewind) While not as sophisticated as Gitolite, this does give finer access controls. These permissions fit in cleanly with the existing users.conf and users.properties files. In Gitblit <= 1.1.0, all your existing user accounts have RW+ access. If you are upgrading to 1.2.0, the RW+ access is *preserved* and you will have to lower/adjust accordingly. -- Implemented *case-insensitive* regex repository permission matching (issue 36) +- Implemented *case-insensitive* regex repository permission matching (issue 36) This allows you to specify a permission like `RW:mygroup/.*` to grant push privileges to all repositories within the *mygroup* project/folder. - Added DELETE, CREATE, and NON-FAST-FORWARD ref change logging - Added support for personal repositories. Personal repositories can be created by accounts with the *create* permission and are stored in *git.repositoriesFolder/~username*. Each user with personal repositories will have a user page, something like the GitHub profile page. Personal repositories have all the same features as common repositories, except personal repositories can be renamed by their owner. -- Added support for server-side forking of a repository to a personal repository (issue 137) -In order to fork a repository, the user account must have the *fork* permission **and** the repository must *allow forks*. The clone inherits the access list of its origin. i.e. if Team A has clone access to the origin repository, then by default Team A also has clone access to the fork. This is to facilitate collaboration. The fork owner may change access to the fork and add/remove users/teams, etc as required however it should be noted that all personal forks will be enumerated in the fork network regardless of access view restrictions. If you really must have an invisible fork, the clone it locally, create a new repository for your invisible fork, and push it back to Gitblit. +- Added support for server-side forking of a repository to a personal repository (issue 137) +In order to fork a repository, the user account must have the *fork* permission **and** the repository must *allow forks*. The clone inherits the access list of its origin. i.e. if Team A has clone access to the origin repository, then by default Team A also has clone access to the fork. This is to facilitate collaboration. The fork owner may change access to the fork and add/remove users/teams, etc as required however it should be noted that all personal forks will be enumerated in the fork network regardless of access view restrictions. If you really must have an invisible fork, the clone it locally, create a new repository for your invisible fork, and push it back to Gitblit. **New:** *web.allowForking=true* -- Added optional *create-on-push* support +- Added optional *create-on-push* support **New:** *git.allowCreateOnPush=true* -- Added **experimental** JGit-based garbage collection service. This service is disabled by default. - **New:** *git.allowGarbageCollection=false* - **New:** *git.garbageCollectionHour = 0* - **New:** *git.defaultGarbageCollectionThreshold = 500k* +- Added **experimental** JGit-based garbage collection service. This service is disabled by default. + **New:** *git.allowGarbageCollection=false* + **New:** *git.garbageCollectionHour = 0* + **New:** *git.defaultGarbageCollectionThreshold = 500k* **New:** *git.defaultGarbageCollectionPeriod = 7 days* -- Added support for X509 client certificate authentication (github/kevinanderson1). (issue 106) -You can require all git servlet access be authenticated by a client certificate. You may also specify the OID fingerprint to use for mapping a certificate to a username. It should be noted that the user account MUST already exist in Gitblit for this authentication mechanism to work; this mechanism can not be used to automatically create user accounts from a certificate. - **New:** *git.requireClientCertificates = false* - **New:** *git.enforceCertificateValidity = true* +- Added support for X509 client certificate authentication (github/kevinanderson1). (issue 106) +You can require all git servlet access be authenticated by a client certificate. You may also specify the OID fingerprint to use for mapping a certificate to a username. It should be noted that the user account MUST already exist in Gitblit for this authentication mechanism to work; this mechanism can not be used to automatically create user accounts from a certificate. + **New:** *git.requireClientCertificates = false* + **New:** *git.enforceCertificateValidity = true* **New:** *git.certificateUsernameOIDs = CN* - Revised clean install certificate generation to create a Gitblit GO Certificate Authority certificate; an SSL certificate signed by the CA certificate; and to create distinct server key and server trust stores. The store files have been renamed! - Added support for Gitblit GO to require usage of client certificates to access the entire server. -This is extreme and should be considered carefully since it affects every https access. The default is to **want** client certificates. Setting this value to *true* changes that to **need** client certificates. +This is extreme and should be considered carefully since it affects every https access. The default is to **want** client certificates. Setting this value to *true* changes that to **need** client certificates. **New:** *server.requireClientCertificates = false* - Added **Gitblit Certificate Authority**, an x509 PKI management tool for Gitblit GO to encourage use of x509 client certificate authentication. -- Added setting to control length of shortened commit ids +- Added setting to control length of shortened commit ids **New:** *web.shortCommitIdLength=8* -- Added alternate compressed download formats: tar.gz, tar.xz, tar.bzip2 (issue 174) +- Added alternate compressed download formats: tar.gz, tar.xz, tar.bzip2 (issue 174) **New:** *web.compressedDownloads = zip gz* - Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*. - Added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135) @@ -88,7 +89,7 @@ This is extreme and should be considered carefully since it affects every https - Expose ReceivePack to Groovy push hooks (issue 125) - Redirect to summary page when refreshing the empty repository page on a repository that is not empty (issue 129) - Emit a warning in the log file if running on a Tomcat-based servlet container which is unfriendly to %2F forward-slash url encoding AND Gitblit is configured to mount parameters with %2F forward-slash url encoding (Github/jpyeron, issue 126) -- LDAP admin attribute setting is now consistent with LDAP teams setting and admin teams list. +- LDAP admin attribute setting is now consistent with LDAP teams setting and admin teams list. If *realm.ldap.maintainTeams==true* **AND** *realm.ldap.admins* is not empty, then User.canAdmin() is controlled by LDAP administrative team membership. Otherwise, User.canAdmin() is controlled by Gitblit. - Support servlet container authentication for existing UserModels (issue 68) diff --git a/src/com/gitblit/GitServlet.java b/src/com/gitblit/GitServlet.java index 42d88c91..94a51be0 100644 --- a/src/com/gitblit/GitServlet.java +++ b/src/com/gitblit/GitServlet.java @@ -124,6 +124,10 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { rp.setAllowDeletes(user.canDeleteRef(repository)); rp.setAllowNonFastForwards(user.canRewindRef(repository)); + if (repository.isFrozen) { + throw new ServiceNotEnabledException(); + } + return rp; } }); diff --git a/tests/com/gitblit/tests/GitServletTest.java b/tests/com/gitblit/tests/GitServletTest.java index e65c61cb..07771a20 100644 --- a/tests/com/gitblit/tests/GitServletTest.java +++ b/tests/com/gitblit/tests/GitServletTest.java @@ -222,6 +222,46 @@ public class GitServletTest { GitBlitSuite.close(git); } + @Test + public void testPushToFrozenRepo() throws Exception { + CloneCommand clone = Git.cloneRepository(); + clone.setURI(MessageFormat.format("{0}/git/test/jgit.git", url)); + clone.setDirectory(jgitFolder); + clone.setBare(false); + clone.setCloneAllBranches(true); + clone.setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)); + GitBlitSuite.close(clone.call()); + assertTrue(true); + + // freeze repo + RepositoryModel model = GitBlit.self().getRepositoryModel("test/jgit.git"); + model.isFrozen = true; + GitBlit.self().updateRepositoryModel(model.name, model, false); + + Git git = Git.open(jgitFolder); + File file = new File(jgitFolder, "TODO"); + OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file, true), Constants.CHARSET); + BufferedWriter w = new BufferedWriter(os); + w.write("// " + new Date().toString() + "\n"); + w.close(); + git.add().addFilepattern(file.getName()).call(); + git.commit().setMessage("test commit").call(); + + try { + git.push().setPushAll().call(); + assertTrue(false); + } catch (Exception e) { + assertTrue(e.getCause().getMessage().contains("access forbidden")); + } + + // unfreeze repo + model.isFrozen = false; + GitBlit.self().updateRepositoryModel(model.name, model, false); + + git.push().setPushAll().call(); + GitBlitSuite.close(git); + } + @Test public void testPushToNonBareRepository() throws Exception { CloneCommand clone = Git.cloneRepository(); -- cgit v1.2.3 From ed32130b4d355c7925deb4b7c4300d41509770f0 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sat, 22 Dec 2012 00:26:10 -0500 Subject: Update to JGit 2.2.0 --- .classpath | 4 ++-- docs/04_releases.mkd | 2 +- src/com/gitblit/Constants.java | 2 +- src/com/gitblit/build/Build.java | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src/com/gitblit') diff --git a/.classpath b/.classpath index 498678bb..e40223d2 100644 --- a/.classpath +++ b/.classpath @@ -21,9 +21,9 @@ - + - + diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index e685ffc8..fe10f5da 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -96,7 +96,7 @@ If *realm.ldap.maintainTeams==true* **AND** *realm.ldap.admins* is not empty, th #### dependency changes - updated to Jetty 7.6.8 -- updated to JGit 2.1.0.201209190230-r +- updated to JGit 2.2.0.201212191850-r - updated to Groovy 1.8.8 - updated to Wicket 1.4.21 - updated to Lucene 3.6.1 diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index d152651b..76c2b464 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -42,7 +42,7 @@ public class Constants { // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. - public static final String JGIT_VERSION = "JGit 2.1.0 (201209190230-r)"; + public static final String JGIT_VERSION = "JGit 2.2.0 (201212191850-r)"; public static final String ADMIN_ROLE = "#admin"; diff --git a/src/com/gitblit/build/Build.java b/src/com/gitblit/build/Build.java index 19d80e78..336c45de 100644 --- a/src/com/gitblit/build/Build.java +++ b/src/com/gitblit/build/Build.java @@ -675,17 +675,17 @@ public class Build { ""); public static final MavenObject JGIT = new MavenObject( - "JGit", "org/eclipse/jgit", "org.eclipse.jgit", "2.1.0.201209190230-r", + "JGit", "org/eclipse/jgit", "org.eclipse.jgit", "2.2.0.201212191850-r", 1600000, 1565000, 3460000, - "5e7296d21645a479a1054fc96f3ec8469cede137", - "5f492aaeae1beda2a31d1efa182f5d34e76d7b77", + "97d0761b9dd618d1f9f6c16c35c3ddf045ba536c", + "08dcf9546f4d61e1b8a50df5da5513006023b64b", ""); public static final MavenObject JGIT_HTTP = new MavenObject( - "JGit", "org/eclipse/jgit", "org.eclipse.jgit.http.server", "2.1.0.201209190230-r", + "JGit", "org/eclipse/jgit", "org.eclipse.jgit.http.server", "2.2.0.201212191850-r", 68000, 62000, 110000, - "0bd9e5801c246d6f8ad9268d18c45ca9915f9a50", - "210c434c38ddcf2126af250018d5845ea41ff502", + "8ad4fc4fb9529d645249bb46ad7e54d98436cb65", + "3385cf294957d1d34c1270b468853aea347b36ca", ""); public static final MavenObject JSCH = new MavenObject( -- cgit v1.2.3 From b461a45f420471f931029898d97464ecc5039f49 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sat, 22 Dec 2012 00:27:04 -0500 Subject: Do not pass null email address to PersonIdent --- src/com/gitblit/wicket/pages/ForksPage.java | 2 +- src/com/gitblit/wicket/pages/UserPage.java | 2 +- src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/pages/ForksPage.java b/src/com/gitblit/wicket/pages/ForksPage.java index 6155f3ed..cc483878 100644 --- a/src/com/gitblit/wicket/pages/ForksPage.java +++ b/src/com/gitblit/wicket/pages/ForksPage.java @@ -58,7 +58,7 @@ public class ForksPage extends RepositoryPage { if (repository.isPersonalRepository()) { UserModel user = GitBlit.self().getUserModel(repository.projectPath.substring(1)); - PersonIdent ident = new PersonIdent(user.getDisplayName(), user.emailAddress); + PersonIdent ident = new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.getDisplayName() : user.emailAddress); item.add(new GravatarImage("anAvatar", ident, 20)); if (pageRepository.equals(repository)) { // do not link to self diff --git a/src/com/gitblit/wicket/pages/UserPage.java b/src/com/gitblit/wicket/pages/UserPage.java index d3e93c61..f4331dd1 100644 --- a/src/com/gitblit/wicket/pages/UserPage.java +++ b/src/com/gitblit/wicket/pages/UserPage.java @@ -97,7 +97,7 @@ public class UserPage extends RootPage { email.setRenderBodyOnly(true); add(email.setVisible(GitBlit.getBoolean(Keys.web.showEmailAddresses, true) && !StringUtils.isEmpty(user.emailAddress))); - PersonIdent person = new PersonIdent(user.getDisplayName(), user.emailAddress); + PersonIdent person = new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.getDisplayName() : user.emailAddress); add(new GravatarImage("gravatar", person, 210)); UserModel sessionUser = GitBlitWebSession.get().getUser(); diff --git a/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java b/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java index 689ee571..4156cd19 100644 --- a/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java +++ b/src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java @@ -138,10 +138,10 @@ public class RegistrantPermissionsPanel extends BasePanel { } } else if (RegistrantType.USER.equals(entry.registrantType)) { // user - PersonIdent ident = new PersonIdent(entry.registrant, null); + PersonIdent ident = new PersonIdent(entry.registrant, ""); UserModel user = GitBlit.self().getUserModel(entry.registrant); if (user != null) { - ident = new PersonIdent(user.getDisplayName(), user.emailAddress); + ident = new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.getDisplayName() : user.emailAddress); } Fragment userFragment = new Fragment("registrant", "userRegistrant", RegistrantPermissionsPanel.this); -- cgit v1.2.3 From 160ee43394f6c645465b2b1dd80c460766d122bc Mon Sep 17 00:00:00 2001 From: James Moger Date: Mon, 31 Dec 2012 16:12:35 -0500 Subject: Final documentation of 1.2.0 release --- docs/04_releases.mkd | 164 ++++++++++++++++++++--------------------- docs/05_roadmap.mkd | 6 +- src/com/gitblit/Constants.java | 4 +- 3 files changed, 88 insertions(+), 86 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index fe10f5da..f8a89b79 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -37,34 +37,34 @@ The permissions model has changed in this release. - RWD (clone and push with ref creation, deletion) - RW+ (clone and push with ref creation, deletion, rewind) While not as sophisticated as Gitolite, this does give finer access controls. These permissions fit in cleanly with the existing users.conf and users.properties files. In Gitblit <= 1.1.0, all your existing user accounts have RW+ access. If you are upgrading to 1.2.0, the RW+ access is *preserved* and you will have to lower/adjust accordingly. -- Implemented *case-insensitive* regex repository permission matching (issue 36) +- Implemented *case-insensitive* regex repository permission matching (issue 36)
This allows you to specify a permission like `RW:mygroup/.*` to grant push privileges to all repositories within the *mygroup* project/folder. - Added DELETE, CREATE, and NON-FAST-FORWARD ref change logging -- Added support for personal repositories. +- Added support for personal repositories.
Personal repositories can be created by accounts with the *create* permission and are stored in *git.repositoriesFolder/~username*. Each user with personal repositories will have a user page, something like the GitHub profile page. Personal repositories have all the same features as common repositories, except personal repositories can be renamed by their owner. -- Added support for server-side forking of a repository to a personal repository (issue 137) -In order to fork a repository, the user account must have the *fork* permission **and** the repository must *allow forks*. The clone inherits the access list of its origin. i.e. if Team A has clone access to the origin repository, then by default Team A also has clone access to the fork. This is to facilitate collaboration. The fork owner may change access to the fork and add/remove users/teams, etc as required however it should be noted that all personal forks will be enumerated in the fork network regardless of access view restrictions. If you really must have an invisible fork, the clone it locally, create a new repository for your invisible fork, and push it back to Gitblit. +- Added support for server-side forking of a repository to a personal repository (issue 137)
+In order to fork a repository, the user account must have the *fork* permission **and** the repository must *allow forks*. The clone inherits the access list of its origin. i.e. if Team A has clone access to the origin repository, then by default Team A also has clone access to the fork. This is to facilitate collaboration. The fork owner may change access to the fork and add/remove users/teams, etc as required however it should be noted that all personal forks will be enumerated in the fork network regardless of access view restrictions. If you really must have an invisible fork, the clone it locally, create a new repository for your invisible fork, and push it back to Gitblit.
**New:** *web.allowForking=true* -- Added optional *create-on-push* support +- Added optional *create-on-push* support
**New:** *git.allowCreateOnPush=true* -- Added **experimental** JGit-based garbage collection service. This service is disabled by default. - **New:** *git.allowGarbageCollection=false* - **New:** *git.garbageCollectionHour = 0* - **New:** *git.defaultGarbageCollectionThreshold = 500k* +- Added **experimental** JGit-based garbage collection service. This service is disabled by default.
+ **New:** *git.allowGarbageCollection=false*
+ **New:** *git.garbageCollectionHour = 0*
+ **New:** *git.defaultGarbageCollectionThreshold = 500k*
**New:** *git.defaultGarbageCollectionPeriod = 7 days* -- Added support for X509 client certificate authentication (github/kevinanderson1). (issue 106) -You can require all git servlet access be authenticated by a client certificate. You may also specify the OID fingerprint to use for mapping a certificate to a username. It should be noted that the user account MUST already exist in Gitblit for this authentication mechanism to work; this mechanism can not be used to automatically create user accounts from a certificate. - **New:** *git.requireClientCertificates = false* - **New:** *git.enforceCertificateValidity = true* +- Added support for X509 client certificate authentication (github/kevinanderson1). (issue 106)
+You can require all git servlet access be authenticated by a client certificate. You may also specify the OID fingerprint to use for mapping a certificate to a username. It should be noted that the user account MUST already exist in Gitblit for this authentication mechanism to work; this mechanism can not be used to automatically create user accounts from a certificate.
+ **New:** *git.requireClientCertificates = false*
+ **New:** *git.enforceCertificateValidity = true*
**New:** *git.certificateUsernameOIDs = CN* - Revised clean install certificate generation to create a Gitblit GO Certificate Authority certificate; an SSL certificate signed by the CA certificate; and to create distinct server key and server trust stores. The store files have been renamed! -- Added support for Gitblit GO to require usage of client certificates to access the entire server. -This is extreme and should be considered carefully since it affects every https access. The default is to **want** client certificates. Setting this value to *true* changes that to **need** client certificates. +- Added support for Gitblit GO to require usage of client certificates to access the entire server.
+This is extreme and should be considered carefully since it affects every https access. The default is to **want** client certificates. Setting this value to *true* changes that to **need** client certificates.
**New:** *server.requireClientCertificates = false* - Added **Gitblit Certificate Authority**, an x509 PKI management tool for Gitblit GO to encourage use of x509 client certificate authentication. -- Added setting to control length of shortened commit ids +- Added setting to control length of shortened commit ids
**New:** *web.shortCommitIdLength=8* -- Added alternate compressed download formats: tar.gz, tar.xz, tar.bzip2 (issue 174) +- Added alternate compressed download formats: tar.gz, tar.xz, tar.bzip2 (issue 174)
**New:** *web.compressedDownloads = zip gz* - Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*. - Added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135) @@ -138,24 +138,24 @@ If you are updating from an earlier release AND you have indexed branches with t #### additions -- Identified repository list is now cached by default to reduce disk io and to improve performance (issue 103) +- Identified repository list is now cached by default to reduce disk io and to improve performance (issue 103)
**New:** *git.cacheRepositoryList=true* -- Preliminary bare repository submodule support +- Preliminary bare repository submodule support
**New:** *git.submoduleUrlPatterns=* - - *git.submoduleUrlPatterns* is a space-delimited list of regular expressions for extracting a repository name from a submodule url. - For example, `git.submoduleUrlPatterns = .*?://github.com/(.*)` would extract *gitblit/gitblit.git* from *git://github.git/gitblit/gitblit.git* + - *git.submoduleUrlPatterns* is a space-delimited list of regular expressions for extracting a repository name from a submodule url.
+ For example, `git.submoduleUrlPatterns = .*?://github.com/(.*)` would extract *gitblit/gitblit.git* from *git://github.git/gitblit/gitblit.git*
**Note:** You may not need this control to work with submodules, but it is there if you do. - If there are no matches from *git.submoduleUrlPatterns* then the repository name is assumed to be whatever comes after the last `/` character *(e.g. gitblit.git)* - Gitblit will try to locate this repository relative to the current repository *(e.g. myfolder/myrepo.git, myfolder/mysubmodule.git)* and then at the root level *(mysubmodule.git)* if that fails. - Submodule references in a working copy will be properly identified as gitlinks, but Gitblit will not traverse into the working copy submodule repository. -- Added a repository setting to control authorization as AUTHENTICATED or NAMED. (issue 117) +- Added a repository setting to control authorization as AUTHENTICATED or NAMED. (issue 117)
NAMED is the original behavior for authorizing against a list of permitted users or permitted teams. AUTHENTICATED allows restricted access for any authenticated user. This is a looser authorization control. -- Added default authorization control setting (AUTHENTICATED or NAMED) +- Added default authorization control setting (AUTHENTICATED or NAMED)
**New:** *git.defaultAuthorizationControl=NAMED* -- Added setting to control how deep Gitblit will recurse into *git.repositoriesFolder* looking for repositories (issue 103) +- Added setting to control how deep Gitblit will recurse into *git.repositoriesFolder* looking for repositories (issue 103)
**New:** *git.searchRecursionDepth=-1* -- Added setting to specify regex exclusions for repositories (issue 103) +- Added setting to specify regex exclusions for repositories (issue 103)
**New:** *git.searchExclusions=* - Blob page now supports displaying images (issue 6) - Non-image binary files can now be downloaded using the RAW link @@ -185,38 +185,38 @@ AUTHENTICATED allows restricted access for any authenticated user. This is a lo #### changes -- **Updated Lucene index version which will force a rebuild of ALL your Lucene indexes** +- **Updated Lucene index version which will force a rebuild of ALL your Lucene indexes**
Make sure to properly set *web.blobEncodings* before starting Gitblit if you are updating! (issue 97) - Changed default layout for web ui from Fixed-Width layout to Responsive layout (issue 101) -- IUserService interface has changed to better accomodate custom authentication and/or custom authorization +- IUserService interface has changed to better accomodate custom authentication and/or custom authorization
The default `users.conf` now supports persisting display names and email addresses. - Updated Japanese translation (Github/zakki) #### additions -- Added setting to allow specification of a robots.txt file (issue 99) +- Added setting to allow specification of a robots.txt file (issue 99)
**New:** *web.robots.txt =* -- Added setting to control Responsive layout or Fixed-Width layout (issue 101) +- Added setting to control Responsive layout or Fixed-Width layout (issue 101)
Responsive layout is now the default. This layout gracefully scales the web ui from a desktop layout to a mobile layout by hiding page components. It is easy to try, just resize your browser or point your Android/iOS device to the url of your Gitblit install. **New:** *web.useResponsiveLayout = true* -- Added setting to control charsets for blob string decoding. Default encodings are UTF-8, ISO-8859-1, and server's default charset. (issue 97) +- Added setting to control charsets for blob string decoding. Default encodings are UTF-8, ISO-8859-1, and server's default charset. (issue 97)
**New:** *web.blobEncodings = UTF-8 ISO-8859-1* -- Exposed JGit's internal configuration settings in gitblit.properties/web.xml (issue 93) - Review your `gitblit.properties` or `web.xml` for detailed explanations of these settings. - **New:** *git.packedGitWindowSize = 8k* - **New:** *git.packedGitLimit = 10m* - **New:** *git.deltaBaseCacheLimit = 10m* - **New:** *git.packedGitOpenFiles = 128* - **New:** *git.streamFileThreshold = 50m* +- Exposed JGit's internal configuration settings in gitblit.properties/web.xml (issue 93)
+ Review your `gitblit.properties` or `web.xml` for detailed explanations of these settings.
+ **New:** *git.packedGitWindowSize = 8k*
+ **New:** *git.packedGitLimit = 10m*
+ **New:** *git.deltaBaseCacheLimit = 10m*
+ **New:** *git.packedGitOpenFiles = 128*
+ **New:** *git.streamFileThreshold = 50m*
**New:** *git.packedGitMmap = false* -- Added default access restriction. Applies to new repositories and repositories that have not been configured with Gitblit. (issue 88) +- Added default access restriction. Applies to new repositories and repositories that have not been configured with Gitblit. (issue 88)
**New:** *git.defaultAccessRestriction = NONE* - Added Ivy 2.2.0 dependency which enables Groovy Grapes, a mechanism to resolve and retrieve library dependencies from a Maven 2 repository within a Groovy push hook script -- Added setting to control Groovy Grape root folder (location where resolved dependencies are stored) - [Grape](http://groovy.codehaus.org/Grape) allows you to add Maven dependencies to your pre-/post-receive hook script classpath. +- Added setting to control Groovy Grape root folder (location where resolved dependencies are stored)
+ [Grape](http://groovy.codehaus.org/Grape) allows you to add Maven dependencies to your pre-/post-receive hook script classpath.
**New:** *groovy.grapeFolder = groovy/grape* - Added LDAP User Service with many new *realm.ldap* keys (Github/jcrygier) -- Added support for custom repository properties for Groovy hooks (Github/jcrygier) +- Added support for custom repository properties for Groovy hooks (Github/jcrygier)
Custom repository properties complement hook scripts by providing text field prompts in the web ui and the Gitblit Manager for the defined properties. This allows your push hooks to be parameterized. - Added script to facilitate proxy environment setup on Linux (Github/mragab) - Added Polish translation (Lukasz Jader) @@ -281,23 +281,23 @@ Make sure to properly set *web.blobEncodings* before starting Gitblit if you are #### additions -- Added optional Lucene branch indexing (issue 16) - **New:** *web.allowLuceneIndexing = true* +- Added optional Lucene branch indexing (issue 16)
+ **New:** *web.allowLuceneIndexing = true*
**New:** *web.luceneIgnoreExtensions = 7z arc arj bin bmp dll doc docx exe gif gz jar jpg lib lzh odg odf odt pdf ppt png so swf xcf xls xlsx zip* Repository branches may be optionally indexed by Lucene for improved searching. To use this feature you must specify which branches to index within the *Edit Repository* page; _no repositories are automatically indexed_. Gitblit will build or incrementally update enrolled repositories on a 2 minute cycle. (i.e you will have to wait 2-3 minutes after respecifying indexed branches or pushing new commits before Gitblit will build/update the repository's Lucene index.) -If a repository has Lucene-indexed branches the *search* form on the repository pages will redirect to the root-level Lucene search page and only the content of those branches can be searched. +If a repository has Lucene-indexed branches the *search* form on the repository pages will redirect to the root-level Lucene search page and only the content of those branches can be searched.
If the repository does not specify any indexed branches then repository commit-traversal search is used. -**Note:** Initial indexing of an existing repository can be memory-exhaustive. Be sure to provide your Gitblit server adequate heap space to index your repositories (e.g. -Xmx1024M). +**Note:** Initial indexing of an existing repository can be memory-exhaustive. Be sure to provide your Gitblit server adequate heap space to index your repositories (e.g. -Xmx1024M).
See the [setup](setup.html) page for additional details. -- Allow specifying timezone to use for Gitblit which is independent of both the JVM and the system timezone (issue 54) +- Allow specifying timezone to use for Gitblit which is independent of both the JVM and the system timezone (issue 54)
**New:** *web.timezone =* -- Added a built-in AJP connector for integrating Gitblit GO into an Apache mod_proxy setup (issue 59) - **New:** *server.ajpPort = 0* +- Added a built-in AJP connector for integrating Gitblit GO into an Apache mod_proxy setup (issue 59)
+ **New:** *server.ajpPort = 0*
**New:** *server.ajpBindInterface = localhost* -- On the Repositories page show a bang *!* character in the color swatch of a repository with a working copy (issue 49) +- On the Repositories page show a bang *!* character in the color swatch of a repository with a working copy (issue 49)
Push requests to these repositories will be rejected. - On all non-bare Repository pages show *WORKING COPY* in the upper right corner (issue 49) -- New setting to prevent display/serving non-bare repositories +- New setting to prevent display/serving non-bare repositories
**New:** *git.onlyAccessBareRepositories = false* - Added *protect-refs.groovy* (Github/plm) - Allow setting default branch (relinking HEAD) to a branch or a tag (Github/plm) @@ -351,31 +351,31 @@ Push requests to these repositories will be rejected. #### additions -- Platform-independent, Groovy push hook script mechanism. -Hook scripts can be set per-repository, per-team, or globally for all repositories. - **New:** *groovy.scriptsFolder = groovy* - **New:** *groovy.preReceiveScripts =* +- Platform-independent, Groovy push hook script mechanism.
+Hook scripts can be set per-repository, per-team, or globally for all repositories.
+ **New:** *groovy.scriptsFolder = groovy*
+ **New:** *groovy.preReceiveScripts =*
**New:** *groovy.postReceiveScripts =* -- *sendmail.groovy* for optional email notifications on push. +- *sendmail.groovy* for optional email notifications on push.
You must properly configure your SMTP server settings in `gitblit.properties` or `web.xml` to use *sendmail.groovy*. -- New global key for mailing lists. This is used in conjunction with the *sendmail.groovy* hook script. All repositories that use the *sendmail.groovy* script will include these addresses in the notification process. Please see the Setup page for more details about configuring sendmail. +- New global key for mailing lists. This is used in conjunction with the *sendmail.groovy* hook script. All repositories that use the *sendmail.groovy* script will include these addresses in the notification process. Please see the Setup page for more details about configuring sendmail.
**New:** *mail.mailingLists =* - *com.gitblit.GitblitUserService*. This is a wrapper object for the built-in user service implementations. For those wanting to only implement custom authentication it is recommended to subclass GitblitUserService and override the appropriate methods. Going forward, this will help insulate custom authentication from new IUserService API and/or changes in model classes. -- New default user service implementation: *com.gitblit.ConfigUserService* (`users.conf`) +- New default user service implementation: *com.gitblit.ConfigUserService* (`users.conf`)
This user service implementation allows for serialization and deserialization of more sophisticated Gitblit User objects without requiring the encoding trickery now present in FileUserService (users.properties). This will open the door for more advanced Gitblit features. -For those upgrading from an earlier Gitblit version, a `users.conf` file will automatically be created for you from your existing `users.properties` file on your first launch of Gitblit however you will have to manually set *realm.userService=users.conf* to switch to the new user service. -The original `users.properties` file and it's corresponding implementation are **deprecated**. +For those upgrading from an earlier Gitblit version, a `users.conf` file will automatically be created for you from your existing `users.properties` file on your first launch of Gitblit however you will have to manually set *realm.userService=users.conf* to switch to the new user service.
+The original `users.properties` file and it's corresponding implementation are **deprecated**.
**New:** *realm.userService = users.conf* - Teams for specifying user-repository access in bulk. Teams may also specify mailing lists addresses and pre- & post- receive hook scripts. -- Gravatar integration +- Gravatar integration
**New:** *web.allowGravatar = true* -- Activity page for aggregated repository activity. This is a timeline of commit activity over the last N days for one or more repositories. - **New:** *web.activityDuration = 14* - **New:** *web.timeFormat = HH:mm* +- Activity page for aggregated repository activity. This is a timeline of commit activity over the last N days for one or more repositories.
+ **New:** *web.activityDuration = 14*
+ **New:** *web.timeFormat = HH:mm*
**New:** *web.datestampLongFormat = EEEE, MMMM d, yyyy* -- *Filters* menu for the Repositories page and Activity page. You can filter by federation set, team, and simple custom regular expressions. Custom expressions can be stored in `gitblit.properties` or `web.xml` or directly defined in your url (issue 27) +- *Filters* menu for the Repositories page and Activity page. You can filter by federation set, team, and simple custom regular expressions. Custom expressions can be stored in `gitblit.properties` or `web.xml` or directly defined in your url (issue 27)
**New:** *web.customFilters=* -- Flash-based 1-step *copy to clipboard* of the primary repository url based on Clippy +- Flash-based 1-step *copy to clipboard* of the primary repository url based on Clippy
**New:** *web.allowFlashCopyToClipboard = true* - JavaScript-based 3-step (click, ctrl+c, enter) *copy to clipboard* of the primary repository url in the event that you do not want to use Flash on your installation - Empty repositories now link to an *empty repository* page which gives some direction to the user for the next step in using Gitblit. This page displays the primary push/clone url of the repository and gives sample syntax for the git command-line client. (issue 31) @@ -385,7 +385,7 @@ The original `users.properties` file and it's corresponding implementation are * #### changes - Dropped display of trailing .git from repository names -- Gitblit GO is now monolithic like the WAR build. (issue 30) +- Gitblit GO is now monolithic like the WAR build. (issue 30)
This change helps adoption of GO in environments without an internet connection or with a restricted connection. - Unit testing framework has been migrated to JUnit4 syntax and the test suite has been redesigned to run all unit tests, including rpc, federation, and git push/clone tests @@ -405,25 +405,25 @@ This change helps adoption of GO in environments without an internet connection **0.7.0**   *released 2011-11-11* - **security**: fixed security hole when cloning clone-restricted repository with TortoiseGit (issue 28) -- improved: updated ui with Twitter's Bootstrap CSS toolkit +- improved: updated ui with Twitter's Bootstrap CSS toolkit
**New:** *web.loginMessage = gitblit* - improved: repositories list performance by caching repository sizes (issue 27) - improved: summary page performance by caching metric calculations (issue 25) -- added: authenticated JSON RPC mechanism - **New:** *web.enableRpcServlet = true* - **New:** *web.enableRpcManagement = false* +- added: authenticated JSON RPC mechanism
+ **New:** *web.enableRpcServlet = true*
+ **New:** *web.enableRpcManagement = false*
**New:** *web.enableRpcAdministration = false* - added: Gitblit API RSS/JSON RPC library - added: Gitblit Manager (Java/Swing Application) for remote administration of a Gitblit server. - added: per-repository setting to skip size calculation (faster repositories page loading) - added: per-repository setting to skip summary metrics calculation (faster summary page loading) - added: IUserService.setup(IStoredSettings) for custom user service implementations -- added: setting to control Gitblit GO context path for proxy setups *(Github/trygvis)* +- added: setting to control Gitblit GO context path for proxy setups *(Github/trygvis)*
**New:** *server.contextPath = /* - added: *combined-md5* password storage option which stores the hash of username+password as the password *(Github/alyandon)* - added: repository owners are automatically granted access for git, feeds, and zip downloads without explicitly selecting them *(Github/dadalar)* - added: RSS feeds now include regex substitutions on commit messages for bug trackers, etc -- fixed: federation protocol timestamps. dates are now serialized to the [iso8601](http://en.wikipedia.org/wiki/ISO_8601) standard. +- fixed: federation protocol timestamps. dates are now serialized to the [iso8601](http://en.wikipedia.org/wiki/ISO_8601) standard.
**This breaks 0.6.0 federation clients/servers.** - fixed: collision on rename for repositories and users - fixed: Gitblit can now browse the Linux kernel repository (issue 25) @@ -440,14 +440,14 @@ This change helps adoption of GO in environments without an internet connection **0.6.0**   *released 2011-09-27* -- added: federation feature to allow gitblit instances (or gitblit federation clients) to pull repositories and, optionally, settings and accounts from other gitblit instances. This is something like [svn-sync](http://svnbook.red-bean.com/en/1.5/svn.ref.svnsync.html) for gitblit. - **New:** *federation.name =* - **New:** *federation.passphrase =* - **New:** *federation.allowProposals = false* - **New:** *federation.proposalsFolder = proposals* - **New:** *federation.defaultFrequency = 60 mins* - **New:** *federation.sets =* - **New:** *mail.* settings for sending emails +- added: federation feature to allow gitblit instances (or gitblit federation clients) to pull repositories and, optionally, settings and accounts from other gitblit instances. This is something like [svn-sync](http://svnbook.red-bean.com/en/1.5/svn.ref.svnsync.html) for gitblit.
+ **New:** *federation.name =*
+ **New:** *federation.passphrase =*
+ **New:** *federation.allowProposals = false*
+ **New:** *federation.proposalsFolder = proposals*
+ **New:** *federation.defaultFrequency = 60 mins*
+ **New:** *federation.sets =*
+ **New:** *mail.* settings for sending emails
**New:** user role *#notfederated* to prevent a user account from being pulled by a federated Gitblit instance - added: google-gson dependency - added: javamail dependency @@ -468,9 +468,9 @@ This change helps adoption of GO in environments without an internet connection - fixed: users can now change their passwords (issue 1) - fixed: always show root repository group first, i.e. don't sort root group with other groups - fixed: tone-down repository group header color -- added: optionally display repository on-disk size on repositories page +- added: optionally display repository on-disk size on repositories page
**New:** *web.showRepositorySizes = true* -- added: forward-slashes ('/', %2F) can be encoded using a custom character to workaround some servlet container default security measures for proxy servers +- added: forward-slashes ('/', %2F) can be encoded using a custom character to workaround some servlet container default security measures for proxy servers
**New:** *web.forwardSlashCharacter = /* - updated: MarkdownPapers 1.1.0 - updated: Jetty 7.4.3 diff --git a/docs/05_roadmap.mkd b/docs/05_roadmap.mkd index 14ad8c19..4ac9b478 100644 --- a/docs/05_roadmap.mkd +++ b/docs/05_roadmap.mkd @@ -21,9 +21,11 @@ This list is volatile. ### IDEAS +* Gitblit: Pull requests +* Gitblit: Watch/Star like github with personalized activity feed +* Gitblit: Push database or orphan branch * Gitblit: Re-use the EGit branch visualization table cell renderer as some sort of servlet * Gitblit: diff should highlight inserted/removed fragment compared to original line -* Gitblit: implement branch permission controls as Groovy pre-receive script. -*Maintain permissions text file similar to a gitolite configuration file or svn authz file.* +* Gitblit: respect Gerrit branch permissions * Gitblit: Consider creating more Git model objects and exposing them via the JSON RPC interface to allow inspection/retrieval of Git commits, Git trees, etc from Gitblit. * Gitblit: Blame coloring by author (issue 2) diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index 76c2b464..d48fcb1c 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -34,11 +34,11 @@ public class Constants { // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. - public static final String VERSION = "1.2.0-SNAPSHOT"; + public static final String VERSION = "1.2.0"; // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. - public static final String VERSION_DATE = "PENDING"; + public static final String VERSION_DATE = "2012-12-31"; // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. -- cgit v1.2.3 From d7eb67bba682c4c59fb2480d9e1830c8477ce71e Mon Sep 17 00:00:00 2001 From: James Moger Date: Mon, 31 Dec 2012 16:13:51 -0500 Subject: Reset build identifiers for the next release --- src/com/gitblit/Constants.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index d48fcb1c..f2067f69 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -34,11 +34,11 @@ public class Constants { // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. - public static final String VERSION = "1.2.0"; + public static final String VERSION = "1.2.1-SNAPSHOT"; // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. - public static final String VERSION_DATE = "2012-12-31"; + public static final String VERSION_DATE = "PENDING"; // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. -- cgit v1.2.3 From 46bdf9829d62a296c66c8a15969308838ea8cbe9 Mon Sep 17 00:00:00 2001 From: James Moger Date: Wed, 2 Jan 2013 16:19:06 -0500 Subject: Fixed NPE with web.allowForking && !git.cacheRepositoryList (issue-182) --- docs/04_releases.mkd | 18 ++++++++++++------ src/com/gitblit/GitBlit.java | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index f8a89b79..a5bdb3e1 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -1,14 +1,22 @@ ## Release History +### Current Release + +**%VERSION%** ([go](http://code.google.com/p/gitblit/downloads/detail?name=%GO%) | [war](http://code.google.com/p/gitblit/downloads/detail?name=%WAR%) | [express](http://code.google.com/p/gitblit/downloads/detail?name=%EXPRESS%) | [fedclient](http://code.google.com/p/gitblit/downloads/detail?name=%FEDCLIENT%) | [manager](http://code.google.com/p/gitblit/downloads/detail?name=%MANAGER%) | [api](http://code.google.com/p/gitblit/downloads/detail?name=%API%)) based on [%JGIT%][jgit]   *released %BUILDDATE%* + +#### fixes + +- Fixed nullpointer when using web.allowForking = true && git.cacheRepositoryList = false (issue 182) + +### Older Releases +

Update Note

-The permissions model has changed in this release. +The permissions model has changed in the 1.2.0 release.

If you are updating your server, you must also update any Gitblit Manager and Federation Client installs to 1.2.0 as well. The data model used by the RPC mechanism has changed slightly for the new permissions infrastructure.

-### Current Release - -**%VERSION%** ([go](http://code.google.com/p/gitblit/downloads/detail?name=%GO%) | [war](http://code.google.com/p/gitblit/downloads/detail?name=%WAR%) | [express](http://code.google.com/p/gitblit/downloads/detail?name=%EXPRESS%) | [fedclient](http://code.google.com/p/gitblit/downloads/detail?name=%FEDCLIENT%) | [manager](http://code.google.com/p/gitblit/downloads/detail?name=%MANAGER%) | [api](http://code.google.com/p/gitblit/downloads/detail?name=%API%)) based on [%JGIT%][jgit]   *released %BUILDDATE%* +**1.2.0** *released 2012-12-31* #### fixes @@ -107,8 +115,6 @@ If *realm.ldap.maintainTeams==true* **AND** *realm.ldap.admins* is not empty, th - added XZ for Java 1.0
-### Older Releases -

Update Note

If you are updating from an earlier release AND you have indexed branches with the Lucene indexing feature, you need to be aware that this release will completely re-index your repositories. Please be sure to provide ample heap resources as appropriate for your installation. diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 076bb765..b497737f 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1739,7 +1739,7 @@ public class GitBlit implements ServletContextListener { ProjectModel project = getProjectModel(userProject); for (String repository : project.repositories) { if (repository.startsWith(userProject)) { - RepositoryModel model = repositoryListCache.get(repository); + RepositoryModel model = getRepositoryModel(repository); if (model.originRepository.equalsIgnoreCase(origin)) { // user has a fork return model.name; -- cgit v1.2.3 From 9910e56056d2f2639d2783257ae9f1bf46aaee58 Mon Sep 17 00:00:00 2001 From: James Moger Date: Wed, 2 Jan 2013 16:35:16 -0500 Subject: Fixed fork origin determination if !git.cacheRepositoryList (issue-182) --- src/com/gitblit/GitBlit.java | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index b497737f..3dcd5a09 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1760,20 +1760,39 @@ public class GitBlit implements ServletContextListener { */ public ForkModel getForkNetwork(String repository) { if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { - // find the root + // find the root, cached RepositoryModel model = repositoryListCache.get(repository.toLowerCase()); while (model.originRepository != null) { model = repositoryListCache.get(model.originRepository); } + ForkModel root = getForkModelFromCache(model.name); + return root; + } else { + // find the root, non-cached + RepositoryModel model = getRepositoryModel(repository.toLowerCase()); + while (model.originRepository != null) { + model = getRepositoryModel(model.originRepository); + } ForkModel root = getForkModel(model.name); return root; } - return null; } - private ForkModel getForkModel(String repository) { + private ForkModel getForkModelFromCache(String repository) { RepositoryModel model = repositoryListCache.get(repository.toLowerCase()); ForkModel fork = new ForkModel(model); + if (!ArrayUtils.isEmpty(model.forks)) { + for (String aFork : model.forks) { + ForkModel fm = getForkModelFromCache(aFork); + fork.forks.add(fm); + } + } + return fork; + } + + private ForkModel getForkModel(String repository) { + RepositoryModel model = getRepositoryModel(repository.toLowerCase()); + ForkModel fork = new ForkModel(model); if (!ArrayUtils.isEmpty(model.forks)) { for (String aFork : model.forks) { ForkModel fm = getForkModel(aFork); -- cgit v1.2.3 From 15dcc074a7db2b3dae95c37e7e8625139f0e37da Mon Sep 17 00:00:00 2001 From: Inaiat Henrique Date: Thu, 3 Jan 2013 14:56:20 -0500 Subject: Fixed loading of Brazilian Portuguese translation from *nix server --- docs/04_releases.mkd | 1 + .../gitblit/wicket/GitBlitWebApp_pt_BR.properties | 442 +++++++++++++++++++++ .../gitblit/wicket/GitBlitWebApp_pt_br.properties | 442 --------------------- .../wicket/pages/EmptyRepositoryPage_pt_BR.html | 53 +++ .../wicket/pages/EmptyRepositoryPage_pt_br.html | 53 --- 5 files changed, 496 insertions(+), 495 deletions(-) create mode 100644 src/com/gitblit/wicket/GitBlitWebApp_pt_BR.properties delete mode 100644 src/com/gitblit/wicket/GitBlitWebApp_pt_br.properties create mode 100644 src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html delete mode 100644 src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_br.html (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index a5bdb3e1..faec56a0 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -7,6 +7,7 @@ #### fixes - Fixed nullpointer when using web.allowForking = true && git.cacheRepositoryList = false (issue 182) +- Fixed loading of Brazilian Portuguese translation from *nix server (github/inaiat) ### Older Releases diff --git a/src/com/gitblit/wicket/GitBlitWebApp_pt_BR.properties b/src/com/gitblit/wicket/GitBlitWebApp_pt_BR.properties new file mode 100644 index 00000000..469d2055 --- /dev/null +++ b/src/com/gitblit/wicket/GitBlitWebApp_pt_BR.properties @@ -0,0 +1,442 @@ +gb.repository = repositório +gb.owner = proprietário +gb.description = descrição +gb.lastChange = última alteração +gb.refs = refs +gb.tag = tag +gb.tags = tags +gb.author = autor +gb.committer = committer +gb.commit = commit +gb.tree = árvore +gb.parent = parent +gb.url = URL +gb.history = histórico +gb.raw = raw +gb.object = object +gb.ticketId = ticket id +gb.ticketAssigned = atribuído +gb.ticketOpenDate = data da abertura +gb.ticketState = estado +gb.ticketComments = comentários +gb.view = visualizar +gb.local = local +gb.remote = remote +gb.branches = branches +gb.patch = patch +gb.diff = diff +gb.log = log +gb.moreLogs = mais commits... +gb.allTags = todas as tags... +gb.allBranches = todos os branches... +gb.summary = resumo +gb.ticket = ticket +gb.newRepository = novo repositório +gb.newUser = novo usuário +gb.commitdiff = commitdiff +gb.tickets = tickets +gb.pageFirst = primeira +gb.pagePrevious anterior +gb.pageNext = próxima +gb.head = HEAD +gb.blame = blame +gb.login = login +gb.logout = logout +gb.username = username +gb.password = password +gb.tagger = tagger +gb.moreHistory = mais histórico... +gb.difftocurrent = diff para a atual +gb.search = pesquisar +gb.searchForAuthor = Procurar por commits cujo autor é +gb.searchForCommitter = Procurar por commits commitados por é +gb.addition = adicionados +gb.modification = modificados +gb.deletion = apagados +gb.rename = renomear +gb.metrics = métricas +gb.stats = estatísticas +gb.markdown = markdown +gb.changedFiles = arquivos alterados +gb.filesAdded = {0} arquivos adicionados +gb.filesModified = {0} arquivos modificados +gb.filesDeleted = {0} arquivos deletados +gb.filesCopied = {0} arquivos copiados +gb.filesRenamed = {0} arquivos renomeados +gb.missingUsername = Username desconhecido +gb.edit = editar +gb.searchTypeTooltip = Selecione o Tipo de Pesquisa +gb.searchTooltip = Pesquisar {0} +gb.delete = deletar +gb.docs = documentos +gb.accessRestriction = restrição de acesso +gb.name = nome +gb.enableTickets = ativar tickets +gb.enableDocs = ativar documentação +gb.save = salvar +gb.showRemoteBranches = mostrar branches remotos +gb.editUsers = editar usuários +gb.confirmPassword = confirmar password +gb.restrictedRepositories = repositórios restritos +gb.canAdmin = pode administrar +gb.notRestricted = visualização anônima, clone, & push +gb.pushRestricted = push autênticado +gb.cloneRestricted = clone & push autênticados +gb.viewRestricted = view, clone, & push autênticados +gb.useTicketsDescription = somente leitura, issues do Ticgit distribuídos +gb.useDocsDescription = enumerar documentação Markdown no repositório +gb.showRemoteBranchesDescription = mostrar branches remotos +gb.canAdminDescription = pode administrar o server Gitblit +gb.permittedUsers = usuários autorizados +gb.isFrozen = congelar +gb.isFrozenDescription = proibir fazer push +gb.zip = zip +gb.showReadme = mostrar readme +gb.showReadmeDescription = mostrar um arquivo \"leia-me\" na página de resumo +gb.nameDescription = usar '/' para agrupar repositórios. e.g. libraries/mycoollib.git +gb.ownerDescription = o proprietário pode editar configurações do repositório +gb.blob = blob +gb.commitActivityTrend = tendência dos commits +gb.commitActivityDOW = commits diários +gb.commitActivityAuthors = principais committers +gb.feed = feed +gb.cancel = cancelar +gb.changePassword = alterar password +gb.isFederated = está federado +gb.federateThis = federar este repositório +gb.federateOrigin = federar o origin +gb.excludeFromFederation = excluir da federação +gb.excludeFromFederationDescription = bloquear instâncias federadas do GitBlit de fazer pull desta conta +gb.tokens = tokens de federação +gb.tokenAllDescription = todos repositórios, usuários & configurações +gb.tokenUnrDescription = todos repositórios & usuários +gb.tokenJurDescription = todos repositórios +gb.federatedRepositoryDefinitions = definições de repositório +gb.federatedUserDefinitions = definições de usuários +gb.federatedSettingDefinitions = definições de configurações +gb.proposals = propostas de federações +gb.received = recebidos +gb.type = tipo +gb.token = token +gb.repositories = repositórios +gb.proposal = propostas +gb.frequency = frequência +gb.folder = pasta +gb.lastPull = último pull +gb.nextPull = próximo pull +gb.inclusions = inclusões +gb.exclusions = excluões +gb.registration = cadastro +gb.registrations = cadastro de federações +gb.sendProposal = enviar proposta +gb.status = status +gb.origin = origin +gb.headRef = default branch (HEAD) +gb.headRefDescription = alterar a ref o qual a HEAD aponta. e.g. refs/heads/master +gb.federationStrategy = estratégia de federação +gb.federationRegistration = cadastro de federações +gb.federationResults = resultados dos pulls de federações +gb.federationSets = ajustes de federações +gb.message = mensagem +gb.myUrlDescription = a url de acesso público para a instância Gitblit +gb.destinationUrl = enviar para +gb.destinationUrlDescription = a url da intância do Gitblit para enviar sua proposta +gb.users = usuários +gb.federation = federação +gb.error = erro +gb.refresh = atualizar +gb.browse = navegar +gb.clone = clonar +gb.filter = filtrar +gb.create = criar +gb.servers = servidores +gb.recent = recente +gb.available = disponível +gb.selected = selecionado +gb.size = tamanho +gb.downloading = downloading +gb.loading = loading +gb.starting = inciando +gb.general = geral +gb.settings = configurações +gb.manage = administrar +gb.lastLogin = último login +gb.skipSizeCalculation = ignorar cálculo do tamanho +gb.skipSizeCalculationDescription = não calcular o tamanho do repositório (reduz o tempo de load da página) +gb.skipSummaryMetrics = ignorar resumo das métricas +gb.skipSummaryMetricsDescription = não calcular métricas na página de resumo +gb.accessLevel = acesso +gb.default = default +gb.setDefault = tornar default +gb.since = desde +gb.status = status +gb.bootDate = data do boot +gb.servletContainer = servlet container +gb.heapMaximum = heap máximo +gb.heapAllocated = alocar heap +gb.heapUsed = usar heap +gb.free = free +gb.version = versão +gb.releaseDate = data de release +gb.date = data +gb.activity = atividade +gb.subscribe = inscrever +gb.branch = branch +gb.maxHits = hits máximos +gb.recentActivity = atividade recente +gb.recentActivityStats = últimos {0} dias / {1} commits por {2} autores +gb.recentActivityNone = últimos {0} dias / nenhum +gb.dailyActivity = atividade diária +gb.activeRepositories = repositórios ativos +gb.activeAuthors = autores ativos +gb.commits = commits +gb.teams = equipes +gb.teamName = nome da equipe +gb.teamMembers = membros +gb.teamMemberships = filiações em equipes +gb.newTeam = nova equipe +gb.permittedTeams = equipes permitidas +gb.emptyRepository = repositório vazio +gb.repositoryUrl = url do repositório +gb.mailingLists = listas de e-mails +gb.preReceiveScripts = pre-receive scripts +gb.postReceiveScripts = post-receive scripts +gb.hookScripts = hook scripts +gb.customFields = campos customizados +gb.customFieldsDescription = campos customizados disponíveis para Groovy hooks +gb.accessPermissions = permissões de acesso +gb.filters = filtros +gb.generalDescription = configurações comuns +gb.accessPermissionsDescription = restringir acesso por usuários e equipes +gb.accessPermissionsForUserDescription = ajustar filiações em equipes ou garantir acesso a repositórios específicos +gb.accessPermissionsForTeamDescription = ajustar membros da equipe e garantir acesso a repositórios específicos +gb.federationRepositoryDescription = compartilhar este repositório com outros servidores Gitblit +gb.hookScriptsDescription = rodar scripts Groovy nos pushes pushes para este servidor Gitblit +gb.reset = reset +gb.pages = páginas +gb.workingCopy = working copy +gb.workingCopyWarning = this repository has a working copy and can not receive pushes +gb.query = query +gb.queryHelp = Standard query syntax é suportada.

Por favor veja Lucene Query Parser Syntax para mais detalhes. +gb.queryResults = resultados {0} - {1} ({2} hits) +gb.noHits = sem hits +gb.authored = foi autor do +gb.committed = committed +gb.indexedBranches = branches indexados +gb.indexedBranchesDescription = selecione os branches para incluir nos seus índices Lucene +gb.noIndexedRepositoriesWarning = nenhum dos seus repositórios foram configurados para indexação do Lucene +gb.undefinedQueryWarning = a query não foi definida! +gb.noSelectedRepositoriesWarning = por favor selecione um ou mais repositórios! +gb.luceneDisabled = indexação do Lucene está desabilitada +gb.failedtoRead = leitura falhou +gb.isNotValidFile = não é um arquivo válido +gb.failedToReadMessage = Falhou em ler mensagens default de {0}! +gb.passwordsDoNotMatch = Passwords não conferem! +gb.passwordTooShort = Password é muito curto. Tamanho mínimo são {0} caracteres. +gb.passwordChanged = Password alterado com sucesso. +gb.passwordChangeAborted = alteração do Password foi abortada. +gb.pleaseSetRepositoryName = Por favor ajuste o nome do repositório! +gb.illegalLeadingSlash = Referências a diretórios raiz começando com (/) são proibidas. +gb.illegalRelativeSlash = Referências a diretórios relativos (../) são proibidas. +gb.illegalCharacterRepositoryName = Caractere ilegal ''{0}'' no nome do repositório! +gb.selectAccessRestriction = Please select access restriction! +gb.selectFederationStrategy = Por favor selecione a estratégia de federação! +gb.pleaseSetTeamName = Por favor insira um nome de equipe! +gb.teamNameUnavailable = O nome de equipe ''{0}'' está indisponível. +gb.teamMustSpecifyRepository = Uma equipe deve especificar pelo menos um repositório. +gb.teamCreated = Nova equipe ''{0}'' criada com sucesso. +gb.pleaseSetUsername = Por favor entre com um username! +gb.usernameUnavailable = Username ''{0}'' está indisponível. +gb.combinedMd5Rename = Gitblit está configurado para usar um hash combinado-md5. Você deve inserir um novo password ao renamear a conta. +gb.userCreated = Novo usuário ''{0}'' criado com sucesso. +gb.couldNotFindFederationRegistration = Não foi possível localizar o registro da federação! +gb.failedToFindGravatarProfile = Falhou em localizar um perfil Gravatar para {0} +gb.branchStats = {0} commits e {1} tags em {2} +gb.repositoryNotSpecified = Repositório não específicado! +gb.repositoryNotSpecifiedFor = Repositório não específicado para {0}! +gb.canNotLoadRepository = Não foi possível carregar o repositório +gb.commitIsNull = Commit está nulo +gb.unauthorizedAccessForRepository = Acesso não autorizado para o repositório +gb.failedToFindCommit = Não foi possível achar o commit \"{0}\" em {1} para {2} página! +gb.couldNotFindFederationProposal = Não foi possível localizar propostas de federação! +gb.invalidUsernameOrPassword = username ou password inválido! +gb.OneProposalToReview = Existe uma proposta de federação aguardando revisão. +gb.nFederationProposalsToReview = Existem {0} propostas de federação aguardando revisão. +gb.couldNotFindTag = Não foi possível localizar a tag {0} +gb.couldNotCreateFederationProposal = Não foi possível criar uma proposta de federation! +gb.pleaseSetGitblitUrl = Por favor insira sua url do Gitblit! +gb.pleaseSetDestinationUrl = Por favor insira a url de destino para sua proposta! +gb.proposalReceived = Proposta recebida com sucesso por {0}. +gb.noGitblitFound = Desculpe, {0} não localizou uma instância do Gitblit em {1}. +gb.noProposals = Desculpe, {0} não está aceitando propostas agora. +gb.noFederation = Desculpe, {0} não está configurado com nenhuma intância do Gitblit. +gb.proposalFailed = Desculpe, {0} não recebeu nenhum dado de proposta! +gb.proposalError = Desculpe, {0} reportou que um erro inesperado ocorreu! +gb.failedToSendProposal = Não foi possível enviar a proposta! +gb.userServiceDoesNotPermitAddUser = {0} não permite adicionar uma conta de usuário! +gb.userServiceDoesNotPermitPasswordChanges = {0} não permite alterações no password! +gb.displayName = nome +gb.emailAddress = e-mail +gb.errorAdminLoginRequired = Administração requer um login +gb.errorOnlyAdminMayCreateRepository = Somente umadministrador pode criar um repositório +gb.errorOnlyAdminOrOwnerMayEditRepository = Somente umadministrador pode editar um repositório +gb.errorAdministrationDisabled = Administração está desabilitada +gb.lastNDays = últimos {0} dias +gb.completeGravatarProfile = Profile completo em Gravatar.com +gb.none = nenhum +gb.line = linha +gb.content = conteúdo +gb.empty = vazio +gb.inherited = herdado +gb.deleteRepository = Deletar repositório \"{0}\"? +gb.repositoryDeleted = Repositório ''{0}'' deletado. +gb.repositoryDeleteFailed = Não foi possível apagar o repositório ''{0}''! +gb.deleteUser = Deletar usuário \"{0}\"? +gb.userDeleted = Usuário ''{0}'' deletado. +gb.userDeleteFailed = Não foi possível apagar o usuário ''{0}''! +gb.time.justNow = agora mesmo +gb.time.today = hoje +gb.time.yesterday = ontem +gb.time.minsAgo = há {0} minutos +gb.time.hoursAgo = há {0} horas +gb.time.daysAgo = há {0} dias +gb.time.weeksAgo = há {0} semanas +gb.time.monthsAgo = há {0} meses +gb.time.oneYearAgo = há 1 ano +gb.time.yearsAgo = há {0} anos +gb.duration.oneDay = 1 dia +gb.duration.days = {0} dias +gb.duration.oneMonth = 1 mês +gb.duration.months = {0} meses +gb.duration.oneYear = 1 ano +gb.duration.years = {0} anos +gb.authorizationControl = controle de autorização +gb.allowAuthenticatedDescription = conceder permissão RW+ para todos os usuários autênticados +gb.allowNamedDescription = conceder permissões refinadas para usuários escolhidos ou equipes +gb.markdownFailure = Não foi possível converter conteúdo Markdown! +gb.clearCache = limpar o cache +gb.projects = projetos +gb.project = projeto +gb.allProjects = todos projetos +gb.copyToClipboard = copiar para o clipboard +gb.fork = fork +gb.forks = forks +gb.forkRepository = fork {0}? +gb.repositoryForked = fork feito em {0} +gb.repositoryForkFailed= não foi possível fazer fork +gb.personalRepositories = repositórios pessoais +gb.allowForks = permitir forks +gb.allowForksDescription = permitir usuários autorizados a fazer fork deste repositório +gb.forkedFrom = forked de +gb.canFork = pode fazer fork +gb.canForkDescription = pode fazer fork de repositórios autorizados para repositórios pessoais +gb.myFork = visualizar meu fork +gb.forksProhibited = forks proibidos +gb.forksProhibitedWarning = este repositório proíbe forks +gb.noForks = {0} não possui forks +gb.forkNotAuthorized = desculpe, você não está autorizado a fazer fork de {0} +gb.forkInProgress = fork em progresso +gb.preparingFork = preparando seu fork... +gb.isFork = é fork +gb.canCreate = pode criar +gb.canCreateDescription = pode criar repositórios pessoais +gb.illegalPersonalRepositoryLocation = seu repositório pessoal deve estar localizado em \"{0}\" +gb.verifyCommitter = verificar committer +gb.verifyCommitterDescription = requer a identidade do committer para combinar com uma conta do Gitblt +gb.verifyCommitterNote = todos os merges requerem "--no-ff" para impor a identidade do committer +gb.repositoryPermissions = permissões de repositório +gb.userPermissions = permissões de usuário +gb.teamPermissions = permissões de equipe +gb.add = add +gb.noPermission = APAGAR ESTA PERMISSÃO +gb.excludePermission = {0} (excluir) +gb.viewPermission = {0} (visualizar) +gb.clonePermission = {0} (clonar) +gb.pushPermission = {0} (push) +gb.createPermission = {0} (push, ref creation) +gb.deletePermission = {0} (push, ref creation+deletion) +gb.rewindPermission = {0} (push, ref creation+deletion+rewind) +gb.permission = permissão +gb.regexPermission = esta permissão foi configurada através da expressão regular \"{0}\" +gb.accessDenied = acesso negado +gb.busyCollectingGarbage = desculpe, o Gitblit está ocupado coletando lixo em {0} +gb.gcPeriod = período do GC +gb.gcPeriodDescription = duração entre as coletas de lixo +gb.gcThreshold = limite do GC +gb.gcThresholdDescription = tamanho total mínimo de objetos \"soltos\" que ativam a coleta de lixo +gb.ownerPermission = proprietário do repositório +gb.administrator = administrador +gb.administratorPermission = administrador do Gitblit +gb.team = equipe +gb.teamPermission = permissão concedida pela filiação a equipe \"{0}\" +gb.missing = faltando! +gb.missingPermission = o repositório para esta permissão está faltando! +gb.mutable = mutável +gb.specified = específico +gb.effective = efetivo +gb.organizationalUnit = unidade organizacional +gb.organization = organização +gb.locality = localidade +gb.stateProvince = estado ou província +gb.countryCode = código do país +gb.properties = propriedades +gb.issued = emitido +gb.expires = expira +gb.expired = expirado +gb.expiring = expirando +gb.revoked = revogado +gb.serialNumber = número serial +gb.certificates = certificados +gb.newCertificate = novo certificado +gb.revokeCertificate = revogar certificado +gb.sendEmail = enviar email +gb.passwordHint = dica de password +gb.ok = ok +gb.invalidExpirationDate = data de expiração inválida! +gb.passwordHintRequired = dica de password requerida! +gb.viewCertificate = visualizar certificado +gb.subject = assunto +gb.issuer = emissor +gb.validFrom = válido a partir de +gb.validUntil = válido até +gb.publicKey = chave pública +gb.signatureAlgorithm = algoritmo de assinatura +gb.sha1FingerPrint = digital SHA-1 +gb.md5FingerPrint = digital MD5 +gb.reason = razão +gb.revokeCertificateReason = Por selecione a razão da revogação do certificado +gb.unspecified = não específico +gb.keyCompromise = comprometimento de chave +gb.caCompromise = compromisso CA +gb.affiliationChanged = afiliação foi alterada +gb.superseded = substituídas +gb.cessationOfOperation = cessação de funcionamento +gb.privilegeWithdrawn = privilégio retirado +gb.time.inMinutes = em {0} minutos +gb.time.inHours = em {0} horas +gb.time.inDays = em {0} dias +gb.hostname = hostname +gb.hostnameRequired = Por favor insira um hostname +gb.newSSLCertificate = novo servidor de certificado SSL +gb.newCertificateDefaults = novos padrões de certificação +gb.duration = duração +gb.certificateRevoked = Certificado {0, número, 0} foi revogado +gb.clientCertificateGenerated = Novo certificado cliente para {0} foi gerado com sucesso +gb.sslCertificateGenerated = Novo servidor de certificado SSL gerado com sucesso para {0} +gb.newClientCertificateMessage = OBSERVAÇÃO:\nO 'password' não é o password do usuário mas sim o password usado para proteger a keystore. Este password não será salvo então você também inserir uma dica que será incluída nas instruções de LEIA-ME do usuário. +gb.certificate = certificado +gb.emailCertificateBundle = pacote certificado de cliente de email +gb.pleaseGenerateClientCertificate = Por favor gere um certificado cliente para {0} +gb.clientCertificateBundleSent = Pacote de certificado de cliente para {0} enviada +gb.enterKeystorePassword = Por favor insira uma chave para keystore do Gitblit +gb.warning = warning +gb.jceWarning = Seu Java Runtime Environment não tem os arquivos \"JCE Unlimited Strength Jurisdiction Policy\".\nIsto irá limitar o tamanho dos passwords que você usará para encriptar suas keystores para 7 caracteres.\nEstes arquivos de políticas são um download opcional da Oracle.\n\nVocê gostaria de continuar e gerar os certificados de infraestrutura de qualquer forma?\n\nRespondendo "Não" irá redirecionar o seu browser para a página de downloads da Oracle, de onde você poderá fazer download desses arquivos. +gb.maxActivityCommits = limitar exibição de commits +gb.maxActivityCommitsDescription = quantidade máxima de commits para contribuir para a página de atividade +gb.noMaximum = ilimitado +gb.attributes = atributos +gb.serveCertificate = servir https com este certificado +gb.sslCertificateGeneratedRestart = Novo certificado SSL de servidor gerado com sucesso para {0}.\nVocê deve reiniciar o Gitblit para usar o novo certificado.\n\nSe você estiver executando com o parâmetro '--alias', você precisará alterá-lo para ''--alias {0}''. +gb.validity = validade +gb.siteName = nome do site +gb.siteNameDescription = breve mas um nome descritivo para seu servidor \ No newline at end of file diff --git a/src/com/gitblit/wicket/GitBlitWebApp_pt_br.properties b/src/com/gitblit/wicket/GitBlitWebApp_pt_br.properties deleted file mode 100644 index 469d2055..00000000 --- a/src/com/gitblit/wicket/GitBlitWebApp_pt_br.properties +++ /dev/null @@ -1,442 +0,0 @@ -gb.repository = repositório -gb.owner = proprietário -gb.description = descrição -gb.lastChange = última alteração -gb.refs = refs -gb.tag = tag -gb.tags = tags -gb.author = autor -gb.committer = committer -gb.commit = commit -gb.tree = árvore -gb.parent = parent -gb.url = URL -gb.history = histórico -gb.raw = raw -gb.object = object -gb.ticketId = ticket id -gb.ticketAssigned = atribuído -gb.ticketOpenDate = data da abertura -gb.ticketState = estado -gb.ticketComments = comentários -gb.view = visualizar -gb.local = local -gb.remote = remote -gb.branches = branches -gb.patch = patch -gb.diff = diff -gb.log = log -gb.moreLogs = mais commits... -gb.allTags = todas as tags... -gb.allBranches = todos os branches... -gb.summary = resumo -gb.ticket = ticket -gb.newRepository = novo repositório -gb.newUser = novo usuário -gb.commitdiff = commitdiff -gb.tickets = tickets -gb.pageFirst = primeira -gb.pagePrevious anterior -gb.pageNext = próxima -gb.head = HEAD -gb.blame = blame -gb.login = login -gb.logout = logout -gb.username = username -gb.password = password -gb.tagger = tagger -gb.moreHistory = mais histórico... -gb.difftocurrent = diff para a atual -gb.search = pesquisar -gb.searchForAuthor = Procurar por commits cujo autor é -gb.searchForCommitter = Procurar por commits commitados por é -gb.addition = adicionados -gb.modification = modificados -gb.deletion = apagados -gb.rename = renomear -gb.metrics = métricas -gb.stats = estatísticas -gb.markdown = markdown -gb.changedFiles = arquivos alterados -gb.filesAdded = {0} arquivos adicionados -gb.filesModified = {0} arquivos modificados -gb.filesDeleted = {0} arquivos deletados -gb.filesCopied = {0} arquivos copiados -gb.filesRenamed = {0} arquivos renomeados -gb.missingUsername = Username desconhecido -gb.edit = editar -gb.searchTypeTooltip = Selecione o Tipo de Pesquisa -gb.searchTooltip = Pesquisar {0} -gb.delete = deletar -gb.docs = documentos -gb.accessRestriction = restrição de acesso -gb.name = nome -gb.enableTickets = ativar tickets -gb.enableDocs = ativar documentação -gb.save = salvar -gb.showRemoteBranches = mostrar branches remotos -gb.editUsers = editar usuários -gb.confirmPassword = confirmar password -gb.restrictedRepositories = repositórios restritos -gb.canAdmin = pode administrar -gb.notRestricted = visualização anônima, clone, & push -gb.pushRestricted = push autênticado -gb.cloneRestricted = clone & push autênticados -gb.viewRestricted = view, clone, & push autênticados -gb.useTicketsDescription = somente leitura, issues do Ticgit distribuídos -gb.useDocsDescription = enumerar documentação Markdown no repositório -gb.showRemoteBranchesDescription = mostrar branches remotos -gb.canAdminDescription = pode administrar o server Gitblit -gb.permittedUsers = usuários autorizados -gb.isFrozen = congelar -gb.isFrozenDescription = proibir fazer push -gb.zip = zip -gb.showReadme = mostrar readme -gb.showReadmeDescription = mostrar um arquivo \"leia-me\" na página de resumo -gb.nameDescription = usar '/' para agrupar repositórios. e.g. libraries/mycoollib.git -gb.ownerDescription = o proprietário pode editar configurações do repositório -gb.blob = blob -gb.commitActivityTrend = tendência dos commits -gb.commitActivityDOW = commits diários -gb.commitActivityAuthors = principais committers -gb.feed = feed -gb.cancel = cancelar -gb.changePassword = alterar password -gb.isFederated = está federado -gb.federateThis = federar este repositório -gb.federateOrigin = federar o origin -gb.excludeFromFederation = excluir da federação -gb.excludeFromFederationDescription = bloquear instâncias federadas do GitBlit de fazer pull desta conta -gb.tokens = tokens de federação -gb.tokenAllDescription = todos repositórios, usuários & configurações -gb.tokenUnrDescription = todos repositórios & usuários -gb.tokenJurDescription = todos repositórios -gb.federatedRepositoryDefinitions = definições de repositório -gb.federatedUserDefinitions = definições de usuários -gb.federatedSettingDefinitions = definições de configurações -gb.proposals = propostas de federações -gb.received = recebidos -gb.type = tipo -gb.token = token -gb.repositories = repositórios -gb.proposal = propostas -gb.frequency = frequência -gb.folder = pasta -gb.lastPull = último pull -gb.nextPull = próximo pull -gb.inclusions = inclusões -gb.exclusions = excluões -gb.registration = cadastro -gb.registrations = cadastro de federações -gb.sendProposal = enviar proposta -gb.status = status -gb.origin = origin -gb.headRef = default branch (HEAD) -gb.headRefDescription = alterar a ref o qual a HEAD aponta. e.g. refs/heads/master -gb.federationStrategy = estratégia de federação -gb.federationRegistration = cadastro de federações -gb.federationResults = resultados dos pulls de federações -gb.federationSets = ajustes de federações -gb.message = mensagem -gb.myUrlDescription = a url de acesso público para a instância Gitblit -gb.destinationUrl = enviar para -gb.destinationUrlDescription = a url da intância do Gitblit para enviar sua proposta -gb.users = usuários -gb.federation = federação -gb.error = erro -gb.refresh = atualizar -gb.browse = navegar -gb.clone = clonar -gb.filter = filtrar -gb.create = criar -gb.servers = servidores -gb.recent = recente -gb.available = disponível -gb.selected = selecionado -gb.size = tamanho -gb.downloading = downloading -gb.loading = loading -gb.starting = inciando -gb.general = geral -gb.settings = configurações -gb.manage = administrar -gb.lastLogin = último login -gb.skipSizeCalculation = ignorar cálculo do tamanho -gb.skipSizeCalculationDescription = não calcular o tamanho do repositório (reduz o tempo de load da página) -gb.skipSummaryMetrics = ignorar resumo das métricas -gb.skipSummaryMetricsDescription = não calcular métricas na página de resumo -gb.accessLevel = acesso -gb.default = default -gb.setDefault = tornar default -gb.since = desde -gb.status = status -gb.bootDate = data do boot -gb.servletContainer = servlet container -gb.heapMaximum = heap máximo -gb.heapAllocated = alocar heap -gb.heapUsed = usar heap -gb.free = free -gb.version = versão -gb.releaseDate = data de release -gb.date = data -gb.activity = atividade -gb.subscribe = inscrever -gb.branch = branch -gb.maxHits = hits máximos -gb.recentActivity = atividade recente -gb.recentActivityStats = últimos {0} dias / {1} commits por {2} autores -gb.recentActivityNone = últimos {0} dias / nenhum -gb.dailyActivity = atividade diária -gb.activeRepositories = repositórios ativos -gb.activeAuthors = autores ativos -gb.commits = commits -gb.teams = equipes -gb.teamName = nome da equipe -gb.teamMembers = membros -gb.teamMemberships = filiações em equipes -gb.newTeam = nova equipe -gb.permittedTeams = equipes permitidas -gb.emptyRepository = repositório vazio -gb.repositoryUrl = url do repositório -gb.mailingLists = listas de e-mails -gb.preReceiveScripts = pre-receive scripts -gb.postReceiveScripts = post-receive scripts -gb.hookScripts = hook scripts -gb.customFields = campos customizados -gb.customFieldsDescription = campos customizados disponíveis para Groovy hooks -gb.accessPermissions = permissões de acesso -gb.filters = filtros -gb.generalDescription = configurações comuns -gb.accessPermissionsDescription = restringir acesso por usuários e equipes -gb.accessPermissionsForUserDescription = ajustar filiações em equipes ou garantir acesso a repositórios específicos -gb.accessPermissionsForTeamDescription = ajustar membros da equipe e garantir acesso a repositórios específicos -gb.federationRepositoryDescription = compartilhar este repositório com outros servidores Gitblit -gb.hookScriptsDescription = rodar scripts Groovy nos pushes pushes para este servidor Gitblit -gb.reset = reset -gb.pages = páginas -gb.workingCopy = working copy -gb.workingCopyWarning = this repository has a working copy and can not receive pushes -gb.query = query -gb.queryHelp = Standard query syntax é suportada.

Por favor veja Lucene Query Parser Syntax para mais detalhes. -gb.queryResults = resultados {0} - {1} ({2} hits) -gb.noHits = sem hits -gb.authored = foi autor do -gb.committed = committed -gb.indexedBranches = branches indexados -gb.indexedBranchesDescription = selecione os branches para incluir nos seus índices Lucene -gb.noIndexedRepositoriesWarning = nenhum dos seus repositórios foram configurados para indexação do Lucene -gb.undefinedQueryWarning = a query não foi definida! -gb.noSelectedRepositoriesWarning = por favor selecione um ou mais repositórios! -gb.luceneDisabled = indexação do Lucene está desabilitada -gb.failedtoRead = leitura falhou -gb.isNotValidFile = não é um arquivo válido -gb.failedToReadMessage = Falhou em ler mensagens default de {0}! -gb.passwordsDoNotMatch = Passwords não conferem! -gb.passwordTooShort = Password é muito curto. Tamanho mínimo são {0} caracteres. -gb.passwordChanged = Password alterado com sucesso. -gb.passwordChangeAborted = alteração do Password foi abortada. -gb.pleaseSetRepositoryName = Por favor ajuste o nome do repositório! -gb.illegalLeadingSlash = Referências a diretórios raiz começando com (/) são proibidas. -gb.illegalRelativeSlash = Referências a diretórios relativos (../) são proibidas. -gb.illegalCharacterRepositoryName = Caractere ilegal ''{0}'' no nome do repositório! -gb.selectAccessRestriction = Please select access restriction! -gb.selectFederationStrategy = Por favor selecione a estratégia de federação! -gb.pleaseSetTeamName = Por favor insira um nome de equipe! -gb.teamNameUnavailable = O nome de equipe ''{0}'' está indisponível. -gb.teamMustSpecifyRepository = Uma equipe deve especificar pelo menos um repositório. -gb.teamCreated = Nova equipe ''{0}'' criada com sucesso. -gb.pleaseSetUsername = Por favor entre com um username! -gb.usernameUnavailable = Username ''{0}'' está indisponível. -gb.combinedMd5Rename = Gitblit está configurado para usar um hash combinado-md5. Você deve inserir um novo password ao renamear a conta. -gb.userCreated = Novo usuário ''{0}'' criado com sucesso. -gb.couldNotFindFederationRegistration = Não foi possível localizar o registro da federação! -gb.failedToFindGravatarProfile = Falhou em localizar um perfil Gravatar para {0} -gb.branchStats = {0} commits e {1} tags em {2} -gb.repositoryNotSpecified = Repositório não específicado! -gb.repositoryNotSpecifiedFor = Repositório não específicado para {0}! -gb.canNotLoadRepository = Não foi possível carregar o repositório -gb.commitIsNull = Commit está nulo -gb.unauthorizedAccessForRepository = Acesso não autorizado para o repositório -gb.failedToFindCommit = Não foi possível achar o commit \"{0}\" em {1} para {2} página! -gb.couldNotFindFederationProposal = Não foi possível localizar propostas de federação! -gb.invalidUsernameOrPassword = username ou password inválido! -gb.OneProposalToReview = Existe uma proposta de federação aguardando revisão. -gb.nFederationProposalsToReview = Existem {0} propostas de federação aguardando revisão. -gb.couldNotFindTag = Não foi possível localizar a tag {0} -gb.couldNotCreateFederationProposal = Não foi possível criar uma proposta de federation! -gb.pleaseSetGitblitUrl = Por favor insira sua url do Gitblit! -gb.pleaseSetDestinationUrl = Por favor insira a url de destino para sua proposta! -gb.proposalReceived = Proposta recebida com sucesso por {0}. -gb.noGitblitFound = Desculpe, {0} não localizou uma instância do Gitblit em {1}. -gb.noProposals = Desculpe, {0} não está aceitando propostas agora. -gb.noFederation = Desculpe, {0} não está configurado com nenhuma intância do Gitblit. -gb.proposalFailed = Desculpe, {0} não recebeu nenhum dado de proposta! -gb.proposalError = Desculpe, {0} reportou que um erro inesperado ocorreu! -gb.failedToSendProposal = Não foi possível enviar a proposta! -gb.userServiceDoesNotPermitAddUser = {0} não permite adicionar uma conta de usuário! -gb.userServiceDoesNotPermitPasswordChanges = {0} não permite alterações no password! -gb.displayName = nome -gb.emailAddress = e-mail -gb.errorAdminLoginRequired = Administração requer um login -gb.errorOnlyAdminMayCreateRepository = Somente umadministrador pode criar um repositório -gb.errorOnlyAdminOrOwnerMayEditRepository = Somente umadministrador pode editar um repositório -gb.errorAdministrationDisabled = Administração está desabilitada -gb.lastNDays = últimos {0} dias -gb.completeGravatarProfile = Profile completo em Gravatar.com -gb.none = nenhum -gb.line = linha -gb.content = conteúdo -gb.empty = vazio -gb.inherited = herdado -gb.deleteRepository = Deletar repositório \"{0}\"? -gb.repositoryDeleted = Repositório ''{0}'' deletado. -gb.repositoryDeleteFailed = Não foi possível apagar o repositório ''{0}''! -gb.deleteUser = Deletar usuário \"{0}\"? -gb.userDeleted = Usuário ''{0}'' deletado. -gb.userDeleteFailed = Não foi possível apagar o usuário ''{0}''! -gb.time.justNow = agora mesmo -gb.time.today = hoje -gb.time.yesterday = ontem -gb.time.minsAgo = há {0} minutos -gb.time.hoursAgo = há {0} horas -gb.time.daysAgo = há {0} dias -gb.time.weeksAgo = há {0} semanas -gb.time.monthsAgo = há {0} meses -gb.time.oneYearAgo = há 1 ano -gb.time.yearsAgo = há {0} anos -gb.duration.oneDay = 1 dia -gb.duration.days = {0} dias -gb.duration.oneMonth = 1 mês -gb.duration.months = {0} meses -gb.duration.oneYear = 1 ano -gb.duration.years = {0} anos -gb.authorizationControl = controle de autorização -gb.allowAuthenticatedDescription = conceder permissão RW+ para todos os usuários autênticados -gb.allowNamedDescription = conceder permissões refinadas para usuários escolhidos ou equipes -gb.markdownFailure = Não foi possível converter conteúdo Markdown! -gb.clearCache = limpar o cache -gb.projects = projetos -gb.project = projeto -gb.allProjects = todos projetos -gb.copyToClipboard = copiar para o clipboard -gb.fork = fork -gb.forks = forks -gb.forkRepository = fork {0}? -gb.repositoryForked = fork feito em {0} -gb.repositoryForkFailed= não foi possível fazer fork -gb.personalRepositories = repositórios pessoais -gb.allowForks = permitir forks -gb.allowForksDescription = permitir usuários autorizados a fazer fork deste repositório -gb.forkedFrom = forked de -gb.canFork = pode fazer fork -gb.canForkDescription = pode fazer fork de repositórios autorizados para repositórios pessoais -gb.myFork = visualizar meu fork -gb.forksProhibited = forks proibidos -gb.forksProhibitedWarning = este repositório proíbe forks -gb.noForks = {0} não possui forks -gb.forkNotAuthorized = desculpe, você não está autorizado a fazer fork de {0} -gb.forkInProgress = fork em progresso -gb.preparingFork = preparando seu fork... -gb.isFork = é fork -gb.canCreate = pode criar -gb.canCreateDescription = pode criar repositórios pessoais -gb.illegalPersonalRepositoryLocation = seu repositório pessoal deve estar localizado em \"{0}\" -gb.verifyCommitter = verificar committer -gb.verifyCommitterDescription = requer a identidade do committer para combinar com uma conta do Gitblt -gb.verifyCommitterNote = todos os merges requerem "--no-ff" para impor a identidade do committer -gb.repositoryPermissions = permissões de repositório -gb.userPermissions = permissões de usuário -gb.teamPermissions = permissões de equipe -gb.add = add -gb.noPermission = APAGAR ESTA PERMISSÃO -gb.excludePermission = {0} (excluir) -gb.viewPermission = {0} (visualizar) -gb.clonePermission = {0} (clonar) -gb.pushPermission = {0} (push) -gb.createPermission = {0} (push, ref creation) -gb.deletePermission = {0} (push, ref creation+deletion) -gb.rewindPermission = {0} (push, ref creation+deletion+rewind) -gb.permission = permissão -gb.regexPermission = esta permissão foi configurada através da expressão regular \"{0}\" -gb.accessDenied = acesso negado -gb.busyCollectingGarbage = desculpe, o Gitblit está ocupado coletando lixo em {0} -gb.gcPeriod = período do GC -gb.gcPeriodDescription = duração entre as coletas de lixo -gb.gcThreshold = limite do GC -gb.gcThresholdDescription = tamanho total mínimo de objetos \"soltos\" que ativam a coleta de lixo -gb.ownerPermission = proprietário do repositório -gb.administrator = administrador -gb.administratorPermission = administrador do Gitblit -gb.team = equipe -gb.teamPermission = permissão concedida pela filiação a equipe \"{0}\" -gb.missing = faltando! -gb.missingPermission = o repositório para esta permissão está faltando! -gb.mutable = mutável -gb.specified = específico -gb.effective = efetivo -gb.organizationalUnit = unidade organizacional -gb.organization = organização -gb.locality = localidade -gb.stateProvince = estado ou província -gb.countryCode = código do país -gb.properties = propriedades -gb.issued = emitido -gb.expires = expira -gb.expired = expirado -gb.expiring = expirando -gb.revoked = revogado -gb.serialNumber = número serial -gb.certificates = certificados -gb.newCertificate = novo certificado -gb.revokeCertificate = revogar certificado -gb.sendEmail = enviar email -gb.passwordHint = dica de password -gb.ok = ok -gb.invalidExpirationDate = data de expiração inválida! -gb.passwordHintRequired = dica de password requerida! -gb.viewCertificate = visualizar certificado -gb.subject = assunto -gb.issuer = emissor -gb.validFrom = válido a partir de -gb.validUntil = válido até -gb.publicKey = chave pública -gb.signatureAlgorithm = algoritmo de assinatura -gb.sha1FingerPrint = digital SHA-1 -gb.md5FingerPrint = digital MD5 -gb.reason = razão -gb.revokeCertificateReason = Por selecione a razão da revogação do certificado -gb.unspecified = não específico -gb.keyCompromise = comprometimento de chave -gb.caCompromise = compromisso CA -gb.affiliationChanged = afiliação foi alterada -gb.superseded = substituídas -gb.cessationOfOperation = cessação de funcionamento -gb.privilegeWithdrawn = privilégio retirado -gb.time.inMinutes = em {0} minutos -gb.time.inHours = em {0} horas -gb.time.inDays = em {0} dias -gb.hostname = hostname -gb.hostnameRequired = Por favor insira um hostname -gb.newSSLCertificate = novo servidor de certificado SSL -gb.newCertificateDefaults = novos padrões de certificação -gb.duration = duração -gb.certificateRevoked = Certificado {0, número, 0} foi revogado -gb.clientCertificateGenerated = Novo certificado cliente para {0} foi gerado com sucesso -gb.sslCertificateGenerated = Novo servidor de certificado SSL gerado com sucesso para {0} -gb.newClientCertificateMessage = OBSERVAÇÃO:\nO 'password' não é o password do usuário mas sim o password usado para proteger a keystore. Este password não será salvo então você também inserir uma dica que será incluída nas instruções de LEIA-ME do usuário. -gb.certificate = certificado -gb.emailCertificateBundle = pacote certificado de cliente de email -gb.pleaseGenerateClientCertificate = Por favor gere um certificado cliente para {0} -gb.clientCertificateBundleSent = Pacote de certificado de cliente para {0} enviada -gb.enterKeystorePassword = Por favor insira uma chave para keystore do Gitblit -gb.warning = warning -gb.jceWarning = Seu Java Runtime Environment não tem os arquivos \"JCE Unlimited Strength Jurisdiction Policy\".\nIsto irá limitar o tamanho dos passwords que você usará para encriptar suas keystores para 7 caracteres.\nEstes arquivos de políticas são um download opcional da Oracle.\n\nVocê gostaria de continuar e gerar os certificados de infraestrutura de qualquer forma?\n\nRespondendo "Não" irá redirecionar o seu browser para a página de downloads da Oracle, de onde você poderá fazer download desses arquivos. -gb.maxActivityCommits = limitar exibição de commits -gb.maxActivityCommitsDescription = quantidade máxima de commits para contribuir para a página de atividade -gb.noMaximum = ilimitado -gb.attributes = atributos -gb.serveCertificate = servir https com este certificado -gb.sslCertificateGeneratedRestart = Novo certificado SSL de servidor gerado com sucesso para {0}.\nVocê deve reiniciar o Gitblit para usar o novo certificado.\n\nSe você estiver executando com o parâmetro '--alias', você precisará alterá-lo para ''--alias {0}''. -gb.validity = validade -gb.siteName = nome do site -gb.siteNameDescription = breve mas um nome descritivo para seu servidor \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html b/src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html new file mode 100644 index 00000000..351ef879 --- /dev/null +++ b/src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html @@ -0,0 +1,53 @@ + + + + + + +

Repositório Vazio

+

+
+
+
+ [repository] é um repositório vazio e não pode ser visualizado pelo Gitblit. +

+ Por favor faça o push de alguns commits para +

+
+ Depois de ter feito push você poderá atualizar esta página para visualizar seu repositório. +
+
+
+ +

Sintaxe dos comandos do Git

+ Se você ainda não tem um repositório local do Git, então você deve primeiro clonar este repositório, fazer commit de alguns arquivos e então fazer push desses commits para o Gitblit. +

+

+		

+ Se você já tem um repositório Git local com alguns commits, então você deve adicionar este repositório como uma referência remota e então fazer push. +

+

+		

+

Aprenda Git

+ Se você estiver com dúvidas sobre como ultilizar essas informações, uma sugestão seria dar uma olhada no livro Git Community Book ou Pro Git para entender melhor como usar o Git. +

+

Alguns clients do Git que são Open Source

+
    +
  • Git - o Git oficial através de linhas de comando
  • +
  • TortoiseGit - Faz integração do Explorer do Windows com o Git (por isso requer o Git Oficial)
  • +
  • Eclipse/EGit - Git para a IDE Eclipse (baseada no JGit, como o Gitblit)
  • +
  • Git Extensions - Interface (em C#) para o Git cuja a característica é a integração com o Windows Explorer e o Visual Studio
  • +
  • GitX (L) - um Cliente do Git para Mac OS X
  • +
+

+

Clients do Git proprietários ou com Código Fechado

+
    +
  • SmartGit - Aplicação Client (em Java) para Git, Mercurial, e SVN (por isso requer o Git Oficial)
  • +
  • SourceTree - Client gratuito para o Mac que suporta Git, Mercurial e SVN
  • +
+ + + \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_br.html b/src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_br.html deleted file mode 100644 index 351ef879..00000000 --- a/src/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_br.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - -

Repositório Vazio

-

-
-
-
- [repository] é um repositório vazio e não pode ser visualizado pelo Gitblit. -

- Por favor faça o push de alguns commits para -

-
- Depois de ter feito push você poderá atualizar esta página para visualizar seu repositório. -
-
-
- -

Sintaxe dos comandos do Git

- Se você ainda não tem um repositório local do Git, então você deve primeiro clonar este repositório, fazer commit de alguns arquivos e então fazer push desses commits para o Gitblit. -

-

-		

- Se você já tem um repositório Git local com alguns commits, então você deve adicionar este repositório como uma referência remota e então fazer push. -

-

-		

-

Aprenda Git

- Se você estiver com dúvidas sobre como ultilizar essas informações, uma sugestão seria dar uma olhada no livro Git Community Book ou Pro Git para entender melhor como usar o Git. -

-

Alguns clients do Git que são Open Source

-
    -
  • Git - o Git oficial através de linhas de comando
  • -
  • TortoiseGit - Faz integração do Explorer do Windows com o Git (por isso requer o Git Oficial)
  • -
  • Eclipse/EGit - Git para a IDE Eclipse (baseada no JGit, como o Gitblit)
  • -
  • Git Extensions - Interface (em C#) para o Git cuja a característica é a integração com o Windows Explorer e o Visual Studio
  • -
  • GitX (L) - um Cliente do Git para Mac OS X
  • -
-

-

Clients do Git proprietários ou com Código Fechado

-
    -
  • SmartGit - Aplicação Client (em Java) para Git, Mercurial, e SVN (por isso requer o Git Oficial)
  • -
  • SourceTree - Client gratuito para o Mac que suporta Git, Mercurial e SVN
  • -
-
- - \ No newline at end of file -- cgit v1.2.3 From 7ec9d3d1b460db339db2770f3aa81af9826deed3 Mon Sep 17 00:00:00 2001 From: James Moger Date: Thu, 3 Jan 2013 17:21:32 -0500 Subject: Build project models from repository cache, when possible (issue-172) --- docs/04_releases.mkd | 1 + src/com/gitblit/GitBlit.java | 43 ++++++++++++++++++++++ src/com/gitblit/wicket/pages/ProjectPage.java | 4 +- src/com/gitblit/wicket/pages/ProjectsPage.java | 7 +--- .../gitblit/wicket/panels/RepositoriesPanel.java | 18 ++++----- 5 files changed, 54 insertions(+), 19 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index faec56a0..32fb0297 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -7,6 +7,7 @@ #### fixes - Fixed nullpointer when using web.allowForking = true && git.cacheRepositoryList = false (issue 182) +- Build project models from the repository model cache, when possible, to reduce page load time (issue 172) - Fixed loading of Brazilian Portuguese translation from *nix server (github/inaiat) ### Older Releases diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 3dcd5a09..96333a07 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1525,6 +1525,49 @@ public class GitBlit implements ServletContextListener { return project; } + /** + * Returns the list of project models that are referenced by the supplied + * repository model list. This is an alternative method exists to ensure + * Gitblit does not call getRepositoryModels(UserModel) twice in a request. + * + * @param repositoryModels + * @param includeUsers + * @return a list of project models + */ + public List getProjectModels(List repositoryModels, boolean includeUsers) { + Map projects = new LinkedHashMap(); + for (RepositoryModel repository : repositoryModels) { + if (!includeUsers && repository.isPersonalRepository()) { + // exclude personal repositories + continue; + } + if (!projects.containsKey(repository.projectPath)) { + ProjectModel project = getProjectModel(repository.projectPath); + if (project == null) { + logger.warn(MessageFormat.format("excluding project \"{0}\" from project list because it is empty!", + repository.projectPath)); + continue; + } + projects.put(repository.projectPath, project); + // clear the repo list in the project because that is the system + // list, not the user-accessible list and start building the + // user-accessible list + project.repositories.clear(); + project.repositories.add(repository.name); + project.lastChange = repository.lastChange; + } else { + // update the user-accessible list + // this is used for repository count + ProjectModel project = projects.get(repository.projectPath); + project.repositories.add(repository.name); + if (project.lastChange.before(repository.lastChange)) { + project.lastChange = repository.lastChange; + } + } + } + return new ArrayList(projects.values()); + } + /** * Workaround JGit. I need to access the raw config object directly in order * to see if the config is dirty so that I can reload a repository model. diff --git a/src/com/gitblit/wicket/pages/ProjectPage.java b/src/com/gitblit/wicket/pages/ProjectPage.java index bc546dfc..e10ca900 100644 --- a/src/com/gitblit/wicket/pages/ProjectPage.java +++ b/src/com/gitblit/wicket/pages/ProjectPage.java @@ -300,8 +300,8 @@ public class ProjectPage extends RootPage { @Override protected List getProjectModels() { if (projectModels.isEmpty()) { - final UserModel user = GitBlitWebSession.get().getUser(); - List projects = GitBlit.self().getProjectModels(user, false); + List repositories = getRepositoryModels(); + List projects = GitBlit.self().getProjectModels(repositories, false); projectModels.addAll(projects); } return projectModels; diff --git a/src/com/gitblit/wicket/pages/ProjectsPage.java b/src/com/gitblit/wicket/pages/ProjectsPage.java index 4e3e6309..7f0b002e 100644 --- a/src/com/gitblit/wicket/pages/ProjectsPage.java +++ b/src/com/gitblit/wicket/pages/ProjectsPage.java @@ -36,7 +36,6 @@ import org.eclipse.jgit.lib.Constants; import com.gitblit.GitBlit; import com.gitblit.Keys; import com.gitblit.models.ProjectModel; -import com.gitblit.models.UserModel; import com.gitblit.utils.MarkdownUtils; import com.gitblit.utils.StringUtils; import com.gitblit.wicket.GitBlitWebSession; @@ -48,8 +47,6 @@ import com.gitblit.wicket.panels.LinkPanel; public class ProjectsPage extends RootPage { - List projectModels = new ArrayList(); - public ProjectsPage() { super(); setup(null); @@ -67,9 +64,7 @@ public class ProjectsPage extends RootPage { @Override protected List getProjectModels() { - final UserModel user = GitBlitWebSession.get().getUser(); - List projects = GitBlit.self().getProjectModels(user, false); - return projects; + return GitBlit.self().getProjectModels(getRepositoryModels(), false); } private void setup(PageParameters params) { diff --git a/src/com/gitblit/wicket/panels/RepositoriesPanel.java b/src/com/gitblit/wicket/panels/RepositoriesPanel.java index d3b8ddbe..976c517f 100644 --- a/src/com/gitblit/wicket/panels/RepositoriesPanel.java +++ b/src/com/gitblit/wicket/panels/RepositoriesPanel.java @@ -123,22 +123,18 @@ public class RepositoriesPanel extends BasePanel { if (rootRepositories.size() > 0) { // inject the root repositories at the top of the page - String rootPath = GitBlit.getString(Keys.web.repositoryRootGroupName, " "); - roots.add(0, rootPath); - groups.put(rootPath, rootRepositories); + roots.add(0, ""); + groups.put("", rootRepositories); } - Map projects = new HashMap(); - for (ProjectModel project : GitBlit.self().getProjectModels(user, true)) { - projects.put(project.name, project); - } List groupedModels = new ArrayList(); for (String root : roots) { List subModels = groups.get(root); - GroupRepositoryModel group = new GroupRepositoryModel(root, subModels.size()); - if (projects.containsKey(root)) { - group.title = projects.get(root).title; - group.description = projects.get(root).description; + ProjectModel project = GitBlit.self().getProjectModel(root); + GroupRepositoryModel group = new GroupRepositoryModel(project.name, subModels.size()); + if (project != null) { + group.title = project.title; + group.description = project.description; } groupedModels.add(group); Collections.sort(subModels); -- cgit v1.2.3 From eee533b4583854b9c875221f1c1f50dfb092868d Mon Sep 17 00:00:00 2001 From: James Moger Date: Thu, 3 Jan 2013 17:22:21 -0500 Subject: Cache and re-use project markdown content (issue-172) --- src/com/gitblit/GitBlit.java | 29 ++++++++++++++++++++++- src/com/gitblit/models/ProjectModel.java | 2 ++ src/com/gitblit/wicket/pages/ProjectPage.java | 33 ++++++--------------------- 3 files changed, 37 insertions(+), 27 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 96333a07..30071bb6 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -154,6 +154,10 @@ public class GitBlit implements ServletContextListener { private final Map projectCache = new ConcurrentHashMap(); private final AtomicReference repositoryListSettingsChecksum = new AtomicReference(""); + + private final ObjectCache projectMarkdownCache = new ObjectCache(); + + private final ObjectCache projectRepositoriesMarkdownCache = new ObjectCache(); private ServletContext servletContext; @@ -1402,7 +1406,30 @@ public class GitBlit implements ServletContextListener { } project.title = projectConfigs.getString("project", name, "title"); project.description = projectConfigs.getString("project", name, "description"); - configs.put(name.toLowerCase(), project); + + // project markdown + File pmkd = new File(getRepositoriesFolder(), (project.isRoot ? "" : name) + "/project.mkd"); + if (pmkd.exists()) { + Date lm = new Date(pmkd.lastModified()); + if (!projectMarkdownCache.hasCurrent(name, lm)) { + String mkd = com.gitblit.utils.FileUtils.readContent(pmkd, "\n"); + projectMarkdownCache.updateObject(name, lm, mkd); + } + project.projectMarkdown = projectMarkdownCache.getObject(name); + } + + // project repositories markdown + File rmkd = new File(getRepositoriesFolder(), (project.isRoot ? "" : name) + "/repositories.mkd"); + if (rmkd.exists()) { + Date lm = new Date(rmkd.lastModified()); + if (!projectRepositoriesMarkdownCache.hasCurrent(name, lm)) { + String mkd = com.gitblit.utils.FileUtils.readContent(rmkd, "\n"); + projectRepositoriesMarkdownCache.updateObject(name, lm, mkd); + } + project.repositoriesMarkdown = projectRepositoriesMarkdownCache.getObject(name); + } + + configs.put(name.toLowerCase(), project); } projectCache.clear(); projectCache.putAll(configs); diff --git a/src/com/gitblit/models/ProjectModel.java b/src/com/gitblit/models/ProjectModel.java index 189a409b..9e5d5233 100644 --- a/src/com/gitblit/models/ProjectModel.java +++ b/src/com/gitblit/models/ProjectModel.java @@ -39,6 +39,8 @@ public class ProjectModel implements Serializable, Comparable { public String description; public final Set repositories = new HashSet(); + public String projectMarkdown; + public String repositoriesMarkdown; public Date lastChange; public final boolean isRoot; diff --git a/src/com/gitblit/wicket/pages/ProjectPage.java b/src/com/gitblit/wicket/pages/ProjectPage.java index e10ca900..7eba0331 100644 --- a/src/com/gitblit/wicket/pages/ProjectPage.java +++ b/src/com/gitblit/wicket/pages/ProjectPage.java @@ -15,9 +15,6 @@ */ package com.gitblit.wicket.pages; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -37,7 +34,6 @@ import org.apache.wicket.markup.html.link.ExternalLink; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.ListDataProvider; -import org.eclipse.jgit.lib.Constants; import com.gitblit.GitBlit; import com.gitblit.Keys; @@ -46,7 +42,6 @@ import com.gitblit.models.Activity; import com.gitblit.models.Metric; import com.gitblit.models.ProjectModel; import com.gitblit.models.RepositoryModel; -import com.gitblit.models.UserModel; import com.gitblit.utils.ActivityUtils; import com.gitblit.utils.MarkdownUtils; import com.gitblit.utils.StringUtils; @@ -111,23 +106,14 @@ public class ProjectPage extends RootPage { add(WicketUtils.syndicationDiscoveryLink(SyndicationServlet.getTitle(project.getDisplayName(), null), feedLink)); - final String projectPath; - if (project.isRoot) { - projectPath = ""; - } else { - projectPath = projectName + "/"; - } - // project markdown message - File pmkd = new File(GitBlit.getRepositoriesFolder(), projectPath + "project.mkd"); - String pmessage = readMarkdown(projectName, pmkd); + String pmessage = transformMarkdown(project.projectMarkdown); Component projectMessage = new Label("projectMessage", pmessage) .setEscapeModelStrings(false).setVisible(pmessage.length() > 0); add(projectMessage); // markdown message above repositories list - File rmkd = new File(GitBlit.getRepositoriesFolder(), projectPath + "repositories.mkd"); - String rmessage = readMarkdown(projectName, rmkd); + String rmessage = transformMarkdown(project.repositoriesMarkdown); Component repositoriesMessage = new Label("repositoriesMessage", rmessage) .setEscapeModelStrings(false).setVisible(rmessage.length() > 0); add(repositoriesMessage); @@ -352,20 +338,15 @@ public class ProjectPage extends RootPage { } return menu; } - - - private String readMarkdown(String projectName, File projectMessage) { + + private String transformMarkdown(String markdown) { String message = ""; - if (projectMessage.exists()) { + if (!StringUtils.isEmpty(markdown)) { // Read user-supplied message try { - FileInputStream fis = new FileInputStream(projectMessage); - InputStreamReader reader = new InputStreamReader(fis, - Constants.CHARACTER_ENCODING); - message = MarkdownUtils.transformMarkdown(reader); - reader.close(); + message = MarkdownUtils.transformMarkdown(markdown); } catch (Throwable t) { - message = getString("gb.failedToRead") + " " + projectMessage; + message = getString("gb.failedToRead") + " " + markdown; warn(message, t); } } -- cgit v1.2.3 From eccc636cc0186c75da04e546a65ea0f977ac5bd3 Mon Sep 17 00:00:00 2001 From: "Dongsu, KIM" Date: Fri, 4 Jan 2013 14:55:59 +0900 Subject: Add more translation to Korean --- src/com/gitblit/wicket/GitBlitWebApp_ko.properties | 130 ++++++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/GitBlitWebApp_ko.properties b/src/com/gitblit/wicket/GitBlitWebApp_ko.properties index 9582ef85..18eda26c 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp_ko.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp_ko.properties @@ -237,7 +237,7 @@ gb.passwordChanged = \uD328\uC2A4\uC6CC\uB4DC\uAC00 \uBCC0\uACBD \uC131\uACF5. gb.passwordChangeAborted = \uD328\uC2A4\uC6CC\uB4DC \uBCC0\uACBD \uCDE8\uC18C\uB428. gb.pleaseSetRepositoryName = \uC800\uC7A5\uC18C \uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694! gb.illegalLeadingSlash = \uC800\uC7A5\uC18C \uC774\uB984 \uB610\uB294 \uD3F4\uB354\uB294 (/) \uB85C \uC2DC\uC791\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. -gb.illegalRelativeSlash = \uC0C1\uB300 \uACBD\uB85C \uC9C0\uC815 (../) \uC740 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. +gb.illegalRelativeSlash = \uC0C1\uB300 \uACBD\uB85C \uC9C0\uC815 (../) \uC740 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.. gb.illegalCharacterRepositoryName = \uBB38\uC790 ''{0}'' \uC800\uC7A5\uC18C \uC774\uB984\uC5D0 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC5B4\uC694! gb.selectAccessRestriction = \uC811\uC18D \uAD8C\uD55C\uC744 \uC120\uD0DD\uD558\uC138\uC694! gb.selectFederationStrategy = \uD398\uB354\uB808\uC774\uC158 \uC815\uCC45\uC744 \uC120\uD0DD\uD558\uC138\uC694! @@ -287,7 +287,7 @@ gb.none = none gb.line = \uB77C\uC778 gb.content = \uB0B4\uC6A9 gb.empty = empty -gb.inherited = inherited +gb.inherited = \uC0C1\uC18D gb.deleteRepository = \"{0}\" \uC800\uC7A5\uC18C\uB97C \uC0AD\uC81C\uD560\uAE4C\uC694? gb.repositoryDeleted = ''{0}'' \uC800\uC7A5\uC18C \uC0AD\uC81C\uB428. gb.repositoryDeleteFailed = ''{0}'' \uC800\uC7A5\uC18C \uC0AD\uC81C \uC2E4\uD328! @@ -315,3 +315,129 @@ gb.allowAuthenticatedDescription = \uBAA8\uB4E0 \uC778\uC99D\uB41C \uC720\uC800\ gb.allowNamedDescription = \uC774\uB984\uC73C\uB85C \uC720\uC800\uB098 \uD300\uC5D0\uAC8C \uAD8C\uD55C \uBD80\uC5EC gb.markdownFailure = \uB9C8\uD06C\uB2E4\uC6B4 \uCEE8\uD150\uD2B8 \uD30C\uC2F1 \uC624\uB958! gb.clearCache = \uCE90\uC2DC \uC9C0\uC6B0\uAE30 +gb.projects = \uD504\uB85C\uC81D\uD2B8\uB4E4 +gb.project = \uD504\uB85C\uC81D\uD2B8 +gb.allProjects = \uBAA8\uB4E0 \uD504\uB85C\uC81D\uD2B8 +gb.copyToClipboard = \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC +gb.fork = \uD3EC\uD06C +gb.forks = \uD3EC\uD06C +gb.forkRepository = fork {0}? +gb.repositoryForked = {0} \uD3EC\uD06C\uB428 +gb.repositoryForkFailed= \uD3EC\uD06C\uC2E4\uD328 +gb.personalRepositories = \uAC1C\uC778 \uC800\uC7A5\uC18C +gb.allowForks = \uD3EC\uD06C \uD5C8\uC6A9 +gb.allowForksDescription = \uC774 \uC800\uC7A5\uC18C\uB97C \uC778\uC99D\uB41C \uC720\uC800\uC5D0\uAC70 \uD3EC\uD06C \uD5C8\uC6A9 +gb.forkedFrom = forked from +gb.canFork = \uD3EC\uD06C \uAC00\uB2A5 +gb.canForkDescription = \uD5C8\uC6A9\uB41C \uC800\uC7A5\uC18C\uB97C \uAC1C\uC778 \uC800\uC7A5\uC18C\uC5D0 \uD3EC\uD06C\uD560 \uC218 \uC788\uC74C +gb.myFork = \uB0B4 \uD3EC\uD06C \uBCF4\uAE30 +gb.forksProhibited = \uD3EC\uD06C \uCC28\uB2E8\uB428 +gb.forksProhibitedWarning = \uC774 \uC800\uC7A5\uC18C\uB294 \uD3EC\uD06C \uCC28\uB2E8\uB418\uC5B4 \uC788\uC74C +gb.noForks = {0} \uB294 \uD3EC\uD06C \uC5C6\uC74C +gb.forkNotAuthorized = \uC8C4\uC1A1\uD569\uB2C8\uB2E4. {0} \uD3EC\uD06C\uC5D0 \uC811\uC18D \uC778\uC99D\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. +gb.forkInProgress = \uD504\uD06C \uC9C4\uD589 \uC911 +gb.preparingFork = \uD3EC\uD06C \uC900\uBE44 \uC911... +gb.isFork = \uD3EC\uD06C\uD55C +gb.canCreate = \uC0DD\uC131 \uAC00\uB2A5 +gb.canCreateDescription = \uAC1C\uC778 \uC800\uC7A5\uC18C\uB97C \uB9CC\uB4E4 \uC218 \uC788\uC74C +gb.illegalPersonalRepositoryLocation = \uAC1C\uC778 \uC800\uC7A5\uC18C\uB294 \uBC18\uB4DC\uC2DC \"{0}\" \uC5D0 \uC704\uCE58\uD574\uC57C \uD569\uB2C8\uB2E4. +gb.verifyCommitter = \uCEE4\uBBF8\uD130 \uD655\uC778 +gb.verifyCommitterDescription = \uCEE4\uBBF8\uD130 ID \uB294 Gitblit ID \uC640 \uB9E4\uCE58\uB418\uC5B4\uC57C \uD568 +gb.verifyCommitterNote = \uBAA8\uB4E0 \uBA38\uC9C0\uB294 \uCEE4\uBBF8\uD130 ID \uB97C \uC801\uC6A9\uD558\uAE30 \uC704\uD574 "--no-ff" \uC635\uC158 \uD544\uC694 +gb.repositoryPermissions = \uC800\uC7A5\uC18C \uAD8C\uD55C +gb.userPermissions = \uC720\uC800 \uAD8C\uD55C +gb.teamPermissions = \uD300 \uAD8C\uD55C +gb.add = \uCD94\uAC00 +gb.noPermission = \uC774 \uAD8C\uD55C \uC0AD\uC81C +gb.excludePermission = {0} (\uC81C\uC678) +gb.viewPermission = {0} (\uBCF4\uAE30) +gb.clonePermission = {0} (\uD074\uB860) +gb.pushPermission = {0} (\uD478\uC2DC) +gb.createPermission = {0} (\uD478\uC2DC, ref \uC0DD\uC131) +gb.deletePermission = {0} (\uD478\uC2DC, ref \uC0DD\uC131+\uC0AD\uC81C) +gb.rewindPermission = {0} (\uD478\uC2DC, ref \uC0DD\uC131+\uC0AD\uC81C+\uB418\uB3CC\uB9AC\uAE30) +gb.permission = \uAD8C\uD55C +gb.regexPermission = \uC774 \uAD8C\uD55C\uC740 \uC815\uADDC\uC2DD \"{0}\" \uB85C\uBD80\uD130 \uC124\uC815\uB428 +gb.accessDenied = \uC811\uC18D \uAC70\uBD80 +gb.busyCollectingGarbage = \uC8C4\uC1A1\uD569\uB2C8\uB2E4. Gitblit \uC740 \uAC00\uBE44\uC9C0 \uCEEC\uB809\uC158 \uC911\uC785\uB2C8\uB2E4. {0} +gb.gcPeriod = GC \uC8FC\uAE30 +gb.gcPeriodDescription = \uAC00\uBE44\uC9C0 \uD074\uB809\uC158\uAC04\uC758 \uC2DC\uAC04 \uAC04\uACA9 +gb.gcThreshold = GC \uAE30\uC900\uC810 +gb.gcThresholdDescription = \uC870\uAE30 \uAC00\uBE44\uC9C0 \uCEEC\uB809\uC158\uC744 \uBC1C\uC0DD\uC2DC\uD0A4\uAE30 \uC704\uD55C \uC624\uBE0C\uC81D\uD2B8\uB4E4\uC758 \uCD5C\uC18C \uC804\uCCB4 \uD06C\uAE30 +gb.ownerPermission = \uC800\uC7A5\uC18C \uC624\uB108 +gb.administrator = \uAD00\uB9AC\uC790 +gb.administratorPermission = Gitblit \uAD00\uB9AC\uC790 +gb.team = \uD300 +gb.teamPermission = \"{0}\" \uD300 \uBA64\uBC84\uC5D0 \uAD8C\uD55C \uC124\uC815\uB428 +gb.missing = \uB204\uB77D! +gb.missingPermission = \uC774 \uAD8C\uD55C\uC744 \uC704\uD55C \uC800\uC7A5\uC18C \uB204\uB77D! +gb.mutable = \uAC00\uBCC0 +gb.specified = \uC9C0\uC815\uB41C +gb.effective = \uD6A8\uACFC\uC801 +gb.organizationalUnit = \uC870\uC9C1 +gb.organization = \uAE30\uAD00 +gb.locality = \uC704\uCE58 +gb.stateProvince = \uB3C4 \uB610\uB294 \uC8FC +gb.countryCode = \uAD6D\uAC00\uCF54\uB4DC +gb.properties = \uC18D\uC131 +gb.issued = \uBC1C\uAE09\uB428 +gb.expires = \uB9CC\uB8CC +gb.expired = \uB9CC\uB8CC\uB428 +gb.expiring = \uB9CC\uB8CC\uC911 +gb.revoked = \uD3D0\uAE30\uB428 +gb.serialNumber = \uC77C\uB828\uBC88\uD638 +gb.certificates = \uC778\uC99D\uC11C +gb.newCertificate = \uC0C8 \uC778\uC99D\uC11C +gb.revokeCertificate = \uC778\uC99D\uC11C \uD3D0\uAE30 +gb.sendEmail = \uBA54\uC77C \uBCF4\uB0B4\uAE30 +gb.passwordHint = \uD328\uC2A4\uC6CC\uB4DC \uD78C\uD2B8 +gb.ok = ok +gb.invalidExpirationDate = \uB9D0\uB8CC\uC77C\uC790 \uC624\uB958! +gb.passwordHintRequired = \uD328\uC2A4\uC6CC\uB4DC \uD78C\uD2B8 \uD544\uC218! +gb.viewCertificate = \uC778\uC99D\uC11C \uBCF4\uAE30 +gb.subject = \uC774\uB984 +gb.issuer = \uBC1C\uAE09\uC790 +gb.validFrom = \uC720\uD6A8\uAE30\uAC04 (\uC2DC\uC791) +gb.validUntil = \uC720\uD6A8\uAE30\uAC04 (\uB05D) +gb.publicKey = \uACF5\uAC1C\uD0A4 +gb.signatureAlgorithm = \uC11C\uBA85 \uC54C\uACE0\uB9AC\uC998 +gb.sha1FingerPrint = SHA-1 \uC9C0\uBB38 \uC54C\uACE0\uB9AC\uC998 +gb.md5FingerPrint = MD5 \uC9C0\uBB38 \uC54C\uACE0\uB9AC\uC998 +gb.reason = \uC774\uC720 +gb.revokeCertificateReason = \uC778\uC99D\uC11C \uD574\uC9C0\uC774\uC720\uB97C \uC120\uD0DD\uD558\uC138\uC694 +gb.unspecified = \uD45C\uC2DC\uD558\uC9C0 \uC54A\uC74C +gb.keyCompromise = \uD0A4 \uC190\uC0C1 +gb.caCompromise = CA \uC190\uC0C1 +gb.affiliationChanged = \uAD00\uACC4 \uBCC0\uACBD\uB428 +gb.superseded = \uB300\uCCB4\uB428 +gb.cessationOfOperation = \uC6B4\uC601 \uC911\uC9C0 +gb.privilegeWithdrawn = \uAD8C\uD55C \uCCA0\uD68C\uB428 +gb.time.inMinutes = {0} \uBD84 +gb.time.inHours = {0} \uC2DC\uAC04 +gb.time.inDays = {0} \uC77C +gb.hostname = \uD638\uC2A4\uD2B8\uBA85 +gb.hostnameRequired = \uD638\uC2A4\uD2B8\uBA85\uC744 \uC785\uB825\uD558\uC138\uC694 +gb.newSSLCertificate = \uC0C8 \uC11C\uBC84 SSL \uC778\uC99D\uC11C +gb.newCertificateDefaults = \uC0C8 \uC778\uC99D\uC11C \uAE30\uBCF8 +gb.duration = \uAE30\uAC04 +gb.certificateRevoked = \uC778\uC99D\uC11C {0,number,0} \uB294 \uD3D0\uAE30\uB418\uC5C8\uC2B5\uB2C8\uB2E4 +gb.clientCertificateGenerated = {0} \uC744(\uB97C) \uC704\uD55C \uC0C8\uB85C\uC6B4 \uD074\uB77C\uC774\uC5B8\uD2B8 \uC778\uC99D\uC11C \uC0DD\uC131 \uC131\uACF5 +gb.sslCertificateGenerated = {0} \uC744(\uB97C) \uC704\uD55C \uC0C8\uB85C\uC6B4 \uC11C\uBC84 SSL \uC778\uC99D\uC11C \uC0DD\uC131 \uC131\uACF5 +gb.newClientCertificateMessage = \uB178\uD2B8:\n'\uD328\uC2A4\uC6CC\uB4DC' \uB294 \uC720\uC800\uC758 \uD328\uC2A4\uC6CC\uB4DC\uAC00 \uC544\uB2C8\uB77C \uC720\uC800\uC758 \uD0A4\uC2A4\uD1A0\uC5B4 \uB97C \uBCF4\uD638\uD558\uAE30 \uC704\uD55C \uAC83\uC785\uB2C8\uB2E4. \uC774 \uD328\uC2A4\uC6CC\uB4DC\uB294 \uC800\uC7A5\uB418\uC9C0 \uC54A\uC73C\uBBC0\uB85C \uC0AC\uC6A9\uC790 README \uC9C0\uCE68\uC5D0 \uD3EC\uD568\uB420 '\uD78C\uD2B8' \uB97C \uBC18\uB4DC\uC2DC \uC785\uB825\uD574\uC57C \uD569\uB2C8\uB2E4. +gb.certificate = \uC778\uC99D\uC11C +gb.emailCertificateBundle = \uC774\uBA54\uC77C \uD074\uB77C\uC774\uC5B8\uD2B8 \uC778\uC99D\uC11C \uBC88\uB4E4 +gb.pleaseGenerateClientCertificate = {0} \uC744(\uB97C) \uC704\uD55C \uD074\uB77C\uC774\uC5B8\uD2B8 \uC778\uC99D\uC11C\uB97C \uC0DD\uC131\uD558\uC138\uC694 +gb.clientCertificateBundleSent = {0} \uC744(\uB97C) \uC704\uD55C \uD074\uB77C\uC774\uC5B8\uD2B8 \uC778\uC99D\uC11C \uBC88\uB4E4 \uBC1C\uC1A1\uB428 +gb.enterKeystorePassword = Gitblit \uD0A4\uC2A4\uD1A0\uC5B4 \uD328\uC2A4\uC6CC\uB4DC\uB97C \uC785\uB825\uD558\uC138\uC694 +gb.warning = \uACBD\uACE0 +gb.jceWarning = \uC790\uBC14 \uC2E4\uD589\uD658\uACBD\uC5D0 \"JCE Unlimited Strength Jurisdiction Policy\" \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\n\uC774\uAC83\uC740 \uD0A4\uC800\uC7A5\uC18C \uC554\uD638\uD654\uC5D0 \uC0AC\uC6A9\uB418\uB294 \uD328\uC2A4\uC6CC\uB4DC\uC758 \uAE38\uC774\uB294 7\uC790\uB85C \uC81C\uD55C\uD569\uB2C8\uB2E4.\n\uC774 \uC815\uCC45 \uD30C\uC77C\uC740 Oracle \uC5D0\uC11C \uC120\uD0DD\uC801\uC73C\uB85C \uB2E4\uC6B4\uB85C\uB4DC\uD574\uC57C \uD569\uB2C8\uB2E4.\n\n\uBB34\uC2DC\uD558\uACE0 \uC778\uC99D\uC11C \uC778\uD504\uB77C\uB97C \uC0DD\uC131\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?\n\n\uC544\uB2C8\uC624(No) \uB77C\uACE0 \uB2F5\uD558\uBA74 \uC815\uCC45\uD30C\uC77C\uC744 \uB2E4\uC6B4\uBC1B\uC744 \uC218 \uC788\uB294 Oracle \uB2E4\uC6B4\uB85C\uB4DC \uD398\uC774\uC9C0\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC548\uB0B4\uD560 \uAC83\uC785\uB2C8\uB2E4. +gb.maxActivityCommits = \uCD5C\uB300 \uC561\uD2F0\uBE44\uD2F0 \uCEE4\uBC0B +gb.maxActivityCommitsDescription = \uC561\uD2F0\uBE44\uD2F0 \uD398\uC774\uC9C0\uC5D0 \uD45C\uC2DC\uD560 \uCD5C\uB300 \uCEE4\uBC0B \uC218 +gb.noMaximum = \uBB34\uC81C\uD55C +gb.attributes = \uC18D\uC131 +gb.serveCertificate = \uC774 \uC778\uC99D\uC11C\uB85C https \uC81C\uACF5 +gb.sslCertificateGeneratedRestart = {0} \uC744(\uB97C) \uC704\uD55C \uC0C8 \uC11C\uBC84 SSL \uC778\uC99D\uC11C\uB97C \uC131\uACF5\uC801\uC73C\uB85C \uC0DD\uC131\uD558\uC600\uC2B5\uB2C8\uB2E4. \n\uC0C8 \uC778\uC99D\uC11C\uB97C \uC0AC\uC6A9\uD558\uB824\uBA74 Gitblit \uC744 \uC7AC\uC2DC\uC791 \uD574\uC57C \uD569\uB2C8\uB2E4.\n\n'--alias' \uD30C\uB77C\uBBF8\uD130\uB85C \uC2E4\uD589\uD55C\uB2E4\uBA74 ''--alias {0}'' \uB85C \uC124\uC815\uD574\uC57C \uD569\uB2C8\uB2E4. +gb.validity = \uC720\uD6A8\uC131 +gb.siteName = \uC0AC\uC774\uD2B8 \uC774\uB984 +gb.siteNameDescription = \uC11C\uBC84\uC758 \uC9E6\uC740 \uC124\uBA85\uC774 \uD3EC\uD568\uB41C \uC774\uB984 +gb.excludeFromActivity = \uC561\uD2F0\uBE44\uD2F0 \uD398\uC774\uC9C0\uC5D0\uC11C \uC81C\uC678 \ No newline at end of file -- cgit v1.2.3 From 4c44e5d31618ae009cfee040fb995b1c63701d93 Mon Sep 17 00:00:00 2001 From: Jeroen Baten Date: Fri, 4 Jan 2013 09:56:43 +0100 Subject: dutch properties file --- src/com/gitblit/wicket/GitBlitWebApp_nl.properties | 443 +++++++++++++++++++++ 1 file changed, 443 insertions(+) create mode 100644 src/com/gitblit/wicket/GitBlitWebApp_nl.properties (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/GitBlitWebApp_nl.properties b/src/com/gitblit/wicket/GitBlitWebApp_nl.properties new file mode 100644 index 00000000..16f76411 --- /dev/null +++ b/src/com/gitblit/wicket/GitBlitWebApp_nl.properties @@ -0,0 +1,443 @@ +gb.repository = repository +gb.owner = owner +gb.description = description +gb.lastChange = last change +gb.refs = refs +gb.tag = tag +gb.tags = tags +gb.author = author +gb.committer = committer +gb.commit = commit +gb.tree = tree +gb.parent = parent +gb.url = URL +gb.history = history +gb.raw = raw +gb.object = object +gb.ticketId = ticket id +gb.ticketAssigned = assigned +gb.ticketOpenDate = open date +gb.ticketState = state +gb.ticketComments = comments +gb.view = view +gb.local = local +gb.remote = remote +gb.branches = branches +gb.patch = patch +gb.diff = diff +gb.log = log +gb.moreLogs = more commits... +gb.allTags = all tags... +gb.allBranches = all branches... +gb.summary = summary +gb.ticket = ticket +gb.newRepository = new repository +gb.newUser = new user +gb.commitdiff = commitdiff +gb.tickets = tickets +gb.pageFirst = first +gb.pagePrevious prev +gb.pageNext = next +gb.head = HEAD +gb.blame = blame +gb.login = login +gb.logout = logout +gb.username = username +gb.password = password +gb.tagger = tagger +gb.moreHistory = more history... +gb.difftocurrent = diff to current +gb.search = search +gb.searchForAuthor = Search for commits authored by +gb.searchForCommitter = Search for commits committed by +gb.addition = addition +gb.modification = modification +gb.deletion = deletion +gb.rename = rename +gb.metrics = metrics +gb.stats = stats +gb.markdown = markdown +gb.changedFiles = changed files +gb.filesAdded = {0} files added +gb.filesModified = {0} files modified +gb.filesDeleted = {0} files deleted +gb.filesCopied = {0} files copied +gb.filesRenamed = {0} files renamed +gb.missingUsername = Missing Username +gb.edit = edit +gb.searchTypeTooltip = Select Search Type +gb.searchTooltip = Search {0} +gb.delete = delete +gb.docs = docs +gb.accessRestriction = access restriction +gb.name = name +gb.enableTickets = enable tickets +gb.enableDocs = enable docs +gb.save = save +gb.showRemoteBranches = show remote branches +gb.editUsers = edit users +gb.confirmPassword = confirm password +gb.restrictedRepositories = restricted repositories +gb.canAdmin = can admin +gb.notRestricted = anonymous view, clone, & push +gb.pushRestricted = authenticated push +gb.cloneRestricted = authenticated clone & push +gb.viewRestricted = authenticated view, clone, & push +gb.useTicketsDescription = readonly, distributed Ticgit issues +gb.useDocsDescription = enumerates Markdown documentation in repository +gb.showRemoteBranchesDescription = show remote branches +gb.canAdminDescription = can administer Gitblit server +gb.permittedUsers = permitted users +gb.isFrozen = is frozen +gb.isFrozenDescription = deny push operations +gb.zip = zip +gb.showReadme = show readme +gb.showReadmeDescription = show a \"readme\" Markdown file on the summary page +gb.nameDescription = use '/' to group repositories. e.g. libraries/mycoollib.git +gb.ownerDescription = the owner may edit repository settings +gb.blob = blob +gb.commitActivityTrend = commit activity trend +gb.commitActivityDOW = commit activity by day of week +gb.commitActivityAuthors = primary authors by commit activity +gb.feed = feed +gb.cancel = cancel +gb.changePassword = change password +gb.isFederated = is federated +gb.federateThis = federate this repository +gb.federateOrigin = federate the origin +gb.excludeFromFederation = exclude from federation +gb.excludeFromFederationDescription = block federated Gitblit instances from pulling this account +gb.tokens = federation tokens +gb.tokenAllDescription = all repositories, users, & settings +gb.tokenUnrDescription = all repositories & users +gb.tokenJurDescription = all repositories +gb.federatedRepositoryDefinitions = repository definitions +gb.federatedUserDefinitions = user definitions +gb.federatedSettingDefinitions = setting definitions +gb.proposals = federation proposals +gb.received = received +gb.type = type +gb.token = token +gb.repositories = repositories +gb.proposal = proposal +gb.frequency = frequency +gb.folder = folder +gb.lastPull = last pull +gb.nextPull = next pull +gb.inclusions = inclusions +gb.exclusions = exclusions +gb.registration = registration +gb.registrations = federation registrations +gb.sendProposal = propose +gb.status = status +gb.origin = origin +gb.headRef = default branch (HEAD) +gb.headRefDescription = change the ref that HEAD links to. e.g. refs/heads/master +gb.federationStrategy = federation strategy +gb.federationRegistration = federation registration +gb.federationResults = federation pull results +gb.federationSets = federation sets +gb.message = message +gb.myUrlDescription = the publicly accessible url for your Gitblit instance +gb.destinationUrl = send to +gb.destinationUrlDescription = the url of the Gitblit instance to send your proposal +gb.users = users +gb.federation = federation +gb.error = error +gb.refresh = refresh +gb.browse = browse +gb.clone = clone +gb.filter = filter +gb.create = create +gb.servers = servers +gb.recent = recent +gb.available = available +gb.selected = selected +gb.size = size +gb.downloading = downloading +gb.loading = loading +gb.starting = starting +gb.general = general +gb.settings = settings +gb.manage = manage +gb.lastLogin = last login +gb.skipSizeCalculation = skip size calculation +gb.skipSizeCalculationDescription = do not calculate the repository size (reduces page load time) +gb.skipSummaryMetrics = skip summary metrics +gb.skipSummaryMetricsDescription = do not calculate metrics on the summary page (reduces page load time) +gb.accessLevel = access level +gb.default = default +gb.setDefault = set default +gb.since = since +gb.status = status +gb.bootDate = boot date +gb.servletContainer = servlet container +gb.heapMaximum = maximum heap +gb.heapAllocated = allocated heap +gb.heapUsed = used heap +gb.free = free +gb.version = version +gb.releaseDate = release date +gb.date = date +gb.activity = activity +gb.subscribe = subscribe +gb.branch = branch +gb.maxHits = max hits +gb.recentActivity = recent activity +gb.recentActivityStats = last {0} days / {1} commits by {2} authors +gb.recentActivityNone = last {0} days / none +gb.dailyActivity = daily activity +gb.activeRepositories = active repositories +gb.activeAuthors = active authors +gb.commits = commits +gb.teams = teams +gb.teamName = team name +gb.teamMembers = team members +gb.teamMemberships = team memberships +gb.newTeam = new team +gb.permittedTeams = permitted teams +gb.emptyRepository = empty repository +gb.repositoryUrl = repository url +gb.mailingLists = mailing lists +gb.preReceiveScripts = pre-receive scripts +gb.postReceiveScripts = post-receive scripts +gb.hookScripts = hook scripts +gb.customFields = custom fields +gb.customFieldsDescription = custom fields available to Groovy hooks +gb.accessPermissions = access permissions +gb.filters = filters +gb.generalDescription = common settings +gb.accessPermissionsDescription = restrict access by users and teams +gb.accessPermissionsForUserDescription = set team memberships or grant access to specific restricted repositories +gb.accessPermissionsForTeamDescription = set team members and grant access to specific restricted repositories +gb.federationRepositoryDescription = share this repository with other Gitblit servers +gb.hookScriptsDescription = run Groovy scripts on pushes to this Gitblit server +gb.reset = reset +gb.pages = pages +gb.workingCopy = working copy +gb.workingCopyWarning = this repository has a working copy and can not receive pushes +gb.query = query +gb.queryHelp = Standard query syntax is supported.

Please see Lucene Query Parser Syntax for details. +gb.queryResults = results {0} - {1} ({2} hits) +gb.noHits = no hits +gb.authored = authored +gb.committed = committed +gb.indexedBranches = indexed branches +gb.indexedBranchesDescription = select the branches to include in your Lucene index +gb.noIndexedRepositoriesWarning = none of your repositories are configured for Lucene indexing +gb.undefinedQueryWarning = query is undefined! +gb.noSelectedRepositoriesWarning = please select one or more repositories! +gb.luceneDisabled = Lucene indexing is disabled +gb.failedtoRead = Failed to read +gb.isNotValidFile = is not a valid file +gb.failedToReadMessage = Failed to read default message from {0}! +gb.passwordsDoNotMatch = Passwords do not match! +gb.passwordTooShort = Password is too short. Minimum length is {0} characters. +gb.passwordChanged = Password successfully changed. +gb.passwordChangeAborted = Password change aborted. +gb.pleaseSetRepositoryName = Please set repository name! +gb.illegalLeadingSlash = Leading root folder references (/) are prohibited. +gb.illegalRelativeSlash = Relative folder references (../) are prohibited. +gb.illegalCharacterRepositoryName = Illegal character ''{0}'' in repository name! +gb.selectAccessRestriction = Please select access restriction! +gb.selectFederationStrategy = Please select federation strategy! +gb.pleaseSetTeamName = Please enter a teamname! +gb.teamNameUnavailable = Team name ''{0}'' is unavailable. +gb.teamMustSpecifyRepository = A team must specify at least one repository. +gb.teamCreated = New team ''{0}'' successfully created. +gb.pleaseSetUsername = Please enter a username! +gb.usernameUnavailable = Username ''{0}'' is unavailable. +gb.combinedMd5Rename = Gitblit is configured for combined-md5 password hashing. You must enter a new password on account rename. +gb.userCreated = New user ''{0}'' successfully created. +gb.couldNotFindFederationRegistration = Could not find federation registration! +gb.failedToFindGravatarProfile = Failed to find Gravatar profile for {0} +gb.branchStats = {0} commits and {1} tags in {2} +gb.repositoryNotSpecified = Repository not specified! +gb.repositoryNotSpecifiedFor = Repository not specified for {0}! +gb.canNotLoadRepository = Can not load repository +gb.commitIsNull = Commit is null +gb.unauthorizedAccessForRepository = Unauthorized access for repository +gb.failedToFindCommit = Failed to find commit \"{0}\" in {1} for {2} page! +gb.couldNotFindFederationProposal = Could not find federation proposal! +gb.invalidUsernameOrPassword = Invalid username or password! +gb.OneProposalToReview = There is 1 federation proposal awaiting review. +gb.nFederationProposalsToReview = There are {0} federation proposals awaiting review. +gb.couldNotFindTag = Could not find tag {0} +gb.couldNotCreateFederationProposal = Could not create federation proposal! +gb.pleaseSetGitblitUrl = Please enter your Gitblit url! +gb.pleaseSetDestinationUrl = Please enter a destination url for your proposal! +gb.proposalReceived = Proposal successfully received by {0}. +gb.noGitblitFound = Sorry, {0} could not find a Gitblit instance at {1}. +gb.noProposals = Sorry, {0} is not accepting proposals at this time. +gb.noFederation = Sorry, {0} is not configured to federate with any Gitblit instances. +gb.proposalFailed = Sorry, {0} did not receive any proposal data! +gb.proposalError = Sorry, {0} reports that an unexpected error occurred! +gb.failedToSendProposal = Failed to send proposal! +gb.userServiceDoesNotPermitAddUser = {0} does not permit adding a user account! +gb.userServiceDoesNotPermitPasswordChanges = {0} does not permit password changes! +gb.displayName = display name +gb.emailAddress = email address +gb.errorAdminLoginRequired = Administration requires a login +gb.errorOnlyAdminMayCreateRepository = Only an administrator may create a repository +gb.errorOnlyAdminOrOwnerMayEditRepository = Only an administrator or the owner may edit a repository +gb.errorAdministrationDisabled = Administration is disabled +gb.lastNDays = last {0} days +gb.completeGravatarProfile = Complete profile on Gravatar.com +gb.none = none +gb.line = line +gb.content = content +gb.empty = empty +gb.inherited = inherited +gb.deleteRepository = Delete repository \"{0}\"? +gb.repositoryDeleted = Repository ''{0}'' deleted. +gb.repositoryDeleteFailed = Failed to delete repository ''{0}''! +gb.deleteUser = Delete user \"{0}\"? +gb.userDeleted = User ''{0}'' deleted. +gb.userDeleteFailed = Failed to delete user ''{0}''! +gb.time.justNow = just now +gb.time.today = today +gb.time.yesterday = yesterday +gb.time.minsAgo = {0} mins ago +gb.time.hoursAgo = {0} hours ago +gb.time.daysAgo = {0} days ago +gb.time.weeksAgo = {0} weeks ago +gb.time.monthsAgo = {0} months ago +gb.time.oneYearAgo = 1 year ago +gb.time.yearsAgo = {0} years ago +gb.duration.oneDay = 1 day +gb.duration.days = {0} days +gb.duration.oneMonth = 1 month +gb.duration.months = {0} months +gb.duration.oneYear = 1 year +gb.duration.years = {0} years +gb.authorizationControl = authorization control +gb.allowAuthenticatedDescription = grant RW+ permission to all authenticated users +gb.allowNamedDescription = grant fine-grained permissions to named users or teams +gb.markdownFailure = Failed to parse Markdown content! +gb.clearCache = clear cache +gb.projects = projects +gb.project = project +gb.allProjects = all projects +gb.copyToClipboard = copy to clipboard +gb.fork = fork +gb.forks = forks +gb.forkRepository = fork {0}? +gb.repositoryForked = {0} has been forked +gb.repositoryForkFailed= fork has failed +gb.personalRepositories = personal repositories +gb.allowForks = allow forks +gb.allowForksDescription = allow authorized users to fork this repository +gb.forkedFrom = forked from +gb.canFork = can fork +gb.canForkDescription = can fork authorized repositories to personal repositories +gb.myFork = view my fork +gb.forksProhibited = forks prohibited +gb.forksProhibitedWarning = this repository forbids forks +gb.noForks = {0} has no forks +gb.forkNotAuthorized = sorry, you are not authorized to fork {0} +gb.forkInProgress = fork in progress +gb.preparingFork = preparing your fork... +gb.isFork = is fork +gb.canCreate = can create +gb.canCreateDescription = can create personal repositories +gb.illegalPersonalRepositoryLocation = your personal repository must be located at \"{0}\" +gb.verifyCommitter = verify committer +gb.verifyCommitterDescription = require committer identity to match pushing Gitblt user account +gb.verifyCommitterNote = all merges require "--no-ff" to enforce committer identity +gb.repositoryPermissions = repository permissions +gb.userPermissions = user permissions +gb.teamPermissions = team permissions +gb.add = add +gb.noPermission = DELETE THIS PERMISSION +gb.excludePermission = {0} (exclude) +gb.viewPermission = {0} (view) +gb.clonePermission = {0} (clone) +gb.pushPermission = {0} (push) +gb.createPermission = {0} (push, ref creation) +gb.deletePermission = {0} (push, ref creation+deletion) +gb.rewindPermission = {0} (push, ref creation+deletion+rewind) +gb.permission = permission +gb.regexPermission = this permission is set from regular expression \"{0}\" +gb.accessDenied = access denied +gb.busyCollectingGarbage = sorry, Gitblit is busy collecting garbage in {0} +gb.gcPeriod = GC period +gb.gcPeriodDescription = duration between garbage collections +gb.gcThreshold = GC threshold +gb.gcThresholdDescription = minimum total size of loose objects to trigger early garbage collection +gb.ownerPermission = repository owner +gb.administrator = admin +gb.administratorPermission = Gitblit administrator +gb.team = team +gb.teamPermission = permission set by \"{0}\" team membership +gb.missing = missing! +gb.missingPermission = the repository for this permission is missing! +gb.mutable = mutable +gb.specified = specified +gb.effective = effective +gb.organizationalUnit = organizational unit +gb.organization = organization +gb.locality = locality +gb.stateProvince = state or province +gb.countryCode = country code +gb.properties = properties +gb.issued = issued +gb.expires = expires +gb.expired = expired +gb.expiring = expiring +gb.revoked = revoked +gb.serialNumber = serial number +gb.certificates = certificates +gb.newCertificate = new certificate +gb.revokeCertificate = revoke certificate +gb.sendEmail = send email +gb.passwordHint = password hint +gb.ok = ok +gb.invalidExpirationDate = invalid expiration date! +gb.passwordHintRequired = password hint required! +gb.viewCertificate = view certificate +gb.subject = subject +gb.issuer = issuer +gb.validFrom = valid from +gb.validUntil = valid until +gb.publicKey = public key +gb.signatureAlgorithm = signature algorithm +gb.sha1FingerPrint = SHA-1 Fingerprint +gb.md5FingerPrint = MD5 Fingerprint +gb.reason = reason +gb.revokeCertificateReason = Please select a reason for certificate revocation +gb.unspecified = unspecified +gb.keyCompromise = key compromise +gb.caCompromise = CA compromise +gb.affiliationChanged = affiliation changed +gb.superseded = superseded +gb.cessationOfOperation = cessation of operation +gb.privilegeWithdrawn = privilege withdrawn +gb.time.inMinutes = in {0} mins +gb.time.inHours = in {0} hours +gb.time.inDays = in {0} days +gb.hostname = hostname +gb.hostnameRequired = Please enter a hostname +gb.newSSLCertificate = new server SSL certificate +gb.newCertificateDefaults = new certificate defaults +gb.duration = duration +gb.certificateRevoked = Certificate {0,number,0} has been revoked +gb.clientCertificateGenerated = Successfully generated new client certificate for {0} +gb.sslCertificateGenerated = Successfully generated new server SSL certificate for {0} +gb.newClientCertificateMessage = NOTE:\nThe 'password' is not the user's password, it is the password to protect the user's keystore. This password is not saved so you must also enter a 'hint' which will be included in the user's README instructions. +gb.certificate = certificate +gb.emailCertificateBundle = email client certificate bundle +gb.pleaseGenerateClientCertificate = Please generate a client certificate for {0} +gb.clientCertificateBundleSent = Client certificate bundle for {0} sent +gb.enterKeystorePassword = Please enter the Gitblit keystore password +gb.warning = warning +gb.jceWarning = Your Java Runtime Environment does not have the \"JCE Unlimited Strength Jurisdiction Policy\" files.\nThis will limit the length of passwords you may use to encrypt your keystores to 7 characters.\nThese policy files are an optional download from Oracle.\n\nWould you like to continue and generate the certificate infrastructure anyway?\n\nAnswering No will direct your browser to Oracle's download page so that you may download the policy files. +gb.maxActivityCommits = max activity commits +gb.maxActivityCommitsDescription = maximum number of commits to contribute to the Activity page +gb.noMaximum = no maximum +gb.attributes = attributes +gb.serveCertificate = serve https with this certificate +gb.sslCertificateGeneratedRestart = Successfully generated new server SSL certificate for {0}.\nYou must restart Gitblit to use the new certificate.\n\nIf you are launching with the '--alias' parameter you will have to set that to ''--alias {0}''. +gb.validity = validity +gb.siteName = site name +gb.siteNameDescription = short, descriptive name of your server +gb.excludeFromActivity = exclude from activity page \ No newline at end of file -- cgit v1.2.3 From 9af47c10c6a268877c1d232c8d71ee6df4f8a7ab Mon Sep 17 00:00:00 2001 From: Jeroen Baten Date: Fri, 4 Jan 2013 11:18:37 +0100 Subject: Dutch translation before spellcheck --- src/com/gitblit/wicket/GitBlitWebApp_nl.properties | 718 ++++++++++----------- 1 file changed, 359 insertions(+), 359 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/GitBlitWebApp_nl.properties b/src/com/gitblit/wicket/GitBlitWebApp_nl.properties index 16f76411..ee45b86c 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp_nl.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp_nl.properties @@ -1,24 +1,24 @@ gb.repository = repository -gb.owner = owner -gb.description = description -gb.lastChange = last change +gb.owner = eigenaar +gb.description = omschrijving +gb.lastChange = laatste wijziging gb.refs = refs gb.tag = tag gb.tags = tags -gb.author = author +gb.author = auteur gb.committer = committer gb.commit = commit gb.tree = tree gb.parent = parent gb.url = URL -gb.history = history +gb.history = historie gb.raw = raw gb.object = object gb.ticketId = ticket id -gb.ticketAssigned = assigned -gb.ticketOpenDate = open date -gb.ticketState = state -gb.ticketComments = comments +gb.ticketAssigned = toegewezen +gb.ticketOpenDate = open datum +gb.ticketState = status +gb.ticketComments = commentaar gb.view = view gb.local = local gb.remote = remote @@ -26,13 +26,13 @@ gb.branches = branches gb.patch = patch gb.diff = diff gb.log = log -gb.moreLogs = more commits... -gb.allTags = all tags... -gb.allBranches = all branches... -gb.summary = summary +gb.moreLogs = meer commits... +gb.allTags = alle tags... +gb.allBranches = alle branches... +gb.summary = samenvatting gb.ticket = ticket -gb.newRepository = new repository -gb.newUser = new user +gb.newRepository = nieuwe repository +gb.newUser = nieuwe gebruiker gb.commitdiff = commitdiff gb.tickets = tickets gb.pageFirst = first @@ -42,402 +42,402 @@ gb.head = HEAD gb.blame = blame gb.login = login gb.logout = logout -gb.username = username -gb.password = password +gb.username = gebruikersnaam +gb.password = wachtwoord gb.tagger = tagger -gb.moreHistory = more history... -gb.difftocurrent = diff to current -gb.search = search -gb.searchForAuthor = Search for commits authored by -gb.searchForCommitter = Search for commits committed by +gb.moreHistory = meer history... +gb.difftocurrent = diff naar current +gb.search = zoeken +gb.searchForAuthor = Zoeken naar commits authored door +gb.searchForCommitter = Zoeken naar commits committed door gb.addition = addition -gb.modification = modification -gb.deletion = deletion -gb.rename = rename -gb.metrics = metrics +gb.modification = wijziging +gb.deletion = verwijdering +gb.rename = hernoem +gb.metrics = metrieken gb.stats = stats gb.markdown = markdown -gb.changedFiles = changed files -gb.filesAdded = {0} files added -gb.filesModified = {0} files modified -gb.filesDeleted = {0} files deleted -gb.filesCopied = {0} files copied -gb.filesRenamed = {0} files renamed -gb.missingUsername = Missing Username +gb.changedFiles = gewijzigde bestanden +gb.filesAdded = {0} bestanden toegevoegd +gb.filesModified = {0} bestanden gewijzigd +gb.filesDeleted = {0} bestanden verwijderd +gb.filesCopied = {0} bestanden gekopieerd +gb.filesRenamed = {0} bestanden hernoemd +gb.missingUsername = Ontbrekende Gebruikersnaam gb.edit = edit -gb.searchTypeTooltip = Select Search Type -gb.searchTooltip = Search {0} -gb.delete = delete +gb.searchTypeTooltip = Selecteer Zoek Type +gb.searchTooltip = Zoek {0} +gb.delete = verwijder gb.docs = docs -gb.accessRestriction = access restriction -gb.name = name +gb.accessRestriction = toegangsbeperking +gb.name = naam gb.enableTickets = enable tickets gb.enableDocs = enable docs -gb.save = save -gb.showRemoteBranches = show remote branches -gb.editUsers = edit users -gb.confirmPassword = confirm password +gb.save = opslaan +gb.showRemoteBranches = toon remote branches +gb.editUsers = wijzig gebruikers +gb.confirmPassword = bevestig wachtwoord gb.restrictedRepositories = restricted repositories -gb.canAdmin = can admin -gb.notRestricted = anonymous view, clone, & push -gb.pushRestricted = authenticated push -gb.cloneRestricted = authenticated clone & push -gb.viewRestricted = authenticated view, clone, & push -gb.useTicketsDescription = readonly, distributed Ticgit issues -gb.useDocsDescription = enumerates Markdown documentation in repository -gb.showRemoteBranchesDescription = show remote branches -gb.canAdminDescription = can administer Gitblit server -gb.permittedUsers = permitted users -gb.isFrozen = is frozen -gb.isFrozenDescription = deny push operations +gb.canAdmin = kan beheren +gb.notRestricted = anoniem view, clone, & push +gb.pushRestricted = geauthenticeerde push +gb.cloneRestricted = geauthenticeerde clone & push +gb.viewRestricted = geauthenticeerde view, clone, & push +gb.useTicketsDescription = readonly, gedistribueerde Ticgit issues +gb.useDocsDescription = enumereer Markdown documentatie in repository +gb.showRemoteBranchesDescription = toon remote branches +gb.canAdminDescription = kan Gitblit server beheren +gb.permittedUsers = toegestande gebruikers +gb.isFrozen = is bevroren +gb.isFrozenDescription = weiger push operaties gb.zip = zip -gb.showReadme = show readme -gb.showReadmeDescription = show a \"readme\" Markdown file on the summary page -gb.nameDescription = use '/' to group repositories. e.g. libraries/mycoollib.git -gb.ownerDescription = the owner may edit repository settings +gb.showReadme = toon readme +gb.showReadmeDescription = toon een \"readme\" Markdown bestand in de samenvattingspagina +gb.nameDescription = gebruik '/' voor het groeperen van repositories. bijv. libraries/mycoollib.git +gb.ownerDescription = de eigenaar mag repository instellingen wijzigen gb.blob = blob -gb.commitActivityTrend = commit activity trend -gb.commitActivityDOW = commit activity by day of week -gb.commitActivityAuthors = primary authors by commit activity +gb.commitActivityTrend = commit activiteit trend +gb.commitActivityDOW = commit activiteit per dag van de week +gb.commitActivityAuthors = primare auteurs op basis van commit activiteit gb.feed = feed -gb.cancel = cancel -gb.changePassword = change password -gb.isFederated = is federated -gb.federateThis = federate this repository -gb.federateOrigin = federate the origin -gb.excludeFromFederation = exclude from federation -gb.excludeFromFederationDescription = block federated Gitblit instances from pulling this account -gb.tokens = federation tokens -gb.tokenAllDescription = all repositories, users, & settings -gb.tokenUnrDescription = all repositories & users -gb.tokenJurDescription = all repositories -gb.federatedRepositoryDefinitions = repository definitions -gb.federatedUserDefinitions = user definitions -gb.federatedSettingDefinitions = setting definitions -gb.proposals = federation proposals -gb.received = received +gb.cancel = afbreken +gb.changePassword = wijzig wachtwoord +gb.isFederated = is gefedereerd +gb.federateThis = federeer deze repository +gb.federateOrigin = federeer deze origin +gb.excludeFromFederation = uitsluiten van federatie +gb.excludeFromFederationDescription = sluit gefedereerde Gitblit instances uit van het pullen van dit account +gb.tokens = federatie tokens +gb.tokenAllDescription = alle repositories, gebruikers, & instellingen +gb.tokenUnrDescription = alle repositories & gebruikers +gb.tokenJurDescription = alle repositories +gb.federatedRepositoryDefinitions = repository definities +gb.federatedUserDefinitions = gebruikersdefinities +gb.federatedSettingDefinitions = instellingendefinities +gb.proposals = federatie voorstellen +gb.received = ontvangen gb.type = type gb.token = token gb.repositories = repositories -gb.proposal = proposal -gb.frequency = frequency -gb.folder = folder -gb.lastPull = last pull -gb.nextPull = next pull -gb.inclusions = inclusions -gb.exclusions = exclusions -gb.registration = registration -gb.registrations = federation registrations -gb.sendProposal = propose +gb.proposal = voorstel +gb.frequency = frequentie +gb.folder = map +gb.lastPull = laatste pull +gb.nextPull = volgende pull +gb.inclusions = inclusies +gb.exclusions = exclusies +gb.registration = registratie +gb.registrations = federatie registraties +gb.sendProposal = voorstel gb.status = status gb.origin = origin gb.headRef = default branch (HEAD) -gb.headRefDescription = change the ref that HEAD links to. e.g. refs/heads/master -gb.federationStrategy = federation strategy -gb.federationRegistration = federation registration -gb.federationResults = federation pull results -gb.federationSets = federation sets -gb.message = message -gb.myUrlDescription = the publicly accessible url for your Gitblit instance -gb.destinationUrl = send to -gb.destinationUrlDescription = the url of the Gitblit instance to send your proposal -gb.users = users -gb.federation = federation -gb.error = error -gb.refresh = refresh -gb.browse = browse +gb.headRefDescription = wijzig de ref waar HEAD naar linkt naar bijv. refs/heads/master +gb.federationStrategy = federatie strategie +gb.federationRegistration = federatie registratie +gb.federationResults = federatie pull resultaten +gb.federationSets = federatie sets +gb.message = melding +gb.myUrlDescription = de publiek toegankelijke url voor uw Gitblit instantie +gb.destinationUrl = zend naar +gb.destinationUrlDescription = de url van de Gitblit instantie voor het verzenden van uw voorstel +gb.users = gebruikers +gb.federation = federatie +gb.error = fout +gb.refresh = ververs +gb.browse = blader gb.clone = clone gb.filter = filter -gb.create = create +gb.create = maak gb.servers = servers gb.recent = recent -gb.available = available -gb.selected = selected -gb.size = size +gb.available = beschikbaar +gb.selected = geselecteerd +gb.size = grootte gb.downloading = downloading gb.loading = loading gb.starting = starting -gb.general = general -gb.settings = settings -gb.manage = manage -gb.lastLogin = last login -gb.skipSizeCalculation = skip size calculation -gb.skipSizeCalculationDescription = do not calculate the repository size (reduces page load time) -gb.skipSummaryMetrics = skip summary metrics -gb.skipSummaryMetricsDescription = do not calculate metrics on the summary page (reduces page load time) -gb.accessLevel = access level -gb.default = default -gb.setDefault = set default -gb.since = since +gb.general = algemeen +gb.settings = instellingen +gb.manage = beheer +gb.lastLogin = laatste login +gb.skipSizeCalculation = geen berekening van de omvang +gb.skipSizeCalculationDescription = geen berekening van de repositoriegrootte (beperkt laadtijd pagina) +gb.skipSummaryMetrics = geen metrieken samenvatting +gb.skipSummaryMetricsDescription = geen berekening van metrieken op de samenvattingspagina (beperkt laadtijd pagina) +gb.accessLevel = toegangsniveau +gb.default = standaard +gb.setDefault = instellen als standaard +gb.since = sinds gb.status = status -gb.bootDate = boot date +gb.bootDate = boot datum gb.servletContainer = servlet container gb.heapMaximum = maximum heap -gb.heapAllocated = allocated heap -gb.heapUsed = used heap -gb.free = free -gb.version = version -gb.releaseDate = release date -gb.date = date -gb.activity = activity -gb.subscribe = subscribe +gb.heapAllocated = toegewezen heap +gb.heapUsed = gebruikte heap +gb.free = beschikbaar +gb.version = versie +gb.releaseDate = release datum +gb.date = datum +gb.activity = activiteit +gb.subscribe = aboneer gb.branch = branch gb.maxHits = max hits -gb.recentActivity = recent activity -gb.recentActivityStats = last {0} days / {1} commits by {2} authors -gb.recentActivityNone = last {0} days / none -gb.dailyActivity = daily activity -gb.activeRepositories = active repositories -gb.activeAuthors = active authors +gb.recentActivity = recente activiteit +gb.recentActivityStats = laatste {0} dagen / {1} commits door {2} auteurs +gb.recentActivityNone = laatste {0} dagen / geen +gb.dailyActivity = dagelijkse activiteit +gb.activeRepositories = actieve repositories +gb.activeAuthors = actieve auteurs gb.commits = commits gb.teams = teams -gb.teamName = team name -gb.teamMembers = team members -gb.teamMemberships = team memberships -gb.newTeam = new team -gb.permittedTeams = permitted teams -gb.emptyRepository = empty repository +gb.teamName = teamnaam +gb.teamMembers = teamleden +gb.teamMemberships = teamlidmaatschappen +gb.newTeam = nieuw team +gb.permittedTeams = toegestane teams +gb.emptyRepository = lege repository gb.repositoryUrl = repository url -gb.mailingLists = mailing lists +gb.mailingLists = mailing lijsten gb.preReceiveScripts = pre-receive scripts gb.postReceiveScripts = post-receive scripts gb.hookScripts = hook scripts -gb.customFields = custom fields -gb.customFieldsDescription = custom fields available to Groovy hooks -gb.accessPermissions = access permissions +gb.customFields = custom velden +gb.customFieldsDescription = custom velden beschikbaar voor Groovy hooks +gb.accessPermissions = toegangsrechten gb.filters = filters -gb.generalDescription = common settings -gb.accessPermissionsDescription = restrict access by users and teams -gb.accessPermissionsForUserDescription = set team memberships or grant access to specific restricted repositories -gb.accessPermissionsForTeamDescription = set team members and grant access to specific restricted repositories -gb.federationRepositoryDescription = share this repository with other Gitblit servers -gb.hookScriptsDescription = run Groovy scripts on pushes to this Gitblit server +gb.generalDescription = algemene instellingen +gb.accessPermissionsDescription = beperk toegang voor gebruikers en teams +gb.accessPermissionsForUserDescription = stel teamlidmaatschappen in of geef toegang tot specifieke besloten repositories +gb.accessPermissionsForTeamDescription = stel teamlidmaatschappen in en geef toegang tot specifieke besloten repositories +gb.federationRepositoryDescription = deel deze repository met andere Gitblit servers +gb.hookScriptsDescription = run Groovy scripts bij pushes naar deze Gitblit server gb.reset = reset -gb.pages = pages -gb.workingCopy = working copy -gb.workingCopyWarning = this repository has a working copy and can not receive pushes +gb.pages = paginas +gb.workingCopy = werkkopie +gb.workingCopyWarning = deze repository heeft een werkkopie en kan geen pushes ontvangen gb.query = query -gb.queryHelp = Standard query syntax is supported.

Please see Lucene Query Parser Syntax for details. -gb.queryResults = results {0} - {1} ({2} hits) -gb.noHits = no hits +gb.queryHelp = Standaard query syntax wordt ondersteund.

Zie aub Lucene Query Parser Syntax voor informatie. +gb.queryResults = resultaten {0} - {1} ({2} hits) +gb.noHits = geen hits gb.authored = authored gb.committed = committed -gb.indexedBranches = indexed branches -gb.indexedBranchesDescription = select the branches to include in your Lucene index -gb.noIndexedRepositoriesWarning = none of your repositories are configured for Lucene indexing -gb.undefinedQueryWarning = query is undefined! -gb.noSelectedRepositoriesWarning = please select one or more repositories! -gb.luceneDisabled = Lucene indexing is disabled -gb.failedtoRead = Failed to read -gb.isNotValidFile = is not a valid file -gb.failedToReadMessage = Failed to read default message from {0}! -gb.passwordsDoNotMatch = Passwords do not match! -gb.passwordTooShort = Password is too short. Minimum length is {0} characters. -gb.passwordChanged = Password successfully changed. -gb.passwordChangeAborted = Password change aborted. -gb.pleaseSetRepositoryName = Please set repository name! -gb.illegalLeadingSlash = Leading root folder references (/) are prohibited. -gb.illegalRelativeSlash = Relative folder references (../) are prohibited. -gb.illegalCharacterRepositoryName = Illegal character ''{0}'' in repository name! -gb.selectAccessRestriction = Please select access restriction! -gb.selectFederationStrategy = Please select federation strategy! -gb.pleaseSetTeamName = Please enter a teamname! -gb.teamNameUnavailable = Team name ''{0}'' is unavailable. -gb.teamMustSpecifyRepository = A team must specify at least one repository. -gb.teamCreated = New team ''{0}'' successfully created. -gb.pleaseSetUsername = Please enter a username! -gb.usernameUnavailable = Username ''{0}'' is unavailable. -gb.combinedMd5Rename = Gitblit is configured for combined-md5 password hashing. You must enter a new password on account rename. -gb.userCreated = New user ''{0}'' successfully created. -gb.couldNotFindFederationRegistration = Could not find federation registration! -gb.failedToFindGravatarProfile = Failed to find Gravatar profile for {0} -gb.branchStats = {0} commits and {1} tags in {2} -gb.repositoryNotSpecified = Repository not specified! -gb.repositoryNotSpecifiedFor = Repository not specified for {0}! -gb.canNotLoadRepository = Can not load repository +gb.indexedBranches = geindexeerde branches +gb.indexedBranchesDescription = kies de branches voor opname in uw Lucene index +gb.noIndexedRepositoriesWarning = geen van uw repositories is geconfigureerd voor Lucene indexering +gb.undefinedQueryWarning = query is niet gedefinieerd! +gb.noSelectedRepositoriesWarning = kies aub één of meerdere repositories! +gb.luceneDisabled = Lucene indexering staat uit +gb.failedtoRead = Lezen is mislukt +gb.isNotValidFile = is geen valide bestand +gb.failedToReadMessage = Het lezen van de standaard boodschap van {0} is mislukt! +gb.passwordsDoNotMatch = Wachtwoorden komen niet overeen! +gb.passwordTooShort = Wachtwoord is te kort. Minimum lengte is {0} karakters. +gb.passwordChanged = Wachtwoord succesvol gewijzigd. +gb.passwordChangeAborted = Wijziging wachtwoord afgebroken. +gb.pleaseSetRepositoryName = Vul aub een repository naam in! +gb.illegalLeadingSlash = Leidende root folder referenties (/) zijn niet toegestaan. +gb.illegalRelativeSlash = Relatieve folder referenties (../) zijn niet toegestaan. +gb.illegalCharacterRepositoryName = Illegaal karakter ''{0}'' in repository naam! +gb.selectAccessRestriction = Stel aub een toegangsbeperking in! +gb.selectFederationStrategy = Selecteer aub een federatie strategie! +gb.pleaseSetTeamName = Vul aub een teamnaam in! +gb.teamNameUnavailable = Teamnaam ''{0}'' is niet beschikbaar. +gb.teamMustSpecifyRepository = Een team moet minimaal één repository specificeren. +gb.teamCreated = Nieuw team ''{0}'' successvol aangemaakt. +gb.pleaseSetUsername = Vul aub een gebruikersnaam in! +gb.usernameUnavailable = Gebruikersnaam ''{0}'' is niet beschikbaar. +gb.combinedMd5Rename = Gitblit is geconfigureerd voor combined-md5 wachtwoord hashing. U moet een nieuw wachtwoord opgeven bij het hernoemen van een account. +gb.userCreated = Nieuwe gebruiker ''{0}'' successvol aangemaakt. +gb.couldNotFindFederationRegistration = Kon de federatie registratie niet vinden! +gb.failedToFindGravatarProfile = Kon het Gravatar profiel voor {0} niet vinden +gb.branchStats = {0} commits en {1} tags in {2} +gb.repositoryNotSpecified = Repository niet gespecificeerd! +gb.repositoryNotSpecifiedFor = Repository niet gespecificeerd voor {0}! +gb.canNotLoadRepository = Kan repository niet laden gb.commitIsNull = Commit is null -gb.unauthorizedAccessForRepository = Unauthorized access for repository -gb.failedToFindCommit = Failed to find commit \"{0}\" in {1} for {2} page! -gb.couldNotFindFederationProposal = Could not find federation proposal! -gb.invalidUsernameOrPassword = Invalid username or password! -gb.OneProposalToReview = There is 1 federation proposal awaiting review. -gb.nFederationProposalsToReview = There are {0} federation proposals awaiting review. -gb.couldNotFindTag = Could not find tag {0} -gb.couldNotCreateFederationProposal = Could not create federation proposal! -gb.pleaseSetGitblitUrl = Please enter your Gitblit url! -gb.pleaseSetDestinationUrl = Please enter a destination url for your proposal! -gb.proposalReceived = Proposal successfully received by {0}. -gb.noGitblitFound = Sorry, {0} could not find a Gitblit instance at {1}. -gb.noProposals = Sorry, {0} is not accepting proposals at this time. -gb.noFederation = Sorry, {0} is not configured to federate with any Gitblit instances. -gb.proposalFailed = Sorry, {0} did not receive any proposal data! -gb.proposalError = Sorry, {0} reports that an unexpected error occurred! -gb.failedToSendProposal = Failed to send proposal! -gb.userServiceDoesNotPermitAddUser = {0} does not permit adding a user account! -gb.userServiceDoesNotPermitPasswordChanges = {0} does not permit password changes! -gb.displayName = display name -gb.emailAddress = email address -gb.errorAdminLoginRequired = Administration requires a login -gb.errorOnlyAdminMayCreateRepository = Only an administrator may create a repository -gb.errorOnlyAdminOrOwnerMayEditRepository = Only an administrator or the owner may edit a repository -gb.errorAdministrationDisabled = Administration is disabled -gb.lastNDays = last {0} days -gb.completeGravatarProfile = Complete profile on Gravatar.com -gb.none = none -gb.line = line -gb.content = content -gb.empty = empty -gb.inherited = inherited -gb.deleteRepository = Delete repository \"{0}\"? -gb.repositoryDeleted = Repository ''{0}'' deleted. -gb.repositoryDeleteFailed = Failed to delete repository ''{0}''! -gb.deleteUser = Delete user \"{0}\"? -gb.userDeleted = User ''{0}'' deleted. -gb.userDeleteFailed = Failed to delete user ''{0}''! -gb.time.justNow = just now -gb.time.today = today -gb.time.yesterday = yesterday -gb.time.minsAgo = {0} mins ago -gb.time.hoursAgo = {0} hours ago -gb.time.daysAgo = {0} days ago -gb.time.weeksAgo = {0} weeks ago -gb.time.monthsAgo = {0} months ago -gb.time.oneYearAgo = 1 year ago -gb.time.yearsAgo = {0} years ago -gb.duration.oneDay = 1 day -gb.duration.days = {0} days -gb.duration.oneMonth = 1 month -gb.duration.months = {0} months -gb.duration.oneYear = 1 year -gb.duration.years = {0} years -gb.authorizationControl = authorization control -gb.allowAuthenticatedDescription = grant RW+ permission to all authenticated users -gb.allowNamedDescription = grant fine-grained permissions to named users or teams -gb.markdownFailure = Failed to parse Markdown content! -gb.clearCache = clear cache -gb.projects = projects +gb.unauthorizedAccessForRepository = Niet toegestande toegang tot repository +gb.failedToFindCommit = Het vinden van commit \"{0}\" in {1} voor {2} pagina is mislukt! +gb.couldNotFindFederationProposal = Kon federatievoorstel niet vinden! +gb.invalidUsernameOrPassword = Onjuiste gebruikersnaam of wachtwoord! +gb.OneProposalToReview = Er is 1 federatie voorstel dat wacht op review. +gb.nFederationProposalsToReview = Er zijn {0} federatie verzoeken die wachten op review. +gb.couldNotFindTag = Kon tag {0} niet vinden +gb.couldNotCreateFederationProposal = Kon geen federatie voorstel maken! +gb.pleaseSetGitblitUrl = Vul aub uw Gitblit url in! +gb.pleaseSetDestinationUrl = Vul aub een bestemmings url in voor uw voorstel! +gb.proposalReceived = Voorstel correct ontvangen door {0}. +gb.noGitblitFound = Sorry, {0} kon geen Gitblit instance vinden op {1}. +gb.noProposals = Sorry, {0} accepteert geen voorstellen op dit moment. +gb.noFederation = Sorry, {0} is niet geconfigureerd voor het federeren met een Gitblit instance. +gb.proposalFailed = Sorry, {0} ontving geen voorstelgegevens! +gb.proposalError = Sorry, {0} rapporteerd dat een onverwachte fout is opgetreden! +gb.failedToSendProposal = Voorstel verzenden is niet gelukt! +gb.userServiceDoesNotPermitAddUser = {0} staat het toevoegen van een gebruikersaccount niet toe! +gb.userServiceDoesNotPermitPasswordChanges = {0} staat wachtwoord wijzigingen niet toe! +gb.displayName = display naam +gb.emailAddress = emailadres +gb.errorAdminLoginRequired = Aanmelden vereist voor beheerwerk +gb.errorOnlyAdminMayCreateRepository = Alleen een beheerder kan een repository maken +gb.errorOnlyAdminOrOwnerMayEditRepository = Alleen een beheerder of de eigenaar kan een repository wijzigen +gb.errorAdministrationDisabled = Beheer is uitgeschakeld +gb.lastNDays = laatste {0} dagen +gb.completeGravatarProfile = Completeer profiel op Gravatar.com +gb.none = geen +gb.line = regel +gb.content = inhoud +gb.empty = leeg +gb.inherited = geërfd +gb.deleteRepository = Verwijder repositorie \"{0}\"? +gb.repositoryDeleted = Repositorie ''{0}'' verwijderd. +gb.repositoryDeleteFailed = Verwijdering van repositorie ''{0}'' mislukt! +gb.deleteUser = Verwijder gebruiker \"{0}\"? +gb.userDeleted = Gebruiker ''{0}'' verwijderd. +gb.userDeleteFailed = FVerwijdering van gebruiker ''{0}'' mislukt! +gb.time.justNow = net +gb.time.today = vandaag +gb.time.yesterday = gisteren +gb.time.minsAgo = {0} minuten geleden +gb.time.hoursAgo = {0} uren geleden +gb.time.daysAgo = {0} dagen geleden +gb.time.weeksAgo = {0} weken geleden +gb.time.monthsAgo = {0} maanden geleden +gb.time.oneYearAgo = 1 jaar geleden +gb.time.yearsAgo = {0} jaren geleden +gb.duration.oneDay = 1 dag +gb.duration.days = {0} dagen +gb.duration.oneMonth = 1 maand +gb.duration.months = {0} maanden +gb.duration.oneYear = 1 jaar +gb.duration.years = {0} jaren +gb.authorizationControl = authorisatie beheer +gb.allowAuthenticatedDescription = ken RW+ rechten toe aan alle geauthenticeerde gebruikers +gb.allowNamedDescription = ken verfijnde rechten toe aan genoemde gebruikers of teams +gb.markdownFailure = Het parsen van Markdown content is mislukt! +gb.clearCache = maak cache leeg +gb.projects = projecten gb.project = project -gb.allProjects = all projects -gb.copyToClipboard = copy to clipboard +gb.allProjects = alle projecten +gb.copyToClipboard = kopieer naar clipboard gb.fork = fork gb.forks = forks gb.forkRepository = fork {0}? -gb.repositoryForked = {0} has been forked -gb.repositoryForkFailed= fork has failed -gb.personalRepositories = personal repositories -gb.allowForks = allow forks -gb.allowForksDescription = allow authorized users to fork this repository -gb.forkedFrom = forked from -gb.canFork = can fork -gb.canForkDescription = can fork authorized repositories to personal repositories -gb.myFork = view my fork -gb.forksProhibited = forks prohibited -gb.forksProhibitedWarning = this repository forbids forks -gb.noForks = {0} has no forks -gb.forkNotAuthorized = sorry, you are not authorized to fork {0} -gb.forkInProgress = fork in progress -gb.preparingFork = preparing your fork... -gb.isFork = is fork -gb.canCreate = can create -gb.canCreateDescription = can create personal repositories -gb.illegalPersonalRepositoryLocation = your personal repository must be located at \"{0}\" -gb.verifyCommitter = verify committer -gb.verifyCommitterDescription = require committer identity to match pushing Gitblt user account -gb.verifyCommitterNote = all merges require "--no-ff" to enforce committer identity -gb.repositoryPermissions = repository permissions -gb.userPermissions = user permissions -gb.teamPermissions = team permissions -gb.add = add -gb.noPermission = DELETE THIS PERMISSION +gb.repositoryForked = {0} is geforked +gb.repositoryForkFailed= fork is mislukt +gb.personalRepositories = personlijke repositories +gb.allowForks = sta forks toe +gb.allowForksDescription = sta geauthoriseerde gebruikers toe om deze repositorie te forken +gb.forkedFrom = geforked vanaf +gb.canFork = kan geforked worden +gb.canForkDescription = kan geauthoriseerde repositories forken naar personlijke repositories +gb.myFork = toon mijn fork +gb.forksProhibited = forks niet toegestaan +gb.forksProhibitedWarning = deze repositorie staat forken niet toe +gb.noForks = {0} heeft geen forks +gb.forkNotAuthorized = sorry, u bent niet geautoriseerd voor het forken van {0} +gb.forkInProgress = bezig met forken +gb.preparingFork = bezig met het maken van uw fork... +gb.isFork = is een fork +gb.canCreate = mag maken +gb.canCreateDescription = mag persoonlijke repositories maken +gb.illegalPersonalRepositoryLocation = uw persoonlijke repository moet te vinden zijn op \"{0}\" +gb.verifyCommitter = controleer committer +gb.verifyCommitterDescription = vereis dat committer identiteit overeen komt met pushing Gitblt gebruikersaccount +gb.verifyCommitterNote = alle merges vereisen "--no-ff" om committer identiteit af te dwingen +gb.repositoryPermissions = repository rechten +gb.userPermissions = gebruikersrechten +gb.teamPermissions = teamrechten +gb.add = toevoegen +gb.noPermission = VERWIJDER DIT RECHT gb.excludePermission = {0} (exclude) gb.viewPermission = {0} (view) gb.clonePermission = {0} (clone) gb.pushPermission = {0} (push) -gb.createPermission = {0} (push, ref creation) -gb.deletePermission = {0} (push, ref creation+deletion) -gb.rewindPermission = {0} (push, ref creation+deletion+rewind) -gb.permission = permission -gb.regexPermission = this permission is set from regular expression \"{0}\" -gb.accessDenied = access denied -gb.busyCollectingGarbage = sorry, Gitblit is busy collecting garbage in {0} -gb.gcPeriod = GC period -gb.gcPeriodDescription = duration between garbage collections -gb.gcThreshold = GC threshold -gb.gcThresholdDescription = minimum total size of loose objects to trigger early garbage collection -gb.ownerPermission = repository owner -gb.administrator = admin -gb.administratorPermission = Gitblit administrator +gb.createPermission = {0} (push, ref creer) +gb.deletePermission = {0} (push, ref creer+verwijdering) +gb.rewindPermission = {0} (push, ref creer+verwijdering+rewind) +gb.permission = recht +gb.regexPermission = dit recht is gezet vanaf de reguliere expressie \"{0}\" +gb.accessDenied = toegang geweigerd +gb.busyCollectingGarbage = sorry, Gitblit is bezig met opruimen in {0} +gb.gcPeriod = opruim periode +gb.gcPeriodDescription = tijdsduur tussen opruimacties +gb.gcThreshold = opruim drempel +gb.gcThresholdDescription = minimum totaalomvang van losse objecten voor het starten van opruimactie +gb.ownerPermission = repository eigenaar +gb.administrator = beheer +gb.administratorPermission = Gitblit beheerder gb.team = team -gb.teamPermission = permission set by \"{0}\" team membership -gb.missing = missing! -gb.missingPermission = the repository for this permission is missing! -gb.mutable = mutable -gb.specified = specified -gb.effective = effective -gb.organizationalUnit = organizational unit -gb.organization = organization -gb.locality = locality -gb.stateProvince = state or province -gb.countryCode = country code -gb.properties = properties -gb.issued = issued -gb.expires = expires -gb.expired = expired -gb.expiring = expiring -gb.revoked = revoked -gb.serialNumber = serial number -gb.certificates = certificates -gb.newCertificate = new certificate -gb.revokeCertificate = revoke certificate -gb.sendEmail = send email -gb.passwordHint = password hint +gb.teamPermission = permissie ingesteld via \"{0}\" teamlidmaatschap +gb.missing = ontbrekend! +gb.missingPermission = de repository voor deze permissie ontbreekt! +gb.mutable = te wijzigen +gb.specified = gespecificeerd +gb.effective = geldig +gb.organizationalUnit = organizatie eenheid +gb.organization = organizatie +gb.locality = localiteit +gb.stateProvince = staat of provincie +gb.countryCode = landcode +gb.properties = eigenschappen +gb.issued = uitgegeven +gb.expires = verloopt op +gb.expired = verlopen +gb.expiring = verloopt +gb.revoked = ingetrokken +gb.serialNumber = serie nummer +gb.certificates = certificaten +gb.newCertificate = nieuwe certificaten +gb.revokeCertificate = trek certificate in +gb.sendEmail = zend email +gb.passwordHint = wachtwoord hint gb.ok = ok -gb.invalidExpirationDate = invalid expiration date! -gb.passwordHintRequired = password hint required! -gb.viewCertificate = view certificate -gb.subject = subject +gb.invalidExpirationDate = ongeldige verloopdatum! +gb.passwordHintRequired = wachtwoord hint vereist! +gb.viewCertificate = toon certificate +gb.subject = onderwerp gb.issuer = issuer -gb.validFrom = valid from -gb.validUntil = valid until -gb.publicKey = public key -gb.signatureAlgorithm = signature algorithm +gb.validFrom = geldig vanaf +gb.validUntil = geldig tot +gb.publicKey = publieke sleutel +gb.signatureAlgorithm = signature algoritme gb.sha1FingerPrint = SHA-1 Fingerprint gb.md5FingerPrint = MD5 Fingerprint -gb.reason = reason -gb.revokeCertificateReason = Please select a reason for certificate revocation -gb.unspecified = unspecified -gb.keyCompromise = key compromise -gb.caCompromise = CA compromise -gb.affiliationChanged = affiliation changed -gb.superseded = superseded -gb.cessationOfOperation = cessation of operation -gb.privilegeWithdrawn = privilege withdrawn -gb.time.inMinutes = in {0} mins -gb.time.inHours = in {0} hours -gb.time.inDays = in {0} days -gb.hostname = hostname -gb.hostnameRequired = Please enter a hostname -gb.newSSLCertificate = new server SSL certificate -gb.newCertificateDefaults = new certificate defaults -gb.duration = duration -gb.certificateRevoked = Certificate {0,number,0} has been revoked -gb.clientCertificateGenerated = Successfully generated new client certificate for {0} -gb.sslCertificateGenerated = Successfully generated new server SSL certificate for {0} -gb.newClientCertificateMessage = NOTE:\nThe 'password' is not the user's password, it is the password to protect the user's keystore. This password is not saved so you must also enter a 'hint' which will be included in the user's README instructions. -gb.certificate = certificate -gb.emailCertificateBundle = email client certificate bundle -gb.pleaseGenerateClientCertificate = Please generate a client certificate for {0} -gb.clientCertificateBundleSent = Client certificate bundle for {0} sent -gb.enterKeystorePassword = Please enter the Gitblit keystore password -gb.warning = warning -gb.jceWarning = Your Java Runtime Environment does not have the \"JCE Unlimited Strength Jurisdiction Policy\" files.\nThis will limit the length of passwords you may use to encrypt your keystores to 7 characters.\nThese policy files are an optional download from Oracle.\n\nWould you like to continue and generate the certificate infrastructure anyway?\n\nAnswering No will direct your browser to Oracle's download page so that you may download the policy files. -gb.maxActivityCommits = max activity commits -gb.maxActivityCommitsDescription = maximum number of commits to contribute to the Activity page -gb.noMaximum = no maximum -gb.attributes = attributes -gb.serveCertificate = serve https with this certificate -gb.sslCertificateGeneratedRestart = Successfully generated new server SSL certificate for {0}.\nYou must restart Gitblit to use the new certificate.\n\nIf you are launching with the '--alias' parameter you will have to set that to ''--alias {0}''. -gb.validity = validity -gb.siteName = site name -gb.siteNameDescription = short, descriptive name of your server -gb.excludeFromActivity = exclude from activity page \ No newline at end of file +gb.reason = reden +gb.revokeCertificateReason = Kies aub een reden voor het intrekken van het certificaat +gb.unspecified = niet gespecificeerd +gb.keyCompromise = sleutel gecompromiteerd +gb.caCompromise = CA gecompromiteerd +gb.affiliationChanged = affiliatie gewijzigd +gb.superseded = opgevolgd +gb.cessationOfOperation = gestaakt +gb.privilegeWithdrawn = privilege ingetrokken +gb.time.inMinutes = in {0} minuten +gb.time.inHours = in {0} uren +gb.time.inDays = in {0} dagen +gb.hostname = hostnaam +gb.hostnameRequired = Vul aub een hostnaam in +gb.newSSLCertificate = nieuw server SSL certificaat +gb.newCertificateDefaults = nieuw certificaat defaults +gb.duration = duur +gb.certificateRevoked = Certificaat {0,number,0} is ingetrokken +gb.clientCertificateGenerated = Nieuw client certificaat voor {0} succesvol gegenereerd +gb.sslCertificateGenerated = Nieuw server SSL certificaat voor {0} succesvol gegenereerd +gb.newClientCertificateMessage = MERK OP:\nHet 'wachtwoord' is niet het wachtwoord van de gebruiker. Het is het wachtwoord voor het afschermen van de sleutelring van de grbruiker. Dit wachtwoord wordt niet opgeslagen dus moet u ook een 'hint' invullen die zal worden opgenomen in de README instructies van de gebruiker. +gb.certificate = certificaat +gb.emailCertificateBundle = email client certificaat bundel +gb.pleaseGenerateClientCertificate = Genereer aub een client certificaat voor {0} +gb.clientCertificateBundleSent = Client certificaat bundel voor {0} verzonden +gb.enterKeystorePassword = Vul aub het Gitblit keystore wachtwoord in +gb.warning = waarschuwing +gb.jceWarning = Uw Java Runtime Environment heeft geen \"JCE Unlimited Strength Jurisdiction Policy\" bestanden.\nDit zal de lengte van wachtwoorden voor het eventueel versleutelen van uw keystores beperken tot 7 karakters.\nDeze policy bestanden zijn een optionele download van Oracle.\n\nWilt u toch doorgaan en de certificaat infrastructuur genereren?\n\nNee antwoorden zal uw browser doorsturen naar de downloadpagina van Oracle zodat u de policybestanden kunt downloaden. +gb.maxActivityCommits = maximum activiteit commits +gb.maxActivityCommitsDescription = maximum aantal commits om bij te dragen aan de Activiteitspagina +gb.noMaximum = geen maximum +gb.attributes = attributen +gb.serveCertificate = gebruik deze certificaten voor https +gb.sslCertificateGeneratedRestart = Nieuwe SSL certificaten voor {0} succesvol gegenereerd.\nU dient Gitblit te herstarten om de nieuwe certificaten te gebruiken.\n\nAls u opstart met de '--alias' parameter moet u die wijzigen naar ''--alias {0}''. +gb.validity = geldigheid +gb.siteName = site naam +gb.siteNameDescription = korte, verduidelijkende naam van deze server +gb.excludeFromActivity = sluit uit van activiteitspagina -- cgit v1.2.3 From 0d93c00b3cce518b2c66f182e3e589f741dc23d0 Mon Sep 17 00:00:00 2001 From: Jeroen Baten Date: Fri, 4 Jan 2013 11:26:02 +0100 Subject: seems ok --- src/com/gitblit/wicket/GitBlitWebApp_nl.properties | 100 ++++++++++----------- 1 file changed, 50 insertions(+), 50 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/GitBlitWebApp_nl.properties b/src/com/gitblit/wicket/GitBlitWebApp_nl.properties index ee45b86c..5471ad8a 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp_nl.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp_nl.properties @@ -1,4 +1,4 @@ -gb.repository = repository +gb.repository = repositorie gb.owner = eigenaar gb.description = omschrijving gb.lastChange = laatste wijziging @@ -31,26 +31,26 @@ gb.allTags = alle tags... gb.allBranches = alle branches... gb.summary = samenvatting gb.ticket = ticket -gb.newRepository = nieuwe repository +gb.newRepository = nieuwe repositorie gb.newUser = nieuwe gebruiker gb.commitdiff = commitdiff gb.tickets = tickets -gb.pageFirst = first -gb.pagePrevious prev -gb.pageNext = next +gb.pageFirst = eerste +gb.pagePrevious = vorige +gb.pageNext = volgende gb.head = HEAD gb.blame = blame -gb.login = login -gb.logout = logout +gb.login = aanmelden +gb.logout = afmelden gb.username = gebruikersnaam gb.password = wachtwoord gb.tagger = tagger -gb.moreHistory = meer history... +gb.moreHistory = meer historie... gb.difftocurrent = diff naar current gb.search = zoeken gb.searchForAuthor = Zoeken naar commits authored door gb.searchForCommitter = Zoeken naar commits committed door -gb.addition = addition +gb.addition = additie gb.modification = wijziging gb.deletion = verwijdering gb.rename = hernoem @@ -84,10 +84,10 @@ gb.pushRestricted = geauthenticeerde push gb.cloneRestricted = geauthenticeerde clone & push gb.viewRestricted = geauthenticeerde view, clone, & push gb.useTicketsDescription = readonly, gedistribueerde Ticgit issues -gb.useDocsDescription = enumereer Markdown documentatie in repository +gb.useDocsDescription = enumereer Markdown documentatie in repositorie gb.showRemoteBranchesDescription = toon remote branches gb.canAdminDescription = kan Gitblit server beheren -gb.permittedUsers = toegestande gebruikers +gb.permittedUsers = toegestane gebruikers gb.isFrozen = is bevroren gb.isFrozenDescription = weiger push operaties gb.zip = zip @@ -98,12 +98,12 @@ gb.ownerDescription = de eigenaar mag repository instellingen wijzigen gb.blob = blob gb.commitActivityTrend = commit activiteit trend gb.commitActivityDOW = commit activiteit per dag van de week -gb.commitActivityAuthors = primare auteurs op basis van commit activiteit +gb.commitActivityAuthors = primaire auteurs op basis van commit activiteit gb.feed = feed gb.cancel = afbreken gb.changePassword = wijzig wachtwoord gb.isFederated = is gefedereerd -gb.federateThis = federeer deze repository +gb.federateThis = federeer deze repositorie gb.federateOrigin = federeer deze origin gb.excludeFromFederation = uitsluiten van federatie gb.excludeFromFederationDescription = sluit gefedereerde Gitblit instances uit van het pullen van dit account @@ -111,7 +111,7 @@ gb.tokens = federatie tokens gb.tokenAllDescription = alle repositories, gebruikers, & instellingen gb.tokenUnrDescription = alle repositories & gebruikers gb.tokenJurDescription = alle repositories -gb.federatedRepositoryDefinitions = repository definities +gb.federatedRepositoryDefinitions = repositorie definities gb.federatedUserDefinitions = gebruikersdefinities gb.federatedSettingDefinitions = instellingendefinities gb.proposals = federatie voorstellen @@ -155,8 +155,8 @@ gb.available = beschikbaar gb.selected = geselecteerd gb.size = grootte gb.downloading = downloading -gb.loading = loading -gb.starting = starting +gb.loading = laden +gb.starting = starten gb.general = algemeen gb.settings = instellingen gb.manage = beheer @@ -196,8 +196,8 @@ gb.teamMembers = teamleden gb.teamMemberships = teamlidmaatschappen gb.newTeam = nieuw team gb.permittedTeams = toegestane teams -gb.emptyRepository = lege repository -gb.repositoryUrl = repository url +gb.emptyRepository = lege repositorie +gb.repositoryUrl = repositorie url gb.mailingLists = mailing lijsten gb.preReceiveScripts = pre-receive scripts gb.postReceiveScripts = post-receive scripts @@ -210,19 +210,19 @@ gb.generalDescription = algemene instellingen gb.accessPermissionsDescription = beperk toegang voor gebruikers en teams gb.accessPermissionsForUserDescription = stel teamlidmaatschappen in of geef toegang tot specifieke besloten repositories gb.accessPermissionsForTeamDescription = stel teamlidmaatschappen in en geef toegang tot specifieke besloten repositories -gb.federationRepositoryDescription = deel deze repository met andere Gitblit servers +gb.federationRepositoryDescription = deel deze repositorie met andere Gitblit servers gb.hookScriptsDescription = run Groovy scripts bij pushes naar deze Gitblit server gb.reset = reset gb.pages = paginas gb.workingCopy = werkkopie -gb.workingCopyWarning = deze repository heeft een werkkopie en kan geen pushes ontvangen +gb.workingCopyWarning = deze repositorie heeft een werkkopie en kan geen pushes ontvangen gb.query = query gb.queryHelp = Standaard query syntax wordt ondersteund.

Zie aub Lucene Query Parser Syntax voor informatie. gb.queryResults = resultaten {0} - {1} ({2} hits) gb.noHits = geen hits gb.authored = authored gb.committed = committed -gb.indexedBranches = geindexeerde branches +gb.indexedBranches = geïndexeerde branches gb.indexedBranchesDescription = kies de branches voor opname in uw Lucene index gb.noIndexedRepositoriesWarning = geen van uw repositories is geconfigureerd voor Lucene indexering gb.undefinedQueryWarning = query is niet gedefinieerd! @@ -235,28 +235,28 @@ gb.passwordsDoNotMatch = Wachtwoorden komen niet overeen! gb.passwordTooShort = Wachtwoord is te kort. Minimum lengte is {0} karakters. gb.passwordChanged = Wachtwoord succesvol gewijzigd. gb.passwordChangeAborted = Wijziging wachtwoord afgebroken. -gb.pleaseSetRepositoryName = Vul aub een repository naam in! +gb.pleaseSetRepositoryName = Vul aub een repositorie naam in! gb.illegalLeadingSlash = Leidende root folder referenties (/) zijn niet toegestaan. gb.illegalRelativeSlash = Relatieve folder referenties (../) zijn niet toegestaan. -gb.illegalCharacterRepositoryName = Illegaal karakter ''{0}'' in repository naam! +gb.illegalCharacterRepositoryName = Illegaal karakter ''{0}'' in repositorie naam! gb.selectAccessRestriction = Stel aub een toegangsbeperking in! gb.selectFederationStrategy = Selecteer aub een federatie strategie! gb.pleaseSetTeamName = Vul aub een teamnaam in! gb.teamNameUnavailable = Teamnaam ''{0}'' is niet beschikbaar. -gb.teamMustSpecifyRepository = Een team moet minimaal één repository specificeren. +gb.teamMustSpecifyRepository = Een team moet minimaal één repositorie specificeren. gb.teamCreated = Nieuw team ''{0}'' successvol aangemaakt. gb.pleaseSetUsername = Vul aub een gebruikersnaam in! gb.usernameUnavailable = Gebruikersnaam ''{0}'' is niet beschikbaar. gb.combinedMd5Rename = Gitblit is geconfigureerd voor combined-md5 wachtwoord hashing. U moet een nieuw wachtwoord opgeven bij het hernoemen van een account. -gb.userCreated = Nieuwe gebruiker ''{0}'' successvol aangemaakt. +gb.userCreated = Nieuwe gebruiker ''{0}'' succesvol aangemaakt. gb.couldNotFindFederationRegistration = Kon de federatie registratie niet vinden! gb.failedToFindGravatarProfile = Kon het Gravatar profiel voor {0} niet vinden gb.branchStats = {0} commits en {1} tags in {2} -gb.repositoryNotSpecified = Repository niet gespecificeerd! -gb.repositoryNotSpecifiedFor = Repository niet gespecificeerd voor {0}! -gb.canNotLoadRepository = Kan repository niet laden +gb.repositoryNotSpecified = Repositorie niet gespecificeerd! +gb.repositoryNotSpecifiedFor = Repositorie niet gespecificeerd voor {0}! +gb.canNotLoadRepository = Kan repositorie niet laden gb.commitIsNull = Commit is null -gb.unauthorizedAccessForRepository = Niet toegestande toegang tot repository +gb.unauthorizedAccessForRepository = Niet toegestane toegang tot repositorie gb.failedToFindCommit = Het vinden van commit \"{0}\" in {1} voor {2} pagina is mislukt! gb.couldNotFindFederationProposal = Kon federatievoorstel niet vinden! gb.invalidUsernameOrPassword = Onjuiste gebruikersnaam of wachtwoord! @@ -265,21 +265,21 @@ gb.nFederationProposalsToReview = Er zijn {0} federatie verzoeken die wachten op gb.couldNotFindTag = Kon tag {0} niet vinden gb.couldNotCreateFederationProposal = Kon geen federatie voorstel maken! gb.pleaseSetGitblitUrl = Vul aub uw Gitblit url in! -gb.pleaseSetDestinationUrl = Vul aub een bestemmings url in voor uw voorstel! +gb.pleaseSetDestinationUrl = Vul aub een bestemmings-url in voor uw voorstel! gb.proposalReceived = Voorstel correct ontvangen door {0}. gb.noGitblitFound = Sorry, {0} kon geen Gitblit instance vinden op {1}. gb.noProposals = Sorry, {0} accepteert geen voorstellen op dit moment. gb.noFederation = Sorry, {0} is niet geconfigureerd voor het federeren met een Gitblit instance. gb.proposalFailed = Sorry, {0} ontving geen voorstelgegevens! -gb.proposalError = Sorry, {0} rapporteerd dat een onverwachte fout is opgetreden! +gb.proposalError = Sorry, {0} rapporteert dat een onverwachte fout is opgetreden! gb.failedToSendProposal = Voorstel verzenden is niet gelukt! gb.userServiceDoesNotPermitAddUser = {0} staat het toevoegen van een gebruikersaccount niet toe! gb.userServiceDoesNotPermitPasswordChanges = {0} staat wachtwoord wijzigingen niet toe! gb.displayName = display naam gb.emailAddress = emailadres gb.errorAdminLoginRequired = Aanmelden vereist voor beheerwerk -gb.errorOnlyAdminMayCreateRepository = Alleen een beheerder kan een repository maken -gb.errorOnlyAdminOrOwnerMayEditRepository = Alleen een beheerder of de eigenaar kan een repository wijzigen +gb.errorOnlyAdminMayCreateRepository = Alleen een beheerder kan een repositorie maken +gb.errorOnlyAdminOrOwnerMayEditRepository = Alleen een beheerder of de eigenaar kan een repositorie wijzigen gb.errorAdministrationDisabled = Beheer is uitgeschakeld gb.lastNDays = laatste {0} dagen gb.completeGravatarProfile = Completeer profiel op Gravatar.com @@ -293,7 +293,7 @@ gb.repositoryDeleted = Repositorie ''{0}'' verwijderd. gb.repositoryDeleteFailed = Verwijdering van repositorie ''{0}'' mislukt! gb.deleteUser = Verwijder gebruiker \"{0}\"? gb.userDeleted = Gebruiker ''{0}'' verwijderd. -gb.userDeleteFailed = FVerwijdering van gebruiker ''{0}'' mislukt! +gb.userDeleteFailed = Verwijdering van gebruiker ''{0}'' mislukt! gb.time.justNow = net gb.time.today = vandaag gb.time.yesterday = gisteren @@ -310,8 +310,8 @@ gb.duration.oneMonth = 1 maand gb.duration.months = {0} maanden gb.duration.oneYear = 1 jaar gb.duration.years = {0} jaren -gb.authorizationControl = authorisatie beheer -gb.allowAuthenticatedDescription = ken RW+ rechten toe aan alle geauthenticeerde gebruikers +gb.authorizationControl = authorisatiebeheer +gb.allowAuthenticatedDescription = ken RW+ rechten toe aan alle geautoriseerde gebruikers gb.allowNamedDescription = ken verfijnde rechten toe aan genoemde gebruikers of teams gb.markdownFailure = Het parsen van Markdown content is mislukt! gb.clearCache = maak cache leeg @@ -329,7 +329,7 @@ gb.allowForks = sta forks toe gb.allowForksDescription = sta geauthoriseerde gebruikers toe om deze repositorie te forken gb.forkedFrom = geforked vanaf gb.canFork = kan geforked worden -gb.canForkDescription = kan geauthoriseerde repositories forken naar personlijke repositories +gb.canForkDescription = kan geauthoriseerde repositories forken naar persoonlijke repositories gb.myFork = toon mijn fork gb.forksProhibited = forks niet toegestaan gb.forksProhibitedWarning = deze repositorie staat forken niet toe @@ -340,7 +340,7 @@ gb.preparingFork = bezig met het maken van uw fork... gb.isFork = is een fork gb.canCreate = mag maken gb.canCreateDescription = mag persoonlijke repositories maken -gb.illegalPersonalRepositoryLocation = uw persoonlijke repository moet te vinden zijn op \"{0}\" +gb.illegalPersonalRepositoryLocation = uw persoonlijke repositorie moet te vinden zijn op \"{0}\" gb.verifyCommitter = controleer committer gb.verifyCommitterDescription = vereis dat committer identiteit overeen komt met pushing Gitblt gebruikersaccount gb.verifyCommitterNote = alle merges vereisen "--no-ff" om committer identiteit af te dwingen @@ -353,9 +353,9 @@ gb.excludePermission = {0} (exclude) gb.viewPermission = {0} (view) gb.clonePermission = {0} (clone) gb.pushPermission = {0} (push) -gb.createPermission = {0} (push, ref creer) -gb.deletePermission = {0} (push, ref creer+verwijdering) -gb.rewindPermission = {0} (push, ref creer+verwijdering+rewind) +gb.createPermission = {0} (push, ref creëer) +gb.deletePermission = {0} (push, ref creëer+verwijdering) +gb.rewindPermission = {0} (push, ref creëer+verwijdering+rewind) gb.permission = recht gb.regexPermission = dit recht is gezet vanaf de reguliere expressie \"{0}\" gb.accessDenied = toegang geweigerd @@ -364,18 +364,18 @@ gb.gcPeriod = opruim periode gb.gcPeriodDescription = tijdsduur tussen opruimacties gb.gcThreshold = opruim drempel gb.gcThresholdDescription = minimum totaalomvang van losse objecten voor het starten van opruimactie -gb.ownerPermission = repository eigenaar +gb.ownerPermission = repositorie eigenaar gb.administrator = beheer gb.administratorPermission = Gitblit beheerder gb.team = team gb.teamPermission = permissie ingesteld via \"{0}\" teamlidmaatschap gb.missing = ontbrekend! -gb.missingPermission = de repository voor deze permissie ontbreekt! +gb.missingPermission = de repositorie voor deze permissie ontbreekt! gb.mutable = te wijzigen gb.specified = gespecificeerd gb.effective = geldig -gb.organizationalUnit = organizatie eenheid -gb.organization = organizatie +gb.organizationalUnit = organisatie eenheid +gb.organization = organisatie gb.locality = localiteit gb.stateProvince = staat of provincie gb.countryCode = landcode @@ -388,13 +388,13 @@ gb.revoked = ingetrokken gb.serialNumber = serie nummer gb.certificates = certificaten gb.newCertificate = nieuwe certificaten -gb.revokeCertificate = trek certificate in +gb.revokeCertificate = trek certificaat in gb.sendEmail = zend email gb.passwordHint = wachtwoord hint gb.ok = ok gb.invalidExpirationDate = ongeldige verloopdatum! gb.passwordHintRequired = wachtwoord hint vereist! -gb.viewCertificate = toon certificate +gb.viewCertificate = toon certificaat gb.subject = onderwerp gb.issuer = issuer gb.validFrom = geldig vanaf @@ -406,8 +406,8 @@ gb.md5FingerPrint = MD5 Fingerprint gb.reason = reden gb.revokeCertificateReason = Kies aub een reden voor het intrekken van het certificaat gb.unspecified = niet gespecificeerd -gb.keyCompromise = sleutel gecompromiteerd -gb.caCompromise = CA gecompromiteerd +gb.keyCompromise = sleutel gecompromitteerd +gb.caCompromise = CA gecompromitteerd gb.affiliationChanged = affiliatie gewijzigd gb.superseded = opgevolgd gb.cessationOfOperation = gestaakt @@ -423,7 +423,7 @@ gb.duration = duur gb.certificateRevoked = Certificaat {0,number,0} is ingetrokken gb.clientCertificateGenerated = Nieuw client certificaat voor {0} succesvol gegenereerd gb.sslCertificateGenerated = Nieuw server SSL certificaat voor {0} succesvol gegenereerd -gb.newClientCertificateMessage = MERK OP:\nHet 'wachtwoord' is niet het wachtwoord van de gebruiker. Het is het wachtwoord voor het afschermen van de sleutelring van de grbruiker. Dit wachtwoord wordt niet opgeslagen dus moet u ook een 'hint' invullen die zal worden opgenomen in de README instructies van de gebruiker. +gb.newClientCertificateMessage = MERK OP:\nHet 'wachtwoord' is niet het wachtwoord van de gebruiker. Het is het wachtwoord voor het afschermen van de sleutelring van de gebruiker. Dit wachtwoord wordt niet opgeslagen dus moet u ook een 'hint' invullen die zal worden opgenomen in de README instructies van de gebruiker. gb.certificate = certificaat gb.emailCertificateBundle = email client certificaat bundel gb.pleaseGenerateClientCertificate = Genereer aub een client certificaat voor {0} -- cgit v1.2.3 From 8427e93f25fb79bc39dbd31ce13fab637e7cfdff Mon Sep 17 00:00:00 2001 From: Jeroen Baten Date: Fri, 4 Jan 2013 11:34:24 -0500 Subject: Completed Dutch translation --- docs/01_features.mkd | 1 + resources/login_nl.mkd | 3 ++ resources/welcome_nl.mkd | 3 ++ .../wicket/pages/EmptyRepositoryPage_nl.html | 53 ++++++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 resources/login_nl.mkd create mode 100644 resources/welcome_nl.mkd create mode 100644 src/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html (limited to 'src/com/gitblit') diff --git a/docs/01_features.mkd b/docs/01_features.mkd index a28b97a9..a768f754 100644 --- a/docs/01_features.mkd +++ b/docs/01_features.mkd @@ -57,6 +57,7 @@ - Polish - Korean - Brazilian Portuguese + - Dutch ## Gitblit GO Features - Out-of-the-box integrated stack requiring minimal configuration diff --git a/resources/login_nl.mkd b/resources/login_nl.mkd new file mode 100644 index 00000000..38224c96 --- /dev/null +++ b/resources/login_nl.mkd @@ -0,0 +1,3 @@ +## Aanmelden aub + +Vul aub uw aanmeldgegevens in voor toegang tot deze Gitblit site. diff --git a/resources/welcome_nl.mkd b/resources/welcome_nl.mkd new file mode 100644 index 00000000..406ca1e0 --- /dev/null +++ b/resources/welcome_nl.mkd @@ -0,0 +1,3 @@ +## Welkom bij Gitblit + +Een snelle en makkelijke manier voor het hosten en bekijken van uw eigen [Git](http://www.git-scm.com) repositories. diff --git a/src/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html b/src/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html new file mode 100644 index 00000000..a8ee2e25 --- /dev/null +++ b/src/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html @@ -0,0 +1,53 @@ + + + + + + +

Empty Repository

+

+
+
+
+ [repository] is een lege repositorie en kan niet bekeken worden door Gitblit. +

+ Push aub een paar commitsome commits naar +

+
+ Nadat u een paar commits gepushed hebt kunt u deze pagina verversen om de repository te bekijken. +
+
+
+ +

Git Command-Line Syntax

+ Als u geen lokale Git repositorie heeft, kunt u deze repository clonen, er een paar bestanden naar committen en deze commits teug pushen naar Gitblit. +

+

+		

+ Als u al een lokale Git repositorie heeft met commits kunt u deze repository als een remote toevoegen en er naar toe pushen. +

+

+		

+

Learn Git

+ Als u niet goed weet wat u met deze informatie aan moet raden we aan om het Git Community Book of Pro Git te bestuderen voor een betere begrip van hoe u Git kunt gebruiken. +

+

Open Source Git Clients

+
    +
  • Git - de officiele, command-line Git
  • +
  • TortoiseGit - Windows bestandsverkenner ingetratie (officiele command-line Git is wel nodig)
  • +
  • Eclipse/EGit - Git voor de Eclipse IDE (gebaseerd op JGit, zoals Gitblit)
  • +
  • Git Extensions - C# frontend voor Git met Windows Explorer en Visual Studio integratie
  • +
  • GitX (L) - een Mac OS X Git client
  • +
+

+

Commercial/Closed-Source Git Clients

+
    +
  • SmartGit - Een Java Git, Mercurial, en SVN client applicatie (officiele command-line Git is wel nodig)
  • +
  • SourceTree - Een gratis Mac Client voor Git, Mercurial, en SVN
  • +
+ + + -- cgit v1.2.3 From 4e3c152fa7e97200855ba0d2716362dbe7976920 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 4 Jan 2013 17:23:23 -0500 Subject: Support local accounts with LdapUserService and RedmineUserService (issue-183) --- docs/04_releases.mkd | 1 + src/com/gitblit/ConfigUserService.java | 4 ++ src/com/gitblit/Constants.java | 8 ++++ src/com/gitblit/GitBlit.java | 32 ++++++++++---- src/com/gitblit/GitblitUserService.java | 49 ++++++++++++++++++--- src/com/gitblit/LdapUserService.java | 18 ++++++-- src/com/gitblit/RedmineUserService.java | 51 +++++++++++++++++----- src/com/gitblit/client/UsersPanel.java | 4 +- src/com/gitblit/client/UsersTableModel.java | 19 +++++--- src/com/gitblit/models/UserModel.java | 8 ++++ src/com/gitblit/wicket/pages/BasePage.java | 2 +- .../gitblit/wicket/pages/ChangePasswordPage.java | 5 ++- src/com/gitblit/wicket/pages/EditTeamPage.java | 2 +- src/com/gitblit/wicket/pages/EditUserPage.java | 12 ++--- src/com/gitblit/wicket/panels/TeamsPanel.java | 2 +- src/com/gitblit/wicket/panels/UsersPanel.html | 4 +- src/com/gitblit/wicket/panels/UsersPanel.java | 4 +- tests/com/gitblit/tests/LdapUserServiceTest.java | 16 +++++++ .../com/gitblit/tests/RedmineUserServiceTest.java | 24 +++++++++- 19 files changed, 211 insertions(+), 54 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index 6aec85c2..d5e777ca 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -12,6 +12,7 @@ #### additions +- Support for locally and remotely authenticated accounts in LdapUserService and RedmineUserService (issue 183) - Added Dutch translation (github/kwoot) #### changes diff --git a/src/com/gitblit/ConfigUserService.java b/src/com/gitblit/ConfigUserService.java index 068bbe3a..67ad0537 100644 --- a/src/com/gitblit/ConfigUserService.java +++ b/src/com/gitblit/ConfigUserService.java @@ -409,6 +409,10 @@ public class ConfigUserService implements IUserService { // Read realm file read(); UserModel model = users.remove(username.toLowerCase()); + if (model == null) { + // user does not exist + return false; + } // remove user from team for (TeamModel team : model.teams) { TeamModel t = teams.get(team.name); diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index f2067f69..ca332690 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -405,6 +405,14 @@ public class Constants { return ordinal() <= COOKIE.ordinal(); } } + + public static enum AccountType { + LOCAL, LDAP, REDMINE; + + public boolean isLocal() { + return this == LOCAL; + } + } @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 30071bb6..74d32df0 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -471,36 +471,48 @@ public class GitBlit implements ServletContextListener { this.userService.setup(settings); } + public boolean supportsAddUser() { + return supportsCredentialChanges(new UserModel("")); + } + /** + * Returns true if the user's credentials can be changed. * + * @param user * @return true if the user service supports credential changes */ - public boolean supportsCredentialChanges() { - return userService.supportsCredentialChanges(); + public boolean supportsCredentialChanges(UserModel user) { + return (user != null && user.isLocalAccount()) || userService.supportsCredentialChanges(); } /** + * Returns true if the user's display name can be changed. * + * @param user * @return true if the user service supports display name changes */ - public boolean supportsDisplayNameChanges() { - return userService.supportsDisplayNameChanges(); + public boolean supportsDisplayNameChanges(UserModel user) { + return (user != null && user.isLocalAccount()) || userService.supportsDisplayNameChanges(); } /** + * Returns true if the user's email address can be changed. * + * @param user * @return true if the user service supports email address changes */ - public boolean supportsEmailAddressChanges() { - return userService.supportsEmailAddressChanges(); + public boolean supportsEmailAddressChanges(UserModel user) { + return (user != null && user.isLocalAccount()) || userService.supportsEmailAddressChanges(); } /** + * Returns true if the user's team memberships can be changed. * + * @param user * @return true if the user service supports team membership changes */ - public boolean supportsTeamMembershipChanges() { - return userService.supportsTeamMembershipChanges(); + public boolean supportsTeamMembershipChanges(UserModel user) { + return (user != null && user.isLocalAccount()) || userService.supportsTeamMembershipChanges(); } /** @@ -789,6 +801,10 @@ public class GitBlit implements ServletContextListener { * @return the effective list of permissions for the user */ public List getUserAccessPermissions(UserModel user) { + if (StringUtils.isEmpty(user.username)) { + // new user + return new ArrayList(); + } Set set = new LinkedHashSet(); set.addAll(user.getRepositoryPermissions()); // Flag missing repositories diff --git a/src/com/gitblit/GitblitUserService.java b/src/com/gitblit/GitblitUserService.java index 141ad8f1..db450cfc 100644 --- a/src/com/gitblit/GitblitUserService.java +++ b/src/com/gitblit/GitblitUserService.java @@ -23,9 +23,11 @@ import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.gitblit.Constants.AccountType; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.DeepCopier; +import com.gitblit.utils.StringUtils; /** * This class wraps the default user service and is recommended as the starting @@ -48,6 +50,8 @@ import com.gitblit.utils.DeepCopier; public class GitblitUserService implements IUserService { protected IUserService serviceImpl; + + protected final String ExternalAccount = "#externalAccount"; private final Logger logger = LoggerFactory.getLogger(GitblitUserService.class); @@ -144,12 +148,16 @@ public class GitblitUserService implements IUserService { @Override public UserModel authenticate(char[] cookie) { - return serviceImpl.authenticate(cookie); + UserModel user = serviceImpl.authenticate(cookie); + setAccountType(user); + return user; } @Override public UserModel authenticate(String username, char[] password) { - return serviceImpl.authenticate(username, password); + UserModel user = serviceImpl.authenticate(username, password); + setAccountType(user); + return user; } @Override @@ -159,7 +167,9 @@ public class GitblitUserService implements IUserService { @Override public UserModel getUserModel(String username) { - return serviceImpl.getUserModel(username); + UserModel user = serviceImpl.getUserModel(username); + setAccountType(user); + return user; } @Override @@ -174,8 +184,8 @@ public class GitblitUserService implements IUserService { @Override public boolean updateUserModel(String username, UserModel model) { - if (supportsCredentialChanges()) { - if (!supportsTeamMembershipChanges()) { + if (model.isLocalAccount() || supportsCredentialChanges()) { + if (!model.isLocalAccount() && !supportsTeamMembershipChanges()) { // teams are externally controlled - copy from original model UserModel existingModel = getUserModel(username); @@ -188,7 +198,7 @@ public class GitblitUserService implements IUserService { if (model.username.equals(username)) { // passwords are not persisted by the backing user service model.password = null; - if (!supportsTeamMembershipChanges()) { + if (!model.isLocalAccount() && !supportsTeamMembershipChanges()) { // teams are externally controlled- copy from original model UserModel existingModel = getUserModel(username); @@ -218,7 +228,11 @@ public class GitblitUserService implements IUserService { @Override public List getAllUsers() { - return serviceImpl.getAllUsers(); + List users = serviceImpl.getAllUsers(); + for (UserModel user : users) { + setAccountType(user); + } + return users; } @Override @@ -300,4 +314,25 @@ public class GitblitUserService implements IUserService { public boolean deleteRepositoryRole(String role) { return serviceImpl.deleteRepositoryRole(role); } + + protected boolean isLocalAccount(String username) { + UserModel user = getUserModel(username); + return user != null && user.isLocalAccount(); + } + + protected void setAccountType(UserModel user) { + if (user != null) { + if (!StringUtils.isEmpty(user.password) + && !ExternalAccount.equalsIgnoreCase(user.password) + && !"StoredInLDAP".equalsIgnoreCase(user.password)) { + user.accountType = AccountType.LOCAL; + } else { + user.accountType = getAccountType(); + } + } + } + + protected AccountType getAccountType() { + return AccountType.LOCAL; + } } diff --git a/src/com/gitblit/LdapUserService.java b/src/com/gitblit/LdapUserService.java index 9ce18f6d..3c032b56 100644 --- a/src/com/gitblit/LdapUserService.java +++ b/src/com/gitblit/LdapUserService.java @@ -25,6 +25,7 @@ import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.gitblit.Constants.AccountType; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.ArrayUtils; @@ -50,9 +51,9 @@ import com.unboundid.util.ssl.TrustAllTrustManager; public class LdapUserService extends GitblitUserService { public static final Logger logger = LoggerFactory.getLogger(LdapUserService.class); - - private IStoredSettings settings; + private IStoredSettings settings; + public LdapUserService() { super(); } @@ -155,9 +156,19 @@ public class LdapUserService extends GitblitUserService { public boolean supportsTeamMembershipChanges() { return !settings.getBoolean(Keys.realm.ldap.maintainTeams, false); } + + @Override + protected AccountType getAccountType() { + return AccountType.LDAP; + } @Override public UserModel authenticate(String username, char[] password) { + if (isLocalAccount(username)) { + // local account, bypass LDAP authentication + return super.authenticate(username, password); + } + String simpleUsername = getSimpleUsername(username); LDAPConnection ldapConnection = getLdapConnection(); @@ -239,7 +250,8 @@ public class LdapUserService extends GitblitUserService { setAdminAttribute(user); // Don't want visibility into the real password, make up a dummy - user.password = "StoredInLDAP"; + user.password = ExternalAccount; + user.accountType = getAccountType(); // Get full name Attribute String displayName = settings.getString(Keys.realm.ldap.displayName, ""); diff --git a/src/com/gitblit/RedmineUserService.java b/src/com/gitblit/RedmineUserService.java index b890f21b..2fa14b73 100644 --- a/src/com/gitblit/RedmineUserService.java +++ b/src/com/gitblit/RedmineUserService.java @@ -9,7 +9,9 @@ import org.apache.wicket.util.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.gitblit.Constants.AccountType; import com.gitblit.models.UserModel; +import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.ConnectionUtils; import com.gitblit.utils.StringUtils; import com.google.gson.Gson; @@ -71,9 +73,19 @@ public class RedmineUserService extends GitblitUserService { public boolean supportsTeamMembershipChanges() { return false; } + + @Override + protected AccountType getAccountType() { + return AccountType.REDMINE; + } @Override public UserModel authenticate(String username, char[] password) { + if (isLocalAccount(username)) { + // local account, bypass Redmine authentication + return super.authenticate(username, password); + } + String urlText = this.settings.getString(Keys.realm.redmine.url, ""); if (!urlText.endsWith("/")) { urlText.concat("/"); @@ -87,19 +99,37 @@ public class RedmineUserService extends GitblitUserService { String login = current.user.login; boolean canAdmin = true; - // non admin user can not get login name if (StringUtils.isEmpty(login)) { - canAdmin = false; login = current.user.mail; + + // non admin user can not get login name + // TODO review this assumption, if it is true, it is undocumented + canAdmin = false; } - - UserModel userModel = new UserModel(login); - userModel.canAdmin = canAdmin; - userModel.displayName = current.user.firstname + " " + current.user.lastname; - userModel.emailAddress = current.user.mail; - userModel.cookie = StringUtils.getSHA1(userModel.username + new String(password)); - - return userModel; + + UserModel user = getUserModel(login); + if (user == null) // create user object for new authenticated user + user = new UserModel(login); + + // create a user cookie + if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) { + user.cookie = StringUtils.getSHA1(user.username + new String(password)); + } + + // update user attributes from Redmine + user.accountType = getAccountType(); + user.canAdmin = canAdmin; + user.displayName = current.user.firstname + " " + current.user.lastname; + user.emailAddress = current.user.mail; + user.password = ExternalAccount; + + // TODO Redmine group mapping for administration & teams + // http://www.redmine.org/projects/redmine/wiki/Rest_Users + + // push the changes to the backing user service + super.updateUserModel(user); + + return user; } catch (IOException e) { logger.error("authenticate", e); } @@ -126,5 +156,4 @@ public class RedmineUserService extends GitblitUserService { public void setTestingCurrentUserAsJson(String json) { this.testingJson = json; } - } diff --git a/src/com/gitblit/client/UsersPanel.java b/src/com/gitblit/client/UsersPanel.java index e14c0010..c53a5791 100644 --- a/src/com/gitblit/client/UsersPanel.java +++ b/src/com/gitblit/client/UsersPanel.java @@ -112,8 +112,8 @@ public abstract class UsersPanel extends JPanel { String name = table.getColumnName(UsersTableModel.Columns.Name.ordinal()); table.getColumn(name).setCellRenderer(nameRenderer); - int w = 125; - name = table.getColumnName(UsersTableModel.Columns.AccessLevel.ordinal()); + int w = 130; + name = table.getColumnName(UsersTableModel.Columns.Type.ordinal()); table.getColumn(name).setMinWidth(w); table.getColumn(name).setMaxWidth(w); name = table.getColumnName(UsersTableModel.Columns.Teams.ordinal()); diff --git a/src/com/gitblit/client/UsersTableModel.java b/src/com/gitblit/client/UsersTableModel.java index b8ce45d4..439d5afb 100644 --- a/src/com/gitblit/client/UsersTableModel.java +++ b/src/com/gitblit/client/UsersTableModel.java @@ -36,7 +36,7 @@ public class UsersTableModel extends AbstractTableModel { List list; enum Columns { - Name, Display_Name, AccessLevel, Teams, Repositories; + Name, Display_Name, Type, Teams, Repositories; @Override public String toString() { @@ -71,8 +71,8 @@ public class UsersTableModel extends AbstractTableModel { return Translation.get("gb.name"); case Display_Name: return Translation.get("gb.displayName"); - case AccessLevel: - return Translation.get("gb.accessLevel"); + case Type: + return Translation.get("gb.type"); case Teams: return Translation.get("gb.teamMemberships"); case Repositories: @@ -101,11 +101,18 @@ public class UsersTableModel extends AbstractTableModel { return model.username; case Display_Name: return model.displayName; - case AccessLevel: + case Type: + StringBuilder sb = new StringBuilder(); + if (model.accountType != null) { + sb.append(model.accountType.name()); + } if (model.canAdmin()) { - return "administrator"; + if (sb.length() > 0) { + sb.append(", "); + } + sb.append("admin"); } - return ""; + return sb.toString(); case Teams: return (model.teams == null || model.teams.size() == 0) ? "" : String .valueOf(model.teams.size()); diff --git a/src/com/gitblit/models/UserModel.java b/src/com/gitblit/models/UserModel.java index ac67ff78..54e81cb5 100644 --- a/src/com/gitblit/models/UserModel.java +++ b/src/com/gitblit/models/UserModel.java @@ -29,6 +29,7 @@ import java.util.TreeSet; import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.AccountType; import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; @@ -73,15 +74,22 @@ public class UserModel implements Principal, Serializable, Comparable // non-persisted fields public boolean isAuthenticated; + public AccountType accountType; public UserModel(String username) { this.username = username; this.isAuthenticated = true; + this.accountType = AccountType.LOCAL; } private UserModel() { this.username = "$anonymous"; this.isAuthenticated = false; + this.accountType = AccountType.LOCAL; + } + + public boolean isLocalAccount() { + return accountType.isLocal(); } /** diff --git a/src/com/gitblit/wicket/pages/BasePage.java b/src/com/gitblit/wicket/pages/BasePage.java index 9d469083..9f981353 100644 --- a/src/com/gitblit/wicket/pages/BasePage.java +++ b/src/com/gitblit/wicket/pages/BasePage.java @@ -433,7 +433,7 @@ public abstract class BasePage extends WebPage { GitBlitWebSession session = GitBlitWebSession.get(); if (session.isLoggedIn()) { UserModel user = session.getUser(); - boolean editCredentials = GitBlit.self().supportsCredentialChanges(); + boolean editCredentials = GitBlit.self().supportsCredentialChanges(user); boolean standardLogin = session.authenticationType.isStandard(); // username, logout, and change password diff --git a/src/com/gitblit/wicket/pages/ChangePasswordPage.java b/src/com/gitblit/wicket/pages/ChangePasswordPage.java index 5e663006..3741853f 100644 --- a/src/com/gitblit/wicket/pages/ChangePasswordPage.java +++ b/src/com/gitblit/wicket/pages/ChangePasswordPage.java @@ -51,12 +51,13 @@ public class ChangePasswordPage extends RootSubPage { throw new RestartResponseException(getApplication().getHomePage()); } - if (!GitBlit.self().supportsCredentialChanges()) { + UserModel user = GitBlitWebSession.get().getUser(); + if (!GitBlit.self().supportsCredentialChanges(user)) { error(MessageFormat.format(getString("gb.userServiceDoesNotPermitPasswordChanges"), GitBlit.getString(Keys.realm.userService, "users.conf")), true); } - setupPage(getString("gb.changePassword"), GitBlitWebSession.get().getUsername()); + setupPage(getString("gb.changePassword"), user.username); StatelessForm form = new StatelessForm("passwordForm") { diff --git a/src/com/gitblit/wicket/pages/EditTeamPage.java b/src/com/gitblit/wicket/pages/EditTeamPage.java index 1991c02a..8344d387 100644 --- a/src/com/gitblit/wicket/pages/EditTeamPage.java +++ b/src/com/gitblit/wicket/pages/EditTeamPage.java @@ -212,7 +212,7 @@ public class EditTeamPage extends RootSubPage { form.add(new SimpleAttributeModifier("autocomplete", "off")); // not all user services support manipulating team memberships - boolean editMemberships = GitBlit.self().supportsTeamMembershipChanges(); + boolean editMemberships = GitBlit.self().supportsTeamMembershipChanges(null); // field names reflective match TeamModel fields form.add(new TextField("name")); diff --git a/src/com/gitblit/wicket/pages/EditUserPage.java b/src/com/gitblit/wicket/pages/EditUserPage.java index 7a01fb68..4939e97c 100644 --- a/src/com/gitblit/wicket/pages/EditUserPage.java +++ b/src/com/gitblit/wicket/pages/EditUserPage.java @@ -55,7 +55,7 @@ public class EditUserPage extends RootSubPage { public EditUserPage() { // create constructor super(); - if (!GitBlit.self().supportsCredentialChanges()) { + if (!GitBlit.self().supportsAddUser()) { error(MessageFormat.format(getString("gb.userServiceDoesNotPermitAddUser"), GitBlit.getString(Keys.realm.userService, "users.conf")), true); } @@ -134,7 +134,7 @@ public class EditUserPage extends RootSubPage { } boolean rename = !StringUtils.isEmpty(oldName) && !oldName.equalsIgnoreCase(username); - if (GitBlit.self().supportsCredentialChanges()) { + if (GitBlit.self().supportsCredentialChanges(userModel)) { if (!userModel.password.equals(confirmPassword.getObject())) { error(getString("gb.passwordsDoNotMatch")); return; @@ -210,16 +210,16 @@ public class EditUserPage extends RootSubPage { form.add(new SimpleAttributeModifier("autocomplete", "off")); // not all user services support manipulating username and password - boolean editCredentials = GitBlit.self().supportsCredentialChanges(); + boolean editCredentials = GitBlit.self().supportsCredentialChanges(userModel); // not all user services support manipulating display name - boolean editDisplayName = GitBlit.self().supportsDisplayNameChanges(); + boolean editDisplayName = GitBlit.self().supportsDisplayNameChanges(userModel); // not all user services support manipulating email address - boolean editEmailAddress = GitBlit.self().supportsEmailAddressChanges(); + boolean editEmailAddress = GitBlit.self().supportsEmailAddressChanges(userModel); // not all user services support manipulating team memberships - boolean editTeams = GitBlit.self().supportsTeamMembershipChanges(); + boolean editTeams = GitBlit.self().supportsTeamMembershipChanges(userModel); // field names reflective match UserModel fields form.add(new TextField("username").setEnabled(editCredentials)); diff --git a/src/com/gitblit/wicket/panels/TeamsPanel.java b/src/com/gitblit/wicket/panels/TeamsPanel.java index cc37c519..b76388b3 100644 --- a/src/com/gitblit/wicket/panels/TeamsPanel.java +++ b/src/com/gitblit/wicket/panels/TeamsPanel.java @@ -40,7 +40,7 @@ public class TeamsPanel extends BasePanel { Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this); adminLinks.add(new BookmarkablePageLink("newTeam", EditTeamPage.class)); - add(adminLinks.setVisible(showAdmin && GitBlit.self().supportsTeamMembershipChanges())); + add(adminLinks.setVisible(showAdmin && GitBlit.self().supportsTeamMembershipChanges(null))); final List teams = GitBlit.self().getAllTeams(); DataView teamsView = new DataView("teamRow", diff --git a/src/com/gitblit/wicket/panels/UsersPanel.html b/src/com/gitblit/wicket/panels/UsersPanel.html index aed985c1..80159610 100644 --- a/src/com/gitblit/wicket/panels/UsersPanel.html +++ b/src/com/gitblit/wicket/panels/UsersPanel.html @@ -17,7 +17,7 @@ [display name] [email address] - [access level] + [type] [team memberships] [repositories] @@ -27,7 +27,7 @@ [username] [display name] [email address] - [access level] + [account type] [team memberships] [repositories] diff --git a/src/com/gitblit/wicket/panels/UsersPanel.java b/src/com/gitblit/wicket/panels/UsersPanel.java index 46c502e5..f5b95e20 100644 --- a/src/com/gitblit/wicket/panels/UsersPanel.java +++ b/src/com/gitblit/wicket/panels/UsersPanel.java @@ -41,7 +41,7 @@ public class UsersPanel extends BasePanel { Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this); adminLinks.add(new BookmarkablePageLink("newUser", EditUserPage.class) - .setVisible(GitBlit.self().supportsCredentialChanges())); + .setVisible(GitBlit.self().supportsAddUser())); add(adminLinks.setVisible(showAdmin)); final List users = GitBlit.self().getAllUsers(); @@ -81,7 +81,7 @@ public class UsersPanel extends BasePanel { item.add(editLink); } - item.add(new Label("accesslevel", entry.canAdmin() ? "administrator" : "")); + item.add(new Label("accountType", entry.accountType.name() + (entry.canAdmin() ? ", admin":""))); item.add(new Label("teams", entry.teams.size() > 0 ? ("" + entry.teams.size()) : "")); item.add(new Label("repositories", entry.permissions.size() > 0 ? ("" + entry.permissions.size()) : "")); diff --git a/tests/com/gitblit/tests/LdapUserServiceTest.java b/tests/com/gitblit/tests/LdapUserServiceTest.java index ffe82640..a928f4a5 100644 --- a/tests/com/gitblit/tests/LdapUserServiceTest.java +++ b/tests/com/gitblit/tests/LdapUserServiceTest.java @@ -31,6 +31,7 @@ import org.junit.Test; import com.gitblit.LdapUserService; import com.gitblit.models.UserModel; import com.gitblit.tests.mock.MemorySettings; +import com.gitblit.utils.StringUtils; import com.unboundid.ldap.listener.InMemoryDirectoryServer; import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; import com.unboundid.ldap.listener.InMemoryListenerConfig; @@ -154,5 +155,20 @@ public class LdapUserServiceTest { UserModel userOneModel = ldapUserService.authenticate("*)(userPassword=userOnePassword", "userOnePassword".toCharArray()); assertNull(userOneModel); } + + @Test + public void testLocalAccount() { + UserModel localAccount = new UserModel("bruce"); + localAccount.displayName = "Bruce Campbell"; + localAccount.password = StringUtils.MD5_TYPE + StringUtils.getMD5("gimmesomesugar"); + ldapUserService.deleteUser(localAccount.username); + assertTrue("Failed to add local account", + ldapUserService.updateUserModel(localAccount)); + assertEquals("Accounts are not equal!", + localAccount, + ldapUserService.authenticate(localAccount.username, "gimmesomesugar".toCharArray())); + assertTrue("Failed to delete local account!", + ldapUserService.deleteUser(localAccount.username)); + } } diff --git a/tests/com/gitblit/tests/RedmineUserServiceTest.java b/tests/com/gitblit/tests/RedmineUserServiceTest.java index 30a8fb20..0e12542d 100644 --- a/tests/com/gitblit/tests/RedmineUserServiceTest.java +++ b/tests/com/gitblit/tests/RedmineUserServiceTest.java @@ -1,9 +1,10 @@ package com.gitblit.tests; import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.util.HashMap; @@ -12,6 +13,7 @@ import org.junit.Test; import com.gitblit.RedmineUserService; import com.gitblit.models.UserModel; import com.gitblit.tests.mock.MemorySettings; +import com.gitblit.utils.StringUtils; public class RedmineUserServiceTest { @@ -29,7 +31,7 @@ public class RedmineUserServiceTest { redmineUserService.setup(new MemorySettings(new HashMap())); redmineUserService.setTestingCurrentUserAsJson(JSON); UserModel userModel = redmineUserService.authenticate("RedmineUserId", "RedmineAPIKey".toCharArray()); - assertThat(userModel.getName(), is("RedmineUserId")); + assertThat(userModel.getName(), is("redmineuserid")); assertThat(userModel.getDisplayName(), is("baz foo")); assertThat(userModel.emailAddress, is("baz@example.com")); assertNotNull(userModel.cookie); @@ -48,5 +50,23 @@ public class RedmineUserServiceTest { assertNotNull(userModel.cookie); assertThat(userModel.canAdmin, is(false)); } + + @Test + public void testLocalAccount() { + RedmineUserService redmineUserService = new RedmineUserService(); + redmineUserService.setup(new MemorySettings(new HashMap())); + + UserModel localAccount = new UserModel("bruce"); + localAccount.displayName = "Bruce Campbell"; + localAccount.password = StringUtils.MD5_TYPE + StringUtils.getMD5("gimmesomesugar"); + redmineUserService.deleteUser(localAccount.username); + assertTrue("Failed to add local account", + redmineUserService.updateUserModel(localAccount)); + assertEquals("Accounts are not equal!", + localAccount, + redmineUserService.authenticate(localAccount.username, "gimmesomesugar".toCharArray())); + assertTrue("Failed to delete local account!", + redmineUserService.deleteUser(localAccount.username)); + } } -- cgit v1.2.3 From f8bb95d50ad925ab16a0a167bc553f036434a2d7 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sat, 5 Jan 2013 10:23:42 -0500 Subject: Draft mechanism to record a push log as a hidden orphan branch --- src/com/gitblit/Constants.java | 2 + src/com/gitblit/GitServlet.java | 55 +++- src/com/gitblit/models/Activity.java | 83 ------ src/com/gitblit/models/PushLogEntry.java | 166 +++++++++++ src/com/gitblit/models/RepositoryCommit.java | 104 +++++++ src/com/gitblit/utils/ActivityUtils.java | 2 +- src/com/gitblit/utils/IssueUtils.java | 16 +- src/com/gitblit/utils/JGitUtils.java | 14 + src/com/gitblit/utils/PushLogUtils.java | 344 +++++++++++++++++++++++ src/com/gitblit/wicket/panels/ActivityPanel.java | 2 +- src/com/gitblit/wicket/panels/RefsPanel.java | 6 + tests/com/gitblit/tests/GitServletTest.java | 15 + tests/com/gitblit/tests/PushLogTest.java | 37 +++ 13 files changed, 751 insertions(+), 95 deletions(-) create mode 100644 src/com/gitblit/models/PushLogEntry.java create mode 100644 src/com/gitblit/models/RepositoryCommit.java create mode 100644 src/com/gitblit/utils/PushLogUtils.java create mode 100644 tests/com/gitblit/tests/PushLogTest.java (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index ca332690..4dd14712 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -88,6 +88,8 @@ public class Constants { public static final String ISO8601 = "yyyy-MM-dd'T'HH:mm:ssZ"; + public static final String R_GITBLIT = "refs/gitblit/"; + public static String getGitBlitVersion() { return NAME + " v" + VERSION; } diff --git a/src/com/gitblit/GitServlet.java b/src/com/gitblit/GitServlet.java index 94a51be0..05f38b9d 100644 --- a/src/com/gitblit/GitServlet.java +++ b/src/com/gitblit/GitServlet.java @@ -29,6 +29,7 @@ import java.util.Collection; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import javax.servlet.ServletConfig; @@ -37,7 +38,9 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory; +import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory; import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.transport.PostReceiveHook; @@ -45,6 +48,8 @@ import org.eclipse.jgit.transport.PreReceiveHook; import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.transport.ReceiveCommand.Result; import org.eclipse.jgit.transport.ReceivePack; +import org.eclipse.jgit.transport.RefFilter; +import org.eclipse.jgit.transport.UploadPack; import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; import org.slf4j.Logger; @@ -55,7 +60,9 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.ClientLogger; import com.gitblit.utils.HttpUtils; +import com.gitblit.utils.IssueUtils; import com.gitblit.utils.JGitUtils; +import com.gitblit.utils.PushLogUtils; import com.gitblit.utils.StringUtils; /** @@ -131,6 +138,35 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { return rp; } }); + + // override the default upload pack to exclude gitblit refs + setUploadPackFactory(new DefaultUploadPackFactory() { + @Override + public UploadPack create(final HttpServletRequest req, final Repository db) + throws ServiceNotEnabledException, ServiceNotAuthorizedException { + UploadPack up = super.create(req, db); + RefFilter refFilter = new RefFilter() { + @Override + public Map filter(Map refs) { + // admin accounts can access all refs + UserModel user = GitBlit.self().authenticate(req); + if (user == null) { + user = UserModel.ANONYMOUS; + } + if (user.canAdmin()) { + return refs; + } + + // normal users can not clone gitblit refs + refs.remove(IssueUtils.GB_ISSUES); + refs.remove(PushLogUtils.GB_PUSHES); + return refs; + } + }; + up.setRefFilter(refFilter); + return up; + } + }); super.init(new GitblitServletConfig(config)); } @@ -264,12 +300,11 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { logger.info("skipping post-receive hooks, no refs created, updated, or removed"); return; } - RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName); - Set scripts = new LinkedHashSet(); - scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository)); - scripts.addAll(repository.postReceiveScripts); + UserModel user = getUserModel(rp); - runGroovy(repository, user, commands, rp, scripts); + RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName); + + // log ref changes for (ReceiveCommand cmd : commands) { if (Result.OK.equals(cmd.getResult())) { // add some logging for important ref changes @@ -288,6 +323,16 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { } } } + + // update push log + PushLogUtils.updatePushLog(user, rp.getRepository(), commands); + logger.info(MessageFormat.format("{0} push log updated", repository.name)); + + // run Groovy hook scripts + Set scripts = new LinkedHashSet(); + scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository)); + scripts.addAll(repository.postReceiveScripts); + runGroovy(repository, user, commands, rp, scripts); // Experimental // runNativeScript(rp, "hooks/post-receive", commands); diff --git a/src/com/gitblit/models/Activity.java b/src/com/gitblit/models/Activity.java index 7e0cb4b3..59405c7f 100644 --- a/src/com/gitblit/models/Activity.java +++ b/src/com/gitblit/models/Activity.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.utils.StringUtils; @@ -127,86 +126,4 @@ public class Activity implements Serializable, Comparable { // reverse chronological order return o.startDate.compareTo(startDate); } - - /** - * Model class to represent a RevCommit, it's source repository, and the - * branch. This class is used by the activity page. - * - * @author James Moger - */ - public static class RepositoryCommit implements Serializable, Comparable { - - private static final long serialVersionUID = 1L; - - public final String repository; - - public final String branch; - - private final RevCommit commit; - - private List refs; - - public RepositoryCommit(String repository, String branch, RevCommit commit) { - this.repository = repository; - this.branch = branch; - this.commit = commit; - } - - public void setRefs(List refs) { - this.refs = refs; - } - - public List getRefs() { - return refs; - } - - public String getName() { - return commit.getName(); - } - - public String getShortName() { - return commit.getName().substring(0, 8); - } - - public String getShortMessage() { - return commit.getShortMessage(); - } - - public int getParentCount() { - return commit.getParentCount(); - } - - public PersonIdent getAuthorIdent() { - return commit.getAuthorIdent(); - } - - public PersonIdent getCommitterIdent() { - return commit.getCommitterIdent(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof RepositoryCommit) { - RepositoryCommit commit = (RepositoryCommit) o; - return repository.equals(commit.repository) && getName().equals(commit.getName()); - } - return false; - } - - @Override - public int hashCode() { - return (repository + commit).hashCode(); - } - - @Override - public int compareTo(RepositoryCommit o) { - // reverse-chronological order - if (commit.getCommitTime() > o.commit.getCommitTime()) { - return -1; - } else if (commit.getCommitTime() < o.commit.getCommitTime()) { - return 1; - } - return 0; - } - } } diff --git a/src/com/gitblit/models/PushLogEntry.java b/src/com/gitblit/models/PushLogEntry.java new file mode 100644 index 00000000..32a7d007 --- /dev/null +++ b/src/com/gitblit/models/PushLogEntry.java @@ -0,0 +1,166 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.models; + +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.revwalk.RevCommit; + +/** + * Model class to represent a push into a repository. + * + * @author James Moger + */ +public class PushLogEntry implements Serializable, Comparable { + + private static final long serialVersionUID = 1L; + + public final String repository; + + public final Date date; + + public final UserModel user; + + private final Set commits; + + /** + * Constructor for specified duration of push from start date. + * + * @param repository + * the repository that received the push + * @param date + * the date of the push + * @param user + * the user who pushed + */ + public PushLogEntry(String repository, Date date, UserModel user) { + this.repository = repository; + this.date = date; + this.user = user; + this.commits = new LinkedHashSet(); + } + + /** + * Adds a commit to the push entry object as long as the commit is not a + * duplicate. + * + * @param branch + * @param commit + * @return a RepositoryCommit, if one was added. Null if this is duplicate + * commit + */ + public RepositoryCommit addCommit(String branch, RevCommit commit) { + RepositoryCommit commitModel = new RepositoryCommit(repository, branch, commit); + if (commits.add(commitModel)) { + return commitModel; + } + return null; + } + + /** + * Returns the list of branches changed by the push. + * + * @return a list of branches + */ + public List getChangedBranches() { + return getChangedRefs(Constants.R_HEADS); + } + + /** + * Returns the list of tags changed by the push. + * + * @return a list of tags + */ + public List getChangedTags() { + return getChangedRefs(Constants.R_TAGS); + } + + /** + * Gets the changed refs in the push. + * + * @param baseRef + * @return the changed refs + */ + protected List getChangedRefs(String baseRef) { + Set refs = new HashSet(); + for (RepositoryCommit commit : commits) { + if (baseRef == null || commit.branch.startsWith(baseRef)) { + refs.add(commit.branch); + } + } + List list = new ArrayList(refs); + Collections.sort(list); + return list; + } + + /** + * The total number of commits in the push. + * + * @return the number of commits in the push + */ + public int getCommitCount() { + return commits.size(); + } + + /** + * Returns all commits in the push. + * + * @return a list of commits + */ + public List getCommits() { + List list = new ArrayList(commits); + Collections.sort(list); + return list; + } + + /** + * Returns all commits that belong to a particular ref + * + * @param ref + * @return a list of commits + */ + public List getCommits(String ref) { + List list = new ArrayList(); + for (RepositoryCommit commit : commits) { + if (commit.branch.equals(ref)) { + list.add(commit); + } + } + Collections.sort(list); + return list; + } + + @Override + public int compareTo(PushLogEntry o) { + // reverse chronological order + return o.date.compareTo(date); + } + + @Override + public String toString() { + return MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1} pushed {2,number,0} commit{3} to {4} ", + date, user.getDisplayName(), commits.size(), commits.size() == 1 ? "":"s", repository); + } +} diff --git a/src/com/gitblit/models/RepositoryCommit.java b/src/com/gitblit/models/RepositoryCommit.java new file mode 100644 index 00000000..3a98f61f --- /dev/null +++ b/src/com/gitblit/models/RepositoryCommit.java @@ -0,0 +1,104 @@ +/* + * Copyright 2011 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.models; + +import java.io.Serializable; +import java.util.List; + +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.revwalk.RevCommit; + +/** + * Model class to represent a RevCommit, it's source repository, and the branch. + * This class is used by the activity page. + * + * @author James Moger + */ +public class RepositoryCommit implements Serializable, Comparable { + + private static final long serialVersionUID = 1L; + + public final String repository; + + public final String branch; + + private final RevCommit commit; + + private List refs; + + public RepositoryCommit(String repository, String branch, RevCommit commit) { + this.repository = repository; + this.branch = branch; + this.commit = commit; + } + + public void setRefs(List refs) { + this.refs = refs; + } + + public List getRefs() { + return refs; + } + + public String getName() { + return commit.getName(); + } + + public String getShortName() { + return commit.getName().substring(0, 8); + } + + public String getShortMessage() { + return commit.getShortMessage(); + } + + public int getParentCount() { + return commit.getParentCount(); + } + + public PersonIdent getAuthorIdent() { + return commit.getAuthorIdent(); + } + + public PersonIdent getCommitterIdent() { + return commit.getCommitterIdent(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof RepositoryCommit) { + RepositoryCommit commit = (RepositoryCommit) o; + return repository.equals(commit.repository) && getName().equals(commit.getName()); + } + return false; + } + + @Override + public int hashCode() { + return (repository + commit).hashCode(); + } + + @Override + public int compareTo(RepositoryCommit o) { + // reverse-chronological order + if (commit.getCommitTime() > o.commit.getCommitTime()) { + return -1; + } else if (commit.getCommitTime() < o.commit.getCommitTime()) { + return 1; + } + return 0; + } +} \ No newline at end of file diff --git a/src/com/gitblit/utils/ActivityUtils.java b/src/com/gitblit/utils/ActivityUtils.java index ef3a55e7..732fdeb1 100644 --- a/src/com/gitblit/utils/ActivityUtils.java +++ b/src/com/gitblit/utils/ActivityUtils.java @@ -36,9 +36,9 @@ import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.GitBlit; import com.gitblit.models.Activity; -import com.gitblit.models.Activity.RepositoryCommit; import com.gitblit.models.GravatarProfile; import com.gitblit.models.RefModel; +import com.gitblit.models.RepositoryCommit; import com.gitblit.models.RepositoryModel; import com.google.gson.reflect.TypeToken; diff --git a/src/com/gitblit/utils/IssueUtils.java b/src/com/gitblit/utils/IssueUtils.java index 7b24ccf7..1b90c7d6 100644 --- a/src/com/gitblit/utils/IssueUtils.java +++ b/src/com/gitblit/utils/IssueUtils.java @@ -76,9 +76,9 @@ public class IssueUtils { public abstract boolean accept(IssueModel issue); } - public static final String GB_ISSUES = "refs/heads/gb-issues"; + public static final String GB_ISSUES = "refs/gitblit/issues"; - static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class); + static final Logger LOGGER = LoggerFactory.getLogger(IssueUtils.class); /** * Log an error message and exception. @@ -111,7 +111,13 @@ public class IssueUtils { * @return a refmodel for the gb-issues branch or null */ public static RefModel getIssuesBranch(Repository repository) { - return JGitUtils.getBranch(repository, "gb-issues"); + List refs = JGitUtils.getRefs(repository, com.gitblit.Constants.R_GITBLIT); + for (RefModel ref : refs) { + if (ref.reference.getName().equals(GB_ISSUES)) { + return ref; + } + } + return null; } /** @@ -394,7 +400,7 @@ public class IssueUtils { public static IssueModel createIssue(Repository repository, Change change) { RefModel issuesBranch = getIssuesBranch(repository); if (issuesBranch == null) { - JGitUtils.createOrphanBranch(repository, "gb-issues", null); + JGitUtils.createOrphanBranch(repository, GB_ISSUES, null); } if (StringUtils.isEmpty(change.author)) { @@ -471,7 +477,7 @@ public class IssueUtils { RefModel issuesBranch = getIssuesBranch(repository); if (issuesBranch == null) { - throw new RuntimeException("gb-issues branch does not exist!"); + throw new RuntimeException(GB_ISSUES + " does not exist!"); } if (StringUtils.isEmpty(issueId)) { diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java index beaa27da..099036e5 100644 --- a/src/com/gitblit/utils/JGitUtils.java +++ b/src/com/gitblit/utils/JGitUtils.java @@ -1457,6 +1457,20 @@ public class JGitUtils { int maxCount) { return getRefs(repository, Constants.R_NOTES, fullName, maxCount); } + + /** + * Returns the list of refs in the specified base ref. If repository does + * not exist or is empty, an empty list is returned. + * + * @param repository + * @param fullName + * if true, /refs/yadayadayada is returned. If false, + * yadayadayada is returned. + * @return list of refs + */ + public static List getRefs(Repository repository, String baseRef) { + return getRefs(repository, baseRef, true, -1); + } /** * Returns a list of references in the repository matching "refs". If the diff --git a/src/com/gitblit/utils/PushLogUtils.java b/src/com/gitblit/utils/PushLogUtils.java new file mode 100644 index 00000000..a3b1d66b --- /dev/null +++ b/src/com/gitblit/utils/PushLogUtils.java @@ -0,0 +1,344 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.utils; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheBuilder; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.RefUpdate.Result; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.ReceiveCommand; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.models.PathModel.PathChangeModel; +import com.gitblit.models.PushLogEntry; +import com.gitblit.models.RefModel; +import com.gitblit.models.UserModel; + +/** + * Utility class for maintaining a pushlog within a git repository on an + * orphan branch. + * + * @author James Moger + * + */ +public class PushLogUtils { + + public static final String GB_PUSHES = "refs/gitblit/pushes"; + + static final Logger LOGGER = LoggerFactory.getLogger(PushLogUtils.class); + + /** + * Log an error message and exception. + * + * @param t + * @param repository + * if repository is not null it MUST be the {0} parameter in the + * pattern. + * @param pattern + * @param objects + */ + private static void error(Throwable t, Repository repository, String pattern, Object... objects) { + List parameters = new ArrayList(); + if (objects != null && objects.length > 0) { + for (Object o : objects) { + parameters.add(o); + } + } + if (repository != null) { + parameters.add(0, repository.getDirectory().getAbsolutePath()); + } + LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t); + } + + /** + * Returns a RefModel for the gb-pushes branch in the repository. If the + * branch can not be found, null is returned. + * + * @param repository + * @return a refmodel for the gb-pushes branch or null + */ + public static RefModel getPushLogBranch(Repository repository) { + List refs = JGitUtils.getRefs(repository, com.gitblit.Constants.R_GITBLIT); + for (RefModel ref : refs) { + if (ref.reference.getName().equals(GB_PUSHES)) { + return ref; + } + } + return null; + } + + /** + * Updates a push log. + * + * @param user + * @param repository + * @param commands + * @return true, if the update was successful + */ + public static boolean updatePushLog(UserModel user, Repository repository, + Collection commands) { + RefModel pushlogBranch = getPushLogBranch(repository); + if (pushlogBranch == null) { + JGitUtils.createOrphanBranch(repository, GB_PUSHES, null); + } + + boolean success = false; + String message = "push"; + + try { + ObjectId headId = repository.resolve(GB_PUSHES + "^{commit}"); + ObjectInserter odi = repository.newObjectInserter(); + try { + // Create the in-memory index of the push log entry + DirCache index = createIndex(repository, headId, commands); + ObjectId indexTreeId = index.writeTree(odi); + + PersonIdent ident = new PersonIdent(user.getDisplayName(), + user.emailAddress == null ? user.username:user.emailAddress); + + // Create a commit object + CommitBuilder commit = new CommitBuilder(); + commit.setAuthor(ident); + commit.setCommitter(ident); + commit.setEncoding(Constants.CHARACTER_ENCODING); + commit.setMessage(message); + commit.setParentId(headId); + commit.setTreeId(indexTreeId); + + // Insert the commit into the repository + ObjectId commitId = odi.insert(commit); + odi.flush(); + + RevWalk revWalk = new RevWalk(repository); + try { + RevCommit revCommit = revWalk.parseCommit(commitId); + RefUpdate ru = repository.updateRef(GB_PUSHES); + ru.setNewObjectId(commitId); + ru.setExpectedOldObjectId(headId); + ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false); + Result rc = ru.forceUpdate(); + switch (rc) { + case NEW: + case FORCED: + case FAST_FORWARD: + success = true; + break; + case REJECTED: + case LOCK_FAILURE: + throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, + ru.getRef(), rc); + default: + throw new JGitInternalException(MessageFormat.format( + JGitText.get().updatingRefFailed, GB_PUSHES, commitId.toString(), + rc)); + } + } finally { + revWalk.release(); + } + } finally { + odi.release(); + } + } catch (Throwable t) { + error(t, repository, "Failed to commit pushlog entry to {0}"); + } + return success; + } + + /** + * Creates an in-memory index of the push log entry. + * + * @param repo + * @param headId + * @param commands + * @return an in-memory index + * @throws IOException + */ + private static DirCache createIndex(Repository repo, ObjectId headId, + Collection commands) throws IOException { + + DirCache inCoreIndex = DirCache.newInCore(); + DirCacheBuilder dcBuilder = inCoreIndex.builder(); + ObjectInserter inserter = repo.newObjectInserter(); + + long now = System.currentTimeMillis(); + Set ignorePaths = new TreeSet(); + try { + // add receive commands to the temporary index + for (ReceiveCommand command : commands) { + // use the ref names as the path names + String path = command.getRefName(); + ignorePaths.add(path); + + StringBuilder change = new StringBuilder(); + change.append(command.getType().name()).append(' '); + switch (command.getType()) { + case CREATE: + change.append(ObjectId.zeroId().getName()); + change.append(' '); + change.append(command.getNewId().getName()); + break; + case UPDATE: + case UPDATE_NONFASTFORWARD: + change.append(command.getOldId().getName()); + change.append(' '); + change.append(command.getNewId().getName()); + break; + case DELETE: + change = null; + break; + } + if (change == null) { + // ref deleted + continue; + } + String content = change.toString(); + + // create an index entry for this attachment + final DirCacheEntry dcEntry = new DirCacheEntry(path); + dcEntry.setLength(content.length()); + dcEntry.setLastModified(now); + dcEntry.setFileMode(FileMode.REGULAR_FILE); + + // insert object + dcEntry.setObjectId(inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))); + + // add to temporary in-core index + dcBuilder.add(dcEntry); + } + + // Traverse HEAD to add all other paths + TreeWalk treeWalk = new TreeWalk(repo); + int hIdx = -1; + if (headId != null) + hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId)); + treeWalk.setRecursive(true); + + while (treeWalk.next()) { + String path = treeWalk.getPathString(); + CanonicalTreeParser hTree = null; + if (hIdx != -1) + hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class); + if (!ignorePaths.contains(path)) { + // add entries from HEAD for all other paths + if (hTree != null) { + // create a new DirCacheEntry with data retrieved from + // HEAD + final DirCacheEntry dcEntry = new DirCacheEntry(path); + dcEntry.setObjectId(hTree.getEntryObjectId()); + dcEntry.setFileMode(hTree.getEntryFileMode()); + + // add to temporary in-core index + dcBuilder.add(dcEntry); + } + } + } + + // release the treewalk + treeWalk.release(); + + // finish temporary in-core index used for this commit + dcBuilder.finish(); + } finally { + inserter.release(); + } + return inCoreIndex; + } + + public static List getPushLog(String repositoryName, Repository repository) { + return getPushLog(repositoryName, repository, null, -1); + } + + public static List getPushLog(String repositoryName, Repository repository, int maxCount) { + return getPushLog(repositoryName, repository, null, maxCount); + } + + public static List getPushLog(String repositoryName, Repository repository, Date minimumDate) { + return getPushLog(repositoryName, repository, minimumDate, -1); + } + + public static List getPushLog(String repositoryName, Repository repository, Date minimumDate, int maxCount) { + List list = new ArrayList(); + RefModel ref = getPushLogBranch(repository); + if (ref == null) { + return list; + } + List pushes; + if (minimumDate == null) { + pushes = JGitUtils.getRevLog(repository, GB_PUSHES, 0, maxCount); + } else { + pushes = JGitUtils.getRevLog(repository, GB_PUSHES, minimumDate); + } + for (RevCommit push : pushes) { + if (push.getAuthorIdent().getName().equalsIgnoreCase("gitblit")) { + // skip gitblit/internal commits + continue; + } + Date date = push.getAuthorIdent().getWhen(); + UserModel user = new UserModel(push.getAuthorIdent().getEmailAddress()); + user.displayName = push.getAuthorIdent().getName(); + PushLogEntry log = new PushLogEntry(repositoryName, date, user); + list.add(log); + List changedRefs = JGitUtils.getFilesInCommit(repository, push); + for (PathChangeModel change : changedRefs) { + switch (change.changeType) { + case DELETE: + break; + case ADD: + case MODIFY: + String content = JGitUtils.getStringContent(repository, push.getTree(), change.path); + String [] fields = content.split(" "); + String oldId = fields[1]; + String newId = fields[2]; + List pushedCommits = JGitUtils.getRevLog(repository, oldId, newId); + for (RevCommit pushedCommit : pushedCommits) { + log.addCommit(change.path, pushedCommit); + } + break; + default: + break; + } + } + } + Collections.sort(list); + return list; + } +} diff --git a/src/com/gitblit/wicket/panels/ActivityPanel.java b/src/com/gitblit/wicket/panels/ActivityPanel.java index 6caee3e7..669c36be 100644 --- a/src/com/gitblit/wicket/panels/ActivityPanel.java +++ b/src/com/gitblit/wicket/panels/ActivityPanel.java @@ -27,7 +27,7 @@ import com.gitblit.Constants; import com.gitblit.GitBlit; import com.gitblit.Keys; import com.gitblit.models.Activity; -import com.gitblit.models.Activity.RepositoryCommit; +import com.gitblit.models.RepositoryCommit; import com.gitblit.utils.StringUtils; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.pages.CommitDiffPage; diff --git a/src/com/gitblit/wicket/panels/RefsPanel.java b/src/com/gitblit/wicket/panels/RefsPanel.java index b4676427..3ba22c0b 100644 --- a/src/com/gitblit/wicket/panels/RefsPanel.java +++ b/src/com/gitblit/wicket/panels/RefsPanel.java @@ -129,8 +129,14 @@ public class RefsPanel extends Panel { name = name.substring(Constants.R_TAGS.length()); cssClass = "tagRef"; } else if (name.startsWith(Constants.R_NOTES)) { + // codereview refs linkClass = CommitPage.class; cssClass = "otherRef"; + } else if (name.startsWith(com.gitblit.Constants.R_GITBLIT)) { + // gitblit refs + linkClass = LogPage.class; + cssClass = "otherRef"; + name = name.substring(com.gitblit.Constants.R_GITBLIT.length()); } Component c = new LinkPanel("refName", null, name, linkClass, diff --git a/tests/com/gitblit/tests/GitServletTest.java b/tests/com/gitblit/tests/GitServletTest.java index 7dce07d8..771c4b9a 100644 --- a/tests/com/gitblit/tests/GitServletTest.java +++ b/tests/com/gitblit/tests/GitServletTest.java @@ -7,9 +7,11 @@ import static org.junit.Assert.assertTrue; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.OutputStreamWriter; import java.text.MessageFormat; import java.util.Date; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.jgit.api.CloneCommand; @@ -18,6 +20,7 @@ import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.storage.file.FileRepository; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RefSpec; @@ -34,9 +37,11 @@ import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.AuthorizationControl; import com.gitblit.GitBlit; import com.gitblit.Keys; +import com.gitblit.models.PushLogEntry; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.JGitUtils; +import com.gitblit.utils.PushLogUtils; public class GitServletTest { @@ -756,4 +761,14 @@ public class GitServletTest { GitBlitSuite.close(git); GitBlit.self().deleteUser(user.username); } + + @Test + public void testPushLog() throws IOException { + String name = "refchecks/ticgit.git"; + File refChecks = new File(GitBlitSuite.REPOSITORIES, name); + FileRepository repository = new FileRepository(refChecks); + List pushes = PushLogUtils.getPushLog(name, repository); + GitBlitSuite.close(repository); + assertTrue("Repository has an empty push log!", pushes.size() > 0); + } } diff --git a/tests/com/gitblit/tests/PushLogTest.java b/tests/com/gitblit/tests/PushLogTest.java new file mode 100644 index 00000000..aa4cf418 --- /dev/null +++ b/tests/com/gitblit/tests/PushLogTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.tests; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import org.eclipse.jgit.storage.file.FileRepository; +import org.junit.Test; + +import com.gitblit.models.PushLogEntry; +import com.gitblit.utils.PushLogUtils; + +public class PushLogTest { + + @Test + public void testPushLog() throws IOException { + String name = "~james/helloworld.git"; + FileRepository repository = new FileRepository(new File(GitBlitSuite.REPOSITORIES, name)); + List pushes = PushLogUtils.getPushLog(name, repository); + GitBlitSuite.close(repository); + } +} \ No newline at end of file -- cgit v1.2.3 From 1b1c19572fc67a1f0f71799739c4bfe53558ec41 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sat, 5 Jan 2013 15:24:50 -0500 Subject: Removed unused and untested native hook code --- src/com/gitblit/GitServlet.java | 81 ----------------------------------------- 1 file changed, 81 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/GitServlet.java b/src/com/gitblit/GitServlet.java index 05f38b9d..b55df04e 100644 --- a/src/com/gitblit/GitServlet.java +++ b/src/com/gitblit/GitServlet.java @@ -18,12 +18,8 @@ package com.gitblit; import groovy.lang.Binding; import groovy.util.GroovyScriptEngine; -import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; import java.text.MessageFormat; import java.util.Collection; import java.util.Enumeration; @@ -284,9 +280,6 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { .getName(), cmd.getResult(), cmd.getMessage())); } } - - // Experimental - // runNativeScript(rp, "hooks/pre-receive", commands); } /** @@ -333,9 +326,6 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository)); scripts.addAll(repository.postReceiveScripts); runGroovy(repository, user, commands, rp, scripts); - - // Experimental - // runNativeScript(rp, "hooks/post-receive", commands); } /** @@ -407,76 +397,5 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { } } } - - /** - * Runs the native push hook script. - * - * http://book.git-scm.com/5_git_hooks.html - * http://longair.net/blog/2011/04/09/missing-git-hooks-documentation/ - * - * @param rp - * @param script - * @param commands - */ - @SuppressWarnings("unused") - protected void runNativeScript(ReceivePack rp, String script, - Collection commands) { - - Repository repository = rp.getRepository(); - File scriptFile = new File(repository.getDirectory(), script); - - int resultCode = 0; - if (scriptFile.exists()) { - try { - logger.debug("executing " + scriptFile); - Process process = Runtime.getRuntime().exec(scriptFile.getAbsolutePath(), null, - repository.getDirectory()); - BufferedReader reader = new BufferedReader(new InputStreamReader( - process.getInputStream())); - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( - process.getOutputStream())); - for (ReceiveCommand command : commands) { - switch (command.getType()) { - case UPDATE: - // updating a ref - writer.append(MessageFormat.format("{0} {1} {2}\n", command.getOldId() - .getName(), command.getNewId().getName(), command.getRefName())); - break; - case CREATE: - // new ref - // oldrev hard-coded to 40? weird. - writer.append(MessageFormat.format("40 {0} {1}\n", command.getNewId() - .getName(), command.getRefName())); - break; - } - } - resultCode = process.waitFor(); - - // read and buffer stdin - // this is supposed to be piped back to the git client. - // not sure how to do that right now. - StringBuilder sb = new StringBuilder(); - String line = null; - while ((line = reader.readLine()) != null) { - sb.append(line).append('\n'); - } - logger.debug(sb.toString()); - } catch (Throwable e) { - resultCode = -1; - logger.error( - MessageFormat.format("Failed to execute {0}", - scriptFile.getAbsolutePath()), e); - } - } - - // reject push - if (resultCode != 0) { - for (ReceiveCommand command : commands) { - command.setResult(Result.REJECTED_OTHER_REASON, MessageFormat.format( - "Native script {0} rejected push or failed", - scriptFile.getAbsolutePath())); - } - } - } } } -- cgit v1.2.3 From 56d91368c3af957681951d3e34de54f025448d72 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sat, 5 Jan 2013 17:22:19 -0500 Subject: Fixed NPE on create repo with mixed case (issue-185) --- docs/04_releases.mkd | 1 + src/com/gitblit/GitBlit.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index d5e777ca..37954625 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -6,6 +6,7 @@ #### fixes +- Fixed nullpointer on creating a repository with mixed case (issue 185) - Fixed nullpointer when using web.allowForking = true && git.cacheRepositoryList = false (issue 182) - Build project models from the repository model cache, when possible, to reduce page load time (issue 172) - Fixed loading of Brazilian Portuguese translation from *nix server (github/inaiat) diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 74d32df0..3eb246b8 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1371,8 +1371,8 @@ public class GitBlit implements ServletContextListener { if (config.isOutdated()) { // reload model logger.info(MessageFormat.format("Config for \"{0}\" has changed. Reloading model and updating cache.", repositoryName)); - model = loadRepositoryModel(repositoryName); - removeFromCachedRepositoryList(repositoryName); + model = loadRepositoryModel(model.name); + removeFromCachedRepositoryList(model.name); addToCachedRepositoryList(model); } else { // update a few repository parameters -- cgit v1.2.3 From 4027303c4df7bc44f02446178bfd9d4c180930c3 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sun, 6 Jan 2013 11:02:52 -0500 Subject: Improve pushlog api and model class --- src/com/gitblit/models/PushLogEntry.java | 52 +++++++++++++++++++++++++--- src/com/gitblit/models/RepositoryCommit.java | 8 +++++ src/com/gitblit/utils/PushLogUtils.java | 8 ++--- 3 files changed, 59 insertions(+), 9 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/models/PushLogEntry.java b/src/com/gitblit/models/PushLogEntry.java index 32a7d007..f625c2a3 100644 --- a/src/com/gitblit/models/PushLogEntry.java +++ b/src/com/gitblit/models/PushLogEntry.java @@ -20,13 +20,16 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.transport.ReceiveCommand; /** * Model class to represent a push into a repository. @@ -44,6 +47,8 @@ public class PushLogEntry implements Serializable, Comparable { public final UserModel user; private final Set commits; + + private final Map refUpdates; /** * Constructor for specified duration of push from start date. @@ -60,6 +65,19 @@ public class PushLogEntry implements Serializable, Comparable { this.date = date; this.user = user; this.commits = new LinkedHashSet(); + this.refUpdates = new HashMap(); + } + + /** + * Tracks the change type for the specified ref. + * + * @param ref + * @param type + */ + public void updateRef(String ref, ReceiveCommand.Type type) { + if (!refUpdates.containsKey(ref)) { + refUpdates.put(ref, type); + } } /** @@ -79,6 +97,20 @@ public class PushLogEntry implements Serializable, Comparable { return null; } + /** + * Returns true if this push contains a non-fastforward ref update. + * + * @return true if this is a non-fastforward push + */ + public boolean isNonFastForward() { + for (Map.Entry entry : refUpdates.entrySet()) { + if (ReceiveCommand.Type.UPDATE_NONFASTFORWARD.equals(entry.getValue())) { + return true; + } + } + return false; + } + /** * Returns the list of branches changed by the push. * @@ -105,9 +137,9 @@ public class PushLogEntry implements Serializable, Comparable { */ protected List getChangedRefs(String baseRef) { Set refs = new HashSet(); - for (RepositoryCommit commit : commits) { - if (baseRef == null || commit.branch.startsWith(baseRef)) { - refs.add(commit.branch); + for (String ref : refUpdates.keySet()) { + if (baseRef == null || ref.startsWith(baseRef)) { + refs.add(ref); } } List list = new ArrayList(refs); @@ -160,7 +192,17 @@ public class PushLogEntry implements Serializable, Comparable { @Override public String toString() { - return MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1} pushed {2,number,0} commit{3} to {4} ", - date, user.getDisplayName(), commits.size(), commits.size() == 1 ? "":"s", repository); + StringBuilder sb = new StringBuilder(); + sb.append(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1} pushed {2,number,0} commit{3} to {4} ", + date, user.getDisplayName(), commits.size(), commits.size() == 1 ? "":"s", repository)); + for (Map.Entry entry : refUpdates.entrySet()) { + String ref = entry.getKey(); + ReceiveCommand.Type type = entry.getValue(); + sb.append("\n ").append(ref).append(' ').append(type.name()).append('\n'); + for (RepositoryCommit commit : getCommits(ref)) { + sb.append(" ").append(commit.toString()).append('\n'); + } + } + return sb.toString(); } } diff --git a/src/com/gitblit/models/RepositoryCommit.java b/src/com/gitblit/models/RepositoryCommit.java index 3a98f61f..e68e8613 100644 --- a/src/com/gitblit/models/RepositoryCommit.java +++ b/src/com/gitblit/models/RepositoryCommit.java @@ -16,6 +16,7 @@ package com.gitblit.models; import java.io.Serializable; +import java.text.MessageFormat; import java.util.List; import org.eclipse.jgit.lib.PersonIdent; @@ -101,4 +102,11 @@ public class RepositoryCommit implements Serializable, Comparable pushedCommits = JGitUtils.getRevLog(repository, oldId, newId); for (RevCommit pushedCommit : pushedCommits) { log.addCommit(change.path, pushedCommit); } - break; - default: - break; } } } -- cgit v1.2.3 From b8fab611aa1d9c12c447014014bec43a1fdc617f Mon Sep 17 00:00:00 2001 From: James Moger Date: Sun, 6 Jan 2013 17:22:43 -0500 Subject: Allow username/password Redmine authentication --- src/com/gitblit/RedmineUserService.java | 121 ++++++++++++--------- .../com/gitblit/tests/RedmineUserServiceTest.java | 6 +- 2 files changed, 75 insertions(+), 52 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/RedmineUserService.java b/src/com/gitblit/RedmineUserService.java index 2fa14b73..504593fb 100644 --- a/src/com/gitblit/RedmineUserService.java +++ b/src/com/gitblit/RedmineUserService.java @@ -86,69 +86,92 @@ public class RedmineUserService extends GitblitUserService { return super.authenticate(username, password); } - String urlText = this.settings.getString(Keys.realm.redmine.url, ""); - if (!urlText.endsWith("/")) { - urlText.concat("/"); + String jsonString = null; + try { + // first attempt by username/password + jsonString = getCurrentUserAsJson(username, password); + } catch (Exception e1) { + logger.warn("Failed to authenticate via username/password against Redmine"); + try { + // second attempt is by apikey + jsonString = getCurrentUserAsJson(null, password); + username = null; + } catch (Exception e2) { + logger.error("Failed to authenticate via apikey against Redmine", e2); + return null; + } } - String apiKey = String.valueOf(password); - + + RedmineCurrent current = null; try { - String jsonString = getCurrentUserAsJson(urlText, apiKey); - - RedmineCurrent current = new Gson().fromJson(jsonString, RedmineCurrent.class); - String login = current.user.login; - - boolean canAdmin = true; - if (StringUtils.isEmpty(login)) { - login = current.user.mail; - - // non admin user can not get login name - // TODO review this assumption, if it is true, it is undocumented - canAdmin = false; - } - - UserModel user = getUserModel(login); - if (user == null) // create user object for new authenticated user - user = new UserModel(login); - - // create a user cookie - if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) { - user.cookie = StringUtils.getSHA1(user.username + new String(password)); - } - - // update user attributes from Redmine - user.accountType = getAccountType(); - user.canAdmin = canAdmin; - user.displayName = current.user.firstname + " " + current.user.lastname; - user.emailAddress = current.user.mail; - user.password = ExternalAccount; - - // TODO Redmine group mapping for administration & teams - // http://www.redmine.org/projects/redmine/wiki/Rest_Users - - // push the changes to the backing user service - super.updateUserModel(user); - - return user; - } catch (IOException e) { - logger.error("authenticate", e); + current = new Gson().fromJson(jsonString, RedmineCurrent.class); + } catch (Exception e) { + logger.error("Failed to deserialize Redmine json response: " + jsonString, e); + return null; + } + + if (StringUtils.isEmpty(username)) { + // if the username has been reset because of apikey authentication + // then use the email address of the user. this is the original + // behavior as contributed by github/mallowlabs + username = current.user.mail; + } + + UserModel user = getUserModel(username); + if (user == null) // create user object for new authenticated user + user = new UserModel(username.toLowerCase()); + + // create a user cookie + if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) { + user.cookie = StringUtils.getSHA1(user.username + new String(password)); + } + + // update user attributes from Redmine + user.accountType = getAccountType(); + user.displayName = current.user.firstname + " " + current.user.lastname; + user.emailAddress = current.user.mail; + user.password = ExternalAccount; + if (!StringUtils.isEmpty(current.user.login)) { + // only admin users can get login name + // evidently this is an undocumented behavior of Redmine + user.canAdmin = true; } - return null; + + // TODO consider Redmine group mapping for team membership + // http://www.redmine.org/projects/redmine/wiki/Rest_Users + + // push the changes to the backing user service + super.updateUserModel(user); + + return user; } - private String getCurrentUserAsJson(String url, String apiKey) throws IOException { + private String getCurrentUserAsJson(String username, char [] password) throws IOException { if (testingJson != null) { // for testing return testingJson; } - String apiUrl = url + "users/current.json?key=" + apiKey; - HttpURLConnection http = (HttpURLConnection) ConnectionUtils.openConnection(apiUrl, null, null); + String url = this.settings.getString(Keys.realm.redmine.url, ""); + if (!url.endsWith("/")) { + url.concat("/"); + } + HttpURLConnection http; + if (username == null) { + // apikey authentication + String apiKey = String.valueOf(password); + String apiUrl = url + "users/current.json?key=" + apiKey; + http = (HttpURLConnection) ConnectionUtils.openConnection(apiUrl, null, null); + } else { + // username/password BASIC authentication + String apiUrl = url + "users/current.json"; + http = (HttpURLConnection) ConnectionUtils.openConnection(apiUrl, username, password); + } http.setRequestMethod("GET"); http.connect(); InputStreamReader reader = new InputStreamReader(http.getInputStream()); return IOUtils.toString(reader); } - + /** * set json response. do NOT invoke from production code. * @param json json diff --git a/tests/com/gitblit/tests/RedmineUserServiceTest.java b/tests/com/gitblit/tests/RedmineUserServiceTest.java index 0e12542d..12fa73ff 100644 --- a/tests/com/gitblit/tests/RedmineUserServiceTest.java +++ b/tests/com/gitblit/tests/RedmineUserServiceTest.java @@ -30,8 +30,8 @@ public class RedmineUserServiceTest { RedmineUserService redmineUserService = new RedmineUserService(); redmineUserService.setup(new MemorySettings(new HashMap())); redmineUserService.setTestingCurrentUserAsJson(JSON); - UserModel userModel = redmineUserService.authenticate("RedmineUserId", "RedmineAPIKey".toCharArray()); - assertThat(userModel.getName(), is("redmineuserid")); + UserModel userModel = redmineUserService.authenticate("RedmineAdminId", "RedmineAPIKey".toCharArray()); + assertThat(userModel.getName(), is("redmineadminid")); assertThat(userModel.getDisplayName(), is("baz foo")); assertThat(userModel.emailAddress, is("baz@example.com")); assertNotNull(userModel.cookie); @@ -44,7 +44,7 @@ public class RedmineUserServiceTest { redmineUserService.setup(new MemorySettings(new HashMap())); redmineUserService.setTestingCurrentUserAsJson(NOT_ADMIN_JSON); UserModel userModel = redmineUserService.authenticate("RedmineUserId", "RedmineAPIKey".toCharArray()); - assertThat(userModel.getName(), is("baz@example.com")); + assertThat(userModel.getName(), is("redmineuserid")); assertThat(userModel.getDisplayName(), is("baz foo")); assertThat(userModel.emailAddress, is("baz@example.com")); assertNotNull(userModel.cookie); -- cgit v1.2.3 From 11573e6b872c96bd07560ec67a97ac758548baba Mon Sep 17 00:00:00 2001 From: James Moger Date: Mon, 7 Jan 2013 08:10:00 -0500 Subject: Fixed NPE when recursively calculating a folder size with a named pipe --- docs/04_releases.mkd | 1 + src/com/gitblit/utils/FileUtils.java | 18 ++++++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index eee19173..26cbd08b 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -6,6 +6,7 @@ #### fixes +- Fixed nullpointer on recursively calculating folder sizes when there is a named pipe in the hierarchy - Fixed nullpointer on creating a repository with mixed case (issue 185) - Fixed nullpointer when using web.allowForking = true && git.cacheRepositoryList = false (issue 182) - Build project models from the repository model cache, when possible, to reduce page load time (issue 172) diff --git a/src/com/gitblit/utils/FileUtils.java b/src/com/gitblit/utils/FileUtils.java index 0b8aeb4a..08348670 100644 --- a/src/com/gitblit/utils/FileUtils.java +++ b/src/com/gitblit/utils/FileUtils.java @@ -176,19 +176,17 @@ public class FileUtils { public static long folderSize(File directory) { if (directory == null || !directory.exists()) { return -1; - } - if (directory.isFile()) { - return directory.length(); - } - long length = 0; - for (File file : directory.listFiles()) { - if (file.isFile()) { - length += file.length(); - } else { + } + if (directory.isDirectory()) { + long length = 0; + for (File file : directory.listFiles()) { length += folderSize(file); } + return length; + } else if (directory.isFile()) { + return directory.length(); } - return length; + return 0; } /** -- cgit v1.2.3 From 7dc890057cc7f02df8f27ab988aa7055cbb3af65 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 8 Jan 2013 15:19:28 -0500 Subject: Wrap pushlog update code with an exception handler, just-in-case --- src/com/gitblit/GitServlet.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/GitServlet.java b/src/com/gitblit/GitServlet.java index b55df04e..ff1ea7e9 100644 --- a/src/com/gitblit/GitServlet.java +++ b/src/com/gitblit/GitServlet.java @@ -318,8 +318,12 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { } // update push log - PushLogUtils.updatePushLog(user, rp.getRepository(), commands); - logger.info(MessageFormat.format("{0} push log updated", repository.name)); + try { + PushLogUtils.updatePushLog(user, rp.getRepository(), commands); + logger.info(MessageFormat.format("{0} push log updated", repository.name)); + } catch (Exception e) { + logger.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e); + } // run Groovy hook scripts Set scripts = new LinkedHashSet(); -- cgit v1.2.3 From 1f82620a088efa2ba3254df1805e229266690673 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 8 Jan 2013 15:19:53 -0500 Subject: Catch a few other possible error scenarios in Redmine user service --- src/com/gitblit/RedmineUserService.java | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/RedmineUserService.java b/src/com/gitblit/RedmineUserService.java index 504593fb..84194d5b 100644 --- a/src/com/gitblit/RedmineUserService.java +++ b/src/com/gitblit/RedmineUserService.java @@ -102,6 +102,11 @@ public class RedmineUserService extends GitblitUserService { } } + if (StringUtils.isEmpty(jsonString)) { + logger.error("Received empty authentication response from Redmine"); + return null; + } + RedmineCurrent current = null; try { current = new Gson().fromJson(jsonString, RedmineCurrent.class); -- cgit v1.2.3 From 5316d20e861640867d10405b25cfe75aeca0a34c Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 11 Jan 2013 23:50:59 -0500 Subject: Fanout service for Sparkleshare clients --- distrib/gitblit.properties | 47 ++ docs/01_features.mkd | 1 + docs/04_releases.mkd | 9 +- src/com/gitblit/GitBlit.java | 34 ++ src/com/gitblit/fanout/FanoutClient.java | 413 +++++++++++++++ src/com/gitblit/fanout/FanoutConstants.java | 36 ++ src/com/gitblit/fanout/FanoutNioService.java | 332 ++++++++++++ src/com/gitblit/fanout/FanoutService.java | 563 +++++++++++++++++++++ .../gitblit/fanout/FanoutServiceConnection.java | 105 ++++ src/com/gitblit/fanout/FanoutSocketService.java | 234 +++++++++ src/com/gitblit/fanout/FanoutStats.java | 98 ++++ tests/com/gitblit/tests/FanoutServiceTest.java | 172 +++++++ tests/com/gitblit/tests/GitBlitSuite.java | 3 +- 13 files changed, 2045 insertions(+), 2 deletions(-) create mode 100644 src/com/gitblit/fanout/FanoutClient.java create mode 100644 src/com/gitblit/fanout/FanoutConstants.java create mode 100644 src/com/gitblit/fanout/FanoutNioService.java create mode 100644 src/com/gitblit/fanout/FanoutService.java create mode 100644 src/com/gitblit/fanout/FanoutServiceConnection.java create mode 100644 src/com/gitblit/fanout/FanoutSocketService.java create mode 100644 src/com/gitblit/fanout/FanoutStats.java create mode 100644 tests/com/gitblit/tests/FanoutServiceTest.java (limited to 'src/com/gitblit') diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index ce269d2c..758137e3 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -365,6 +365,53 @@ groovy.postReceiveScripts = # SINCE 1.0.0 groovy.customFields = +# +# Fanout Settings +# + +# Fanout is a PubSub notification service that can be used by Sparkleshare +# to eliminate repository change polling. The fanout service runs in a separate +# thread on a separate port from the Gitblit http/https application. +# This service is provided so that Sparkleshare may be used with Gitblit in +# firewalled environments or where reliance on Sparkleshare's default notifications +# server (notifications.sparkleshare.org) is unwanted. +# +# This service maintains an open socket connection from the client to the +# Fanout PubSub service. This service may not work properly behind a proxy server. + +# Specify the interface for Fanout to bind it's service. +# You may specify an ip or an empty value to bind to all interfaces. +# Specifying localhost will result in Gitblit ONLY listening to requests to +# localhost. +# +# SINCE 1.2.1 +# RESTART REQUIRED +fanout.bindInterface = localhost + +# port for serving the Fanout PubSub service. <= 0 disables this service. +# On Unix/Linux systems, ports < 1024 require root permissions. +# Recommended value: 17000 +# +# SINCE 1.2.1 +# RESTART REQUIRED +fanout.port = 0 + +# Use Fanout NIO service. If false, a multi-threaded socket service will be used. +# Be advised, the socket implementation spawns a thread per connection plus the +# connection acceptor thread. The NIO implementation is completely single-threaded. +# +# SINCE 1.2.1 +# RESTART REQUIRED +fanout.useNio = true + +# Concurrent connection limit. <= 0 disables concurrent connection throttling. +# If > 0, only the specified number of concurrent connections will be allowed +# and all other connections will be rejected. +# +# SINCE 1.2.1 +# RESTART REQUIRED +fanout.connectionLimit = 0 + # # Authentication Settings # diff --git a/docs/01_features.mkd b/docs/01_features.mkd index 038acd06..fa3efea4 100644 --- a/docs/01_features.mkd +++ b/docs/01_features.mkd @@ -37,6 +37,7 @@ - Git-notes display support - Submodule support - Push log based on a hidden, orphan branch refs/gitblit/pushes +- Fanout PubSub notifications service for self-hosted [Sparkleshare](http://sparkleshare.org) use - gh-pages display support (Jekyll is not supported) - Branch metrics (uses Google Charts) - HEAD and Branch RSS feeds diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index 26cbd08b..d77c7326 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -14,7 +14,14 @@ #### additions -- Implemented a simple push log based on a hidden, orphan branch refs/gitblit/pushes (issue 177) +- Fanout PubSub service for self-hosted [Sparkleshare](http://sparkleshare.org) notifications.
+This service is disabled by default.
+ **New:** *fanout.bindInterface = localhost*
+ **New:** *fanout.port = 0*
+ **New:** *fanout.useNio = true*
+ **New:** *fanout.connectionLimit = 0* +- Implemented a simple push log based on a hidden, orphan branch refs/gitblit/pushes (issue 177)
+The push log is not currently visible in the ui, but the data will be collected and it will be exposed to the ui in the next release. - Support for locally and remotely authenticated accounts in LdapUserService and RedmineUserService (issue 183) - Added Dutch translation (github/kwoot) diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 3eb246b8..489ba63c 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -85,6 +85,9 @@ import com.gitblit.Constants.FederationStrategy; import com.gitblit.Constants.FederationToken; import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; +import com.gitblit.fanout.FanoutNioService; +import com.gitblit.fanout.FanoutService; +import com.gitblit.fanout.FanoutSocketService; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.FederationSet; @@ -180,6 +183,8 @@ public class GitBlit implements ServletContextListener { private TimeZone timezone; private FileBasedConfig projectConfigs; + + private FanoutService fanoutService; public GitBlit() { if (gitblit == null) { @@ -3133,6 +3138,32 @@ public class GitBlit implements ServletContextListener { } ContainerUtils.CVE_2007_0450.test(); + + // startup Fanout PubSub service + if (settings.getInteger(Keys.fanout.port, 0) > 0) { + String bindInterface = settings.getString(Keys.fanout.bindInterface, null); + int port = settings.getInteger(Keys.fanout.port, FanoutService.DEFAULT_PORT); + boolean useNio = settings.getBoolean(Keys.fanout.useNio, true); + int limit = settings.getInteger(Keys.fanout.connectionLimit, 0); + + if (useNio) { + if (StringUtils.isEmpty(bindInterface)) { + fanoutService = new FanoutNioService(port); + } else { + fanoutService = new FanoutNioService(bindInterface, port); + } + } else { + if (StringUtils.isEmpty(bindInterface)) { + fanoutService = new FanoutSocketService(port); + } else { + fanoutService = new FanoutSocketService(bindInterface, port); + } + } + + fanoutService.setConcurrentConnectionLimit(limit); + fanoutService.setAllowAllChannelAnnouncements(false); + fanoutService.start(); + } } private void logTimezone(String type, TimeZone zone) { @@ -3206,6 +3237,9 @@ public class GitBlit implements ServletContextListener { scheduledExecutor.shutdownNow(); luceneExecutor.close(); gcExecutor.close(); + if (fanoutService != null) { + fanoutService.stop(); + } } /** diff --git a/src/com/gitblit/fanout/FanoutClient.java b/src/com/gitblit/fanout/FanoutClient.java new file mode 100644 index 00000000..b9ace4be --- /dev/null +++ b/src/com/gitblit/fanout/FanoutClient.java @@ -0,0 +1,413 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.fanout; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Fanout client class. + * + * @author James Moger + * + */ +public class FanoutClient implements Runnable { + + private final static Logger logger = LoggerFactory.getLogger(FanoutClient.class); + + private final int clientTimeout = 500; + private final int reconnectTimeout = 2000; + private final String host; + private final int port; + private final List listeners; + + private String id; + private volatile Selector selector; + private volatile SocketChannel socketCh; + private Thread clientThread; + + private final AtomicBoolean isConnected; + private final AtomicBoolean isRunning; + private final AtomicBoolean isAutomaticReconnect; + private final ByteBuffer writeBuffer; + private final ByteBuffer readBuffer; + private final CharsetDecoder decoder; + + private final Set subscriptions; + private boolean resubscribe; + + public interface FanoutListener { + public void pong(Date timestamp); + public void announcement(String channel, String message); + } + + public static class FanoutAdapter implements FanoutListener { + public void pong(Date timestamp) { } + public void announcement(String channel, String message) { } + } + + public static void main(String args[]) throws Exception { + FanoutClient client = new FanoutClient("localhost", 2000); + client.addListener(new FanoutAdapter() { + + @Override + public void pong(Date timestamp) { + System.out.println("Pong. " + timestamp); + } + + @Override + public void announcement(String channel, String message) { + System.out.println(MessageFormat.format("Here ye, Here ye. {0} says {1}", channel, message)); + } + }); + client.start(); + + Thread.sleep(5000); + client.ping(); + client.subscribe("james"); + client.announce("james", "12345"); + client.subscribe("c52f99d16eb5627877ae957df7ce1be102783bd5"); + + while (true) { + Thread.sleep(10000); + client.ping(); + } + } + + public FanoutClient(String host, int port) { + this.host = host; + this.port = port; + readBuffer = ByteBuffer.allocateDirect(FanoutConstants.BUFFER_LENGTH); + writeBuffer = ByteBuffer.allocateDirect(FanoutConstants.BUFFER_LENGTH); + decoder = Charset.forName(FanoutConstants.CHARSET).newDecoder(); + listeners = Collections.synchronizedList(new ArrayList()); + subscriptions = new LinkedHashSet(); + isRunning = new AtomicBoolean(false); + isConnected = new AtomicBoolean(false); + isAutomaticReconnect = new AtomicBoolean(true); + } + + public void addListener(FanoutListener listener) { + listeners.add(listener); + } + + public void removeListener(FanoutListener listener) { + listeners.remove(listener); + } + + public boolean isAutomaticReconnect() { + return isAutomaticReconnect.get(); + } + + public void setAutomaticReconnect(boolean value) { + isAutomaticReconnect.set(value); + } + + public void ping() { + confirmConnection(); + write("ping"); + } + + public void status() { + confirmConnection(); + write("status"); + } + + public void subscribe(String channel) { + confirmConnection(); + if (subscriptions.add(channel)) { + write("subscribe " + channel); + } + } + + public void unsubscribe(String channel) { + confirmConnection(); + if (subscriptions.remove(channel)) { + write("unsubscribe " + channel); + } + } + + public void announce(String channel, String message) { + confirmConnection(); + write("announce " + channel + " " + message); + } + + private void confirmConnection() { + if (!isConnected()) { + throw new RuntimeException("Fanout client is disconnected!"); + } + } + + public boolean isConnected() { + return isRunning.get() && socketCh != null && isConnected.get(); + } + + /** + * Start client connection and return immediately. + */ + public void start() { + if (isRunning.get()) { + logger.warn("Fanout client is already running"); + return; + } + clientThread = new Thread(this, "Fanout client"); + clientThread.start(); + } + + /** + * Start client connection and wait until it has connected. + */ + public void startSynchronously() { + start(); + while (!isConnected()) { + try { + Thread.sleep(100); + } catch (Exception e) { + } + } + } + + /** + * Stops client connection. This method returns when the connection has + * been completely shutdown. + */ + public void stop() { + if (!isRunning.get()) { + logger.warn("Fanout client is not running"); + return; + } + isRunning.set(false); + try { + if (clientThread != null) { + clientThread.join(); + clientThread = null; + } + } catch (InterruptedException e1) { + } + } + + @Override + public void run() { + resetState(); + + isRunning.set(true); + while (isRunning.get()) { + // (re)connect + if (socketCh == null) { + try { + InetAddress addr = InetAddress.getByName(host); + socketCh = SocketChannel.open(new InetSocketAddress(addr, port)); + socketCh.configureBlocking(false); + selector = Selector.open(); + id = FanoutConstants.getLocalSocketId(socketCh.socket()); + socketCh.register(selector, SelectionKey.OP_READ); + } catch (Exception e) { + logger.error(MessageFormat.format("failed to open client connection to {0}:{1,number,0}", host, port), e); + try { + Thread.sleep(reconnectTimeout); + } catch (InterruptedException x) { + } + continue; + } + } + + // read/write + try { + selector.select(clientTimeout); + + Iterator i = selector.selectedKeys().iterator(); + while (i.hasNext()) { + SelectionKey key = i.next(); + i.remove(); + + if (key.isReadable()) { + // read message + String content = read(); + String[] lines = content.split("\n"); + for (String reply : lines) { + logger.trace(MessageFormat.format("fanout client {0} received: {1}", id, reply)); + if (!processReply(reply)) { + logger.error(MessageFormat.format("fanout client {0} received unknown message", id)); + } + } + } else if (key.isWritable()) { + // resubscribe + if (resubscribe) { + resubscribe = false; + logger.info(MessageFormat.format("fanout client {0} re-subscribing to {1} channels", id, subscriptions.size())); + for (String subscription : subscriptions) { + write("subscribe " + subscription); + } + } + socketCh.register(selector, SelectionKey.OP_READ); + } + } + } catch (IOException e) { + logger.error(MessageFormat.format("fanout client {0} error: {1}", id, e.getMessage())); + closeChannel(); + if (!isAutomaticReconnect.get()) { + isRunning.set(false); + continue; + } + } + } + + closeChannel(); + resetState(); + } + + protected void resetState() { + readBuffer.clear(); + writeBuffer.clear(); + isRunning.set(false); + isConnected.set(false); + } + + private void closeChannel() { + try { + if (socketCh != null) { + socketCh.close(); + socketCh = null; + selector.close(); + selector = null; + isConnected.set(false); + } + } catch (IOException x) { + } + } + + protected boolean processReply(String reply) { + String[] fields = reply.split("!", 2); + if (fields.length == 1) { + try { + long time = Long.parseLong(fields[0]); + Date date = new Date(time); + firePong(date); + } catch (Exception e) { + } + return true; + } else if (fields.length == 2) { + String channel = fields[0]; + String message = fields[1]; + if (FanoutConstants.CH_DEBUG.equals(channel)) { + // debug messages are for internal use + if (FanoutConstants.MSG_CONNECTED.equals(message)) { + isConnected.set(true); + resubscribe = subscriptions.size() > 0; + if (resubscribe) { + try { + // register for async resubscribe + socketCh.register(selector, SelectionKey.OP_WRITE); + } catch (Exception e) { + logger.error("an error occurred", e); + } + } + } + logger.debug(MessageFormat.format("fanout client {0} < {1}", id, reply)); + } else { + fireAnnouncement(channel, message); + } + return true; + } else { + // unknown message + return false; + } + } + + protected void firePong(Date timestamp) { + logger.info(MessageFormat.format("fanout client {0} < pong {1,date,yyyy-MM-dd HH:mm:ss}", id, timestamp)); + for (FanoutListener listener : listeners) { + try { + listener.pong(timestamp); + } catch (Throwable t) { + logger.error("FanoutListener threw an exception!", t); + } + } + } + protected void fireAnnouncement(String channel, String message) { + logger.info(MessageFormat.format("fanout client {0} < announcement {1} {2}", id, channel, message)); + for (FanoutListener listener : listeners) { + try { + listener.announcement(channel, message); + } catch (Throwable t) { + logger.error("FanoutListener threw an exception!", t); + } + } + } + + protected synchronized String read() throws IOException { + readBuffer.clear(); + long len = socketCh.read(readBuffer); + + if (len == -1) { + logger.error(MessageFormat.format("fanout client {0} lost connection to {1}:{2,number,0}, end of stream", id, host, port)); + socketCh.close(); + return null; + } else { + readBuffer.flip(); + String content = decoder.decode(readBuffer).toString(); + readBuffer.clear(); + return content; + } + } + + protected synchronized boolean write(String message) { + try { + logger.info(MessageFormat.format("fanout client {0} > {1}", id, message)); + byte [] bytes = message.getBytes(FanoutConstants.CHARSET); + writeBuffer.clear(); + writeBuffer.put(bytes); + if (bytes[bytes.length - 1] != 0xa) { + writeBuffer.put((byte) 0xa); + } + writeBuffer.flip(); + + // loop until write buffer has been completely sent + long written = 0; + long toWrite = writeBuffer.remaining(); + while (written != toWrite) { + written += socketCh.write(writeBuffer); + try { + Thread.sleep(10); + } catch (Exception x) { + } + } + return true; + } catch (IOException e) { + logger.error("fanout client {0} error: {1}", id, e.getMessage()); + } + return false; + } +} \ No newline at end of file diff --git a/src/com/gitblit/fanout/FanoutConstants.java b/src/com/gitblit/fanout/FanoutConstants.java new file mode 100644 index 00000000..6e6964c9 --- /dev/null +++ b/src/com/gitblit/fanout/FanoutConstants.java @@ -0,0 +1,36 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.fanout; + +import java.net.Socket; + +public class FanoutConstants { + + public final static String CHARSET = "ISO-8859-1"; + public final static int BUFFER_LENGTH = 512; + public final static String CH_ALL = "all"; + public final static String CH_DEBUG = "debug"; + public final static String MSG_CONNECTED = "connected..."; + public final static String MSG_BUSY = "busy"; + + public static String getRemoteSocketId(Socket socket) { + return socket.getInetAddress().getHostAddress() + ":" + socket.getPort(); + } + + public static String getLocalSocketId(Socket socket) { + return socket.getInetAddress().getHostAddress() + ":" + socket.getLocalPort(); + } +} diff --git a/src/com/gitblit/fanout/FanoutNioService.java b/src/com/gitblit/fanout/FanoutNioService.java new file mode 100644 index 00000000..65d022ab --- /dev/null +++ b/src/com/gitblit/fanout/FanoutNioService.java @@ -0,0 +1,332 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.fanout; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A single-thread NIO implementation of https://github.com/travisghansen/fanout + * + * This implementation uses channels and selectors, which are the Java analog of + * the Linux epoll mechanism used in the original fanout C code. + * + * @author James Moger + * + */ +public class FanoutNioService extends FanoutService { + + private final static Logger logger = LoggerFactory.getLogger(FanoutNioService.class); + + private volatile ServerSocketChannel serviceCh; + private volatile Selector selector; + + public static void main(String[] args) throws Exception { + FanoutNioService pubsub = new FanoutNioService(null, DEFAULT_PORT); + pubsub.setStrictRequestTermination(false); + pubsub.setAllowAllChannelAnnouncements(false); + pubsub.start(); + } + + /** + * Create a single-threaded fanout service. + * + * @param host + * @param port + * the port for running the fanout PubSub service + * @throws IOException + */ + public FanoutNioService(int port) { + this(null, port); + } + + /** + * Create a single-threaded fanout service. + * + * @param bindInterface + * the ip address to bind for the service, may be null + * @param port + * the port for running the fanout PubSub service + * @throws IOException + */ + public FanoutNioService(String bindInterface, int port) { + super(bindInterface, port, "Fanout nio service"); + } + + @Override + protected boolean isConnected() { + return serviceCh != null; + } + + @Override + protected boolean connect() { + if (serviceCh == null) { + try { + serviceCh = ServerSocketChannel.open(); + serviceCh.configureBlocking(false); + serviceCh.socket().setReuseAddress(true); + serviceCh.socket().bind(host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port)); + selector = Selector.open(); + serviceCh.register(selector, SelectionKey.OP_ACCEPT); + logger.info(MessageFormat.format("{0} is ready on {1}:{2,number,0}", + name, host == null ? "0.0.0.0" : host, port)); + } catch (IOException e) { + logger.error(MessageFormat.format("failed to open {0} on {1}:{2,number,0}", + name, name, host == null ? "0.0.0.0" : host, port), e); + return false; + } + } + return true; + } + + @Override + protected void disconnect() { + try { + if (serviceCh != null) { + // close all active client connections + Map clients = getCurrentClientSockets(); + for (Map.Entry client : clients.entrySet()) { + closeClientSocket(client.getKey(), client.getValue()); + } + + // close service socket channel + logger.debug(MessageFormat.format("closing {0} socket channel", name)); + serviceCh.socket().close(); + serviceCh.close(); + serviceCh = null; + selector.close(); + selector = null; + } + } catch (IOException e) { + logger.error(MessageFormat.format("failed to disconnect {0}", name), e); + } + } + + @Override + protected void listen() throws IOException { + while (selector.select(serviceTimeout) > 0) { + Set keys = selector.selectedKeys(); + Iterator keyItr = keys.iterator(); + while (keyItr.hasNext()) { + SelectionKey key = (SelectionKey) keyItr.next(); + if (key.isAcceptable()) { + // new fanout client connection + ServerSocketChannel sch = (ServerSocketChannel) key.channel(); + try { + SocketChannel ch = sch.accept(); + ch.configureBlocking(false); + configureClientSocket(ch.socket()); + + FanoutNioConnection connection = new FanoutNioConnection(ch); + addConnection(connection); + + // register to send the queued message + ch.register(selector, SelectionKey.OP_WRITE, connection); + } catch (IOException e) { + logger.error("error accepting fanout connection", e); + } + } else if (key.isReadable()) { + // read fanout client request + SocketChannel ch = (SocketChannel) key.channel(); + FanoutNioConnection connection = (FanoutNioConnection) key.attachment(); + try { + connection.read(ch, isStrictRequestTermination()); + int replies = 0; + Iterator reqItr = connection.requestQueue.iterator(); + while (reqItr.hasNext()) { + String req = reqItr.next(); + String reply = processRequest(connection, req); + reqItr.remove(); + if (reply != null) { + replies++; + } + } + + if (replies > 0) { + // register to send the replies to requests + ch.register(selector, SelectionKey.OP_WRITE, connection); + } else { + // re-register for next read + ch.register(selector, SelectionKey.OP_READ, connection); + } + } catch (IOException e) { + logger.error(MessageFormat.format("fanout connection {0} error: {1}", connection.id, e.getMessage())); + removeConnection(connection); + closeClientSocket(connection.id, ch); + } + } else if (key.isWritable()) { + // asynchronous reply to fanout client request + SocketChannel ch = (SocketChannel) key.channel(); + FanoutNioConnection connection = (FanoutNioConnection) key.attachment(); + try { + connection.write(ch); + + if (hasConnection(connection)) { + // register for next read + ch.register(selector, SelectionKey.OP_READ, connection); + } else { + // Connection was rejected due to load or + // some other reason. Close it. + closeClientSocket(connection.id, ch); + } + } catch (IOException e) { + logger.error(MessageFormat.format("fanout connection {0}: {1}", connection.id, e.getMessage())); + removeConnection(connection); + closeClientSocket(connection.id, ch); + } + } + keyItr.remove(); + } + } + } + + protected void closeClientSocket(String id, SocketChannel ch) { + try { + ch.close(); + } catch (IOException e) { + logger.error(MessageFormat.format("fanout connection {0}", id), e); + } + } + + protected void broadcast(Collection connections, String channel, String message) { + super.broadcast(connections, channel, message); + + // register queued write + Map sockets = getCurrentClientSockets(); + for (FanoutServiceConnection connection : connections) { + SocketChannel ch = sockets.get(connection.id); + if (ch == null) { + logger.warn(MessageFormat.format("fanout connection {0} has been disconnected", connection.id)); + removeConnection(connection); + continue; + } + try { + ch.register(selector, SelectionKey.OP_WRITE, connection); + } catch (IOException e) { + logger.error(MessageFormat.format("failed to register write op for fanout connection {0}", connection.id)); + } + } + } + + protected Map getCurrentClientSockets() { + Map sockets = new HashMap(); + for (SelectionKey key : selector.keys()) { + if (key.channel() instanceof SocketChannel) { + SocketChannel ch = (SocketChannel) key.channel(); + String id = FanoutConstants.getRemoteSocketId(ch.socket()); + sockets.put(id, ch); + } + } + return sockets; + } + + /** + * FanoutNioConnection handles reading/writing messages from a remote fanout + * connection. + * + * @author James Moger + * + */ + static class FanoutNioConnection extends FanoutServiceConnection { + final ByteBuffer readBuffer; + final ByteBuffer writeBuffer; + final List requestQueue; + final List replyQueue; + final CharsetDecoder decoder; + + FanoutNioConnection(SocketChannel ch) { + super(ch.socket()); + readBuffer = ByteBuffer.allocate(FanoutConstants.BUFFER_LENGTH); + writeBuffer = ByteBuffer.allocate(FanoutConstants.BUFFER_LENGTH); + requestQueue = new ArrayList(); + replyQueue = new ArrayList(); + decoder = Charset.forName(FanoutConstants.CHARSET).newDecoder(); + } + + protected void read(SocketChannel ch, boolean strictRequestTermination) throws CharacterCodingException, IOException { + long bytesRead = 0; + readBuffer.clear(); + bytesRead = ch.read(readBuffer); + readBuffer.flip(); + if (bytesRead == -1) { + throw new IOException("lost client connection, end of stream"); + } + if (readBuffer.limit() == 0) { + return; + } + CharBuffer cbuf = decoder.decode(readBuffer); + String req = cbuf.toString(); + String [] lines = req.split(strictRequestTermination ? "\n" : "\n|\r"); + requestQueue.addAll(Arrays.asList(lines)); + } + + protected void write(SocketChannel ch) throws IOException { + Iterator itr = replyQueue.iterator(); + while (itr.hasNext()) { + String reply = itr.next(); + writeBuffer.clear(); + logger.debug(MessageFormat.format("fanout reply to {0}: {1}", id, reply)); + byte [] bytes = reply.getBytes(FanoutConstants.CHARSET); + writeBuffer.put(bytes); + if (bytes[bytes.length - 1] != 0xa) { + writeBuffer.put((byte) 0xa); + } + writeBuffer.flip(); + + // loop until write buffer has been completely sent + int written = 0; + int toWrite = writeBuffer.remaining(); + while (written != toWrite) { + written += ch.write(writeBuffer); + try { + Thread.sleep(10); + } catch (Exception x) { + } + } + itr.remove(); + } + writeBuffer.clear(); + } + + @Override + protected void reply(String content) throws IOException { + // queue the reply + // replies are transmitted asynchronously from the requests + replyQueue.add(content); + } + } +} \ No newline at end of file diff --git a/src/com/gitblit/fanout/FanoutService.java b/src/com/gitblit/fanout/FanoutService.java new file mode 100644 index 00000000..cbfd8a24 --- /dev/null +++ b/src/com/gitblit/fanout/FanoutService.java @@ -0,0 +1,563 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.fanout; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base class for Fanout service implementations. + * + * Subclass implementations can be used as a Sparkleshare PubSub notification + * server. This allows Sparkleshare to be used in conjunction with Gitblit + * behind a corporate firewall that restricts or prohibits client internet access + * to the default Sparkleshare PubSub server: notifications.sparkleshare.org + * + * @author James Moger + * + */ +public abstract class FanoutService implements Runnable { + + private final static Logger logger = LoggerFactory.getLogger(FanoutService.class); + + public final static int DEFAULT_PORT = 17000; + + protected final static int serviceTimeout = 5000; + + protected final String host; + protected final int port; + protected final String name; + + private Thread serviceThread; + + private final Map connections; + private final Map> subscriptions; + + protected final AtomicBoolean isRunning; + private final AtomicBoolean strictRequestTermination; + private final AtomicBoolean allowAllChannelAnnouncements; + private final AtomicInteger concurrentConnectionLimit; + + private final Date bootDate; + private final AtomicLong rejectedConnectionCount; + private final AtomicInteger peakConnectionCount; + private final AtomicLong totalConnections; + private final AtomicLong totalAnnouncements; + private final AtomicLong totalMessages; + private final AtomicLong totalSubscribes; + private final AtomicLong totalUnsubscribes; + private final AtomicLong totalPings; + + protected FanoutService(String host, int port, String name) { + this.host = host; + this.port = port; + this.name = name; + + connections = new ConcurrentHashMap(); + subscriptions = new ConcurrentHashMap>(); + subscriptions.put(FanoutConstants.CH_ALL, new ConcurrentSkipListSet()); + + isRunning = new AtomicBoolean(false); + strictRequestTermination = new AtomicBoolean(false); + allowAllChannelAnnouncements = new AtomicBoolean(false); + concurrentConnectionLimit = new AtomicInteger(0); + + bootDate = new Date(); + rejectedConnectionCount = new AtomicLong(0); + peakConnectionCount = new AtomicInteger(0); + totalConnections = new AtomicLong(0); + totalAnnouncements = new AtomicLong(0); + totalMessages = new AtomicLong(0); + totalSubscribes = new AtomicLong(0); + totalUnsubscribes = new AtomicLong(0); + totalPings = new AtomicLong(0); + } + + /* + * Abstract methods + */ + + protected abstract boolean isConnected(); + + protected abstract boolean connect(); + + protected abstract void listen() throws IOException; + + protected abstract void disconnect(); + + /** + * Returns true if the service requires \n request termination. + * + * @return true if request requires \n termination + */ + public boolean isStrictRequestTermination() { + return strictRequestTermination.get(); + } + + /** + * Control the termination of fanout requests. If true, fanout requests must + * be terminated with \n. If false, fanout requests may be terminated with + * \n, \r, \r\n, or \n\r. This is useful for debugging with a telnet client. + * + * @param isStrictTermination + */ + public void setStrictRequestTermination(boolean isStrictTermination) { + strictRequestTermination.set(isStrictTermination); + } + + /** + * Returns the maximum allowable concurrent fanout connections. + * + * @return the maximum allowable concurrent connection count + */ + public int getConcurrentConnectionLimit() { + return concurrentConnectionLimit.get(); + } + + /** + * Sets the maximum allowable concurrent fanout connection count. + * + * @param value + */ + public void setConcurrentConnectionLimit(int value) { + concurrentConnectionLimit.set(value); + } + + /** + * Returns true if connections are allowed to announce on the all channel. + * + * @return true if connections are allowed to announce on the all channel + */ + public boolean allowAllChannelAnnouncements() { + return allowAllChannelAnnouncements.get(); + } + + /** + * Allows/prohibits connections from announcing on the ALL channel. + * + * @param value + */ + public void setAllowAllChannelAnnouncements(boolean value) { + allowAllChannelAnnouncements.set(value); + } + + /** + * Returns the current connections + * + * @param channel + * @return map of current connections keyed by their id + */ + public Map getCurrentConnections() { + return connections; + } + + /** + * Returns all subscriptions + * + * @return map of current subscriptions keyed by channel name + */ + public Map> getCurrentSubscriptions() { + return subscriptions; + } + + /** + * Returns the subscriptions for the specified channel + * + * @param channel + * @return set of subscribed connections for the specified channel + */ + public Set getCurrentSubscriptions(String channel) { + return subscriptions.get(channel); + } + + /** + * Returns the runtime statistics object for this service. + * + * @return stats + */ + public FanoutStats getStatistics() { + FanoutStats stats = new FanoutStats(); + + // settings + stats.allowAllChannelAnnouncements = allowAllChannelAnnouncements(); + stats.concurrentConnectionLimit = getConcurrentConnectionLimit(); + stats.strictRequestTermination = isStrictRequestTermination(); + + // runtime stats + stats.bootDate = bootDate; + stats.rejectedConnectionCount = rejectedConnectionCount.get(); + stats.peakConnectionCount = peakConnectionCount.get(); + stats.totalConnections = totalConnections.get(); + stats.totalAnnouncements = totalAnnouncements.get(); + stats.totalMessages = totalMessages.get(); + stats.totalSubscribes = totalSubscribes.get(); + stats.totalUnsubscribes = totalUnsubscribes.get(); + stats.totalPings = totalPings.get(); + stats.currentConnections = connections.size(); + stats.currentChannels = subscriptions.size(); + stats.currentSubscriptions = subscriptions.size() * connections.size(); + return stats; + } + + /** + * Returns true if the service is ready. + * + * @return true, if the service is ready + */ + public boolean isReady() { + if (isRunning.get()) { + return isConnected(); + } + return false; + } + + /** + * Start the Fanout service thread and immediatel return. + * + */ + public void start() { + if (isRunning.get()) { + logger.warn(MessageFormat.format("{0} is already running", name)); + return; + } + serviceThread = new Thread(this); + serviceThread.setName(MessageFormat.format("{0} {1}:{2,number,0}", name, host == null ? "all" : host, port)); + serviceThread.start(); + } + + /** + * Start the Fanout service thread and wait until it is accepting connections. + * + */ + public void startSynchronously() { + start(); + while (!isReady()) { + try { + Thread.sleep(100); + } catch (Exception e) { + } + } + } + + /** + * Stop the Fanout service. This method returns when the service has been + * completely shutdown. + */ + public void stop() { + if (!isRunning.get()) { + logger.warn(MessageFormat.format("{0} is not running", name)); + return; + } + logger.info(MessageFormat.format("stopping {0}...", name)); + isRunning.set(false); + try { + if (serviceThread != null) { + serviceThread.join(); + serviceThread = null; + } + } catch (InterruptedException e1) { + logger.error("", e1); + } + logger.info(MessageFormat.format("stopped {0}", name)); + } + + /** + * Main execution method of the service + */ + @Override + public final void run() { + disconnect(); + resetState(); + isRunning.set(true); + while (isRunning.get()) { + if (connect()) { + try { + listen(); + } catch (IOException e) { + logger.error(MessageFormat.format("error processing {0}", name), e); + isRunning.set(false); + } + } else { + try { + Thread.sleep(serviceTimeout); + } catch (InterruptedException x) { + } + } + } + disconnect(); + resetState(); + } + + protected void resetState() { + // reset state data + connections.clear(); + subscriptions.clear(); + rejectedConnectionCount.set(0); + peakConnectionCount.set(0); + totalConnections.set(0); + totalAnnouncements.set(0); + totalMessages.set(0); + totalSubscribes.set(0); + totalUnsubscribes.set(0); + totalPings.set(0); + } + + /** + * Configure the client connection socket. + * + * @param socket + * @throws SocketException + */ + protected void configureClientSocket(Socket socket) throws SocketException { + socket.setKeepAlive(true); + socket.setSoLinger(true, 0); // immediately discard any remaining data + } + + /** + * Add the connection to the connections map. + * + * @param connection + * @return false if the connection was rejected due to too many concurrent + * connections + */ + protected boolean addConnection(FanoutServiceConnection connection) { + int limit = getConcurrentConnectionLimit(); + if (limit > 0 && connections.size() > limit) { + logger.info(MessageFormat.format("hit {0,number,0} connection limit, rejecting fanout connection", concurrentConnectionLimit)); + increment(rejectedConnectionCount); + connection.busy(); + return false; + } + + // add the connection to our map + connections.put(connection.id, connection); + + // track peak number of concurrent connections + if (connections.size() > peakConnectionCount.get()) { + peakConnectionCount.set(connections.size()); + } + + logger.info("fanout new connection " + connection.id); + connection.connected(); + return true; + } + + /** + * Remove the connection from the connections list and from subscriptions. + * + * @param connection + */ + protected void removeConnection(FanoutServiceConnection connection) { + connections.remove(connection.id); + Iterator>> itr = subscriptions.entrySet().iterator(); + while (itr.hasNext()) { + Map.Entry> entry = itr.next(); + Set subscriptions = entry.getValue(); + subscriptions.remove(connection); + if (!FanoutConstants.CH_ALL.equals(entry.getKey())) { + if (subscriptions.size() == 0) { + itr.remove(); + logger.info(MessageFormat.format("fanout remove channel {0}, no subscribers", entry.getKey())); + } + } + } + logger.info(MessageFormat.format("fanout connection {0} removed", connection.id)); + } + + /** + * Tests to see if the connection is being monitored by the service. + * + * @param connection + * @return true if the service is monitoring the connection + */ + protected boolean hasConnection(FanoutServiceConnection connection) { + return connections.containsKey(connection.id); + } + + /** + * Reply to a connection on the specified channel. + * + * @param connection + * @param channel + * @param message + * @return the reply + */ + protected String reply(FanoutServiceConnection connection, String channel, String message) { + if (channel != null && channel.length() > 0) { + increment(totalMessages); + } + return connection.reply(channel, message); + } + + /** + * Service method to broadcast a message to all connections. + * + * @param message + */ + public void broadcastAll(String message) { + broadcast(connections.values(), FanoutConstants.CH_ALL, message); + increment(totalAnnouncements); + } + + /** + * Service method to broadcast a message to connections subscribed to the + * channel. + * + * @param message + */ + public void broadcast(String channel, String message) { + List connections = new ArrayList(subscriptions.get(channel)); + broadcast(connections, channel, message); + increment(totalAnnouncements); + } + + /** + * Broadcast a message to connections subscribed to the specified channel. + * + * @param connections + * @param channel + * @param message + */ + protected void broadcast(Collection connections, String channel, String message) { + for (FanoutServiceConnection connection : connections) { + reply(connection, channel, message); + } + } + + /** + * Process an incoming Fanout request. + * + * @param connection + * @param req + * @return the reply to the request, may be null + */ + protected String processRequest(FanoutServiceConnection connection, String req) { + logger.info(MessageFormat.format("fanout request from {0}: {1}", connection.id, req)); + String[] fields = req.split(" ", 3); + String action = fields[0]; + String channel = fields.length >= 2 ? fields[1] : null; + String message = fields.length >= 3 ? fields[2] : null; + try { + return processRequest(connection, action, channel, message); + } catch (IllegalArgumentException e) { + // invalid action + logger.error(MessageFormat.format("fanout connection {0} requested invalid action {1}", connection.id, action)); + logger.error(asHexArray(req)); + } + return null; + } + + /** + * Process the Fanout request. + * + * @param connection + * @param action + * @param channel + * @param message + * @return the reply to the request, may be null + * @throws IllegalArgumentException + */ + protected String processRequest(FanoutServiceConnection connection, String action, String channel, String message) throws IllegalArgumentException { + if ("ping".equals(action)) { + // ping + increment(totalPings); + return reply(connection, null, "" + System.currentTimeMillis()); + } else if ("info".equals(action)) { + // info + String info = getStatistics().info(); + return reply(connection, null, info); + } else if ("announce".equals(action)) { + // announcement + if (!allowAllChannelAnnouncements.get() && FanoutConstants.CH_ALL.equals(channel)) { + // prohibiting connection-sourced all announcements + logger.warn(MessageFormat.format("fanout connection {0} attempted to announce {1} on ALL channel", connection.id, message)); + } else if ("debug".equals(channel)) { + // prohibiting connection-sourced debug announcements + logger.warn(MessageFormat.format("fanout connection {0} attempted to announce {1} on DEBUG channel", connection.id, message)); + } else { + // acceptable announcement + List connections = new ArrayList(subscriptions.get(channel)); + connections.remove(connection); // remove announcer + broadcast(connections, channel, message); + increment(totalAnnouncements); + } + } else if ("subscribe".equals(action)) { + // subscribe + if (!subscriptions.containsKey(channel)) { + logger.info(MessageFormat.format("fanout new channel {0}", channel)); + subscriptions.put(channel, new ConcurrentSkipListSet()); + } + subscriptions.get(channel).add(connection); + logger.debug(MessageFormat.format("fanout connection {0} subscribed to channel {1}", connection.id, channel)); + increment(totalSubscribes); + } else if ("unsubscribe".equals(action)) { + // unsubscribe + if (subscriptions.containsKey(channel)) { + subscriptions.get(channel).remove(connection); + if (subscriptions.get(channel).size() == 0) { + subscriptions.remove(channel); + } + increment(totalUnsubscribes); + } + } else { + // invalid action + throw new IllegalArgumentException(action); + } + return null; + } + + private String asHexArray(String req) { + StringBuilder sb = new StringBuilder(); + for (char c : req.toCharArray()) { + sb.append(Integer.toHexString(c)).append(' '); + } + return "[ " + sb.toString().trim() + " ]"; + } + + /** + * Increment a long and prevent negative rollover. + * + * @param counter + */ + private void increment(AtomicLong counter) { + long v = counter.incrementAndGet(); + if (v < 0) { + counter.set(0); + } + } + + @Override + public String toString() { + return name; + } +} \ No newline at end of file diff --git a/src/com/gitblit/fanout/FanoutServiceConnection.java b/src/com/gitblit/fanout/FanoutServiceConnection.java new file mode 100644 index 00000000..f7f2c959 --- /dev/null +++ b/src/com/gitblit/fanout/FanoutServiceConnection.java @@ -0,0 +1,105 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.fanout; + +import java.io.IOException; +import java.net.Socket; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * FanoutServiceConnection handles reading/writing messages from a remote fanout + * connection. + * + * @author James Moger + * + */ +public abstract class FanoutServiceConnection implements Comparable { + + private static final Logger logger = LoggerFactory.getLogger(FanoutServiceConnection.class); + + public final String id; + + protected FanoutServiceConnection(Socket socket) { + this.id = FanoutConstants.getRemoteSocketId(socket); + } + + protected abstract void reply(String content) throws IOException; + + /** + * Send the connection a debug channel connected message. + * + * @param message + */ + protected void connected() { + reply(FanoutConstants.CH_DEBUG, FanoutConstants.MSG_CONNECTED); + } + + /** + * Send the connection a debug channel busy message. + * + * @param message + */ + protected void busy() { + reply(FanoutConstants.CH_DEBUG, FanoutConstants.MSG_BUSY); + } + + /** + * Send the connection a message for the specified channel. + * + * @param channel + * @param message + * @return the reply + */ + protected String reply(String channel, String message) { + String content; + if (channel != null) { + content = channel + "!" + message; + } else { + content = message; + } + try { + reply(content); + } catch (Exception e) { + logger.error("failed to reply to fanout connection " + id, e); + } + return content; + } + + @Override + public int compareTo(FanoutServiceConnection c) { + return id.compareTo(c.id); + } + + @Override + public boolean equals(Object o) { + if (o instanceof FanoutServiceConnection) { + return id.equals(((FanoutServiceConnection) o).id); + } + return false; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public String toString() { + return id; + } +} \ No newline at end of file diff --git a/src/com/gitblit/fanout/FanoutSocketService.java b/src/com/gitblit/fanout/FanoutSocketService.java new file mode 100644 index 00000000..07c18f90 --- /dev/null +++ b/src/com/gitblit/fanout/FanoutSocketService.java @@ -0,0 +1,234 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.fanout; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.text.MessageFormat; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A multi-threaded socket implementation of https://github.com/travisghansen/fanout + * + * This implementation creates a master acceptor thread which accepts incoming + * fanout connections and then spawns a daemon thread for each accepted connection. + * If there are 100 concurrent fanout connections, there are 101 threads. + * + * @author James Moger + * + */ +public class FanoutSocketService extends FanoutService { + + private final static Logger logger = LoggerFactory.getLogger(FanoutSocketService.class); + + private volatile ServerSocket serviceSocket; + + public static void main(String[] args) throws Exception { + FanoutSocketService pubsub = new FanoutSocketService(null, DEFAULT_PORT); + pubsub.setStrictRequestTermination(false); + pubsub.setAllowAllChannelAnnouncements(false); + pubsub.start(); + } + + /** + * Create a multi-threaded fanout service. + * + * @param port + * the port for running the fanout PubSub service + * @throws IOException + */ + public FanoutSocketService(int port) { + this(null, port); + } + + /** + * Create a multi-threaded fanout service. + * + * @param bindInterface + * the ip address to bind for the service, may be null + * @param port + * the port for running the fanout PubSub service + * @throws IOException + */ + public FanoutSocketService(String bindInterface, int port) { + super(bindInterface, port, "Fanout socket service"); + } + + @Override + protected boolean isConnected() { + return serviceSocket != null; + } + + @Override + protected boolean connect() { + if (serviceSocket == null) { + try { + serviceSocket = new ServerSocket(); + serviceSocket.setReuseAddress(true); + serviceSocket.setSoTimeout(serviceTimeout); + serviceSocket.bind(host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port)); + logger.info(MessageFormat.format("{0} is ready on {1}:{2,number,0}", + name, host == null ? "0.0.0.0" : host, serviceSocket.getLocalPort())); + } catch (IOException e) { + logger.error(MessageFormat.format("failed to open {0} on {1}:{2,number,0}", + name, host == null ? "0.0.0.0" : host, port), e); + return false; + } + } + return true; + } + + @Override + protected void disconnect() { + try { + if (serviceSocket != null) { + logger.debug(MessageFormat.format("closing {0} server socket", name)); + serviceSocket.close(); + serviceSocket = null; + } + } catch (IOException e) { + logger.error(MessageFormat.format("failed to disconnect {0}", name), e); + } + } + + /** + * This accepts incoming fanout connections and spawns connection threads. + */ + @Override + protected void listen() throws IOException { + try { + Socket socket; + socket = serviceSocket.accept(); + configureClientSocket(socket); + + FanoutSocketConnection connection = new FanoutSocketConnection(socket); + + if (addConnection(connection)) { + // spawn connection daemon thread + Thread connectionThread = new Thread(connection); + connectionThread.setDaemon(true); + connectionThread.setName("Fanout " + connection.id); + connectionThread.start(); + } else { + // synchronously close the connection and remove it + removeConnection(connection); + connection.closeConnection(); + connection = null; + } + } catch (SocketTimeoutException e) { + // ignore accept timeout exceptions + } + } + + /** + * FanoutSocketConnection handles reading/writing messages from a remote fanout + * connection. + * + * @author James Moger + * + */ + class FanoutSocketConnection extends FanoutServiceConnection implements Runnable { + Socket socket; + + FanoutSocketConnection(Socket socket) { + super(socket); + this.socket = socket; + } + + /** + * Connection thread read/write method. + */ + @Override + public void run() { + try { + StringBuilder sb = new StringBuilder(); + BufferedInputStream is = new BufferedInputStream(socket.getInputStream()); + byte[] buffer = new byte[FanoutConstants.BUFFER_LENGTH]; + int len = 0; + while (true) { + while (is.available() > 0) { + len = is.read(buffer); + for (int i = 0; i < len; i++) { + byte b = buffer[i]; + if (b == 0xa || (!isStrictRequestTermination() && b == 0xd)) { + String req = sb.toString(); + sb.setLength(0); + if (req.length() > 0) { + // ignore empty request strings + processRequest(this, req); + } + } else { + sb.append((char) b); + } + } + } + + if (!isRunning.get()) { + // service has stopped, terminate client connection + break; + } else { + Thread.sleep(500); + } + } + } catch (Throwable t) { + if (t instanceof SocketException) { + logger.error(MessageFormat.format("fanout connection {0}: {1}", id, t.getMessage())); + } else if (t instanceof SocketTimeoutException) { + logger.error(MessageFormat.format("fanout connection {0}: {1}", id, t.getMessage())); + } else { + logger.error(MessageFormat.format("exception while handling fanout connection {0}", id), t); + } + } finally { + closeConnection(); + } + + logger.info(MessageFormat.format("thread for fanout connection {0} is finished", id)); + } + + @Override + protected void reply(String content) throws IOException { + // synchronously send reply + logger.debug(MessageFormat.format("fanout reply to {0}: {1}", id, content)); + OutputStream os = socket.getOutputStream(); + byte [] bytes = content.getBytes(FanoutConstants.CHARSET); + os.write(bytes); + if (bytes[bytes.length - 1] != 0xa) { + os.write(0xa); + } + os.flush(); + } + + protected void closeConnection() { + // close the connection socket + try { + socket.close(); + } catch (IOException e) { + } + socket = null; + + // remove this connection from the service + removeConnection(this); + } + } +} \ No newline at end of file diff --git a/src/com/gitblit/fanout/FanoutStats.java b/src/com/gitblit/fanout/FanoutStats.java new file mode 100644 index 00000000..b06884d3 --- /dev/null +++ b/src/com/gitblit/fanout/FanoutStats.java @@ -0,0 +1,98 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.fanout; + +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.Date; + +/** + * Encapsulates the runtime stats of a fanout service. + * + * @author James Moger + * + */ +public class FanoutStats implements Serializable { + + private static final long serialVersionUID = 1L; + + public long concurrentConnectionLimit; + public boolean allowAllChannelAnnouncements; + public boolean strictRequestTermination; + + public Date bootDate; + public long rejectedConnectionCount; + public int peakConnectionCount; + public long currentChannels; + public long currentSubscriptions; + public long currentConnections; + public long totalConnections; + public long totalAnnouncements; + public long totalMessages; + public long totalSubscribes; + public long totalUnsubscribes; + public long totalPings; + + public String info() { + int i = 0; + StringBuilder sb = new StringBuilder(); + sb.append(infoStr(i++, "boot date")); + sb.append(infoStr(i++, "strict request termination")); + sb.append(infoStr(i++, "allow connection \"all\" announcements")); + sb.append(infoInt(i++, "concurrent connection limit")); + sb.append(infoInt(i++, "concurrent limit rejected connections")); + sb.append(infoInt(i++, "peak connections")); + sb.append(infoInt(i++, "current connections")); + sb.append(infoInt(i++, "current channels")); + sb.append(infoInt(i++, "current subscriptions")); + sb.append(infoInt(i++, "user-requested subscriptions")); + sb.append(infoInt(i++, "total connections")); + sb.append(infoInt(i++, "total announcements")); + sb.append(infoInt(i++, "total messages")); + sb.append(infoInt(i++, "total subscribes")); + sb.append(infoInt(i++, "total unsubscribes")); + sb.append(infoInt(i++, "total pings")); + String template = sb.toString(); + + String info = MessageFormat.format(template, + bootDate.toString(), + Boolean.toString(strictRequestTermination), + Boolean.toString(allowAllChannelAnnouncements), + concurrentConnectionLimit, + rejectedConnectionCount, + peakConnectionCount, + currentConnections, + currentChannels, + currentSubscriptions, + currentSubscriptions == 0 ? 0 : (currentSubscriptions - currentConnections), + totalConnections, + totalAnnouncements, + totalMessages, + totalSubscribes, + totalUnsubscribes, + totalPings); + return info; + } + + private String infoStr(int index, String label) { + return label + ": {" + index + "}\n"; + } + + private String infoInt(int index, String label) { + return label + ": {" + index + ",number,0}\n"; + } + +} diff --git a/tests/com/gitblit/tests/FanoutServiceTest.java b/tests/com/gitblit/tests/FanoutServiceTest.java new file mode 100644 index 00000000..28e5d82d --- /dev/null +++ b/tests/com/gitblit/tests/FanoutServiceTest.java @@ -0,0 +1,172 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.tests; + +import static org.junit.Assert.assertEquals; + +import java.text.MessageFormat; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import com.gitblit.fanout.FanoutService; +import com.gitblit.fanout.FanoutClient; +import com.gitblit.fanout.FanoutClient.FanoutAdapter; +import com.gitblit.fanout.FanoutNioService; +import com.gitblit.fanout.FanoutService; +import com.gitblit.fanout.FanoutSocketService; + +public class FanoutServiceTest { + + int fanoutPort = FanoutService.DEFAULT_PORT; + + @Test + public void testNioPubSub() throws Exception { + testPubSub(new FanoutNioService(fanoutPort)); + } + + @Test + public void testSocketPubSub() throws Exception { + testPubSub(new FanoutSocketService(fanoutPort)); + } + + @Test + public void testNioDisruptionAndRecovery() throws Exception { + testDisruption(new FanoutNioService(fanoutPort)); + } + + @Test + public void testSocketDisruptionAndRecovery() throws Exception { + testDisruption(new FanoutSocketService(fanoutPort)); + } + + protected void testPubSub(FanoutService service) throws Exception { + System.out.println(MessageFormat.format("\n\n========================================\nPUBSUB TEST {0}\n========================================\n\n", service.toString())); + service.startSynchronously(); + + final Map announcementsA = new ConcurrentHashMap(); + FanoutClient clientA = new FanoutClient("localhost", fanoutPort); + clientA.addListener(new FanoutAdapter() { + + @Override + public void announcement(String channel, String message) { + announcementsA.put(channel, message); + } + }); + + clientA.startSynchronously(); + + final Map announcementsB = new ConcurrentHashMap(); + FanoutClient clientB = new FanoutClient("localhost", fanoutPort); + clientB.addListener(new FanoutAdapter() { + @Override + public void announcement(String channel, String message) { + announcementsB.put(channel, message); + } + }); + clientB.startSynchronously(); + + + // subscribe clients A and B to the channels + clientA.subscribe("a"); + clientA.subscribe("b"); + clientA.subscribe("c"); + + clientB.subscribe("a"); + clientB.subscribe("b"); + clientB.subscribe("c"); + + // give async messages a chance to be delivered + Thread.sleep(1000); + + clientA.announce("a", "apple"); + clientA.announce("b", "banana"); + clientA.announce("c", "cantelope"); + + clientB.announce("a", "avocado"); + clientB.announce("b", "beet"); + clientB.announce("c", "carrot"); + + // give async messages a chance to be delivered + Thread.sleep(2000); + + // confirm that client B received client A's announcements + assertEquals("apple", announcementsB.get("a")); + assertEquals("banana", announcementsB.get("b")); + assertEquals("cantelope", announcementsB.get("c")); + + // confirm that client A received client B's announcements + assertEquals("avocado", announcementsA.get("a")); + assertEquals("beet", announcementsA.get("b")); + assertEquals("carrot", announcementsA.get("c")); + + clientA.stop(); + clientB.stop(); + service.stop(); + } + + protected void testDisruption(FanoutService service) throws Exception { + System.out.println(MessageFormat.format("\n\n========================================\nDISRUPTION TEST {0}\n========================================\n\n", service.toString())); + service.startSynchronously(); + + final AtomicInteger pongCount = new AtomicInteger(0); + FanoutClient client = new FanoutClient("localhost", fanoutPort); + client.addListener(new FanoutAdapter() { + @Override + public void pong(Date timestamp) { + pongCount.incrementAndGet(); + } + }); + client.startSynchronously(); + + // ping and wait for pong + client.ping(); + Thread.sleep(500); + + // restart client + client.stop(); + Thread.sleep(1000); + client.startSynchronously(); + + // ping and wait for pong + client.ping(); + Thread.sleep(500); + + assertEquals(2, pongCount.get()); + + // now disrupt service + service.stop(); + Thread.sleep(2000); + service.startSynchronously(); + + // wait for reconnect + Thread.sleep(2000); + + // ping and wait for pong + client.ping(); + Thread.sleep(500); + + // kill all + client.stop(); + service.stop(); + + // confirm expected pong count + assertEquals(3, pongCount.get()); + } +} \ No newline at end of file diff --git a/tests/com/gitblit/tests/GitBlitSuite.java b/tests/com/gitblit/tests/GitBlitSuite.java index bb734eb7..5220a6a3 100644 --- a/tests/com/gitblit/tests/GitBlitSuite.java +++ b/tests/com/gitblit/tests/GitBlitSuite.java @@ -59,7 +59,8 @@ import com.gitblit.utils.JGitUtils; MarkdownUtilsTest.class, JGitUtilsTest.class, SyndicationUtilsTest.class, DiffUtilsTest.class, MetricUtilsTest.class, TicgitUtilsTest.class, X509UtilsTest.class, GitBlitTest.class, FederationTests.class, RpcTests.class, GitServletTest.class, - GroovyScriptTest.class, LuceneExecutorTest.class, IssuesTest.class, RepositoryModelTest.class }) + GroovyScriptTest.class, LuceneExecutorTest.class, IssuesTest.class, RepositoryModelTest.class, + FanoutServiceTest.class }) public class GitBlitSuite { public static final File REPOSITORIES = new File("git"); -- cgit v1.2.3 From e4e68298c2f55c93dc2464e26a24b119a649e642 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 11 Jan 2013 23:53:32 -0500 Subject: Show indicators for Sparkleshared repositories --- build.xml | 3 +++ resources/folder_star_16x16.png | Bin 0 -> 830 bytes resources/folder_star_32x32.png | Bin 0 -> 1529 bytes resources/star_16x16.png | Bin 0 -> 611 bytes resources/star_32x32.png | Bin 0 -> 1548 bytes src/com/gitblit/GitBlit.java | 1 + src/com/gitblit/PagesServlet.java | 2 +- src/com/gitblit/client/IndicatorsRenderer.java | 8 +++++++ src/com/gitblit/models/RepositoryModel.java | 6 ++++++ src/com/gitblit/utils/IssueUtils.java | 2 +- src/com/gitblit/utils/JGitUtils.java | 24 +++++++++++++++++---- src/com/gitblit/wicket/GitBlitWebApp.properties | 3 ++- src/com/gitblit/wicket/pages/RawPage.java | 4 ++-- src/com/gitblit/wicket/pages/RepositoryPage.html | 2 +- src/com/gitblit/wicket/pages/RepositoryPage.java | 8 +++++++ .../wicket/panels/ProjectRepositoryPanel.html | 1 + .../wicket/panels/ProjectRepositoryPanel.java | 6 ++++++ .../gitblit/wicket/panels/RepositoriesPanel.html | 2 +- .../gitblit/wicket/panels/RepositoriesPanel.java | 7 ++++++ 19 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 resources/folder_star_16x16.png create mode 100644 resources/folder_star_32x32.png create mode 100644 resources/star_16x16.png create mode 100644 resources/star_32x32.png (limited to 'src/com/gitblit') diff --git a/build.xml b/build.xml index 00885979..200fc91c 100644 --- a/build.xml +++ b/build.xml @@ -717,12 +717,15 @@ + + + diff --git a/resources/folder_star_16x16.png b/resources/folder_star_16x16.png new file mode 100644 index 00000000..ae8fdeda Binary files /dev/null and b/resources/folder_star_16x16.png differ diff --git a/resources/folder_star_32x32.png b/resources/folder_star_32x32.png new file mode 100644 index 00000000..d2a076a9 Binary files /dev/null and b/resources/folder_star_32x32.png differ diff --git a/resources/star_16x16.png b/resources/star_16x16.png new file mode 100644 index 00000000..883e4dec Binary files /dev/null and b/resources/star_16x16.png differ diff --git a/resources/star_32x32.png b/resources/star_32x32.png new file mode 100644 index 00000000..92865b19 Binary files /dev/null and b/resources/star_32x32.png differ diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 489ba63c..f417b3eb 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1715,6 +1715,7 @@ public class GitBlit implements ServletContextListener { } model.HEAD = JGitUtils.getHEADRef(r); model.availableRefs = JGitUtils.getAvailableHeadTargets(r); + model.sparkleshareId = JGitUtils.getSparkleshareId(r); r.close(); if (model.origin != null && model.origin.startsWith("file://")) { diff --git a/src/com/gitblit/PagesServlet.java b/src/com/gitblit/PagesServlet.java index ad9276b4..91f25b70 100644 --- a/src/com/gitblit/PagesServlet.java +++ b/src/com/gitblit/PagesServlet.java @@ -170,7 +170,7 @@ public class PagesServlet extends HttpServlet { content = JGitUtils.getStringContent(r, tree, resource, encodings).getBytes( Constants.ENCODING); } else { - content = JGitUtils.getByteContent(r, tree, resource); + content = JGitUtils.getByteContent(r, tree, resource, false); } response.setContentType(contentType); } catch (Exception e) { diff --git a/src/com/gitblit/client/IndicatorsRenderer.java b/src/com/gitblit/client/IndicatorsRenderer.java index 59ce6dd1..44b39d01 100644 --- a/src/com/gitblit/client/IndicatorsRenderer.java +++ b/src/com/gitblit/client/IndicatorsRenderer.java @@ -55,6 +55,8 @@ public class IndicatorsRenderer extends JPanel implements TableCellRenderer, Ser private final ImageIcon federatedIcon; private final ImageIcon forkIcon; + + private final ImageIcon sparkleshareIcon; public IndicatorsRenderer() { super(new FlowLayout(FlowLayout.RIGHT, 1, 0)); @@ -67,6 +69,7 @@ public class IndicatorsRenderer extends JPanel implements TableCellRenderer, Ser frozenIcon = new ImageIcon(getClass().getResource("/cold_16x16.png")); federatedIcon = new ImageIcon(getClass().getResource("/federated_16x16.png")); forkIcon = new ImageIcon(getClass().getResource("/commit_divide_16x16.png")); + sparkleshareIcon = new ImageIcon(getClass().getResource("/star_16x16.png")); } @Override @@ -80,6 +83,11 @@ public class IndicatorsRenderer extends JPanel implements TableCellRenderer, Ser if (value instanceof RepositoryModel) { StringBuilder tooltip = new StringBuilder(); RepositoryModel model = (RepositoryModel) value; + if (model.isSparkleshared()) { + JLabel icon = new JLabel(sparkleshareIcon); + tooltip.append(Translation.get("gb.isSparkleshared")).append("
"); + add(icon); + } if (model.isFork()) { JLabel icon = new JLabel(forkIcon); tooltip.append(Translation.get("gb.isFork")).append("
"); diff --git a/src/com/gitblit/models/RepositoryModel.java b/src/com/gitblit/models/RepositoryModel.java index 5be33a2d..022fd200 100644 --- a/src/com/gitblit/models/RepositoryModel.java +++ b/src/com/gitblit/models/RepositoryModel.java @@ -82,6 +82,7 @@ public class RepositoryModel implements Serializable, Comparable getSubmodules(Repository repository, RevTree tree) { List list = new ArrayList(); - byte [] blob = getByteContent(repository, tree, ".gitmodules"); + byte [] blob = getByteContent(repository, tree, ".gitmodules", false); if (blob == null) { return list; } @@ -1734,4 +1736,18 @@ public class JGitUtils { } return success; } + + /** + * Reads the sparkleshare id, if present, from the repository. + * + * @param repository + * @return an id or null + */ + public static String getSparkleshareId(Repository repository) { + byte[] content = getByteContent(repository, null, ".sparkleshare", false); + if (content == null) { + return null; + } + return StringUtils.decodeString(content); + } } diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index 16f76411..dfdf70c2 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -440,4 +440,5 @@ gb.sslCertificateGeneratedRestart = Successfully generated new server SSL certif gb.validity = validity gb.siteName = site name gb.siteNameDescription = short, descriptive name of your server -gb.excludeFromActivity = exclude from activity page \ No newline at end of file +gb.excludeFromActivity = exclude from activity page +gb.isSparkleshared = repository is Sparkleshared \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/RawPage.java b/src/com/gitblit/wicket/pages/RawPage.java index 7f6ed139..28e8bae2 100644 --- a/src/com/gitblit/wicket/pages/RawPage.java +++ b/src/com/gitblit/wicket/pages/RawPage.java @@ -109,7 +109,7 @@ public class RawPage extends WebPage { switch (type) { case 2: // image blobs - byte[] image = JGitUtils.getByteContent(r, commit.getTree(), blobPath); + byte[] image = JGitUtils.getByteContent(r, commit.getTree(), blobPath, true); response.setContentType("image/" + extension.toLowerCase()); response.setContentLength(image.length); try { @@ -120,7 +120,7 @@ public class RawPage extends WebPage { break; case 3: // binary blobs (download) - byte[] binary = JGitUtils.getByteContent(r, commit.getTree(), blobPath); + byte[] binary = JGitUtils.getByteContent(r, commit.getTree(), blobPath, true); response.setContentLength(binary.length); response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\""); diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.html b/src/com/gitblit/wicket/pages/RepositoryPage.html index 63a894da..d49f0188 100644 --- a/src/com/gitblit/wicket/pages/RepositoryPage.html +++ b/src/com/gitblit/wicket/pages/RepositoryPage.html @@ -52,7 +52,7 @@
-
[project title]/[repository name] [page name]
+
[project title]/[repository name] [page name]
[origin repository]
diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java index aac527d7..16087fa6 100644 --- a/src/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/com/gitblit/wicket/pages/RepositoryPage.java @@ -246,6 +246,14 @@ public abstract class RepositoryPage extends BasePage { } } + // show sparkleshare folder icon + if (model.isSparkleshared()) { + add(WicketUtils.newImage("repositoryIcon", "folder_star_32x32.png", + getString("gb.isSparkleshared"))); + } else { + add(WicketUtils.newClearPixel("repositoryIcon").setVisible(false)); + } + if (getRepositoryModel().isBare) { add(new Label("workingCopyIndicator").setVisible(false)); } else { diff --git a/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.html b/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.html index 46781536..9b621d5a 100644 --- a/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.html +++ b/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.html @@ -38,6 +38,7 @@
+ diff --git a/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java b/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java index 50f0d52d..3c9bf7f0 100644 --- a/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java +++ b/src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java @@ -87,6 +87,12 @@ public class ProjectRepositoryPanel extends BasePanel { add(forkFrag); } + if (entry.isSparkleshared()) { + add(WicketUtils.newImage("sparkleshareIcon", "star_16x16.png", localizer.getString("gb.isSparkleshared", parent))); + } else { + add(WicketUtils.newClearPixel("sparkleshareIcon").setVisible(false)); + } + add(new BookmarkablePageLink("tickets", TicketsPage.class, pp).setVisible(entry.useTickets)); add(new BookmarkablePageLink("docs", DocsPage.class, pp).setVisible(entry.useDocs)); diff --git a/src/com/gitblit/wicket/panels/RepositoriesPanel.html b/src/com/gitblit/wicket/panels/RepositoriesPanel.html index 42f9f1f2..81a4c6eb 100644 --- a/src/com/gitblit/wicket/panels/RepositoriesPanel.html +++ b/src/com/gitblit/wicket/panels/RepositoriesPanel.html @@ -89,7 +89,7 @@ [repository name] [repository description] [repository owner] - + [last change] [repository size] diff --git a/src/com/gitblit/wicket/panels/RepositoriesPanel.java b/src/com/gitblit/wicket/panels/RepositoriesPanel.java index 976c517f..ee5edfce 100644 --- a/src/com/gitblit/wicket/panels/RepositoriesPanel.java +++ b/src/com/gitblit/wicket/panels/RepositoriesPanel.java @@ -233,6 +233,13 @@ public class RepositoriesPanel extends BasePanel { .setEscapeModelStrings(false)); } + if (entry.isSparkleshared()) { + row.add(WicketUtils.newImage("sparkleshareIcon", "star_16x16.png", + getString("gb.isSparkleshared"))); + } else { + row.add(WicketUtils.newClearPixel("sparkleshareIcon").setVisible(false)); + } + if (entry.isFork()) { row.add(WicketUtils.newImage("forkIcon", "commit_divide_16x16.png", getString("gb.isFork"))); -- cgit v1.2.3 From abd3cc2d610981493b5e9333108d271ae37f7726 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sat, 12 Jan 2013 19:51:42 -0500 Subject: Refresh authenticated user model for each page request (issue-186) --- docs/04_releases.mkd | 3 ++- src/com/gitblit/wicket/pages/BasePage.java | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index d77c7326..3d17a9b2 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -6,7 +6,8 @@ #### fixes -- Fixed nullpointer on recursively calculating folder sizes when there is a named pipe in the hierarchy +- Fixed nullpointer on recursively calculating folder sizes when there is a named pipe or symlink in the hierarchy +- Fixed bug where permission changes were not visible in the web ui to a logged-in user until the user logged-out and then logged back in again (issue-186) - Fixed nullpointer on creating a repository with mixed case (issue 185) - Fixed nullpointer when using web.allowForking = true && git.cacheRepositoryList = false (issue 182) - Build project models from the repository model cache, when possible, to reduce page load time (issue 172) diff --git a/src/com/gitblit/wicket/pages/BasePage.java b/src/com/gitblit/wicket/pages/BasePage.java index 9f981353..c733c992 100644 --- a/src/com/gitblit/wicket/pages/BasePage.java +++ b/src/com/gitblit/wicket/pages/BasePage.java @@ -136,7 +136,10 @@ public abstract class BasePage extends WebPage { private void login() { GitBlitWebSession session = GitBlitWebSession.get(); if (session.isLoggedIn() && !session.isSessionInvalidated()) { - // already have a session + // already have a session, refresh usermodel to pick up + // any changes to permissions or roles (issue-186) + UserModel user = GitBlit.self().getUserModel(session.getUser().username); + session.setUser(user); return; } -- cgit v1.2.3 From 9cc56a1f60eff2ce1db40b7078eab92e78602e1c Mon Sep 17 00:00:00 2001 From: James Moger Date: Sun, 13 Jan 2013 16:49:37 -0500 Subject: Fix submodule links on commit, comitdiff, and tree page (issue-178) --- src/com/gitblit/wicket/pages/CommitDiffPage.java | 13 +++++-------- src/com/gitblit/wicket/pages/CommitPage.java | 13 ++++++------- src/com/gitblit/wicket/pages/TreePage.java | 4 ++-- 3 files changed, 13 insertions(+), 17 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/pages/CommitDiffPage.java b/src/com/gitblit/wicket/pages/CommitDiffPage.java index 585ce8e3..3ad70742 100644 --- a/src/com/gitblit/wicket/pages/CommitDiffPage.java +++ b/src/com/gitblit/wicket/pages/CommitDiffPage.java @@ -15,14 +15,14 @@ */ package com.gitblit.wicket.pages; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; -import java.text.MessageFormat; - import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.link.ExternalLink; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.ListDataProvider; @@ -150,15 +150,12 @@ public class CommitDiffPage extends RepositoryPage { // quick links if (entry.isSubmodule()) { // submodule - item.add(new BookmarkablePageLink("patch", PatchPage.class, WicketUtils - .newPathParameter(submodulePath, entry.objectId, entry.path)).setEnabled(false)); + item.add(new ExternalLink("patch", "").setEnabled(false)); item.add(new BookmarkablePageLink("view", CommitPage.class, WicketUtils .newObjectParameter(submodulePath, entry.objectId)).setEnabled(hasSubmodule)); - item.add(new BookmarkablePageLink("blame", BlamePage.class, WicketUtils - .newPathParameter(submodulePath, entry.objectId, entry.path)).setEnabled(false)); + item.add(new ExternalLink("blame", "").setEnabled(false)); item.add(new BookmarkablePageLink("history", HistoryPage.class, WicketUtils - .newPathParameter(submodulePath, entry.objectId, entry.path)) - .setEnabled(hasSubmodule)); + .newPathParameter(repositoryName, entry.commitId, entry.path))); } else { // tree or blob item.add(new BookmarkablePageLink("patch", PatchPage.class, WicketUtils diff --git a/src/com/gitblit/wicket/pages/CommitPage.java b/src/com/gitblit/wicket/pages/CommitPage.java index 17621ad0..c5a24c8d 100644 --- a/src/com/gitblit/wicket/pages/CommitPage.java +++ b/src/com/gitblit/wicket/pages/CommitPage.java @@ -22,6 +22,7 @@ import java.util.List; import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.link.ExternalLink; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.ListDataProvider; @@ -184,16 +185,14 @@ public class CommitPage extends RepositoryPage { if (entry.isSubmodule()) { // submodule item.add(new BookmarkablePageLink("diff", BlobDiffPage.class, WicketUtils - .newPathParameter(submodulePath, entry.objectId, entry.path)) - .setEnabled(false)); + .newPathParameter(repositoryName, entry.commitId, entry.path)) + .setEnabled(!entry.changeType.equals(ChangeType.ADD))); item.add(new BookmarkablePageLink("view", CommitPage.class, WicketUtils .newObjectParameter(submodulePath, entry.objectId)).setEnabled(hasSubmodule)); - item.add(new BookmarkablePageLink("blame", BlamePage.class, WicketUtils - .newPathParameter(submodulePath, entry.objectId, entry.path)) - .setEnabled(false)); + item.add(new ExternalLink("blame", "").setEnabled(false)); item.add(new BookmarkablePageLink("history", HistoryPage.class, WicketUtils - .newPathParameter(submodulePath, entry.objectId, entry.path)) - .setEnabled(hasSubmodule)); + .newPathParameter(repositoryName, entry.commitId, entry.path)) + .setEnabled(!entry.changeType.equals(ChangeType.ADD))); } else { // tree or blob item.add(new BookmarkablePageLink("diff", BlobDiffPage.class, WicketUtils diff --git a/src/com/gitblit/wicket/pages/TreePage.java b/src/com/gitblit/wicket/pages/TreePage.java index b8ce7b48..bc27f0c2 100644 --- a/src/com/gitblit/wicket/pages/TreePage.java +++ b/src/com/gitblit/wicket/pages/TreePage.java @@ -137,8 +137,8 @@ public class TreePage extends RepositoryPage { WicketUtils.newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule)); links.add(new BookmarkablePageLink("history", HistoryPage.class, - WicketUtils.newPathParameter(submodulePath, submoduleId, - "")).setEnabled(hasSubmodule)); + WicketUtils.newPathParameter(repositoryName, entry.commitId, + entry.path))); links.add(new CompressedDownloadsPanel("compressedLinks", baseUrl, submodulePath, submoduleId, "").setEnabled(hasSubmodule)); item.add(links); -- cgit v1.2.3 From 6f44df97395bb11d41720bd796fadf1f829104d4 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sun, 13 Jan 2013 16:50:00 -0500 Subject: Eliminate unused method --- src/com/gitblit/wicket/pages/RepositoryPage.java | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/com/gitblit') diff --git a/src/com/gitblit/wicket/pages/RepositoryPage.java b/src/com/gitblit/wicket/pages/RepositoryPage.java index 16087fa6..78ffe80d 100644 --- a/src/com/gitblit/wicket/pages/RepositoryPage.java +++ b/src/com/gitblit/wicket/pages/RepositoryPage.java @@ -368,10 +368,6 @@ public abstract class RepositoryPage extends BasePage { return submodules; } - protected Map getSubmodules() { - return submodules; - } - protected SubmoduleModel getSubmodule(String path) { SubmoduleModel model = submodules.get(path); if (model == null) { -- cgit v1.2.3 From 9727511ad07db2c332f164fe399108768457bdae Mon Sep 17 00:00:00 2001 From: James Moger Date: Sun, 13 Jan 2013 17:28:05 -0500 Subject: Null checks when generating a fork network (issue-187) --- docs/04_releases.mkd | 1 + src/com/gitblit/GitBlit.java | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index 3d17a9b2..af702f2c 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -7,6 +7,7 @@ #### fixes - Fixed nullpointer on recursively calculating folder sizes when there is a named pipe or symlink in the hierarchy +- Added nullchecking when concurrently forking a repository and trying to display it's fork network (issue-187) - Fixed bug where permission changes were not visible in the web ui to a logged-in user until the user logged-out and then logged back in again (issue-186) - Fixed nullpointer on creating a repository with mixed case (issue 185) - Fixed nullpointer when using web.allowForking = true && git.cacheRepositoryList = false (issue 182) diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index f417b3eb..e3ecebd8 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -1872,11 +1872,16 @@ public class GitBlit implements ServletContextListener { private ForkModel getForkModelFromCache(String repository) { RepositoryModel model = repositoryListCache.get(repository.toLowerCase()); + if (model == null) { + return null; + } ForkModel fork = new ForkModel(model); if (!ArrayUtils.isEmpty(model.forks)) { for (String aFork : model.forks) { ForkModel fm = getForkModelFromCache(aFork); - fork.forks.add(fm); + if (fm != null) { + fork.forks.add(fm); + } } } return fork; @@ -1884,11 +1889,16 @@ public class GitBlit implements ServletContextListener { private ForkModel getForkModel(String repository) { RepositoryModel model = getRepositoryModel(repository.toLowerCase()); + if (model == null) { + return null; + } ForkModel fork = new ForkModel(model); if (!ArrayUtils.isEmpty(model.forks)) { for (String aFork : model.forks) { ForkModel fm = getForkModel(aFork); - fork.forks.add(fm); + if (fm != null) { + fork.forks.add(fm); + } } } return fork; -- cgit v1.2.3 From 19e902522d1d55f042daf076a1f7299b7c220061 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sun, 13 Jan 2013 17:31:29 -0500 Subject: Set the new objectid for all diff entries (issue-178) --- docs/04_releases.mkd | 1 + src/com/gitblit/utils/JGitUtils.java | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index af702f2c..ee31b692 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -11,6 +11,7 @@ - Fixed bug where permission changes were not visible in the web ui to a logged-in user until the user logged-out and then logged back in again (issue-186) - Fixed nullpointer on creating a repository with mixed case (issue 185) - Fixed nullpointer when using web.allowForking = true && git.cacheRepositoryList = false (issue 182) +- Likely fix for commit and commitdiff page failures when a submodule reference changes (issue 178) - Build project models from the repository model cache, when possible, to reduce page load time (issue 172) - Fixed loading of Brazilian Portuguese translation from *nix server (github/inaiat) diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java index e1127708..815f8b5a 100644 --- a/src/com/gitblit/utils/JGitUtils.java +++ b/src/com/gitblit/utils/JGitUtils.java @@ -743,11 +743,7 @@ public class JGitUtils { df.setDetectRenames(true); List diffs = df.scan(parent.getTree(), commit.getTree()); for (DiffEntry diff : diffs) { - String objectId = null; - if (FileMode.GITLINK.equals(diff.getNewMode())) { - objectId = diff.getNewId().name(); - } - + String objectId = diff.getNewId().name(); if (diff.getChangeType().equals(ChangeType.DELETE)) { list.add(new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff .getNewMode().getBits(), objectId, commit.getId().getName(), diff -- cgit v1.2.3 From 657a6596eb95635abd29c0a21befffc43da49d09 Mon Sep 17 00:00:00 2001 From: James Moger Date: Sun, 13 Jan 2013 18:09:58 -0500 Subject: Improve history display of a submodule link --- docs/04_releases.mkd | 1 + src/com/gitblit/utils/JGitUtils.java | 26 ++++++ src/com/gitblit/wicket/panels/HistoryPanel.java | 102 ++++++++++++++++++++++++ 3 files changed, 129 insertions(+) (limited to 'src/com/gitblit') diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index ee31b692..fa088b2a 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -30,6 +30,7 @@ The push log is not currently visible in the ui, but the data will be collected #### changes +- Improve history display of a submodule link - Updated Korean translation (github/ds5apn) - Updated checkstyle definition (github/mystygage) diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java index 815f8b5a..1f2ae943 100644 --- a/src/com/gitblit/utils/JGitUtils.java +++ b/src/com/gitblit/utils/JGitUtils.java @@ -1616,6 +1616,32 @@ public class JGitUtils { } return null; } + + public static String getSubmoduleCommitId(Repository repository, String path, RevCommit commit) { + String commitId = null; + RevWalk rw = new RevWalk(repository); + TreeWalk tw = new TreeWalk(repository); + tw.setFilter(PathFilterGroup.createFromStrings(Collections.singleton(path))); + try { + tw.reset(commit.getTree()); + while (tw.next()) { + if (tw.isSubtree() && !path.equals(tw.getPathString())) { + tw.enterSubtree(); + continue; + } + if (FileMode.GITLINK == tw.getFileMode(0)) { + commitId = tw.getObjectId(0).getName(); + break; + } + } + } catch (Throwable t) { + error(t, repository, "{0} can't find {1} in commit {2}", path, commit.name()); + } finally { + rw.dispose(); + tw.release(); + } + return commitId; + } /** * Returns the list of notes entered about the commit from the refs/notes diff --git a/src/com/gitblit/wicket/panels/HistoryPanel.java b/src/com/gitblit/wicket/panels/HistoryPanel.java index 0f586031..e5878635 100644 --- a/src/com/gitblit/wicket/panels/HistoryPanel.java +++ b/src/com/gitblit/wicket/panels/HistoryPanel.java @@ -15,10 +15,14 @@ */ package com.gitblit.wicket.panels; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; @@ -38,6 +42,7 @@ import com.gitblit.Constants; import com.gitblit.GitBlit; import com.gitblit.Keys; import com.gitblit.models.PathModel; +import com.gitblit.models.SubmoduleModel; import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.RefModel; import com.gitblit.utils.JGitUtils; @@ -69,6 +74,11 @@ public class HistoryPanel extends BasePanel { RevCommit commit = JGitUtils.getCommit(r, objectId); List paths = JGitUtils.getFilesInCommit(r, commit); + Map submodules = new HashMap(); + for (SubmoduleModel model : JGitUtils.getSubmodules(r, commit.getTree())) { + submodules.put(model.path, model); + } + PathModel matchingPath = null; for (PathModel p : paths) { if (p.path.equals(path)) { @@ -99,7 +109,20 @@ public class HistoryPanel extends BasePanel { } final boolean isTree = matchingPath == null ? true : matchingPath.isTree(); + final boolean isSubmodule = matchingPath == null ? true : matchingPath.isSubmodule(); + // submodule + SubmoduleModel submodule = getSubmodule(submodules, repositoryName, matchingPath.path); + final String submodulePath; + final boolean hasSubmodule; + if (submodule != null) { + submodulePath = submodule.gitblitPath; + hasSubmodule = submodule.hasSubmodule; + } else { + submodulePath = ""; + hasSubmodule = false; + } + final Map> allRefs = JGitUtils.getAllRefs(r, showRemoteRefs); List commits; if (pageResults) { @@ -175,6 +198,23 @@ public class HistoryPanel extends BasePanel { WicketUtils.setHtmlTooltip(commitHash, entry.getName()); item.add(commitHash); + Fragment links = new Fragment("historyLinks", "treeLinks", this); + links.add(new BookmarkablePageLink("commitdiff", CommitDiffPage.class, + WicketUtils.newObjectParameter(repositoryName, entry.getName()))); + item.add(links); + } else if (isSubmodule) { + // submodule + item.add(new Label("hashLabel", submodulePath + "@")); + Repository repository = GitBlit.self().getRepository(repositoryName); + String submoduleId = JGitUtils.getSubmoduleCommitId(repository, path, entry); + repository.close(); + LinkPanel commitHash = new LinkPanel("hashLink", null, submoduleId.substring(0, hashLen), + TreePage.class, WicketUtils.newObjectParameter( + submodulePath, submoduleId)); + WicketUtils.setCssClass(commitHash, "shortsha1"); + WicketUtils.setHtmlTooltip(commitHash, submoduleId); + item.add(commitHash.setEnabled(hasSubmodule)); + Fragment links = new Fragment("historyLinks", "treeLinks", this); links.add(new BookmarkablePageLink("commitdiff", CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName()))); @@ -230,4 +270,66 @@ public class HistoryPanel extends BasePanel { public boolean hasMore() { return hasMore; } + + protected SubmoduleModel getSubmodule(Map submodules, String repositoryName, String path) { + SubmoduleModel model = submodules.get(path); + if (model == null) { + // undefined submodule?! + model = new SubmoduleModel(path.substring(path.lastIndexOf('/') + 1), path, path); + model.hasSubmodule = false; + model.gitblitPath = model.name; + return model; + } else { + // extract the repository name from the clone url + List patterns = GitBlit.getStrings(Keys.git.submoduleUrlPatterns); + String submoduleName = StringUtils.extractRepositoryPath(model.url, patterns.toArray(new String[0])); + + // determine the current path for constructing paths relative + // to the current repository + String currentPath = ""; + if (repositoryName.indexOf('/') > -1) { + currentPath = repositoryName.substring(0, repositoryName.lastIndexOf('/') + 1); + } + + // try to locate the submodule repository + // prefer bare to non-bare names + List candidates = new ArrayList(); + + // relative + candidates.add(currentPath + StringUtils.stripDotGit(submoduleName)); + candidates.add(candidates.get(candidates.size() - 1) + ".git"); + + // relative, no subfolder + if (submoduleName.lastIndexOf('/') > -1) { + String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1); + candidates.add(currentPath + StringUtils.stripDotGit(name)); + candidates.add(currentPath + candidates.get(candidates.size() - 1) + ".git"); + } + + // absolute + candidates.add(StringUtils.stripDotGit(submoduleName)); + candidates.add(candidates.get(candidates.size() - 1) + ".git"); + + // absolute, no subfolder + if (submoduleName.lastIndexOf('/') > -1) { + String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1); + candidates.add(StringUtils.stripDotGit(name)); + candidates.add(candidates.get(candidates.size() - 1) + ".git"); + } + + // create a unique, ordered set of candidate paths + Set paths = new LinkedHashSet(candidates); + for (String candidate : paths) { + if (GitBlit.self().hasRepository(candidate)) { + model.hasSubmodule = true; + model.gitblitPath = candidate; + return model; + } + } + + // we do not have a copy of the submodule, but we need a path + model.gitblitPath = candidates.get(0); + return model; + } + } } -- cgit v1.2.3 From 93d506581010cdb6308ae3d282a8bc513966d70d Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 15 Jan 2013 22:17:08 -0500 Subject: Support --baseFolder parameter and small data reorganization --- .gitignore | 3 +- build.xml | 210 ++++++--- distrib/authority.cmd | 2 +- distrib/gitblit | 5 +- distrib/gitblit-centos | 5 +- distrib/gitblit-stop.cmd | 2 +- distrib/gitblit-ubuntu | 3 +- distrib/gitblit.cmd | 2 +- distrib/gitblit.properties | 47 +- distrib/groovy/.gitignore | 1 + distrib/groovy/blockpush.groovy | 94 ++++ distrib/groovy/jenkins.groovy | 76 +++ distrib/groovy/localclone.groovy | 106 +++++ distrib/groovy/protect-refs.groovy | 113 +++++ distrib/groovy/sendmail-html.groovy | 516 +++++++++++++++++++++ distrib/groovy/sendmail.groovy | 176 +++++++ distrib/groovy/thebuggenie.groovy | 88 ++++ distrib/installService.cmd | 4 +- distrib/projects.conf | 3 + docs/01_setup.mkd | 134 +++--- docs/04_releases.mkd | 18 +- groovy/.gitignore | 1 - groovy/blockpush.groovy | 94 ---- groovy/jenkins.groovy | 76 --- groovy/localclone.groovy | 106 ----- groovy/protect-refs.groovy | 113 ----- groovy/sendmail-html.groovy | 516 --------------------- groovy/sendmail.groovy | 176 ------- groovy/thebuggenie.groovy | 88 ---- src/WEB-INF/web.xml | 24 + src/com/gitblit/Constants.java | 8 +- src/com/gitblit/FederationClient.java | 2 +- src/com/gitblit/GitBlit.java | 111 +++-- src/com/gitblit/GitBlitServer.java | 54 ++- src/com/gitblit/GitServlet.java | 2 +- src/com/gitblit/GitblitUserService.java | 2 +- src/com/gitblit/LdapUserService.java | 2 +- src/com/gitblit/RedmineUserService.java | 2 +- src/com/gitblit/RobotsTxtServlet.java | 10 +- src/com/gitblit/authority/GitblitAuthority.java | 38 +- src/com/gitblit/build/Build.java | 2 +- src/com/gitblit/build/BuildWebXml.java | 64 +-- src/com/gitblit/utils/FileUtils.java | 15 + .../gitblit/wicket/pages/ChangePasswordPage.java | 2 +- src/com/gitblit/wicket/pages/EditUserPage.java | 2 +- src/com/gitblit/wicket/pages/RepositoriesPage.java | 2 +- test-gitblit.properties | 6 +- tests/com/gitblit/tests/GitBlitSuite.java | 5 +- tests/com/gitblit/tests/GitBlitTest.java | 2 +- 49 files changed, 1704 insertions(+), 1429 deletions(-) create mode 100644 distrib/groovy/.gitignore create mode 100644 distrib/groovy/blockpush.groovy create mode 100644 distrib/groovy/jenkins.groovy create mode 100644 distrib/groovy/localclone.groovy create mode 100644 distrib/groovy/protect-refs.groovy create mode 100644 distrib/groovy/sendmail-html.groovy create mode 100644 distrib/groovy/sendmail.groovy create mode 100644 distrib/groovy/thebuggenie.groovy create mode 100644 distrib/projects.conf delete mode 100644 groovy/.gitignore delete mode 100644 groovy/blockpush.groovy delete mode 100644 groovy/jenkins.groovy delete mode 100644 groovy/localclone.groovy delete mode 100644 groovy/protect-refs.groovy delete mode 100644 groovy/sendmail-html.groovy delete mode 100644 groovy/sendmail.groovy delete mode 100644 groovy/thebuggenie.groovy (limited to 'src/com/gitblit') diff --git a/.gitignore b/.gitignore index e335123e..65b74abc 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ /deploy /*.jks /x509test -/certs \ No newline at end of file +/certs +/data diff --git a/build.xml b/build.xml index 200fc91c..4a4633c3 100644 --- a/build.xml +++ b/build.xml @@ -12,10 +12,11 @@ - + - + + @@ -106,22 +107,66 @@ --> - - + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -186,18 +231,45 @@ + + + + - + + + + + + + + + + + + + + + + + + + + + + + - - + + + @@ -234,20 +306,8 @@ - - - - - - - - - - - - - + @@ -383,9 +443,6 @@ - - - @@ -404,19 +461,28 @@ + + + + + + + + + + - - - + + + - - + @@ -426,8 +492,6 @@ - - @@ -467,7 +531,7 @@ - + @@ -554,7 +618,7 @@ Building Gitblit Federation Client ${gb.version} - + @@ -575,16 +639,21 @@ - + - + + + + + + @@ -619,10 +688,20 @@ + + + + + + + + + + - - - + + + @@ -663,6 +742,8 @@ + + @@ -680,7 +761,7 @@ - + @@ -695,7 +776,7 @@ Building Gitblit Manager ${gb.version} - + @@ -747,13 +828,18 @@ - + - + + + + + + @@ -765,7 +851,7 @@ Building Gitblit Authority ${gb.version} - + @@ -812,13 +898,15 @@ - + - - + + + + @@ -835,7 +923,7 @@ Building Gitblit API Library ${gb.version} - + @@ -850,7 +938,7 @@ - + @@ -862,7 +950,7 @@ - + @@ -872,18 +960,20 @@ - - + + - + + + + + - - @@ -892,6 +982,16 @@ + + + + + + + + + + diff --git a/distrib/authority.cmd b/distrib/authority.cmd index 145f5242..75cb0cf7 100644 --- a/distrib/authority.cmd +++ b/distrib/authority.cmd @@ -1 +1 @@ -@java -jar authority.jar +@java -jar authority.jar --baseFolder data diff --git a/distrib/gitblit b/distrib/gitblit index cd1f967e..6c74d547 100644 --- a/distrib/gitblit +++ b/distrib/gitblit @@ -3,6 +3,7 @@ set -e GITBLIT_PATH=/opt/gitblit +GITBLIT_BASE_FOLDER=/opt/gitblit/data GITBLIT_HTTP_PORT=0 GITBLIT_HTTPS_PORT=8443 source ${GITBLIT_PATH}/java-proxy-config.sh @@ -14,13 +15,13 @@ case "$1" in start) log_action_begin_msg "Starting gitblit server" cd $GITBLIT_PATH - $JAVA $GITBLIT_PATH/gitblit.jar --httpsPort $GITBLIT_HTTPS_PORT --httpPort $GITBLIT_HTTP_PORT > /dev/null & + $JAVA $GITBLIT_PATH/gitblit.jar --httpsPort $GITBLIT_HTTPS_PORT --httpPort $GITBLIT_HTTP_PORT --baseFolder $GITBLIT_BASE_FOLDER > /dev/null & log_action_end_msg $? ;; stop) log_action_begin_msg "Stopping gitblit server" cd $GITBLIT_PATH - $JAVA $GITBLIT_PATH/gitblit.jar --stop > /dev/null & + $JAVA $GITBLIT_PATH/gitblit.jar --baseFolder $GITBLIT_BASE_FOLDER --stop > /dev/null & log_action_end_msg $? ;; force-reload|restart) diff --git a/distrib/gitblit-centos b/distrib/gitblit-centos index c608097e..04c9a9b4 100644 --- a/distrib/gitblit-centos +++ b/distrib/gitblit-centos @@ -6,6 +6,7 @@ # change theses values (default values) GITBLIT_PATH=/opt/gitblit +GITBLIT_BASE_FOLDER=/opt/gitblit/data GITBLIT_HTTP_PORT=0 GITBLIT_HTTPS_PORT=8443 source ${GITBLIT_PATH}/java-proxy-config.sh @@ -19,7 +20,7 @@ case "$1" in then echo $"Starting gitblit server" cd $GITBLIT_PATH - $JAVA $GITBLIT_PATH/gitblit.jar --httpsPort $GITBLIT_HTTPS_PORT --httpPort $GITBLIT_HTTP_PORT > /dev/null & + $JAVA $GITBLIT_PATH/gitblit.jar --httpsPort $GITBLIT_HTTPS_PORT --httpPort $GITBLIT_HTTP_PORT --baseFolder $GITBLIT_BASE_FOLDER > /dev/null & echo "." exit $RETVAL fi @@ -30,7 +31,7 @@ case "$1" in then echo $"Stopping gitblit server" cd $GITBLIT_PATH - $JAVA $GITBLIT_PATH/gitblit.jar --stop > /dev/null & + $JAVA $GITBLIT_PATH/gitblit.jar --baseFolder $GITBLIT_BASE_FOLDER --stop > /dev/null & echo "." exit $RETVAL fi diff --git a/distrib/gitblit-stop.cmd b/distrib/gitblit-stop.cmd index c139d57b..5820c491 100644 --- a/distrib/gitblit-stop.cmd +++ b/distrib/gitblit-stop.cmd @@ -1 +1 @@ -@java -jar gitblit.jar --stop +@java -jar gitblit.jar --stop --baseFolder data diff --git a/distrib/gitblit-ubuntu b/distrib/gitblit-ubuntu index b047ed97..4ff275d0 100644 --- a/distrib/gitblit-ubuntu +++ b/distrib/gitblit-ubuntu @@ -8,9 +8,10 @@ PATH=/sbin:/bin:/usr/bin:/usr/sbin # change theses values (default values) GITBLIT_PATH=/opt/gitblit +GITBLIT_BASE_FOLDER=/opt/gitblit/data GITBLIT_USER="gitblit" source ${GITBLIT_PATH}/java-proxy-config.sh -ARGS="-server -Xmx1024M ${JAVA_PROXY_CONFIG} -Djava.awt.headless=true -jar gitblit.jar" +ARGS="-server -Xmx1024M ${JAVA_PROXY_CONFIG} -Djava.awt.headless=true -jar gitblit.jar --baseFolder $GITBLIT_BASE_FOLDER" RETVAL=0 diff --git a/distrib/gitblit.cmd b/distrib/gitblit.cmd index ce96a797..3006a687 100644 --- a/distrib/gitblit.cmd +++ b/distrib/gitblit.cmd @@ -1 +1 @@ -@java -jar gitblit.jar +@java -jar gitblit.jar --baseFolder data diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index 758137e3..f5cc19b6 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -1,4 +1,19 @@ # +# Gitblit Settings +# + +# This settings file supports parameterization from the command-line for the +# following command-line parameters: +# +# --baseFolder ${baseFolder} SINCE 1.2.1 +# +# Settings that support ${baseFolder} parameter substitution are indicated with the +# BASEFOLDER attribute. If the --baseFolder argument is unspecified, ${baseFolder} +# and it's trailing / will be discarded from the setting value leaving a relative +# path that is equivalent to pre-1.2.1 releases. +# +# e.g. "${baseFolder}/git" becomes "git", if --baseFolder is unspecified +# # Git Servlet Settings # @@ -10,7 +25,8 @@ # # SINCE 0.5.0 # RESTART REQUIRED -git.repositoriesFolder = git +# BASEFOLDER +git.repositoriesFolder = ${baseFolder}/git # Build the available repository list at startup and cache this list for reuse. # This reduces disk io when presenting the repositories page, responding to rpcs, @@ -299,14 +315,16 @@ git.packedGitMmap = false # # RESTART REQUIRED # SINCE 0.8.0 -groovy.scriptsFolder = groovy +# BASEFOLDER +groovy.scriptsFolder = ${baseFolder}/groovy # Specify the directory Grape uses for downloading libraries. # http://groovy.codehaus.org/Grape # # RESTART REQUIRED # SINCE 1.0.0 -groovy.grapeFolder = groovy/grape +# BASEFOLDER +groovy.grapeFolder = ${baseFolder}/groovy/grape # Scripts to execute on Pre-Receive. # @@ -437,7 +455,8 @@ web.allowCookieAuthentication = true # Config file for storing project metadata # # SINCE 1.2.0 -web.projectsFile = projects.conf +# BASEFOLDER +web.projectsFile = ${baseFolder}/projects.conf # Either the full path to a user config file (users.conf) # OR the full path to a simple user properties file (users.properties) @@ -451,7 +470,8 @@ web.projectsFile = projects.conf # # SINCE 0.5.0 # RESTART REQUIRED -realm.userService = users.conf +# BASEFOLDER +realm.userService = ${baseFolder}/users.conf # How to store passwords. # Valid values are plain, md5, or combined-md5. md5 is the hash of password. @@ -510,7 +530,8 @@ web.enableRpcAdministration = false # http://googlewebmastercentral.blogspot.com/2008/06/improving-on-robots-exclusion-protocol.html # # SINCE 1.0.0 -web.robots.txt = +# BASEFOLDER +web.robots.txt = ${baseFolder}/robots.txt # If true, the web ui layout will respond and adapt to the browser's dimensions. # if false, the web ui will use a 940px fixed-width layout. @@ -609,6 +630,7 @@ web.showFederationRegistrations = false # Specifying "gitblit" uses the internal login message. # # SINCE 0.7.0 +# BASEFOLDER web.loginMessage = gitblit # This is the message displayed above the repositories table. @@ -616,6 +638,7 @@ web.loginMessage = gitblit # Specifying "gitblit" uses the internal welcome message. # # SINCE 0.5.0 +# BASEFOLDER web.repositoriesMessage = gitblit # Ordered list of charsets/encodings to use when trying to display a blob. @@ -925,7 +948,8 @@ federation.allowProposals = false # Use forward slashes even on Windows!! # # SINCE 0.6.0 -federation.proposalsFolder = proposals +# BASEFOLDER +federation.proposalsFolder = ${baseFolder}/proposals # The default pull frequency if frequency is unspecified on a registration # @@ -1027,7 +1051,8 @@ realm.ldap.password = password # # SINCE 1.0.0 # RESTART REQUIRED -realm.ldap.backingUserService = users.conf +# BASEFOLDER +realm.ldap.backingUserService = ${baseFolder}/users.conf # Delegate team membership control to LDAP. # @@ -1123,7 +1148,8 @@ realm.ldap.email = email # default: users.conf # # RESTART REQUIRED -realm.redmine.backingUserService = users.conf +# BASEFOLDER +realm.redmine.backingUserService = ${baseFolder}/users.conf # URL of the Redmine. realm.redmine.url = http://example.com/redmine @@ -1136,7 +1162,8 @@ realm.redmine.url = http://example.com/redmine # # SINCE 0.5.0 # RESTART REQUIRED -server.tempFolder = temp +# BASEFOLDER +server.tempFolder = ${baseFolder}/temp # Use Jetty NIO connectors. If false, Jetty Socket connectors will be used. # diff --git a/distrib/groovy/.gitignore b/distrib/groovy/.gitignore new file mode 100644 index 00000000..e58dc47f --- /dev/null +++ b/distrib/groovy/.gitignore @@ -0,0 +1 @@ +/grape diff --git a/distrib/groovy/blockpush.groovy b/distrib/groovy/blockpush.groovy new file mode 100644 index 00000000..caef3306 --- /dev/null +++ b/distrib/groovy/blockpush.groovy @@ -0,0 +1,94 @@ +/* + * Copyright 2011 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.text.MessageFormat; + +import com.gitblit.GitBlit +import com.gitblit.models.RepositoryModel +import com.gitblit.models.UserModel +import org.eclipse.jgit.transport.ReceiveCommand +import org.eclipse.jgit.transport.ReceiveCommand.Result +import org.slf4j.Logger +import com.gitblit.utils.ClientLogger + +/** + * Sample Gitblit Pre-Receive Hook: blockpush + * + * This script could and perhaps should be further developed to provide + * a full repository-branch permissions system similar to gitolite or gitosis. + * + * The Pre-Receive hook is executed after an incoming push has been parsed, + * validated, and objects have been written but BEFORE the refs are updated. + * This is the appropriate point to block a push for some reason. + * + * This script is only executed when pushing to *Gitblit*, not to other Git + * tooling you may be using. + * + * If this script is specified in *groovy.preReceiveScripts* of gitblit.properties + * or web.xml then it will be executed by any repository when it receives a + * push. If you choose to share your script then you may have to consider + * tailoring control-flow based on repository access restrictions. + * + * Scripts may also be specified per-repository in the repository settings page. + * Shared scripts will be excluded from this list of available scripts. + * + * This script is dynamically reloaded and it is executed within it's own + * exception handler so it will not crash another script nor crash Gitblit. + * + * If you want this hook script to fail and abort all subsequent scripts in the + * chain, "return false" at the appropriate failure points. + * + * Bound Variables: + * gitblit Gitblit Server com.gitblit.GitBlit + * repository Gitblit Repository com.gitblit.models.RepositoryModel + * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack + * user Gitblit User com.gitblit.models.UserModel + * commands JGit commands Collection + * url Base url for Gitblit String + * logger Logs messages to Gitblit org.slf4j.Logger + * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger + * + * Accessing Gitblit Custom Fields: + * def myCustomField = repository.customFields.myCustomField + * + */ + +// Indicate we have started the script +logger.info("blockpush hook triggered by ${user.username} for ${repository.name}: checking ${commands.size} commands") + +/* + * Example rejection of pushes to the master branch of example.git + */ +def blocked = false +switch (repository.name) { + case 'ex@mple.git': + for (ReceiveCommand command : commands) { + def updatedRef = command.refName + if (updatedRef.equals('refs/heads/master')) { + // to reject a command set it's result to anything other than Result.NOT_ATTEMPTED + command.setResult(Result.REJECTED_OTHER_REASON, "You are not permitted to write to ${repository.name}:${updatedRef}") + blocked = true + } + } + break + + default: + break +} + +if (blocked) { + // return false to break the push hook chain + return false +} \ No newline at end of file diff --git a/distrib/groovy/jenkins.groovy b/distrib/groovy/jenkins.groovy new file mode 100644 index 00000000..d76a3d66 --- /dev/null +++ b/distrib/groovy/jenkins.groovy @@ -0,0 +1,76 @@ +/* + * Copyright 2011 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import com.gitblit.GitBlit +import com.gitblit.Keys +import com.gitblit.models.RepositoryModel +import com.gitblit.models.UserModel +import com.gitblit.utils.JGitUtils +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.revwalk.RevCommit +import org.eclipse.jgit.transport.ReceiveCommand +import org.eclipse.jgit.transport.ReceiveCommand.Result +import org.slf4j.Logger + +/** + * Sample Gitblit Post-Receive Hook: jenkins + * + * The Post-Receive hook is executed AFTER the pushed commits have been applied + * to the Git repository. This is the appropriate point to trigger an + * integration build or to send a notification. + * + * This script is only executed when pushing to *Gitblit*, not to other Git + * tooling you may be using. + * + * If this script is specified in *groovy.postReceiveScripts* of gitblit.properties + * or web.xml then it will be executed by any repository when it receives a + * push. If you choose to share your script then you may have to consider + * tailoring control-flow based on repository access restrictions. + * + * Scripts may also be specified per-repository in the repository settings page. + * Shared scripts will be excluded from this list of available scripts. + * + * This script is dynamically reloaded and it is executed within it's own + * exception handler so it will not crash another script nor crash Gitblit. + * + * Bound Variables: + * gitblit Gitblit Server com.gitblit.GitBlit + * repository Gitblit Repository com.gitblit.models.RepositoryModel + * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack + * user Gitblit User com.gitblit.models.UserModel + * commands JGit commands Collection + * url Base url for Gitblit String + * logger Logs messages to Gitblit org.slf4j.Logger + * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger + * + * Accessing Gitblit Custom Fields: + * def myCustomField = repository.customFields.myCustomField + * + */ +// Indicate we have started the script +logger.info("jenkins hook triggered by ${user.username} for ${repository.name}") + +// This script requires Jenkins Git plugin 1.1.14 or later +// http://kohsuke.org/2011/12/01/polling-must-die-triggering-jenkins-builds-from-a-git-hook/ + +// define your jenkins url here or set groovy.jenkinsServer in +// gitblit.properties or web.xml +def jenkinsUrl = gitblit.getString('groovy.jenkinsServer', 'http://yourserver/jenkins') + +// define the trigger url +def triggerUrl = jenkinsUrl + "/git/notifyCommit?url=$url/git/$repository.name" + +// trigger the build +new URL(triggerUrl).getContent() diff --git a/distrib/groovy/localclone.groovy b/distrib/groovy/localclone.groovy new file mode 100644 index 00000000..49b7f8b3 --- /dev/null +++ b/distrib/groovy/localclone.groovy @@ -0,0 +1,106 @@ +/* + * Copyright 2012 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import com.gitblit.GitBlit +import com.gitblit.Keys +import com.gitblit.models.RepositoryModel +import com.gitblit.models.TeamModel +import com.gitblit.models.UserModel +import com.gitblit.utils.JGitUtils +import com.gitblit.utils.StringUtils +import java.text.SimpleDateFormat +import org.eclipse.jgit.api.CloneCommand +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.lib.Config +import org.eclipse.jgit.revwalk.RevCommit +import org.eclipse.jgit.transport.ReceiveCommand +import org.eclipse.jgit.transport.ReceiveCommand.Result +import org.eclipse.jgit.util.FileUtils +import org.slf4j.Logger + +/** + * Sample Gitblit Post-Receive Hook: localclone + * + * The Post-Receive hook is executed AFTER the pushed commits have been applied + * to the Git repository. This is the appropriate point to trigger an + * integration build or to send a notification. + * + * This script is only executed when pushing to *Gitblit*, not to other Git + * tooling you may be using. + * + * If this script is specified in *groovy.postReceiveScripts* of gitblit.properties + * or web.xml then it will be executed by any repository when it receives a + * push. If you choose to share your script then you may have to consider + * tailoring control-flow based on repository access restrictions. + * + * Scripts may also be specified per-repository in the repository settings page. + * Shared scripts will be excluded from this list of available scripts. + * + * This script is dynamically reloaded and it is executed within it's own + * exception handler so it will not crash another script nor crash Gitblit. + * + * If you want this hook script to fail and abort all subsequent scripts in the + * chain, "return false" at the appropriate failure points. + * + * Bound Variables: + * gitblit Gitblit Server com.gitblit.GitBlit + * repository Gitblit Repository com.gitblit.models.RepositoryModel + * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack + * user Gitblit User com.gitblit.models.UserModel + * commands JGit commands Collection + * url Base url for Gitblit String + * logger Logs messages to Gitblit org.slf4j.Logger + * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger + * + * Accessing Gitblit Custom Fields: + * def myCustomField = repository.customFields.myCustomField + * + */ + +// Indicate we have started the script +logger.info("localclone hook triggered by ${user.username} for ${repository.name}") + +def rootFolder = 'c:/test' +def bare = false +def cloneAllBranches = true +def cloneBranch = 'refs/heads/master' +def includeSubmodules = true + +def repoName = repository.name +def destinationFolder = new File(rootFolder, StringUtils.stripDotGit(repoName)) +def srcUrl = 'file://' + new File(GitBlit.getRepositoriesFolder(), repoName).absolutePath + +// delete any previous clone +if (destinationFolder.exists()) { + FileUtils.delete(destinationFolder, FileUtils.RECURSIVE) +} + +// clone the repository +logger.info("cloning ${srcUrl} to ${destinationFolder}") +CloneCommand cmd = Git.cloneRepository(); +cmd.setBare(bare) +if (cloneAllBranches) + cmd.setCloneAllBranches(true) +else + cmd.setBranch(cloneBranch) +cmd.setCloneSubmodules(includeSubmodules) +cmd.setURI(srcUrl) +cmd.setDirectory(destinationFolder) +Git git = cmd.call(); +git.repository.close() + +// report clone operation success back to pushing Git client +clientLogger.info("${repoName} cloned to ${destinationFolder}") \ No newline at end of file diff --git a/distrib/groovy/protect-refs.groovy b/distrib/groovy/protect-refs.groovy new file mode 100644 index 00000000..b1b611f4 --- /dev/null +++ b/distrib/groovy/protect-refs.groovy @@ -0,0 +1,113 @@ +/* + * Copyright 2012 Philip L. McMahon. + * + * Derived from blockpush.groovy, copyright 2011 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import com.gitblit.GitBlit +import com.gitblit.models.RepositoryModel +import com.gitblit.models.UserModel + +import org.eclipse.jgit.transport.ReceiveCommand +import org.eclipse.jgit.transport.ReceiveCommand.Result +import org.slf4j.Logger + +/** + * Sample Gitblit Pre-Receive Hook: protect-refs + * + * This script provides basic authorization of receive command types for a list + * of known ref patterns. Command types and unmatched ref patterns will be + * ignored, meaning this script has an "allow by default" policy. + * + * This script works best when a repository requires authentication on push, but + * can be used to enforce fast-forward commits or prohibit ref deletion by + * setting the *authorizedTeams* variable to an empty list and adding a ".+" + * entry to the *protectedRefs* list. + * + * The Pre-Receive hook is executed after an incoming push has been parsed, + * validated, and objects have been written but BEFORE the refs are updated. + * This is the appropriate point to block a push for some reason. + * + * This script is only executed when pushing to *Gitblit*, not to other Git + * tooling you may be using. + * + * If this script is specified in *groovy.preReceiveScripts* of gitblit.properties + * or web.xml then it will be executed by any repository when it receives a + * push. If you choose to share your script then you may have to consider + * tailoring control-flow based on repository access restrictions. + * + * Scripts may also be specified per-repository in the repository settings page. + * Shared scripts will be excluded from this list of available scripts. + * + * This script is dynamically reloaded and it is executed within it's own + * exception handler so it will not crash another script nor crash Gitblit. + * + * This script may reject one or more commands, but will never return false. + * Subsequent scripts, if any, will always be invoked. + * + * Bound Variables: + * gitblit Gitblit Server com.gitblit.GitBlit + * repository Gitblit Repository com.gitblit.models.RepositoryModel + * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack + * user Gitblit User com.gitblit.models.UserModel + * commands JGit commands Collection + * url Base url for Gitblit String + * logger Logs messages to Gitblit org.slf4j.Logger + * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger + * + * Accessing Gitblit Custom Fields: + * def myCustomField = repository.customFields.myCustomField + * + */ + +// map of protected command types to returned results type +// commands not included will skip authz check +def protectedCmds = [ + UPDATE_NONFASTFORWARD: Result.REJECTED_NONFASTFORWARD, + DELETE: Result.REJECTED_NODELETE +] + +// list of regex patterns for protected refs +def protectedRefs = [ + "refs/heads/master", + "refs/tags/.+" +] + +// teams which are authorized to perform protected commands on protected refs +def authorizedTeams = [ "admins" ] + +for (ReceiveCommand command : commands) { + def updateType = command.type + def updatedRef = command.refName + + // find first regex which matches updated ref, if any + def refPattern = protectedRefs.find { updatedRef.matches ~it } + + // find rejection result for update type, if any + def result = protectedCmds[updateType.name()] + + // command requires authz if ref is protected and has a mapped rejection result + if (refPattern && result) { + + // verify user is a member of any authorized team + def team = authorizedTeams.find { user.isTeamMember it } + if (team) { + // don't adjust command result + logger.info "${user.username} authorized for ${updateType} of protected ref ${repository.name}:${updatedRef} (${command.oldId.name} -> ${command.newId.name})" + } else { + // mark command result as rejected + command.setResult(result, "${user.username} cannot ${updateType} protected ref ${repository.name}:${updatedRef} matching pattern ${refPattern}") + } + } +} diff --git a/distrib/groovy/sendmail-html.groovy b/distrib/groovy/sendmail-html.groovy new file mode 100644 index 00000000..16920735 --- /dev/null +++ b/distrib/groovy/sendmail-html.groovy @@ -0,0 +1,516 @@ +/* + * Copyright 2012 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import com.gitblit.GitBlit +import com.gitblit.Keys +import com.gitblit.models.RepositoryModel +import com.gitblit.models.TeamModel +import com.gitblit.models.UserModel +import com.gitblit.utils.JGitUtils +import java.text.SimpleDateFormat + +import org.eclipse.jgit.api.Status; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.diff.DiffEntry; +import org.eclipse.jgit.diff.DiffFormatter; +import org.eclipse.jgit.diff.RawTextComparator; +import org.eclipse.jgit.diff.DiffEntry.ChangeType; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.IndexDiff; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.lib.Config +import org.eclipse.jgit.patch.FileHeader; +import org.eclipse.jgit.revwalk.RevCommit +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.ReceiveCommand +import org.eclipse.jgit.transport.ReceiveCommand.Result +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.EmptyTreeIterator; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.eclipse.jgit.util.io.DisabledOutputStream; +import org.slf4j.Logger +import groovy.xml.MarkupBuilder + +import java.io.IOException; +import java.security.MessageDigest + + +/** + * Sample Gitblit Post-Receive Hook: sendmail-html + * + * The Post-Receive hook is executed AFTER the pushed commits have been applied + * to the Git repository. This is the appropriate point to trigger an + * integration build or to send a notification. + * + * This script is only executed when pushing to *Gitblit*, not to other Git + * tooling you may be using. + * + * If this script is specified in *groovy.postReceiveScripts* of gitblit.properties + * or web.xml then it will be executed by any repository when it receives a + * push. If you choose to share your script then you may have to consider + * tailoring control-flow based on repository access restrictions. + * + * Scripts may also be specified per-repository in the repository settings page. + * Shared scripts will be excluded from this list of available scripts. + * + * This script is dynamically reloaded and it is executed within it's own + * exception handler so it will not crash another script nor crash Gitblit. + * + * If you want this hook script to fail and abort all subsequent scripts in the + * chain, "return false" at the appropriate failure points. + * + * Bound Variables: + * gitblit Gitblit Server com.gitblit.GitBlit + * repository Gitblit Repository com.gitblit.models.RepositoryModel + * user Gitblit User com.gitblit.models.UserModel + * commands JGit commands Collection + * url Base url for Gitblit java.lang.String + * logger Logs messages to Gitblit org.slf4j.Logger + * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger + * + * Accessing Gitblit Custom Fields: + * def myCustomField = repository.customFields.myCustomField + * + */ + +com.gitblit.models.UserModel userModel = user + +// Indicate we have started the script +logger.info("sendmail-html hook triggered by ${user.username} for ${repository.name}") + +/* + * Primitive email notification. + * This requires the mail settings to be properly configured in Gitblit. + */ + +Repository r = gitblit.getRepository(repository.name) + +// reuse existing repository config settings, if available +Config config = r.getConfig() +def mailinglist = config.getString('hooks', null, 'mailinglist') +def emailprefix = config.getString('hooks', null, 'emailprefix') + +// set default values +def toAddresses = [] +if (emailprefix == null) { + emailprefix = '[Gitblit]' +} + +if (mailinglist != null) { + def addrs = mailinglist.split(/(,|\s)/) + toAddresses.addAll(addrs) +} + +// add all mailing lists defined in gitblit.properties or web.xml +toAddresses.addAll(GitBlit.getStrings(Keys.mail.mailingLists)) + +// add all team mailing lists +def teams = gitblit.getRepositoryTeams(repository) +for (team in teams) { + TeamModel model = gitblit.getTeamModel(team) + if (model.mailingLists) { + toAddresses.addAll(model.mailingLists) + } +} + +// add all mailing lists for the repository +toAddresses.addAll(repository.mailingLists) + +// define the summary and commit urls +def repo = repository.name +def summaryUrl = url + "/summary?r=$repo" +def baseCommitUrl = url + "/commit?r=$repo&h=" +def baseBlobDiffUrl = url + "/blobdiff/?r=$repo&h=" +def baseCommitDiffUrl = url + "/commitdiff/?r=$repo&h=" +def forwardSlashChar = gitblit.getString(Keys.web.forwardSlashCharacter, '/') + +if (gitblit.getBoolean(Keys.web.mountParameters, true)) { + repo = repo.replace('/', forwardSlashChar).replace('/', '%2F') + summaryUrl = url + "/summary/$repo" + baseCommitUrl = url + "/commit/$repo/" + baseBlobDiffUrl = url + "/blobdiff/$repo/" + baseCommitDiffUrl = url + "/commitdiff/$repo/" +} + +class HtmlMailWriter { + Repository repository + def url + def baseCommitUrl + def baseCommitDiffUrl + def baseBlobDiffUrl + def mountParameters + def forwardSlashChar + def includeGravatar + def shortCommitIdLength + def commitCount = 0 + def commands + def writer = new StringWriter(); + def builder = new MarkupBuilder(writer) + + def writeStyle() { + builder.style(type:"text/css", ''' + .table td { + vertical-align: middle; + } + tr.noborder td { + border: none; + padding-top: 0px; + } + .gravatar-column { + width: 5%; + } + .author-column { + width: 20%; + } + .commit-column { + width: 5%; + } + .status-column { + width: 10%; + } + .table-disable-hover.table tbody tr:hover td, + .table-disable-hover.table tbody tr:hover th { + background-color: inherit; + } + .table-disable-hover.table-striped tbody tr:nth-child(odd):hover td, + .table-disable-hover.table-striped tbody tr:nth-child(odd):hover th { + background-color: #f9f9f9; + } + ''') + } + + def writeBranchTitle(type, name, action, number) { + builder.div('class' : 'pageTitle') { + builder.span('class':'project') { + mkp.yield "$type " + span('class': 'repository', name ) + if (number > 0) { + mkp.yield " $action ($number commits)" + } else { + mkp.yield " $action" + } + } + } + } + + def writeBranchDeletedTitle(type, name) { + builder.div('class' : 'pageTitle', 'style':'color:red') { + builder.span('class':'project') { + mkp.yield "$type " + span('class': 'repository', name ) + mkp.yield " deleted" + } + } + } + + def commitUrl(RevCommit commit) { + "${baseCommitUrl}$commit.id.name" + } + + def commitDiffUrl(RevCommit commit) { + "${baseCommitDiffUrl}$commit.id.name" + } + + def encoded(String path) { + path.replace('/', forwardSlashChar).replace('/', '%2F') + } + + def blobDiffUrl(objectId, path) { + if (mountParameters) { + // REST style + "${baseBlobDiffUrl}${objectId.name()}/${encoded(path)}" + } else { + "${baseBlobDiffUrl}${objectId.name()}&f=${path}" + } + + } + + def writeCommitTable(commits, includeChangedPaths=true) { + // Write commits table + builder.table('class':"table table-disable-hover") { + thead { + tr { + th(colspan: includeGravatar ? 2 : 1, "Author") + th( "Commit" ) + th( "Message" ) + } + } + tbody() { + + // Write all the commits + for (commit in commits) { + writeCommit(commit) + + if (includeChangedPaths) { + // Write detail on that particular commit + tr('class' : 'noborder') { + td (colspan: includeGravatar ? 3 : 2) + td (colspan:2) { writeStatusTable(commit) } + } + } + } + } + } + } + + def writeCommit(commit) { + def abbreviated = repository.newObjectReader().abbreviate(commit.id, shortCommitIdLength).name() + def author = commit.authorIdent.name + def email = commit.authorIdent.emailAddress + def message = commit.shortMessage + builder.tr { + if (includeGravatar) { + td('class':"gravatar-column") { + img(src:gravatarUrl(email), 'class':"gravatar") + } + } + td('class':"author-column", author) + td('class':"commit-column") { + a(href:commitUrl(commit)) { + span('class':"label label-info", abbreviated ) + } + } + td { + mkp.yield message + a('class':'link', href:commitDiffUrl(commit), " [commitdiff]" ) + } + } + } + + def writeStatusLabel(style, tooltip) { + builder.span('class' : style, 'title' : tooltip ) + } + + def writeAddStatusLine(ObjectId id, FileHeader header) { + builder.td('class':'changeType') { + writeStatusLabel("addition", "addition") + } + builder.td { + a(href:blobDiffUrl(id, header.newPath), header.newPath) + } + } + + def writeCopyStatusLine(ObjectId id, FileHeader header) { + builder.td('class':'changeType') { + writeStatusLabel("rename", "rename") + } + builder.td() { + a(href:blobDiffUrl(id, header.newPath), header.oldPath + " copied to " + header.newPath) + } + } + + def writeDeleteStatusLine(ObjectId id, FileHeader header) { + builder.td('class':'changeType') { + writeStatusLabel("deletion", "deletion") + } + builder.td() { + a(href:blobDiffUrl(id, header.oldPath), header.oldPath) + } + } + + def writeModifyStatusLine(ObjectId id, FileHeader header) { + builder.td('class':'changeType') { + writeStatusLabel("modification", "modification") + } + builder.td() { + a(href:blobDiffUrl(id, header.oldPath), header.oldPath) + } + } + + def writeRenameStatusLine(ObjectId id, FileHeader header) { + builder.td('class':'changeType') { + writeStatusLabel("rename", "rename") + } + builder.td() { + mkp.yield header.oldPath + mkp.yieldUnescaped " -&rt; " + a(href:blobDiffUrl(id, header.newPath), header.newPath) + } + } + + def writeStatusLine(ObjectId id, FileHeader header) { + builder.tr { + switch (header.changeType) { + case ChangeType.ADD: + writeAddStatusLine(id, header) + break; + case ChangeType.COPY: + writeCopyStatusLine(id, header) + break; + case ChangeType.DELETE: + writeDeleteStatusLine(id, header) + break; + case ChangeType.MODIFY: + writeModifyStatusLine(id, header) + break; + case ChangeType.RENAME: + writeRenameStatusLine(id, header) + break; + } + } + } + + def writeStatusTable(RevCommit commit) { + DiffFormatter formatter = new DiffFormatter(DisabledOutputStream.INSTANCE) + formatter.setRepository(repository) + formatter.setDetectRenames(true) + formatter.setDiffComparator(RawTextComparator.DEFAULT); + + def diffs + RevWalk rw = new RevWalk(repository) + if (commit.parentCount > 0) { + RevCommit parent = rw.parseCommit(commit.parents[0].id) + diffs = formatter.scan(parent.tree, commit.tree) + } else { + diffs = formatter.scan(new EmptyTreeIterator(), + new CanonicalTreeParser(null, rw.objectReader, commit.tree)) + } + rw.dispose() + // Write status table + builder.table('class':"plain") { + tbody() { + for (DiffEntry entry in diffs) { + FileHeader header = formatter.toFileHeader(entry) + writeStatusLine(commit.id, header) + } + } + } + } + + + def md5(text) { + + def digest = MessageDigest.getInstance("MD5") + + //Quick MD5 of text + def hash = new BigInteger(1, digest.digest(text.getBytes())) + .toString(16) + .padLeft(32, "0") + hash.toString() + } + + def gravatarUrl(email) { + def cleaned = email.trim().toLowerCase() + "http://www.gravatar.com/avatar/${md5(cleaned)}?s=30" + } + + def writeNavbar() { + builder.div('class':"navbar navbar-fixed-top") { + div('class':"navbar-inner") { + div('class':"container") { + a('class':"brand", href:"${url}", title:"GitBlit") { + img(src:"${url}/gitblt_25_white.png", + width:"79", + height:"25", + 'class':"logo") + } + } + } + } + } + + def write() { + builder.html { + head { + link(rel:"stylesheet", href:"${url}/bootstrap/css/bootstrap.css") + link(rel:"stylesheet", href:"${url}/gitblit.css") + link(rel:"stylesheet", href:"${url}/bootstrap/css/bootstrap-responsive.css") + writeStyle() + } + body { + + writeNavbar() + + div('class':"container") { + + for (command in commands) { + def ref = command.refName + def refType = 'Branch' + if (ref.startsWith('refs/heads/')) { + ref = command.refName.substring('refs/heads/'.length()) + } else if (ref.startsWith('refs/tags/')) { + ref = command.refName.substring('refs/tags/'.length()) + refType = 'Tag' + } + + switch (command.type) { + case ReceiveCommand.Type.CREATE: + def commits = JGitUtils.getRevLog(repository, command.oldId.name, command.newId.name).reverse() + commitCount += commits.size() + if (refType == 'Branch') { + // new branch + writeBranchTitle(refType, ref, "created", commits.size()) + writeCommitTable(commits, true) + } else { + // new tag + writeBranchTitle(refType, ref, "created", 0) + writeCommitTable(commits, false) + } + break + case ReceiveCommand.Type.UPDATE: + def commits = JGitUtils.getRevLog(repository, command.oldId.name, command.newId.name).reverse() + commitCount += commits.size() + // fast-forward branch commits table + // Write header + writeBranchTitle(refType, ref, "updated", commits.size()) + writeCommitTable(commits) + break + case ReceiveCommand.Type.UPDATE_NONFASTFORWARD: + def commits = JGitUtils.getRevLog(repository, command.oldId.name, command.newId.name).reverse() + commitCount += commits.size() + // non-fast-forward branch commits table + // Write header + writeBranchTitle(refType, ref, "updated [NON fast-forward]", commits.size()) + writeCommitTable(commits) + break + case ReceiveCommand.Type.DELETE: + // deleted branch/tag + writeBranchDeletedTitle(refType, ref) + break + default: + break + } + } + } + } + } + writer.toString() + } + +} + +def mailWriter = new HtmlMailWriter() +mailWriter.repository = r +mailWriter.baseCommitUrl = baseCommitUrl +mailWriter.baseBlobDiffUrl = baseBlobDiffUrl +mailWriter.baseCommitDiffUrl = baseCommitDiffUrl +mailWriter.forwardSlashChar = forwardSlashChar +mailWriter.commands = commands +mailWriter.url = url +mailWriter.mountParameters = GitBlit.getBoolean(Keys.web.mountParameters, true) +mailWriter.includeGravatar = GitBlit.getBoolean(Keys.web.allowGravatar, true) +mailWriter.shortCommitIdLength = GitBlit.getInteger(Keys.web.shortCommitIdLength, 8) + +def content = mailWriter.write() + +// close the repository reference +r.close() + +// tell Gitblit to send the message (Gitblit filters duplicate addresses) +def repositoryName = repository.name.substring(0, repository.name.length() - 4) +gitblit.sendHtmlMail("${emailprefix} ${userModel.displayName} pushed ${mailWriter.commitCount} commits => $repositoryName", + content, + toAddresses) diff --git a/distrib/groovy/sendmail.groovy b/distrib/groovy/sendmail.groovy new file mode 100644 index 00000000..c832bc64 --- /dev/null +++ b/distrib/groovy/sendmail.groovy @@ -0,0 +1,176 @@ +/* + * Copyright 2011 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import com.gitblit.GitBlit +import com.gitblit.Keys +import com.gitblit.models.RepositoryModel +import com.gitblit.models.TeamModel +import com.gitblit.models.UserModel +import com.gitblit.utils.JGitUtils +import java.text.SimpleDateFormat +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.lib.Config +import org.eclipse.jgit.revwalk.RevCommit +import org.eclipse.jgit.transport.ReceiveCommand +import org.eclipse.jgit.transport.ReceiveCommand.Result +import org.slf4j.Logger + +/** + * Sample Gitblit Post-Receive Hook: sendmail + * + * The Post-Receive hook is executed AFTER the pushed commits have been applied + * to the Git repository. This is the appropriate point to trigger an + * integration build or to send a notification. + * + * This script is only executed when pushing to *Gitblit*, not to other Git + * tooling you may be using. + * + * If this script is specified in *groovy.postReceiveScripts* of gitblit.properties + * or web.xml then it will be executed by any repository when it receives a + * push. If you choose to share your script then you may have to consider + * tailoring control-flow based on repository access restrictions. + * + * Scripts may also be specified per-repository in the repository settings page. + * Shared scripts will be excluded from this list of available scripts. + * + * This script is dynamically reloaded and it is executed within it's own + * exception handler so it will not crash another script nor crash Gitblit. + * + * If you want this hook script to fail and abort all subsequent scripts in the + * chain, "return false" at the appropriate failure points. + * + * Bound Variables: + * gitblit Gitblit Server com.gitblit.GitBlit + * repository Gitblit Repository com.gitblit.models.RepositoryModel + * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack + * user Gitblit User com.gitblit.models.UserModel + * commands JGit commands Collection + * url Base url for Gitblit String + * logger Logs messages to Gitblit org.slf4j.Logger + * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger + * + * Accessing Gitblit Custom Fields: + * def myCustomField = repository.customFields.myCustomField + * + */ + +// Indicate we have started the script +logger.info("sendmail hook triggered by ${user.username} for ${repository.name}") + +/* + * Primitive email notification. + * This requires the mail settings to be properly configured in Gitblit. + */ + +Repository r = gitblit.getRepository(repository.name) + +// reuse existing repository config settings, if available +Config config = r.getConfig() +def mailinglist = config.getString('hooks', null, 'mailinglist') +def emailprefix = config.getString('hooks', null, 'emailprefix') + +// set default values +def toAddresses = [] +if (emailprefix == null) +emailprefix = '[Gitblit]' + +if (mailinglist != null) { + def addrs = mailinglist.split(/(,|\s)/) + toAddresses.addAll(addrs) +} + +// add all mailing lists defined in gitblit.properties or web.xml +toAddresses.addAll(gitblit.getStrings(Keys.mail.mailingLists)) + +// add all team mailing lists +def teams = gitblit.getRepositoryTeams(repository) +for (team in teams) { + TeamModel model = gitblit.getTeamModel(team) + if (model.mailingLists) { + toAddresses.addAll(model.mailingLists) + } +} + +// add all mailing lists for the repository +toAddresses.addAll(repository.mailingLists) + +// define the summary and commit urls +def repo = repository.name +def summaryUrl +def commitUrl +if (gitblit.getBoolean(Keys.web.mountParameters, true)) { + repo = repo.replace('/', gitblit.getString(Keys.web.forwardSlashCharacter, '/')).replace('/', '%2F') + summaryUrl = url + "/summary/$repo" + commitUrl = url + "/commit/$repo/" +} else { + summaryUrl = url + "/summary?r=$repo" + commitUrl = url + "/commit?r=$repo&h=" +} + +// construct a simple text summary of the changes contained in the push +def branchBreak = '>---------------------------------------------------------------\n' +def commitBreak = '\n\n ----\n' +def commitCount = 0 +def changes = '' +SimpleDateFormat df = new SimpleDateFormat(gitblit.getString(Keys.web.datetimestampLongFormat, 'EEEE, MMMM d, yyyy h:mm a z')) +def table = { "\n ${JGitUtils.getDisplayName(it.authorIdent)}\n ${df.format(JGitUtils.getCommitDate(it))}\n\n $it.shortMessage\n\n $commitUrl$it.id.name" } +for (command in commands) { + def ref = command.refName + def refType = 'branch' + if (ref.startsWith('refs/heads/')) { + ref = command.refName.substring('refs/heads/'.length()) + } else if (ref.startsWith('refs/tags/')) { + ref = command.refName.substring('refs/tags/'.length()) + refType = 'tag' + } + + switch (command.type) { + case ReceiveCommand.Type.CREATE: + def commits = JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() + commitCount += commits.size() + // new branch + changes += "\n$branchBreak new $refType $ref created ($commits.size commits)\n$branchBreak" + changes += commits.collect(table).join(commitBreak) + changes += '\n' + break + case ReceiveCommand.Type.UPDATE: + def commits = JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() + commitCount += commits.size() + // fast-forward branch commits table + changes += "\n$branchBreak $ref $refType updated ($commits.size commits)\n$branchBreak" + changes += commits.collect(table).join(commitBreak) + changes += '\n' + break + case ReceiveCommand.Type.UPDATE_NONFASTFORWARD: + def commits = JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() + commitCount += commits.size() + // non-fast-forward branch commits table + changes += "\n$branchBreak $ref $refType updated [NON fast-forward] ($commits.size commits)\n$branchBreak" + changes += commits.collect(table).join(commitBreak) + changes += '\n' + break + case ReceiveCommand.Type.DELETE: + // deleted branch/tag + changes += "\n$branchBreak $ref $refType deleted\n$branchBreak" + break + default: + break + } +} +// close the repository reference +r.close() + +// tell Gitblit to send the message (Gitblit filters duplicate addresses) +gitblit.sendMail("$emailprefix $user.username pushed $commitCount commits => $repository.name", "$summaryUrl\n$changes", toAddresses) \ No newline at end of file diff --git a/distrib/groovy/thebuggenie.groovy b/distrib/groovy/thebuggenie.groovy new file mode 100644 index 00000000..b4385a26 --- /dev/null +++ b/distrib/groovy/thebuggenie.groovy @@ -0,0 +1,88 @@ +/* + * Copyright 2011 Wolfgang Gassler gassler.org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.gitblit.GitBlit +import com.gitblit.Keys +import com.gitblit.models.RepositoryModel +import com.gitblit.models.TeamModel +import com.gitblit.models.UserModel +import com.gitblit.utils.JGitUtils +import java.text.SimpleDateFormat +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.lib.Config +import org.eclipse.jgit.revwalk.RevCommit +import org.eclipse.jgit.transport.ReceiveCommand +import org.eclipse.jgit.transport.ReceiveCommand.Result +import org.slf4j.Logger +import org.eclipse.jgit.lib.IndexDiff +import org.eclipse.jgit.lib.Constants +import com.gitblit.utils.DiffUtils + +/** + * Gitblit Post-Receive Hook: thebuggenie + * www.thebuggenie.com + * + * Submit the commit information to thebuggenie bug tracker by calling thebuggenie client tool + * + * Config of the Script: + * + * Setup a custom gitblit field in the proprties file of gitblit by adding the following line + * groovy.customFields = "thebuggenieProjectId=TheBugGennie project id (used for thebuggenie hoocks)" + * This field allows to specify the project id of thebuggenie project in the edit section of gitblit + * + * Furthermore you need to set the path to thebuggenie client tool by adding the following property to + * the gitblit properties file + * thebuggenie.tbg_cli = /var/www/thebuggenie_root/tbg_cli + */ + +// Indicate we have started the script +logger.info("thebuggenie hook triggered by ${user.username} for ${repository.name}") + +//fetch the repository data +Repository r = gitblit.getRepository(repository.name) + +//get project id which is defined in the git repo metadata +def tbgProjectId = repository.customFields.thebuggenieProjectId +//get path to the thebuggenie client tool which is defined in the gitblit proprties files +def tbgCliPath = gitblit.getString('thebuggenie.tbg_cli', '/var/www/thebuggenie/tbg_cli') +def tbgCliDirPath = new File(tbgCliPath).getParent() + +for(command in commands) { + //fetch all pushed commits + def commits = JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() + for (commit in commits) { + //get hashes and author data of commit + def oldhash = commit.getParent(0).getId().getName() + def newhash = commit.getId().getName() + def authorIdent = commit.getAuthorIdent() + def author = "${authorIdent.name} <${authorIdent.emailAddress}>" + //fetch all changed files of the commit + def files = JGitUtils.getFilesInCommit(r,commit) + def changedFiles = "" + for (f in files) { + //transform file data to the format which is needed by thebuggenie + changedFiles += f.changeType.toString().substring(0,1)+"\t${f.path}\n" + } + //ok let's submit all information to thebuggenie by calling the client tool +// def shc = "$tbgCliPath vcs_integration:report_commit $tbgProjectId \"$author\" $newhash \"${commit.fullMessage}\" \"$changedFiles\" $oldhash ${commit.commitTime}" + def shc = [tbgCliPath, "vcs_integration:report_commit", tbgProjectId, author, newhash, commit.getFullMessage(), changedFiles, oldhash, commit.getCommitTime()]; + logger.info("executing in path " + tbgCliDirPath + ": "+shc) + shc.execute(null, new File(tbgCliDirPath)) + } +} + +// close the repository reference +r.close() diff --git a/distrib/installService.cmd b/distrib/installService.cmd index 77b641e8..d254d679 100644 --- a/distrib/installService.cmd +++ b/distrib/installService.cmd @@ -25,12 +25,12 @@ SET ARCH=amd64 --StartPath="%CD%" ^ --StartClass=com.gitblit.Launcher ^ --StartMethod=main ^ - --StartParams="--storePassword;gitblit" ^ + --StartParams="--storePassword;gitblit;--baseFolder;%CD%\data" ^ --StartMode=jvm ^ --StopPath="%CD%" ^ --StopClass=com.gitblit.Launcher ^ --StopMethod=main ^ - --StopParams="--stop" ^ + --StopParams="--stop;--baseFolder;%CD%\data" ^ --StopMode=jvm ^ --Classpath="%CD%\gitblit.jar" ^ --Jvm=auto ^ diff --git a/distrib/projects.conf b/distrib/projects.conf new file mode 100644 index 00000000..d43f4829 --- /dev/null +++ b/distrib/projects.conf @@ -0,0 +1,3 @@ +[project "main"] + title = Main Repositories + description = main group of repositories \ No newline at end of file diff --git a/docs/01_setup.mkd b/docs/01_setup.mkd index be0e191f..cea1d692 100644 --- a/docs/01_setup.mkd +++ b/docs/01_setup.mkd @@ -1,53 +1,55 @@ -## Gitblit WAR Setup +## Gitblit WAR Installation & Setup 1. Download [Gitblit WAR %VERSION%](http://code.google.com/p/gitblit/downloads/detail?name=%WAR%) to the webapps folder of your servlet container. 2. You may have to manually extract the WAR (zip file) to a folder within your webapps folder. -3. Copy the `WEB-INF/users.conf` file to a location outside the webapps folder that is accessible by your servlet container. -Optionally copy the example hook scripts in `WEB-INF/groovy` to a location outside the webapps folder that is accesible by your servlet container. -4. The Gitblit webapp is configured through its `web.xml` file. -Open `web.xml` in your favorite text editor and make sure to review and set: - - <context-parameter> *git.repositoryFolder* (set the full path to your repositories folder) - - <context-parameter> *groovy.scriptsFolder* (set the full path to your Groovy hook scripts folder) - - <context-parameter> *groovy.grapeFolder* (set the full path to your Groovy Grape artifact cache) - - <context-parameter> *web.projectsFile* (set the full path to your projects metadata file) - - <context-parameter> *realm.userService* (set the full path to `users.conf`) +3. By default, the Gitblit webapp is configured through `WEB-INF/data/gitblit.properties`.
+Open `WEB-INF/data/gitblit.properties` in your favorite text editor and make sure to review and set: - <context-parameter> *git.packedGitLimit* (set larger than the size of your largest repository) - <context-parameter> *git.streamFileThreshold* (set larger than the size of your largest committed file) -5. You may have to restart your servlet container. -6. Open your browser to or whatever the url should be. -7. Enter the default administrator credentials: **admin / admin** and click the *Login* button +4. You may have to restart your servlet container. +5. Open your browser to or whatever the url should be. +6. Enter the default administrator credentials: **admin / admin** and click the *Login* button **NOTE:** Make sure to change the administrator username and/or password!! -## Gitblit GO Setup +### WAR Data Location +By default, Gitblit WAR stores all data (users, settings, repositories, etc) in `${contextFolder}/WEB-INF/data`. This is fine for a quick setup, but there are many reasons why you don't want to keep your data within the webapps folder of your servlet container. You may specify an external location for your data by editing `WEB-INF/web.xml` and manipulating the *baseFolder* context parameter. Choose a location that is writeable by your servlet container. Your servlet container may be smart enough to recognize the change and to restart Gitblit. + +On the next restart of Gitblit, Gitblit will copy the contents of the `WEB-INF/data` folder to your specified *baseFolder* **IF** the file `${baseFolder}/gitblit.properties` does not already exist. This allows you to get going with minimal fuss. + +Specifying an alternate *baseFolder* also allows for simpler upgrades in the future. + +## Gitblit GO Installation & Setup 1. Download and unzip [Gitblit GO %VERSION%](http://code.google.com/p/gitblit/downloads/detail?name=%GO%). *Its best to eliminate spaces in the path name.* -2. The server itself is configured through a simple text file. -Open `gitblit.properties` in your favorite text editor and make sure to review and set: - - *git.repositoryFolder* (path may be relative or absolute) - - *groovy.scriptsFolder* (path may be relative or absolute) - - *groovy.grapeFolder* (path may be relative or absolute) - - *server.tempFolder* (path may be relative or absolute) +2. The server itself is configured through a simple text file.
+Open `data/gitblit.properties` in your favorite text editor and make sure to review and set: - *server.httpPort* and *server.httpsPort* - *server.httpBindInterface* and *server.httpsBindInterface* - *server.storePassword* **https** is strongly recommended because passwords are insecurely transmitted form your browser/git client using Basic authentication! - *git.packedGitLimit* (set larger than the size of your largest repository) - *git.streamFileThreshold* (set larger than the size of your largest committed file) -3. Execute `authority.cmd` or `java -jar authority.jar` from a command-line +3. Execute `authority.cmd` or `java -jar authority.jar --baseFolder data` from a command-line 1. fill out the fields in the *new certificate defaults* dialog 2. enter the store password used in *server.storePassword* when prompted. This generates an SSL certificate for **localhost**. 3. you may want to generate an SSL certificate for the hostname or ip address hostnames you are serving from
**NOTE:** You can only have **one** SSL certificate specified for a port. 5. exit the authority app -4. Execute `gitblit.cmd` or `java -jar gitblit.jar` from a command-line +4. Execute `gitblit.cmd` or `java -jar gitblit.jar --baseFolder data` from a command-line 5. Open your browser to or depending on your chosen configuration. 6. Enter the default administrator credentials: **admin / admin** and click the *Login* button **NOTE:** Make sure to change the administrator username and/or password!! +### GO Data Location + +By default, Gitblit GO stores all data (users, settings, repositories, etc) in the `data` subfolder of your GO installation. You may specify an external location for your data on the command-line by setting the *--baseFolder* argument. If you relocate the data folder then you must supply the *--baseFolder* argument to both GO and the Certificate Authority. + +If you are deploying Gitblit to a *nix platform, you might consider moving the data folder out of the GO installation folder and then creating a symlink named "data" that points to your moved folder. + ### Creating your own Self-Signed SSL Certificate Gitblit GO (and Gitblit Certificate Authority) automatically generates a Certificate Authority (CA) certificate and an ssl certificate signed by this CA certificate that is bound to *localhost*. -Remote Eclipse/EGit/JGit clients (<= 2.1.0) will fail to communicate using this certificate because JGit always verifies the hostname of the certificate, regardless of the *http.sslVerify=false* client-side setting. +Remote Eclipse/EGit/JGit clients (<= 2.2.0) will fail to communicate using this certificate because JGit always verifies the hostname of the certificate, regardless of the *http.sslVerify=false* client-side setting. The EGit failure message is something like: @@ -56,7 +58,7 @@ The EGit failure message is something like: If you want to serve your repositories to another machine over https then you will want to generate a new certificate for the hostname or ip address you are serving from. -1. `authority.cmd` or `java -jar authority.jar` +1. `authority.cmd` or `java -jar authority.jar --baseFolder data` 2. Click the *new ssl certificate* button (red rosette in the toolbar in upper left of window) 3. Enter the hostname or ip address 4. Make sure the checkbox *serve https with this certificate* is checked @@ -64,11 +66,11 @@ If you want to serve your repositories to another machine over https then you wi If you decide to change the value of *server.storePassword* (recommended) after you have already started Gitblit or Gitblit Certificate Authority, then you will have to delete the following files and then restart the Gitblit Certificate Authority app: -1. serverKeyStore.jks -2. serverTrustStore.jks -3. certs/caKeyStore.jks -4. certs/ca.crt -5. certs/caRevocationList.crl (optional) +1. data/serverKeyStore.jks +2. data/serverTrustStore.jks +3. data/certs/caKeyStore.jks +4. data/certs/ca.crt +5. data/certs/caRevocationList.crl (optional) ### Client SSL Certificates SINCE 1.2.0 @@ -84,6 +86,7 @@ Gitblit must be able to map the DN of the certificate to an *existing* account u How do you make your servlet container trust a client certificate? In the WAR variant, you will have to manually setup your servlet container to: + 1. want/need client certificates 2. trust a CA certificate used to sign your client certificates 3. generate client certificates signed by your CA certificate @@ -92,9 +95,9 @@ Alternatively, Gitblit GO is designed to facilitate use of client certificate au #### Creating SSL Certificates with Gitblit Certificate Authority -When you generate a new client certificate, a zip file bundle is created which includes a P12 keystore for browsers and a PEM keystore for Git. Both of these are password-protected. Additionally, a personalized README file is generated with setup instructions for popular browsers and Git. The README is generated from `certs\instructions.tmpl` and can be modified to suit your needs. +When you generate a new client certificate, a zip file bundle is created which includes a P12 keystore for browsers and a PEM keystore for Git. Both of these are password-protected. Additionally, a personalized README file is generated with setup instructions for popular browsers and Git. The README is generated from `data\certs\instructions.tmpl` and can be modified to suit your needs. -1. `authority.cmd` or `java -jar authority.jar` +1. `authority.cmd` or `java -jar authority.jar --baseFolder data` 2. Select the user for which to generate the certificate 3. Click the *new certificate* button and enter the expiration date of the certificate. You must also enter a password for the generated keystore. This password is *not* the same as the user's login password. This password is used to protect the privatekey and public certificate you will generate for the selected user. You must also enter a password hint for the user. 4. If your mail server settings are properly configured you will have a *send email* checkbox which you can use to immediately send the generated certificate bundle to the user. @@ -130,6 +133,7 @@ C:\Program Files\Java\jre6\bin\server\jvm.dll #### Command-Line Parameters Command-Line parameters override the values in `gitblit.properties` at runtime. + --baseFolder The default base folder for all relative file reference settings --repositoriesFolder Git Repositories Folder --userService Authentication and Authorization Service (filename or fully qualified classname) --useNio Use NIO Connector else use Socket Connector. @@ -143,7 +147,7 @@ Command-Line parameters override the values in `gitblit.properties` at runtime. **Example** - java -jar gitblit.jar --userService c:\myrealm.config --storePassword something + java -jar gitblit.jar --userService c:/myrealm.config --storePassword something --baseFolder c:/data #### Overriding Gitblit GO's Log4j Configuration @@ -221,9 +225,6 @@ ProxyPreserveHost On Alternatively, you can respecify *web.forwardSlashCharacter*. ## Upgrading Gitblit -Generally, upgrading is easy. - -Since Gitblit does not use a database the only files you have to worry about are your configuration file (`gitblit.properties` or `web.xml`) and possibly your `users.conf` or `users.properties` file. Any important changes to the setting keys or default values will always be mentioned in the [release log](releases.html). @@ -231,26 +232,47 @@ Gitblit v0.8.0 introduced a new default user service implementation which serial `users.properties` and its user service implementation are deprecated as of v0.8.0. -### Upgrading Gitblit WAR -1. Backup your `web.xml` file -Backup your `web.properties` file (if you have one, these are the setting overrides from using the RPC administration service) -2. Delete currently deployed gitblit WAR -3. Deploy new WAR and overwrite the `web.xml` file with your backup -4. Review and optionally apply any new settings as indicated in the [release log](releases.html). +### Upgrading Gitblit WAR (1.2.1+) +1. Make sure your `WEB-INF/web.xml` *baseFolder* context parameter is not `${contextFolder}/WEB-INF/data`!
+If it is, move your `WEB-INF/data` folder to a location writeable by your servlet container. +2. Deploy new WAR +3. Edit the new WAR's `WEB-INF/web.xml` file and set the *baseFolder* context parameter to your external baseFolder. +4. Review and optionally apply any new settings as indicated in the [release log](releases.html) to `${baseFolder}/gitblit.properties`. -### Upgrading Gitblit GO +### Upgrading Gitblit GO (1.2.1+) -1. Backup your `gitblit.properties` file -2. Backup your `users.properties` file *(if it is located in the Gitblit GO folder)* -OR -Backup your `users.conf` file *(if it is located in the Gitblit GO folder)* -3. Backup your Groovy hook scripts -4. Unzip Gitblit GO to a new folder -5. Overwrite the `gitblit.properties` file with your backup -6. Overwrite the `users.properties` file with your backup *(if it was located in the Gitblit GO folder)* -OR -Overwrite the `users.conf` file with your backup *(if it was located in the Gitblit GO folder)* -7. Review and optionally apply any new settings as indicated in the [release log](releases.html). +1. Unzip Gitblit GO to a new folder +2. Copy your `data` folder from your current Gitblit installation to the new folder and overwrite any conflicts +3. Review and optionally apply any new settings as indicated in the [release log](releases.html) to `data/gitblit.properties`. + +In *nix systems, there are other tricks you can play like symlinking the `data` folder or symlinking the GO folder. +All platforms support the *--baseFolder* command-line argument. + +### Upgrading Gitblit WAR (pre-1.2.1) + +1. Create a `data` as outlined in step 1 of *Upgrading Gitblit GO (pre-1.2.1)* +2. Copy your existing web.xml to your data folder +3. Deploy new WAR +4. Copy the new WAR's `WEB-INF/data/gitblit.properties` file to your data folder +5. Manually apply any changes you made to your original web.xml file to the gitblit.properties file you copied to your data folder +6. Edit the new WAR's `WEB-INF/web.xml` file and set the *baseFolder* context parameter to your external baseFolder. + +### Upgrading Gitblit GO (pre-1.2.1) +1. Create a `data` folder and copy the following files and folders to it: + - users.conf + - projects.conf *(if you have one)* + - gitblit.properties + - serverKeystore.jks + - serverTrustStore.jks + - certs folder + - groovy folder + - proposals folder + - and any other custom files (robots.txt, welcome/login markdown files, etc) +2. Unzip Gitblit GO to a new folder +3. Copy your `data` folder and overwrite the folder of the same name in the just-unzipped version +4. Review and optionally apply any new settings as indicated in the [release log](releases.html) to `data/gitblit.properties`. + +**NOTE:** You may need to adjust your service definitions to include the `--baseFolder data` argument. #### Upgrading Windows Service You may need to delete your old service definition and install a new one depending on what has changed in the release. @@ -277,7 +299,7 @@ All repository settings are stored within the repository `.git/config` file unde federationSets = #### Repository Names -Repository names must be unique and are CASE-SENSITIVE ON CASE-SENSITIVE FILESYSTEMS. The name must be composed of letters, digits, or `/ _ - . ~`
+Repository names must be case-insensitive-unique but are CASE-SENSITIVE ON CASE-SENSITIVE FILESYSTEMS. The name must be composed of letters, digits, or `/ _ - . ~`
Whitespace is illegal. Repositories can be grouped within subfolders. e.g. *libraries/mycoollib.git* and *libraries/myotherlib.git* @@ -366,6 +388,10 @@ All checks are case-insensitive. You can not use fast-forward merges on your client when using committer verification. You must specify *--no-ff* to ensure that a merge commit is created with your identity as the committer. Only the first parent chain is traversed when verifying commits. +#### Push Log + +Gitblit v1.2.1 introduces an incomplete push mechanism. All pushes are logged since 1.2.1, but the log has not yet been exposed through the web ui. This will be a feature of an upcoming release. + ### Teams Since v0.8.0, Gitblit supports *teams* for the original `users.properties` user service and the current default user service `users.conf`. Teams have assigned users and assigned repositories. A user can be a member of multiple teams and a repository may belong to multiple teams. This allows the administrator to quickly add a user to a team without having to keep track of all the appropriate repositories. diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index fa088b2a..a76e3c79 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -2,6 +2,14 @@ ### Current Release +
+

Update Note 1.2.1

+Because there are now several types of files and folders that must be considered Gitblit data, the default location for data has changed. +

You will need to move a few files around when upgrading. Please see the Upgrading section of the setup page for details.

+ +Express Users make sure to update your web.xml file with the ${baseFolder} values! +
+ **%VERSION%** ([go](http://code.google.com/p/gitblit/downloads/detail?name=%GO%) | [war](http://code.google.com/p/gitblit/downloads/detail?name=%WAR%) | [express](http://code.google.com/p/gitblit/downloads/detail?name=%EXPRESS%) | [fedclient](http://code.google.com/p/gitblit/downloads/detail?name=%FEDCLIENT%) | [manager](http://code.google.com/p/gitblit/downloads/detail?name=%MANAGER%) | [api](http://code.google.com/p/gitblit/downloads/detail?name=%API%)) based on [%JGIT%][jgit]   *released %BUILDDATE%* #### fixes @@ -10,7 +18,7 @@ - Added nullchecking when concurrently forking a repository and trying to display it's fork network (issue-187) - Fixed bug where permission changes were not visible in the web ui to a logged-in user until the user logged-out and then logged back in again (issue-186) - Fixed nullpointer on creating a repository with mixed case (issue 185) -- Fixed nullpointer when using web.allowForking = true && git.cacheRepositoryList = false (issue 182) +- Fixed nullpointer when using *web.allowForking = true* && *git.cacheRepositoryList = false* (issue 182) - Likely fix for commit and commitdiff page failures when a submodule reference changes (issue 178) - Build project models from the repository model cache, when possible, to reduce page load time (issue 172) - Fixed loading of Brazilian Portuguese translation from *nix server (github/inaiat) @@ -30,6 +38,10 @@ The push log is not currently visible in the ui, but the data will be collected #### changes +- Gitblit GO and Gitblit WAR are now both configured by `gitblit.properties`. WAR is no longer configured by `web.xml`.
+However, Express for OpenShift continues to be configured by `web.xml`. +- Support for a *--baseFolder* command-line argument for Gitblit GO and Gitblit Certificate Authority +- Support for specifying a *${baseFolder}* parameter in `gitblit.properties` and `web.xml` for several settings - Improve history display of a submodule link - Updated Korean translation (github/ds5apn) - Updated checkstyle definition (github/mystygage) @@ -37,7 +49,7 @@ The push log is not currently visible in the ui, but the data will be collected ### Older Releases
-

Update Note

+

Update Note 1.2.0

The permissions model has changed in the 1.2.0 release.

If you are updating your server, you must also update any Gitblit Manager and Federation Client installs to 1.2.0 as well. The data model used by the RPC mechanism has changed slightly for the new permissions infrastructure.

@@ -142,7 +154,7 @@ If *realm.ldap.maintainTeams==true* **AND** *realm.ldap.admins* is not empty, th
-

Update Note

+

Update Note 1.1.0

If you are updating from an earlier release AND you have indexed branches with the Lucene indexing feature, you need to be aware that this release will completely re-index your repositories. Please be sure to provide ample heap resources as appropriate for your installation.
diff --git a/groovy/.gitignore b/groovy/.gitignore deleted file mode 100644 index e58dc47f..00000000 --- a/groovy/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/grape diff --git a/groovy/blockpush.groovy b/groovy/blockpush.groovy deleted file mode 100644 index caef3306..00000000 --- a/groovy/blockpush.groovy +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2011 gitblit.com. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.text.MessageFormat; - -import com.gitblit.GitBlit -import com.gitblit.models.RepositoryModel -import com.gitblit.models.UserModel -import org.eclipse.jgit.transport.ReceiveCommand -import org.eclipse.jgit.transport.ReceiveCommand.Result -import org.slf4j.Logger -import com.gitblit.utils.ClientLogger - -/** - * Sample Gitblit Pre-Receive Hook: blockpush - * - * This script could and perhaps should be further developed to provide - * a full repository-branch permissions system similar to gitolite or gitosis. - * - * The Pre-Receive hook is executed after an incoming push has been parsed, - * validated, and objects have been written but BEFORE the refs are updated. - * This is the appropriate point to block a push for some reason. - * - * This script is only executed when pushing to *Gitblit*, not to other Git - * tooling you may be using. - * - * If this script is specified in *groovy.preReceiveScripts* of gitblit.properties - * or web.xml then it will be executed by any repository when it receives a - * push. If you choose to share your script then you may have to consider - * tailoring control-flow based on repository access restrictions. - * - * Scripts may also be specified per-repository in the repository settings page. - * Shared scripts will be excluded from this list of available scripts. - * - * This script is dynamically reloaded and it is executed within it's own - * exception handler so it will not crash another script nor crash Gitblit. - * - * If you want this hook script to fail and abort all subsequent scripts in the - * chain, "return false" at the appropriate failure points. - * - * Bound Variables: - * gitblit Gitblit Server com.gitblit.GitBlit - * repository Gitblit Repository com.gitblit.models.RepositoryModel - * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack - * user Gitblit User com.gitblit.models.UserModel - * commands JGit commands Collection - * url Base url for Gitblit String - * logger Logs messages to Gitblit org.slf4j.Logger - * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger - * - * Accessing Gitblit Custom Fields: - * def myCustomField = repository.customFields.myCustomField - * - */ - -// Indicate we have started the script -logger.info("blockpush hook triggered by ${user.username} for ${repository.name}: checking ${commands.size} commands") - -/* - * Example rejection of pushes to the master branch of example.git - */ -def blocked = false -switch (repository.name) { - case 'ex@mple.git': - for (ReceiveCommand command : commands) { - def updatedRef = command.refName - if (updatedRef.equals('refs/heads/master')) { - // to reject a command set it's result to anything other than Result.NOT_ATTEMPTED - command.setResult(Result.REJECTED_OTHER_REASON, "You are not permitted to write to ${repository.name}:${updatedRef}") - blocked = true - } - } - break - - default: - break -} - -if (blocked) { - // return false to break the push hook chain - return false -} \ No newline at end of file diff --git a/groovy/jenkins.groovy b/groovy/jenkins.groovy deleted file mode 100644 index d76a3d66..00000000 --- a/groovy/jenkins.groovy +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2011 gitblit.com. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import com.gitblit.GitBlit -import com.gitblit.Keys -import com.gitblit.models.RepositoryModel -import com.gitblit.models.UserModel -import com.gitblit.utils.JGitUtils -import org.eclipse.jgit.lib.Repository -import org.eclipse.jgit.revwalk.RevCommit -import org.eclipse.jgit.transport.ReceiveCommand -import org.eclipse.jgit.transport.ReceiveCommand.Result -import org.slf4j.Logger - -/** - * Sample Gitblit Post-Receive Hook: jenkins - * - * The Post-Receive hook is executed AFTER the pushed commits have been applied - * to the Git repository. This is the appropriate point to trigger an - * integration build or to send a notification. - * - * This script is only executed when pushing to *Gitblit*, not to other Git - * tooling you may be using. - * - * If this script is specified in *groovy.postReceiveScripts* of gitblit.properties - * or web.xml then it will be executed by any repository when it receives a - * push. If you choose to share your script then you may have to consider - * tailoring control-flow based on repository access restrictions. - * - * Scripts may also be specified per-repository in the repository settings page. - * Shared scripts will be excluded from this list of available scripts. - * - * This script is dynamically reloaded and it is executed within it's own - * exception handler so it will not crash another script nor crash Gitblit. - * - * Bound Variables: - * gitblit Gitblit Server com.gitblit.GitBlit - * repository Gitblit Repository com.gitblit.models.RepositoryModel - * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack - * user Gitblit User com.gitblit.models.UserModel - * commands JGit commands Collection - * url Base url for Gitblit String - * logger Logs messages to Gitblit org.slf4j.Logger - * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger - * - * Accessing Gitblit Custom Fields: - * def myCustomField = repository.customFields.myCustomField - * - */ -// Indicate we have started the script -logger.info("jenkins hook triggered by ${user.username} for ${repository.name}") - -// This script requires Jenkins Git plugin 1.1.14 or later -// http://kohsuke.org/2011/12/01/polling-must-die-triggering-jenkins-builds-from-a-git-hook/ - -// define your jenkins url here or set groovy.jenkinsServer in -// gitblit.properties or web.xml -def jenkinsUrl = gitblit.getString('groovy.jenkinsServer', 'http://yourserver/jenkins') - -// define the trigger url -def triggerUrl = jenkinsUrl + "/git/notifyCommit?url=$url/git/$repository.name" - -// trigger the build -new URL(triggerUrl).getContent() diff --git a/groovy/localclone.groovy b/groovy/localclone.groovy deleted file mode 100644 index 49b7f8b3..00000000 --- a/groovy/localclone.groovy +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2012 gitblit.com. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import com.gitblit.GitBlit -import com.gitblit.Keys -import com.gitblit.models.RepositoryModel -import com.gitblit.models.TeamModel -import com.gitblit.models.UserModel -import com.gitblit.utils.JGitUtils -import com.gitblit.utils.StringUtils -import java.text.SimpleDateFormat -import org.eclipse.jgit.api.CloneCommand -import org.eclipse.jgit.api.Git -import org.eclipse.jgit.lib.Repository -import org.eclipse.jgit.lib.Config -import org.eclipse.jgit.revwalk.RevCommit -import org.eclipse.jgit.transport.ReceiveCommand -import org.eclipse.jgit.transport.ReceiveCommand.Result -import org.eclipse.jgit.util.FileUtils -import org.slf4j.Logger - -/** - * Sample Gitblit Post-Receive Hook: localclone - * - * The Post-Receive hook is executed AFTER the pushed commits have been applied - * to the Git repository. This is the appropriate point to trigger an - * integration build or to send a notification. - * - * This script is only executed when pushing to *Gitblit*, not to other Git - * tooling you may be using. - * - * If this script is specified in *groovy.postReceiveScripts* of gitblit.properties - * or web.xml then it will be executed by any repository when it receives a - * push. If you choose to share your script then you may have to consider - * tailoring control-flow based on repository access restrictions. - * - * Scripts may also be specified per-repository in the repository settings page. - * Shared scripts will be excluded from this list of available scripts. - * - * This script is dynamically reloaded and it is executed within it's own - * exception handler so it will not crash another script nor crash Gitblit. - * - * If you want this hook script to fail and abort all subsequent scripts in the - * chain, "return false" at the appropriate failure points. - * - * Bound Variables: - * gitblit Gitblit Server com.gitblit.GitBlit - * repository Gitblit Repository com.gitblit.models.RepositoryModel - * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack - * user Gitblit User com.gitblit.models.UserModel - * commands JGit commands Collection - * url Base url for Gitblit String - * logger Logs messages to Gitblit org.slf4j.Logger - * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger - * - * Accessing Gitblit Custom Fields: - * def myCustomField = repository.customFields.myCustomField - * - */ - -// Indicate we have started the script -logger.info("localclone hook triggered by ${user.username} for ${repository.name}") - -def rootFolder = 'c:/test' -def bare = false -def cloneAllBranches = true -def cloneBranch = 'refs/heads/master' -def includeSubmodules = true - -def repoName = repository.name -def destinationFolder = new File(rootFolder, StringUtils.stripDotGit(repoName)) -def srcUrl = 'file://' + new File(GitBlit.getRepositoriesFolder(), repoName).absolutePath - -// delete any previous clone -if (destinationFolder.exists()) { - FileUtils.delete(destinationFolder, FileUtils.RECURSIVE) -} - -// clone the repository -logger.info("cloning ${srcUrl} to ${destinationFolder}") -CloneCommand cmd = Git.cloneRepository(); -cmd.setBare(bare) -if (cloneAllBranches) - cmd.setCloneAllBranches(true) -else - cmd.setBranch(cloneBranch) -cmd.setCloneSubmodules(includeSubmodules) -cmd.setURI(srcUrl) -cmd.setDirectory(destinationFolder) -Git git = cmd.call(); -git.repository.close() - -// report clone operation success back to pushing Git client -clientLogger.info("${repoName} cloned to ${destinationFolder}") \ No newline at end of file diff --git a/groovy/protect-refs.groovy b/groovy/protect-refs.groovy deleted file mode 100644 index b1b611f4..00000000 --- a/groovy/protect-refs.groovy +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2012 Philip L. McMahon. - * - * Derived from blockpush.groovy, copyright 2011 gitblit.com. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import com.gitblit.GitBlit -import com.gitblit.models.RepositoryModel -import com.gitblit.models.UserModel - -import org.eclipse.jgit.transport.ReceiveCommand -import org.eclipse.jgit.transport.ReceiveCommand.Result -import org.slf4j.Logger - -/** - * Sample Gitblit Pre-Receive Hook: protect-refs - * - * This script provides basic authorization of receive command types for a list - * of known ref patterns. Command types and unmatched ref patterns will be - * ignored, meaning this script has an "allow by default" policy. - * - * This script works best when a repository requires authentication on push, but - * can be used to enforce fast-forward commits or prohibit ref deletion by - * setting the *authorizedTeams* variable to an empty list and adding a ".+" - * entry to the *protectedRefs* list. - * - * The Pre-Receive hook is executed after an incoming push has been parsed, - * validated, and objects have been written but BEFORE the refs are updated. - * This is the appropriate point to block a push for some reason. - * - * This script is only executed when pushing to *Gitblit*, not to other Git - * tooling you may be using. - * - * If this script is specified in *groovy.preReceiveScripts* of gitblit.properties - * or web.xml then it will be executed by any repository when it receives a - * push. If you choose to share your script then you may have to consider - * tailoring control-flow based on repository access restrictions. - * - * Scripts may also be specified per-repository in the repository settings page. - * Shared scripts will be excluded from this list of available scripts. - * - * This script is dynamically reloaded and it is executed within it's own - * exception handler so it will not crash another script nor crash Gitblit. - * - * This script may reject one or more commands, but will never return false. - * Subsequent scripts, if any, will always be invoked. - * - * Bound Variables: - * gitblit Gitblit Server com.gitblit.GitBlit - * repository Gitblit Repository com.gitblit.models.RepositoryModel - * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack - * user Gitblit User com.gitblit.models.UserModel - * commands JGit commands Collection - * url Base url for Gitblit String - * logger Logs messages to Gitblit org.slf4j.Logger - * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger - * - * Accessing Gitblit Custom Fields: - * def myCustomField = repository.customFields.myCustomField - * - */ - -// map of protected command types to returned results type -// commands not included will skip authz check -def protectedCmds = [ - UPDATE_NONFASTFORWARD: Result.REJECTED_NONFASTFORWARD, - DELETE: Result.REJECTED_NODELETE -] - -// list of regex patterns for protected refs -def protectedRefs = [ - "refs/heads/master", - "refs/tags/.+" -] - -// teams which are authorized to perform protected commands on protected refs -def authorizedTeams = [ "admins" ] - -for (ReceiveCommand command : commands) { - def updateType = command.type - def updatedRef = command.refName - - // find first regex which matches updated ref, if any - def refPattern = protectedRefs.find { updatedRef.matches ~it } - - // find rejection result for update type, if any - def result = protectedCmds[updateType.name()] - - // command requires authz if ref is protected and has a mapped rejection result - if (refPattern && result) { - - // verify user is a member of any authorized team - def team = authorizedTeams.find { user.isTeamMember it } - if (team) { - // don't adjust command result - logger.info "${user.username} authorized for ${updateType} of protected ref ${repository.name}:${updatedRef} (${command.oldId.name} -> ${command.newId.name})" - } else { - // mark command result as rejected - command.setResult(result, "${user.username} cannot ${updateType} protected ref ${repository.name}:${updatedRef} matching pattern ${refPattern}") - } - } -} diff --git a/groovy/sendmail-html.groovy b/groovy/sendmail-html.groovy deleted file mode 100644 index 16920735..00000000 --- a/groovy/sendmail-html.groovy +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright 2012 gitblit.com. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import com.gitblit.GitBlit -import com.gitblit.Keys -import com.gitblit.models.RepositoryModel -import com.gitblit.models.TeamModel -import com.gitblit.models.UserModel -import com.gitblit.utils.JGitUtils -import java.text.SimpleDateFormat - -import org.eclipse.jgit.api.Status; -import org.eclipse.jgit.api.errors.JGitInternalException; -import org.eclipse.jgit.diff.DiffEntry; -import org.eclipse.jgit.diff.DiffFormatter; -import org.eclipse.jgit.diff.RawTextComparator; -import org.eclipse.jgit.diff.DiffEntry.ChangeType; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.IndexDiff; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.Repository -import org.eclipse.jgit.lib.Config -import org.eclipse.jgit.patch.FileHeader; -import org.eclipse.jgit.revwalk.RevCommit -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.transport.ReceiveCommand -import org.eclipse.jgit.transport.ReceiveCommand.Result -import org.eclipse.jgit.treewalk.FileTreeIterator; -import org.eclipse.jgit.treewalk.EmptyTreeIterator; -import org.eclipse.jgit.treewalk.CanonicalTreeParser; -import org.eclipse.jgit.util.io.DisabledOutputStream; -import org.slf4j.Logger -import groovy.xml.MarkupBuilder - -import java.io.IOException; -import java.security.MessageDigest - - -/** - * Sample Gitblit Post-Receive Hook: sendmail-html - * - * The Post-Receive hook is executed AFTER the pushed commits have been applied - * to the Git repository. This is the appropriate point to trigger an - * integration build or to send a notification. - * - * This script is only executed when pushing to *Gitblit*, not to other Git - * tooling you may be using. - * - * If this script is specified in *groovy.postReceiveScripts* of gitblit.properties - * or web.xml then it will be executed by any repository when it receives a - * push. If you choose to share your script then you may have to consider - * tailoring control-flow based on repository access restrictions. - * - * Scripts may also be specified per-repository in the repository settings page. - * Shared scripts will be excluded from this list of available scripts. - * - * This script is dynamically reloaded and it is executed within it's own - * exception handler so it will not crash another script nor crash Gitblit. - * - * If you want this hook script to fail and abort all subsequent scripts in the - * chain, "return false" at the appropriate failure points. - * - * Bound Variables: - * gitblit Gitblit Server com.gitblit.GitBlit - * repository Gitblit Repository com.gitblit.models.RepositoryModel - * user Gitblit User com.gitblit.models.UserModel - * commands JGit commands Collection - * url Base url for Gitblit java.lang.String - * logger Logs messages to Gitblit org.slf4j.Logger - * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger - * - * Accessing Gitblit Custom Fields: - * def myCustomField = repository.customFields.myCustomField - * - */ - -com.gitblit.models.UserModel userModel = user - -// Indicate we have started the script -logger.info("sendmail-html hook triggered by ${user.username} for ${repository.name}") - -/* - * Primitive email notification. - * This requires the mail settings to be properly configured in Gitblit. - */ - -Repository r = gitblit.getRepository(repository.name) - -// reuse existing repository config settings, if available -Config config = r.getConfig() -def mailinglist = config.getString('hooks', null, 'mailinglist') -def emailprefix = config.getString('hooks', null, 'emailprefix') - -// set default values -def toAddresses = [] -if (emailprefix == null) { - emailprefix = '[Gitblit]' -} - -if (mailinglist != null) { - def addrs = mailinglist.split(/(,|\s)/) - toAddresses.addAll(addrs) -} - -// add all mailing lists defined in gitblit.properties or web.xml -toAddresses.addAll(GitBlit.getStrings(Keys.mail.mailingLists)) - -// add all team mailing lists -def teams = gitblit.getRepositoryTeams(repository) -for (team in teams) { - TeamModel model = gitblit.getTeamModel(team) - if (model.mailingLists) { - toAddresses.addAll(model.mailingLists) - } -} - -// add all mailing lists for the repository -toAddresses.addAll(repository.mailingLists) - -// define the summary and commit urls -def repo = repository.name -def summaryUrl = url + "/summary?r=$repo" -def baseCommitUrl = url + "/commit?r=$repo&h=" -def baseBlobDiffUrl = url + "/blobdiff/?r=$repo&h=" -def baseCommitDiffUrl = url + "/commitdiff/?r=$repo&h=" -def forwardSlashChar = gitblit.getString(Keys.web.forwardSlashCharacter, '/') - -if (gitblit.getBoolean(Keys.web.mountParameters, true)) { - repo = repo.replace('/', forwardSlashChar).replace('/', '%2F') - summaryUrl = url + "/summary/$repo" - baseCommitUrl = url + "/commit/$repo/" - baseBlobDiffUrl = url + "/blobdiff/$repo/" - baseCommitDiffUrl = url + "/commitdiff/$repo/" -} - -class HtmlMailWriter { - Repository repository - def url - def baseCommitUrl - def baseCommitDiffUrl - def baseBlobDiffUrl - def mountParameters - def forwardSlashChar - def includeGravatar - def shortCommitIdLength - def commitCount = 0 - def commands - def writer = new StringWriter(); - def builder = new MarkupBuilder(writer) - - def writeStyle() { - builder.style(type:"text/css", ''' - .table td { - vertical-align: middle; - } - tr.noborder td { - border: none; - padding-top: 0px; - } - .gravatar-column { - width: 5%; - } - .author-column { - width: 20%; - } - .commit-column { - width: 5%; - } - .status-column { - width: 10%; - } - .table-disable-hover.table tbody tr:hover td, - .table-disable-hover.table tbody tr:hover th { - background-color: inherit; - } - .table-disable-hover.table-striped tbody tr:nth-child(odd):hover td, - .table-disable-hover.table-striped tbody tr:nth-child(odd):hover th { - background-color: #f9f9f9; - } - ''') - } - - def writeBranchTitle(type, name, action, number) { - builder.div('class' : 'pageTitle') { - builder.span('class':'project') { - mkp.yield "$type " - span('class': 'repository', name ) - if (number > 0) { - mkp.yield " $action ($number commits)" - } else { - mkp.yield " $action" - } - } - } - } - - def writeBranchDeletedTitle(type, name) { - builder.div('class' : 'pageTitle', 'style':'color:red') { - builder.span('class':'project') { - mkp.yield "$type " - span('class': 'repository', name ) - mkp.yield " deleted" - } - } - } - - def commitUrl(RevCommit commit) { - "${baseCommitUrl}$commit.id.name" - } - - def commitDiffUrl(RevCommit commit) { - "${baseCommitDiffUrl}$commit.id.name" - } - - def encoded(String path) { - path.replace('/', forwardSlashChar).replace('/', '%2F') - } - - def blobDiffUrl(objectId, path) { - if (mountParameters) { - // REST style - "${baseBlobDiffUrl}${objectId.name()}/${encoded(path)}" - } else { - "${baseBlobDiffUrl}${objectId.name()}&f=${path}" - } - - } - - def writeCommitTable(commits, includeChangedPaths=true) { - // Write commits table - builder.table('class':"table table-disable-hover") { - thead { - tr { - th(colspan: includeGravatar ? 2 : 1, "Author") - th( "Commit" ) - th( "Message" ) - } - } - tbody() { - - // Write all the commits - for (commit in commits) { - writeCommit(commit) - - if (includeChangedPaths) { - // Write detail on that particular commit - tr('class' : 'noborder') { - td (colspan: includeGravatar ? 3 : 2) - td (colspan:2) { writeStatusTable(commit) } - } - } - } - } - } - } - - def writeCommit(commit) { - def abbreviated = repository.newObjectReader().abbreviate(commit.id, shortCommitIdLength).name() - def author = commit.authorIdent.name - def email = commit.authorIdent.emailAddress - def message = commit.shortMessage - builder.tr { - if (includeGravatar) { - td('class':"gravatar-column") { - img(src:gravatarUrl(email), 'class':"gravatar") - } - } - td('class':"author-column", author) - td('class':"commit-column") { - a(href:commitUrl(commit)) { - span('class':"label label-info", abbreviated ) - } - } - td { - mkp.yield message - a('class':'link', href:commitDiffUrl(commit), " [commitdiff]" ) - } - } - } - - def writeStatusLabel(style, tooltip) { - builder.span('class' : style, 'title' : tooltip ) - } - - def writeAddStatusLine(ObjectId id, FileHeader header) { - builder.td('class':'changeType') { - writeStatusLabel("addition", "addition") - } - builder.td { - a(href:blobDiffUrl(id, header.newPath), header.newPath) - } - } - - def writeCopyStatusLine(ObjectId id, FileHeader header) { - builder.td('class':'changeType') { - writeStatusLabel("rename", "rename") - } - builder.td() { - a(href:blobDiffUrl(id, header.newPath), header.oldPath + " copied to " + header.newPath) - } - } - - def writeDeleteStatusLine(ObjectId id, FileHeader header) { - builder.td('class':'changeType') { - writeStatusLabel("deletion", "deletion") - } - builder.td() { - a(href:blobDiffUrl(id, header.oldPath), header.oldPath) - } - } - - def writeModifyStatusLine(ObjectId id, FileHeader header) { - builder.td('class':'changeType') { - writeStatusLabel("modification", "modification") - } - builder.td() { - a(href:blobDiffUrl(id, header.oldPath), header.oldPath) - } - } - - def writeRenameStatusLine(ObjectId id, FileHeader header) { - builder.td('class':'changeType') { - writeStatusLabel("rename", "rename") - } - builder.td() { - mkp.yield header.oldPath - mkp.yieldUnescaped " -&rt; " - a(href:blobDiffUrl(id, header.newPath), header.newPath) - } - } - - def writeStatusLine(ObjectId id, FileHeader header) { - builder.tr { - switch (header.changeType) { - case ChangeType.ADD: - writeAddStatusLine(id, header) - break; - case ChangeType.COPY: - writeCopyStatusLine(id, header) - break; - case ChangeType.DELETE: - writeDeleteStatusLine(id, header) - break; - case ChangeType.MODIFY: - writeModifyStatusLine(id, header) - break; - case ChangeType.RENAME: - writeRenameStatusLine(id, header) - break; - } - } - } - - def writeStatusTable(RevCommit commit) { - DiffFormatter formatter = new DiffFormatter(DisabledOutputStream.INSTANCE) - formatter.setRepository(repository) - formatter.setDetectRenames(true) - formatter.setDiffComparator(RawTextComparator.DEFAULT); - - def diffs - RevWalk rw = new RevWalk(repository) - if (commit.parentCount > 0) { - RevCommit parent = rw.parseCommit(commit.parents[0].id) - diffs = formatter.scan(parent.tree, commit.tree) - } else { - diffs = formatter.scan(new EmptyTreeIterator(), - new CanonicalTreeParser(null, rw.objectReader, commit.tree)) - } - rw.dispose() - // Write status table - builder.table('class':"plain") { - tbody() { - for (DiffEntry entry in diffs) { - FileHeader header = formatter.toFileHeader(entry) - writeStatusLine(commit.id, header) - } - } - } - } - - - def md5(text) { - - def digest = MessageDigest.getInstance("MD5") - - //Quick MD5 of text - def hash = new BigInteger(1, digest.digest(text.getBytes())) - .toString(16) - .padLeft(32, "0") - hash.toString() - } - - def gravatarUrl(email) { - def cleaned = email.trim().toLowerCase() - "http://www.gravatar.com/avatar/${md5(cleaned)}?s=30" - } - - def writeNavbar() { - builder.div('class':"navbar navbar-fixed-top") { - div('class':"navbar-inner") { - div('class':"container") { - a('class':"brand", href:"${url}", title:"GitBlit") { - img(src:"${url}/gitblt_25_white.png", - width:"79", - height:"25", - 'class':"logo") - } - } - } - } - } - - def write() { - builder.html { - head { - link(rel:"stylesheet", href:"${url}/bootstrap/css/bootstrap.css") - link(rel:"stylesheet", href:"${url}/gitblit.css") - link(rel:"stylesheet", href:"${url}/bootstrap/css/bootstrap-responsive.css") - writeStyle() - } - body { - - writeNavbar() - - div('class':"container") { - - for (command in commands) { - def ref = command.refName - def refType = 'Branch' - if (ref.startsWith('refs/heads/')) { - ref = command.refName.substring('refs/heads/'.length()) - } else if (ref.startsWith('refs/tags/')) { - ref = command.refName.substring('refs/tags/'.length()) - refType = 'Tag' - } - - switch (command.type) { - case ReceiveCommand.Type.CREATE: - def commits = JGitUtils.getRevLog(repository, command.oldId.name, command.newId.name).reverse() - commitCount += commits.size() - if (refType == 'Branch') { - // new branch - writeBranchTitle(refType, ref, "created", commits.size()) - writeCommitTable(commits, true) - } else { - // new tag - writeBranchTitle(refType, ref, "created", 0) - writeCommitTable(commits, false) - } - break - case ReceiveCommand.Type.UPDATE: - def commits = JGitUtils.getRevLog(repository, command.oldId.name, command.newId.name).reverse() - commitCount += commits.size() - // fast-forward branch commits table - // Write header - writeBranchTitle(refType, ref, "updated", commits.size()) - writeCommitTable(commits) - break - case ReceiveCommand.Type.UPDATE_NONFASTFORWARD: - def commits = JGitUtils.getRevLog(repository, command.oldId.name, command.newId.name).reverse() - commitCount += commits.size() - // non-fast-forward branch commits table - // Write header - writeBranchTitle(refType, ref, "updated [NON fast-forward]", commits.size()) - writeCommitTable(commits) - break - case ReceiveCommand.Type.DELETE: - // deleted branch/tag - writeBranchDeletedTitle(refType, ref) - break - default: - break - } - } - } - } - } - writer.toString() - } - -} - -def mailWriter = new HtmlMailWriter() -mailWriter.repository = r -mailWriter.baseCommitUrl = baseCommitUrl -mailWriter.baseBlobDiffUrl = baseBlobDiffUrl -mailWriter.baseCommitDiffUrl = baseCommitDiffUrl -mailWriter.forwardSlashChar = forwardSlashChar -mailWriter.commands = commands -mailWriter.url = url -mailWriter.mountParameters = GitBlit.getBoolean(Keys.web.mountParameters, true) -mailWriter.includeGravatar = GitBlit.getBoolean(Keys.web.allowGravatar, true) -mailWriter.shortCommitIdLength = GitBlit.getInteger(Keys.web.shortCommitIdLength, 8) - -def content = mailWriter.write() - -// close the repository reference -r.close() - -// tell Gitblit to send the message (Gitblit filters duplicate addresses) -def repositoryName = repository.name.substring(0, repository.name.length() - 4) -gitblit.sendHtmlMail("${emailprefix} ${userModel.displayName} pushed ${mailWriter.commitCount} commits => $repositoryName", - content, - toAddresses) diff --git a/groovy/sendmail.groovy b/groovy/sendmail.groovy deleted file mode 100644 index c832bc64..00000000 --- a/groovy/sendmail.groovy +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2011 gitblit.com. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import com.gitblit.GitBlit -import com.gitblit.Keys -import com.gitblit.models.RepositoryModel -import com.gitblit.models.TeamModel -import com.gitblit.models.UserModel -import com.gitblit.utils.JGitUtils -import java.text.SimpleDateFormat -import org.eclipse.jgit.lib.Repository -import org.eclipse.jgit.lib.Config -import org.eclipse.jgit.revwalk.RevCommit -import org.eclipse.jgit.transport.ReceiveCommand -import org.eclipse.jgit.transport.ReceiveCommand.Result -import org.slf4j.Logger - -/** - * Sample Gitblit Post-Receive Hook: sendmail - * - * The Post-Receive hook is executed AFTER the pushed commits have been applied - * to the Git repository. This is the appropriate point to trigger an - * integration build or to send a notification. - * - * This script is only executed when pushing to *Gitblit*, not to other Git - * tooling you may be using. - * - * If this script is specified in *groovy.postReceiveScripts* of gitblit.properties - * or web.xml then it will be executed by any repository when it receives a - * push. If you choose to share your script then you may have to consider - * tailoring control-flow based on repository access restrictions. - * - * Scripts may also be specified per-repository in the repository settings page. - * Shared scripts will be excluded from this list of available scripts. - * - * This script is dynamically reloaded and it is executed within it's own - * exception handler so it will not crash another script nor crash Gitblit. - * - * If you want this hook script to fail and abort all subsequent scripts in the - * chain, "return false" at the appropriate failure points. - * - * Bound Variables: - * gitblit Gitblit Server com.gitblit.GitBlit - * repository Gitblit Repository com.gitblit.models.RepositoryModel - * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack - * user Gitblit User com.gitblit.models.UserModel - * commands JGit commands Collection - * url Base url for Gitblit String - * logger Logs messages to Gitblit org.slf4j.Logger - * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger - * - * Accessing Gitblit Custom Fields: - * def myCustomField = repository.customFields.myCustomField - * - */ - -// Indicate we have started the script -logger.info("sendmail hook triggered by ${user.username} for ${repository.name}") - -/* - * Primitive email notification. - * This requires the mail settings to be properly configured in Gitblit. - */ - -Repository r = gitblit.getRepository(repository.name) - -// reuse existing repository config settings, if available -Config config = r.getConfig() -def mailinglist = config.getString('hooks', null, 'mailinglist') -def emailprefix = config.getString('hooks', null, 'emailprefix') - -// set default values -def toAddresses = [] -if (emailprefix == null) -emailprefix = '[Gitblit]' - -if (mailinglist != null) { - def addrs = mailinglist.split(/(,|\s)/) - toAddresses.addAll(addrs) -} - -// add all mailing lists defined in gitblit.properties or web.xml -toAddresses.addAll(gitblit.getStrings(Keys.mail.mailingLists)) - -// add all team mailing lists -def teams = gitblit.getRepositoryTeams(repository) -for (team in teams) { - TeamModel model = gitblit.getTeamModel(team) - if (model.mailingLists) { - toAddresses.addAll(model.mailingLists) - } -} - -// add all mailing lists for the repository -toAddresses.addAll(repository.mailingLists) - -// define the summary and commit urls -def repo = repository.name -def summaryUrl -def commitUrl -if (gitblit.getBoolean(Keys.web.mountParameters, true)) { - repo = repo.replace('/', gitblit.getString(Keys.web.forwardSlashCharacter, '/')).replace('/', '%2F') - summaryUrl = url + "/summary/$repo" - commitUrl = url + "/commit/$repo/" -} else { - summaryUrl = url + "/summary?r=$repo" - commitUrl = url + "/commit?r=$repo&h=" -} - -// construct a simple text summary of the changes contained in the push -def branchBreak = '>---------------------------------------------------------------\n' -def commitBreak = '\n\n ----\n' -def commitCount = 0 -def changes = '' -SimpleDateFormat df = new SimpleDateFormat(gitblit.getString(Keys.web.datetimestampLongFormat, 'EEEE, MMMM d, yyyy h:mm a z')) -def table = { "\n ${JGitUtils.getDisplayName(it.authorIdent)}\n ${df.format(JGitUtils.getCommitDate(it))}\n\n $it.shortMessage\n\n $commitUrl$it.id.name" } -for (command in commands) { - def ref = command.refName - def refType = 'branch' - if (ref.startsWith('refs/heads/')) { - ref = command.refName.substring('refs/heads/'.length()) - } else if (ref.startsWith('refs/tags/')) { - ref = command.refName.substring('refs/tags/'.length()) - refType = 'tag' - } - - switch (command.type) { - case ReceiveCommand.Type.CREATE: - def commits = JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() - commitCount += commits.size() - // new branch - changes += "\n$branchBreak new $refType $ref created ($commits.size commits)\n$branchBreak" - changes += commits.collect(table).join(commitBreak) - changes += '\n' - break - case ReceiveCommand.Type.UPDATE: - def commits = JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() - commitCount += commits.size() - // fast-forward branch commits table - changes += "\n$branchBreak $ref $refType updated ($commits.size commits)\n$branchBreak" - changes += commits.collect(table).join(commitBreak) - changes += '\n' - break - case ReceiveCommand.Type.UPDATE_NONFASTFORWARD: - def commits = JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() - commitCount += commits.size() - // non-fast-forward branch commits table - changes += "\n$branchBreak $ref $refType updated [NON fast-forward] ($commits.size commits)\n$branchBreak" - changes += commits.collect(table).join(commitBreak) - changes += '\n' - break - case ReceiveCommand.Type.DELETE: - // deleted branch/tag - changes += "\n$branchBreak $ref $refType deleted\n$branchBreak" - break - default: - break - } -} -// close the repository reference -r.close() - -// tell Gitblit to send the message (Gitblit filters duplicate addresses) -gitblit.sendMail("$emailprefix $user.username pushed $commitCount commits => $repository.name", "$summaryUrl\n$changes", toAddresses) \ No newline at end of file diff --git a/groovy/thebuggenie.groovy b/groovy/thebuggenie.groovy deleted file mode 100644 index b4385a26..00000000 --- a/groovy/thebuggenie.groovy +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2011 Wolfgang Gassler gassler.org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import com.gitblit.GitBlit -import com.gitblit.Keys -import com.gitblit.models.RepositoryModel -import com.gitblit.models.TeamModel -import com.gitblit.models.UserModel -import com.gitblit.utils.JGitUtils -import java.text.SimpleDateFormat -import org.eclipse.jgit.lib.Repository -import org.eclipse.jgit.lib.Config -import org.eclipse.jgit.revwalk.RevCommit -import org.eclipse.jgit.transport.ReceiveCommand -import org.eclipse.jgit.transport.ReceiveCommand.Result -import org.slf4j.Logger -import org.eclipse.jgit.lib.IndexDiff -import org.eclipse.jgit.lib.Constants -import com.gitblit.utils.DiffUtils - -/** - * Gitblit Post-Receive Hook: thebuggenie - * www.thebuggenie.com - * - * Submit the commit information to thebuggenie bug tracker by calling thebuggenie client tool - * - * Config of the Script: - * - * Setup a custom gitblit field in the proprties file of gitblit by adding the following line - * groovy.customFields = "thebuggenieProjectId=TheBugGennie project id (used for thebuggenie hoocks)" - * This field allows to specify the project id of thebuggenie project in the edit section of gitblit - * - * Furthermore you need to set the path to thebuggenie client tool by adding the following property to - * the gitblit properties file - * thebuggenie.tbg_cli = /var/www/thebuggenie_root/tbg_cli - */ - -// Indicate we have started the script -logger.info("thebuggenie hook triggered by ${user.username} for ${repository.name}") - -//fetch the repository data -Repository r = gitblit.getRepository(repository.name) - -//get project id which is defined in the git repo metadata -def tbgProjectId = repository.customFields.thebuggenieProjectId -//get path to the thebuggenie client tool which is defined in the gitblit proprties files -def tbgCliPath = gitblit.getString('thebuggenie.tbg_cli', '/var/www/thebuggenie/tbg_cli') -def tbgCliDirPath = new File(tbgCliPath).getParent() - -for(command in commands) { - //fetch all pushed commits - def commits = JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() - for (commit in commits) { - //get hashes and author data of commit - def oldhash = commit.getParent(0).getId().getName() - def newhash = commit.getId().getName() - def authorIdent = commit.getAuthorIdent() - def author = "${authorIdent.name} <${authorIdent.emailAddress}>" - //fetch all changed files of the commit - def files = JGitUtils.getFilesInCommit(r,commit) - def changedFiles = "" - for (f in files) { - //transform file data to the format which is needed by thebuggenie - changedFiles += f.changeType.toString().substring(0,1)+"\t${f.path}\n" - } - //ok let's submit all information to thebuggenie by calling the client tool -// def shc = "$tbgCliPath vcs_integration:report_commit $tbgProjectId \"$author\" $newhash \"${commit.fullMessage}\" \"$changedFiles\" $oldhash ${commit.commitTime}" - def shc = [tbgCliPath, "vcs_integration:report_commit", tbgProjectId, author, newhash, commit.getFullMessage(), changedFiles, oldhash, commit.getCommitTime()]; - logger.info("executing in path " + tbgCliDirPath + ": "+shc) - shc.execute(null, new File(tbgCliDirPath)) - } -} - -// close the repository reference -r.close() diff --git a/src/WEB-INF/web.xml b/src/WEB-INF/web.xml index 85b24d55..75ccf9bd 100644 --- a/src/WEB-INF/web.xml +++ b/src/WEB-INF/web.xml @@ -3,6 +3,30 @@ xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> + + + baseFolder + ${contextFolder}/WEB-INF/data + +