Compare commits

..

91 Commits
4.0 ... 4.2

Author SHA1 Message Date
Naoki Takezoe
7876a60106 Fix doc 2016-07-02 10:40:12 +09:00
Naoki Takezoe
38e71001cb 4.2.0 release 2016-07-02 10:39:01 +09:00
Naoki Takezoe
a1307b7464 Fix NumberFormatException 2016-07-02 02:19:06 +09:00
Naoki Takezoe
fd413d36ad Merge branch 'master' of https://github.com/gitbucket/gitbucket 2016-07-02 02:18:49 +09:00
Naoki Takezoe
509dfc57ca Fix sidebar scrolling 2016-07-02 02:01:51 +09:00
Matthieu Brouillard
95bdd6228e Merge pull request #1224 from McFoggy/issue-1168-json
handle empty password for JSON webhook tests, fixes #1168
2016-06-30 13:30:58 +02:00
Matthieu Brouillard
fd1430371a handle empty password for JSON webhook tests, fixes #1168 2016-06-30 10:47:41 +02:00
Naoki Takezoe
437f944c6e Rename octicons directory 2016-06-26 00:54:41 +09:00
Naoki Takezoe
f64b6e10bb Remove AdminLTEOptions 2016-06-26 00:53:04 +09:00
Naoki Takezoe
5925bd3772 Bump Octicons to 4.2.0 2016-06-26 00:29:01 +09:00
Naoki Takezoe
fe8d4616db Content is scrollable 2016-06-26 00:07:46 +09:00
Naoki Takezoe
99b40974c3 Fix displayed version 2016-06-25 23:41:24 +09:00
Naoki Takezoe
c463590ede Fix styles for AdminLTE 2016-06-25 23:37:01 +09:00
Naoki Takezoe
82163eebc2 Fix styles of account pages 2016-06-25 23:16:00 +09:00
Naoki Takezoe
f1e427f926 Remove unnecessary overflow: hidden; 2016-06-25 22:09:52 +09:00
Naoki Takezoe
c21dcdca80 Apply AdminLTE sidemenu to dashboard 2016-06-25 16:51:38 +09:00
Naoki Takezoe
2ce51472c3 Introduce AdminLTE 2016-06-25 12:52:45 +09:00
Naoki Takezoe
514b1aeec1 Remove link to the commit from the commit message because there are an other link to the commit. 2016-06-21 12:55:41 +09:00
Naoki Takezoe
3f34622fe0 Merge pull request #1221 from seratch/bump-deps
Bump patch version of Scalatra and some libs
2016-06-19 14:05:00 +09:00
Kaz Sera
8c6d5b8178 Bump minor version of libs 2016-06-19 12:03:50 +09:00
Kaz Sera
1971c29fd0 Bump sbt version to 0.13.11 2016-06-19 12:02:32 +09:00
Shintaro Murakami
40ca9b6682 Merge pull request #1220 from mrkm4ntr/link-to-plugin
Add link to gitbucket-network-plugin
2016-06-16 00:38:50 +09:00
Shintaro Murakami
81aeed6f6c Add link to gitbucket-network-plugin 2016-06-16 00:16:45 +09:00
Naoki Takezoe
bf50b1bf82 Add option to allow non-contributors to edit Wiki pages 2016-06-12 09:24:30 +09:00
Naoki Takezoe
1094e8ca2d Change order of columns 2016-06-12 08:23:57 +09:00
Naoki Takezoe
860ccce89b (refs #1208) Update document about schema migration 2016-06-12 08:14:01 +09:00
Naoki Takezoe
59e5993eba Update document 2016-06-12 00:53:20 +09:00
Naoki Takezoe
c08627e5d6 (refs #80) Add options to turn-off Wiki and Issues 2016-06-10 14:22:27 +09:00
Naoki Takezoe
3e0bb46699 Fix invalid margin of the plugin list 2016-06-09 22:10:42 +09:00
Naoki Takezoe
9a972e40ef (refs #1216) Add sending test mail capability 2016-06-09 21:30:28 +09:00
Naoki Takezoe
99ad0db1f6 Fix system settings page 2016-06-09 18:42:17 +09:00
Naoki Takezoe
12d11bc80c (refs #1212) GC button was finished 2016-06-09 00:32:51 +09:00
Naoki Takezoe
dc98079b55 (refs #1212) Add GC button to the danger zone 2016-06-08 20:52:40 +09:00
Naoki Takezoe
88f56126a6 (refs #1215) Open H2 console in new window 2016-06-08 19:48:39 +09:00
Naoki Takezoe
313c9976c8 (refs #1217) Fix permission toggle button in the group editing page 2016-06-08 19:38:09 +09:00
Naoki Takezoe
5b6a1d0adc (refs #1196)Fix search results paging for Wiki 2016-06-04 12:19:54 +09:00
Naoki Takezoe
6db43e6ca7 4.1.0 release 2016-06-04 12:14:15 +09:00
Naoki Takezoe
46177e814c Merge remote-tracking branch 'origin/master' 2016-06-01 22:06:15 +09:00
Naoki Takezoe
2fe79baed8 Close issue by pull request title 2016-06-01 22:06:00 +09:00
Naoki Takezoe
02e17c76a7 Merge pull request #1203 from MasanoriMT/webhook_proxy_support
Add support for proxy setting
2016-06-01 21:38:21 +09:00
Naoki Takezoe
4d8acfd286 Merge pull request #1211 from gitbucket/pull-request-title
Default value of pull request title
2016-06-01 01:26:55 +09:00
Naoki Takezoe
4a5c287b8f Default value of pull request title 2016-06-01 01:08:59 +09:00
Naoki Takezoe
0d4047b4ee Fix presentation of pull request page 2016-05-30 09:19:11 +09:00
Naoki Takezoe
2b730ef180 Fix presentation of branch list 2016-05-30 09:05:01 +09:00
Naoki Takezoe
ecbe7228b9 Fix top margin of titles 2016-05-29 21:17:29 +09:00
Naoki Takezoe
e36d0f65d6 Accept some file types as Wiki attachement file 2016-05-29 21:12:15 +09:00
Naoki Takezoe
30818fb797 Stop PostgreSQL before Travis test 2016-05-28 23:43:26 +09:00
Naoki Takezoe
6d7685fcce Fix Travis test 2016-05-28 23:31:00 +09:00
Naoki Takezoe
2cec5be3d9 Format 2016-05-28 23:28:25 +09:00
Naoki Takezoe
038b70ff0b Format 2016-05-28 22:18:57 +09:00
Naoki Takezoe
23a5f7dcf9 Fix pull request status styles 2016-05-28 04:16:13 +09:00
matoh
baa27d6090 Add support for proxy setting 2016-05-27 10:30:26 +09:00
Naoki Takezoe
f71acfcbe8 Skip external database test in Travis build 2016-05-26 20:01:54 +09:00
Naoki Takezoe
c65e843491 Use Travis provided DB in migration test 2016-05-26 19:01:06 +09:00
Naoki Takezoe
9103c88f0e Add migration test 2016-05-26 15:36:49 +09:00
Naoki Takezoe
90170f0fcc Add TODO comment 2016-05-25 19:15:31 +09:00
Naoki Takezoe
2e3de336cb Add TODO comment 2016-05-25 19:12:46 +09:00
Naoki Takezoe
da50d317e7 Merge pull request #1197 from apkd/master
Fix source display on mobile devices
2016-05-25 13:46:53 +09:00
Naoki Takezoe
7a91e14b03 Merge pull request #1198 from team-lab/fix-1192-branchprotection-setting
fix #1192 Cannot disable option "Require status checks to pass before…
2016-05-25 13:45:29 +09:00
Naoki Takezoe
58eff0a1a3 Merge pull request #1199 from team-lab/fix-jrebel
fix jrebel integration
2016-05-25 13:31:50 +09:00
Naoki Takezoe
8559f3e354 Merge pull request #1200 from team-lab/fix-java8-MaxPermSize-warning
remove -XX:MaxPermSize from java option
2016-05-25 13:31:37 +09:00
nazoking
7606097a4d remove -XX:MaxPermSize from java option 2016-05-24 16:46:19 +09:00
nazoking
dfbace3d26 fix jrebel integration 2016-05-23 17:22:18 +09:00
nazoking
d61ab632f1 fix #1192 Cannot disable option "Require status checks to pass before merging" 2016-05-23 15:31:39 +09:00
apkd
c3b341d945 Fix source display on mobile devices
The raw/mobile/history buttons used to incorrectly overflow on mobile devices, obscuring the entire (!) source view.
2016-05-22 16:21:05 +02:00
Naoki Takezoe
59f063627c Fix style 2016-05-19 14:18:31 +09:00
Naoki Takezoe
b4aba76005 Merge remote-tracking branch 'origin/master' 2016-05-19 12:01:59 +09:00
Naoki Takezoe
81afea350d (refs #1195)Fix date in the commit list 2016-05-19 12:01:41 +09:00
Matthieu Brouillard
5c5da60dd6 Merge pull request #1194 from shiena/patch/fix-missing-ctype
Fix a missing ctype from test hook
2016-05-18 07:25:32 +02:00
Mitsuhiro Koga
89c69cdfc2 Fix a missing ctype from test hook 2016-05-18 02:14:08 +09:00
Naoki Takezoe
0789010248 (refs #533)Add checking for the last one administrator. 2016-05-14 14:01:55 -04:00
Naoki Takezoe
37c23f615f (refs #533)Add remove user checking for the last one administrator. 2016-05-14 08:04:30 -04:00
Naoki Takezoe
b4d3573a84 Fix layout 2016-05-13 17:33:24 -04:00
Naoki Takezoe
5161ece63b (refs #1104)Unique checking for the public key 2016-05-13 15:43:44 -04:00
Naoki Takezoe
5b7955cee6 Merge pull request #1104 from ritschwumm/wip/generic
generic ssh user
2016-05-13 15:30:59 -04:00
Naoki Takezoe
60099e2b0d (refs #1190, #1191)Fix tab floating 2016-05-13 08:32:39 -04:00
Naoki Takezoe
f04c486251 Bump markedj 1.0.9-SNAPSHOT 2016-05-13 00:28:13 -04:00
Herr Ritschwumm
7b23bbf9ba move filtering into the database 2016-05-05 23:15:22 +02:00
Herr Ritschwumm
0a532d9774 optionally all accounts can now use the same generic ssh username. does not work for accounts sharing their key with another account.
based on work Fabian Markert's work.
2016-05-05 23:15:22 +02:00
Herr Ritschwumm
208b98285c small cleanup 2016-05-05 23:15:22 +02:00
Naoki Takezoe
56c9aa32a4 Merge pull request #1185 from dariko/system_admin_typo
typo: DAT(A)BASE_URL
2016-05-05 16:38:09 +09:00
Naoki Takezoe
35080a9f33 (refs #1167)Fix commit information in the branch view 2016-05-05 16:08:38 +09:00
Dario Zanzico
6c41505c91 typo: DAT(A)BASE_URL 2016-05-05 09:03:31 +02:00
Naoki Takezoe
05bfaafe32 Tweak branch list presentation 2016-05-05 16:00:04 +09:00
Naoki Takezoe
7534a88607 (refs #1182)Remove code which is deleting the temporary directory on bootstrap 2016-05-05 12:51:02 +09:00
Naoki Takezoe
d7817d3d88 Update README.md 2016-05-03 01:03:12 +09:00
Naoki Takezoe
37780d467d Update README.md 2016-05-03 01:00:54 +09:00
Naoki Takezoe
ad4af67b30 (refs #1172)Fix: Some of issue filters has been impossible to turn off 2016-05-02 20:17:50 +09:00
Naoki Takezoe
29b0c22b0e Fix radio button layout 2016-05-02 15:48:18 +09:00
Naoki Takezoe
c400678550 (refs #1178)Check group member isn't group account 2016-05-02 15:47:59 +09:00
Naoki Takezoe
3c40e93346 Fix use type selection buttons 2016-05-02 15:36:45 +09:00
158 changed files with 12717 additions and 1979 deletions

View File

@@ -1,6 +1,11 @@
language: scala language: scala
sudo: false sudo: true
script: script:
- sbt test - sbt test
jdk: jdk:
- oraclejdk8 - oraclejdk8
before_script:
- sudo apt-get install libaio1
- sudo /etc/init.d/mysql stop
- sudo /etc/init.d/postgresql stop

View File

@@ -49,6 +49,7 @@ GitBucket has the plug-in system to extend GitBucket from outside of GitBucket.
- [gitbucket-desktopnotify-plugin](https://github.com/yoshiyoshifujii/gitbucket-desktopnotify-plugin) - [gitbucket-desktopnotify-plugin](https://github.com/yoshiyoshifujii/gitbucket-desktopnotify-plugin)
- [gitbucket-commitgraphs-plugin](https://github.com/yoshiyoshifujii/gitbucket-commitgraphs-plugin) - [gitbucket-commitgraphs-plugin](https://github.com/yoshiyoshifujii/gitbucket-commitgraphs-plugin)
- [gitbucket-asciidoctor-plugin](https://github.com/lefou/gitbucket-asciidoctor-plugin) - [gitbucket-asciidoctor-plugin](https://github.com/lefou/gitbucket-asciidoctor-plugin)
- [gitbucket-network-plugin](https://github.com/mrkm4ntr/gitbucket-network-plugin)
You can find community plugins other than them at [gitbucket community plugins](http://gitbucket-plugins.github.io/). You can find community plugins other than them at [gitbucket community plugins](http://gitbucket-plugins.github.io/).
@@ -62,13 +63,28 @@ Support
- First priority of GitBucket is easy installation and API compatibility with GitHub, so we might reject if your request is against it. - First priority of GitBucket is easy installation and API compatibility with GitHub, so we might reject if your request is against it.
Release Notes Release Notes
-------- -------------
### 4.2 - 2 Jul 2016
- New UI based on [AdminLTE](https://github.com/almasaeed2010/AdminLTE)
- git gc
- Issues and Wiki have been possible to be disabled
- SMTP configuration test mail
### 4.1 - 4 Jun 2016
- Generic ssh user
- Improve branch protection UI
- Default value of pull request title
### 4.0 - 30 Apr 2016 ### 4.0 - 30 Apr 2016
- MySQL and PostgreSQL support - MySQL and PostgreSQL support
- Data export and import - Data export and import
- Migration system has been switched to [solidbase](https://github.com/gitbucket/solidbase) - Migration system has been switched to [solidbase](https://github.com/gitbucket/solidbase)
**Note:** You can upgrade to GitBucket 4.0 from 3.14. If your GitBucket is 3.13 or before, you have to upgrade 3.14 at first.
### 3.14 - 30 Apr 2016 ### 3.14 - 30 Apr 2016
- File attachment and search for wiki pages - File attachment and search for wiki pages

View File

@@ -1,8 +1,8 @@
val Organization = "gitbucket" val Organization = "gitbucket"
val Name = "gitbucket" val Name = "gitbucket"
val GitBucketVersion = "4.0.0" val GitBucketVersion = "4.2.0"
val ScalatraVersion = "2.4.0" val ScalatraVersion = "2.4.1"
val JettyVersion = "9.3.6.v20151106" val JettyVersion = "9.3.9.v20160517"
lazy val root = (project in file(".")).enablePlugins(SbtTwirl, JettyPlugin) lazy val root = (project in file(".")).enablePlugins(SbtTwirl, JettyPlugin)
@@ -21,36 +21,38 @@ resolvers ++= Seq(
) )
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-java8-compat" % "0.7.0", "org.scala-lang.modules" %% "scala-java8-compat" % "0.7.0",
"org.eclipse.jgit" % "org.eclipse.jgit.http.server" % "4.1.1.201511131810-r", "org.eclipse.jgit" % "org.eclipse.jgit.http.server" % "4.1.2.201602141800-r",
"org.eclipse.jgit" % "org.eclipse.jgit.archive" % "4.1.1.201511131810-r", "org.eclipse.jgit" % "org.eclipse.jgit.archive" % "4.1.2.201602141800-r",
"org.scalatra" %% "scalatra" % ScalatraVersion, "org.scalatra" %% "scalatra" % ScalatraVersion,
"org.scalatra" %% "scalatra-json" % ScalatraVersion, "org.scalatra" %% "scalatra-json" % ScalatraVersion,
"org.json4s" %% "json4s-jackson" % "3.3.0", "org.json4s" %% "json4s-jackson" % "3.3.0",
"io.github.gitbucket" %% "scalatra-forms" % "1.0.0", "io.github.gitbucket" %% "scalatra-forms" % "1.0.0",
"commons-io" % "commons-io" % "2.4", "commons-io" % "commons-io" % "2.4",
"io.github.gitbucket" % "solidbase" % "1.0.0", "io.github.gitbucket" % "solidbase" % "1.0.0",
"io.github.gitbucket" % "markedj" % "1.0.8", "io.github.gitbucket" % "markedj" % "1.0.9-SNAPSHOT",
"org.apache.commons" % "commons-compress" % "1.10", "org.apache.commons" % "commons-compress" % "1.11",
"org.apache.commons" % "commons-email" % "1.4", "org.apache.commons" % "commons-email" % "1.4",
"org.apache.httpcomponents" % "httpclient" % "4.5.1", "org.apache.httpcomponents" % "httpclient" % "4.5.1",
"org.apache.sshd" % "apache-sshd" % "1.0.0", "org.apache.sshd" % "apache-sshd" % "1.0.0",
"org.apache.tika" % "tika-core" % "1.11", "org.apache.tika" % "tika-core" % "1.13",
"com.typesafe.slick" %% "slick" % "2.1.0", "com.typesafe.slick" %% "slick" % "2.1.0",
"com.novell.ldap" % "jldap" % "2009-10-07", "com.novell.ldap" % "jldap" % "2009-10-07",
"com.h2database" % "h2" % "1.4.190", "com.h2database" % "h2" % "1.4.192",
"mysql" % "mysql-connector-java" % "5.1.38", "mysql" % "mysql-connector-java" % "5.1.39",
"org.postgresql" % "postgresql" % "9.4.1208", "org.postgresql" % "postgresql" % "9.4.1208",
"ch.qos.logback" % "logback-classic" % "1.1.1", "ch.qos.logback" % "logback-classic" % "1.1.7",
"com.zaxxer" % "HikariCP" % "2.4.5", "com.zaxxer" % "HikariCP" % "2.4.6",
"com.typesafe" % "config" % "1.3.0", "com.typesafe" % "config" % "1.3.0",
"com.typesafe.akka" %% "akka-actor" % "2.3.14", "com.typesafe.akka" %% "akka-actor" % "2.3.15",
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0", "fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0",
"com.enragedginger" %% "akka-quartz-scheduler" % "1.4.0-akka-2.3.x" exclude("c3p0","c3p0"), "com.enragedginger" %% "akka-quartz-scheduler" % "1.4.0-akka-2.3.x" exclude("c3p0","c3p0"),
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided", "org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided", "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
"junit" % "junit" % "4.12" % "test", "junit" % "junit" % "4.12" % "test",
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test", "org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
"org.scalaz" %% "scalaz-core" % "7.2.0" % "test" "org.scalaz" %% "scalaz-core" % "7.2.4" % "test",
"com.wix" % "wix-embedded-mysql" % "1.0.3" % "test",
"ru.yandex.qatools.embed" % "postgresql-embedded" % "1.14" % "test"
) )
// Twirl settings // Twirl settings
@@ -60,10 +62,14 @@ play.twirl.sbt.Import.TwirlKeys.templateImports += "gitbucket.core._"
scalacOptions := Seq("-deprecation", "-language:postfixOps", "-Ybackend:GenBCode", "-Ydelambdafy:method", "-target:jvm-1.8") scalacOptions := Seq("-deprecation", "-language:postfixOps", "-Ybackend:GenBCode", "-Ydelambdafy:method", "-target:jvm-1.8")
javacOptions in compile ++= Seq("-target", "8", "-source", "8") javacOptions in compile ++= Seq("-target", "8", "-source", "8")
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml" javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"
testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "junitxml", "console")
// Test settings
//testOptions in Test += Tests.Argument("-l", "ExternalDBTest")
javaOptions in Test += "-Dgitbucket.home=target/gitbucket_home_for_test" javaOptions in Test += "-Dgitbucket.home=target/gitbucket_home_for_test"
testOptions in Test += Tests.Setup( () => new java.io.File("target/gitbucket_home_for_test").mkdir() ) testOptions in Test += Tests.Setup( () => new java.io.File("target/gitbucket_home_for_test").mkdir() )
fork in Test := true fork in Test := true
// Packaging options
packageOptions += Package.MainClass("JettyLauncher") packageOptions += Package.MainClass("JettyLauncher")
// Assembly settings // Assembly settings
@@ -78,12 +84,13 @@ assemblyMergeStrategy in assembly := {
} }
// JRebel // JRebel
Seq(jrebelSettings: _*)
jrebel.webLinks += (target in webappPrepare).value jrebel.webLinks += (target in webappPrepare).value
jrebel.enabled := System.getenv().get("JREBEL") != null jrebel.enabled := System.getenv().get("JREBEL") != null
javaOptions in Jetty ++= Option(System.getenv().get("JREBEL")).toSeq.flatMap { path => javaOptions in Jetty ++= Option(System.getenv().get("JREBEL")).toSeq.flatMap { path =>
Seq("-noverify", "-XX:+UseConcMarkSweepGC", "-XX:+CMSClassUnloadingEnabled", s"-javaagent:${path}") Seq("-noverify", "-XX:+UseConcMarkSweepGC", "-XX:+CMSClassUnloadingEnabled", s"-javaagent:${path}")
} }
jrebelSettings
// Create executable war file // Create executable war file
val executableConfig = config("executable").hide val executableConfig = config("executable").hide

View File

@@ -1,37 +1,54 @@
Automatic Schema Updating Automatic Schema Updating
======== ========
GitBucket uses H2 database to manage project and account data. GitBucket updates database schema automatically in the first run after the upgrading. GitBucket updates database schema automatically using [Solidbase](https://github.com/gitbucket/solidbase) in the first run after the upgrading.
To release a new version of GitBucket, add the version definition to the [gitbucket.core.servlet.AutoUpdate](https://github.com/gitbucket/gitbucket/blob/master/src/main/scala/gitbucket/core/servlet/AutoUpdate.scala) at first. To release a new version of GitBucket, add the version definition to the [gitbucket.core.GitBucketCoreModule](https://github.com/gitbucket/gitbucket/blob/master/src/main/scala/gitbucket/core/GitBucketCoreModule.scala) at first.
```scala ```scala
object AutoUpdate { object GitBucketCoreModule extends Module("gitbucket-core",
... new Version("4.0.0",
/** new LiquibaseMigration("update/gitbucket-core_4.0.xml"),
* The history of versions. A head of this sequence is the current GitBucket version. new SqlMigration("update/gitbucket-core_4.0.sql")
*/ ),
val versions = Seq( new Version("4.1.0"),
Version(1, 0) new Version("4.2.0",
new LiquibaseMigration("update/gitbucket-core_4.2.xml")
) )
...
```
Next, add a SQL file which updates database schema into [/src/main/resources/update/](https://github.com/gitbucket/gitbucket/tree/master/src/main/resources/update) as ```MAJOR_MINOR.sql```.
GitBucket stores the current version to ```GITBUCKET_HOME/version``` and checks it at start-up. If the stored version differs from the actual version, it executes differences of SQL files between the stored version and the actual version. And ```GITBUCKET_HOME/version``` is updated by the actual version.
We can also add any Scala code for upgrade GitBucket which modifies resources other than database. Override ```Version.update``` like below:
```scala
val versions = Seq(
new Version(1, 3){
override def update(conn: Connection): Unit = {
super.update(conn)
// Add any code here!
}
},
Version(1, 2),
Version(1, 1),
Version(1, 0)
) )
``` ```
Next, add a XML file which updates database schema into [/src/main/resources/update/](https://github.com/gitbucket/gitbucket/tree/master/src/main/resources/update) with a filenane defined in `GitBucketCoreModule`.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<changeSet>
<addColumn tableName="REPOSITORY">
<column name="ENABLE_WIKI" type="boolean" nullable="false" defaultValueBoolean="true"/>
<column name="ENABLE_ISSUES" type="boolean" nullable="false" defaultValueBoolean="true"/>
<column name="EXTERNAL_WIKI_URL" type="varchar(200)" nullable="true"/>
<column name="EXTERNAL_ISSUES_URL" type="varchar(200)" nullable="true"/>
</addColumn>
</changeSet>
```
Solidbase stores the current version to `VERSIONS` table and checks it at start-up. If the stored version differs from the actual version, it executes differences between the stored version and the actual version.
We can add the SQL file instead of the XML file using `SqlMigration`. It try to load a SQL file from classpath as following order:
1. Specified path (if specified)
2. `${moduleId}_${version}_${database}.sql`
3. `${moduleId}_${version}.sql`
Also we can add any code by extending `Migration`:
```scala
object GitBucketCoreModule extends Module("gitbucket-core",
new Version("4.0.0", new Migration(){
override def migrate(moduleId: String, version: String, context: java.util.Map[String, String]): Unit = {
...
}
})
)
```
See more details [README of Solidbase](https://github.com/gitbucket/solidbase).

View File

@@ -20,13 +20,13 @@ val JettyVersion = "9.3.6.v20151106"
```scala ```scala
object GitBucketCoreModule extends Module("gitbucket-core", object GitBucketCoreModule extends Module("gitbucket-core",
// add new version definition
new Version("4.1.0",
new LiquibaseMigration("update/gitbucket-core_4.1.xml")
),
new Version("4.0.0", new Version("4.0.0",
new LiquibaseMigration("update/gitbucket-core_4.0.xml"), new LiquibaseMigration("update/gitbucket-core_4.0.xml"),
new SqlMigration("update/gitbucket-core_4.0.sql") new SqlMigration("update/gitbucket-core_4.0.sql")
),
// add new version definition
new Version("4.1.0",
new LiquibaseMigration("update/gitbucket-core_4.1.xml")
) )
) )
``` ```
@@ -41,7 +41,7 @@ Note: Release operation requires [Ant](http://ant.apache.org/) and [Maven](https
Run `sbt executable`. The release war file and fingerprint are generated into `target/executable/gitbucket.war`. Run `sbt executable`. The release war file and fingerprint are generated into `target/executable/gitbucket.war`.
```bash ```bash
$sbt executable $ sbt executable
``` ```
### Deploy assembly jar file ### Deploy assembly jar file

View File

@@ -1 +1 @@
sbt.version=0.13.9 sbt.version=0.13.11

View File

@@ -1,2 +1,2 @@
set SCRIPT_DIR=%~dp0 set SCRIPT_DIR=%~dp0
java %JAVA_OPTS% -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar "%SCRIPT_DIR%\sbt-launch-0.13.9.jar" %* java %JAVA_OPTS% -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar "%SCRIPT_DIR%\sbt-launch-0.13.9.jar" %*

2
sbt.sh
View File

@@ -1,2 +1,2 @@
#!/bin/sh #!/bin/sh
java $JAVA_OPTS -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar `dirname $0`/sbt-launch-0.13.9.jar "$@" java $JAVA_OPTS -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar `dirname $0`/sbt-launch-0.13.9.jar "$@"

View File

@@ -43,10 +43,9 @@ public class JettyLauncher {
WebAppContext context = new WebAppContext(); WebAppContext context = new WebAppContext();
File tmpDir = new File(getGitBucketHome(), "tmp"); File tmpDir = new File(getGitBucketHome(), "tmp");
if(tmpDir.exists()){ if(!tmpDir.exists()){
deleteDirectory(tmpDir); tmpDir.mkdirs();
} }
tmpDir.mkdirs();
context.setTempDirectory(tmpDir); context.setTempDirectory(tmpDir);
ProtectionDomain domain = JettyLauncher.class.getProtectionDomain(); ProtectionDomain domain = JettyLauncher.class.getProtectionDomain();

View File

@@ -20,7 +20,7 @@
<!-- <!--
<logger name="service.WebHookService" level="DEBUG" /> <logger name="service.WebHookService" level="DEBUG" />
<logger name="servlet" level="DEBUG" /> <logger name="servlet" level="DEBUG" />
-->
<logger name="scala.slick.jdbc.JdbcBackend.statement" level="DEBUG" /> <logger name="scala.slick.jdbc.JdbcBackend.statement" level="DEBUG" />
-->
</configuration> </configuration>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<changeSet>
<addColumn tableName="REPOSITORY">
<column name="ENABLE_ISSUES" type="boolean" nullable="false" defaultValueBoolean="true"/>
<column name="EXTERNAL_ISSUES_URL" type="varchar(200)" nullable="true"/>
<column name="ENABLE_WIKI" type="boolean" nullable="false" defaultValueBoolean="true"/>
<column name="ALLOW_WIKI_EDITING" type="boolean" nullable="false" defaultValueBoolean="false"/>
<column name="EXTERNAL_WIKI_URL" type="varchar(200)" nullable="true"/>
</addColumn>
</changeSet>

View File

@@ -7,5 +7,9 @@ object GitBucketCoreModule extends Module("gitbucket-core",
new Version("4.0.0", new Version("4.0.0",
new LiquibaseMigration("update/gitbucket-core_4.0.xml"), new LiquibaseMigration("update/gitbucket-core_4.0.xml"),
new SqlMigration("update/gitbucket-core_4.0.sql") new SqlMigration("update/gitbucket-core_4.0.sql")
),
new Version("4.1.0"),
new Version("4.2.0",
new LiquibaseMigration("update/gitbucket-core_4.2.xml")
) )
) )

View File

@@ -14,7 +14,7 @@ object ApiBranchProtection{
def apply(info: ProtectedBranchService.ProtectedBranchInfo): ApiBranchProtection = ApiBranchProtection( def apply(info: ProtectedBranchService.ProtectedBranchInfo): ApiBranchProtection = ApiBranchProtection(
enabled = info.enabled, enabled = info.enabled,
required_status_checks = Some(Status(EnforcementLevel(info.enabled, info.includeAdministrators), info.contexts))) required_status_checks = Some(Status(EnforcementLevel(info.enabled && info.contexts.nonEmpty, info.includeAdministrators), info.contexts)))
val statusNone = Status(Off, Seq.empty) val statusNone = Status(Off, Seq.empty)
case class Status(enforcement_level: EnforcementLevel, contexts: Seq[String]) case class Status(enforcement_level: EnforcementLevel, contexts: Seq[String])
sealed class EnforcementLevel(val name: String) sealed class EnforcementLevel(val name: String)
@@ -44,4 +44,3 @@ object ApiBranchProtection{
} }
)) ))
} }

View File

@@ -31,7 +31,8 @@ case class ApiPullRequest(
} }
object ApiPullRequest{ object ApiPullRequest{
def apply(issue: Issue, pullRequest: PullRequest, headRepo: ApiRepository, baseRepo: ApiRepository, user: ApiUser): ApiPullRequest = ApiPullRequest( def apply(issue: Issue, pullRequest: PullRequest, headRepo: ApiRepository, baseRepo: ApiRepository, user: ApiUser): ApiPullRequest =
ApiPullRequest(
number = issue.issueId, number = issue.issueId,
updated_at = issue.updatedDate, updated_at = issue.updatedDate,
created_at = issue.registeredDate, created_at = issue.registeredDate,

View File

@@ -14,11 +14,11 @@ case class ApiRepository(
`private`: Boolean, `private`: Boolean,
default_branch: String, default_branch: String,
owner: ApiUser)(urlIsHtmlUrl: Boolean) { owner: ApiUser)(urlIsHtmlUrl: Boolean) {
val forks_count = forks val forks_count = forks
val watchers_count = watchers val watchers_count = watchers
val url = if(urlIsHtmlUrl){ val url = if(urlIsHtmlUrl){
ApiPath(s"/${full_name}") ApiPath(s"/${full_name}")
}else{ } else {
ApiPath(s"/api/v3/repos/${full_name}") ApiPath(s"/api/v3/repos/${full_name}")
} }
val http_url = ApiPath(s"/git/${full_name}.git") val http_url = ApiPath(s"/git/${full_name}.git")
@@ -34,14 +34,14 @@ object ApiRepository{
watchers: Int = 0, watchers: Int = 0,
urlIsHtmlUrl: Boolean = false): ApiRepository = urlIsHtmlUrl: Boolean = false): ApiRepository =
ApiRepository( ApiRepository(
name = repository.repositoryName, name = repository.repositoryName,
full_name = s"${repository.userName}/${repository.repositoryName}", full_name = s"${repository.userName}/${repository.repositoryName}",
description = repository.description.getOrElse(""), description = repository.description.getOrElse(""),
watchers = 0, watchers = 0,
forks = forkedCount, forks = forkedCount,
`private` = repository.isPrivate, `private` = repository.isPrivate,
default_branch = repository.defaultBranch, default_branch = repository.defaultBranch,
owner = owner owner = owner
)(urlIsHtmlUrl) )(urlIsHtmlUrl)
def apply(repositoryInfo: RepositoryInfo, owner: ApiUser): ApiRepository = def apply(repositoryInfo: RepositoryInfo, owner: ApiUser): ApiRepository =

View File

@@ -155,7 +155,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
get("/:userName/_edit")(oneselfOnly { get("/:userName/_edit")(oneselfOnly {
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName).map { x => getAccountByUserName(userName).map { x =>
html.edit(x, flash.get("info")) html.edit(x, flash.get("info"), flash.get("error"))
} getOrElse NotFound } getOrElse NotFound
}) })
@@ -178,7 +178,11 @@ trait AccountControllerBase extends AccountManagementControllerBase {
get("/:userName/_delete")(oneselfOnly { get("/:userName/_delete")(oneselfOnly {
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName, true).foreach { account => getAccountByUserName(userName, true).map { account =>
if(isLastAdministrator(account)){
flash += "error" -> "Account can't be removed because this is last one administrator."
redirect(s"/${userName}/_edit")
} else {
// // Remove repositories // // Remove repositories
// getRepositoryNamesOfUser(userName).foreach { repositoryName => // getRepositoryNamesOfUser(userName).foreach { repositoryName =>
// deleteRepository(userName, repositoryName) // deleteRepository(userName, repositoryName)
@@ -187,14 +191,12 @@ trait AccountControllerBase extends AccountManagementControllerBase {
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName)) // FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
// } // }
// // Remove from GROUP_MEMBER, COLLABORATOR and REPOSITORY // // Remove from GROUP_MEMBER, COLLABORATOR and REPOSITORY
// removeUserRelatedData(userName) removeUserRelatedData(userName)
updateAccount(account.copy(isRemoved = true))
removeUserRelatedData(userName) session.invalidate
updateAccount(account.copy(isRemoved = true)) redirect("/")
} }
} getOrElse NotFound
session.invalidate
redirect("/")
}) })
get("/:userName/_ssh")(oneselfOnly { get("/:userName/_ssh")(oneselfOnly {
@@ -447,8 +449,8 @@ trait AccountControllerBase extends AccountManagementControllerBase {
private def validPublicKey: Constraint = new Constraint(){ private def validPublicKey: Constraint = new Constraint(){
override def validate(name: String, value: String, messages: Messages): Option[String] = SshUtil.str2PublicKey(value) match { override def validate(name: String, value: String, messages: Messages): Option[String] = SshUtil.str2PublicKey(value) match {
case Some(_) => None case Some(_) if !getAllKeys().exists(_.publicKey == value) => None
case None => Some("Key is invalid.") case _ => Some("Key is invalid.")
} }
} }

View File

@@ -119,7 +119,9 @@ trait ApiControllerBase extends ControllerBase {
}) getOrElse NotFound }) getOrElse NotFound
}) })
/** https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection */ /**
* https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection
*/
patch("/api/v3/repos/:owner/:repo/branches/:branch")(ownerOnly { repository => patch("/api/v3/repos/:owner/:repo/branches/:branch")(ownerOnly { repository =>
import gitbucket.core.api._ import gitbucket.core.api._
(for{ (for{
@@ -261,18 +263,28 @@ trait ApiControllerBase extends ControllerBase {
* https://developer.github.com/v3/pulls/#list-pull-requests * https://developer.github.com/v3/pulls/#list-pull-requests
*/ */
get("/api/v3/repos/:owner/:repository/pulls")(referrersOnly { repository => get("/api/v3/repos/:owner/:repository/pulls")(referrersOnly { repository =>
val page = IssueSearchCondition.page(request) val page = IssueSearchCondition.page(request)
// TODO: more api spec condition // TODO: more api spec condition
val condition = IssueSearchCondition(request) val condition = IssueSearchCondition(request)
val baseOwner = getAccountByUserName(repository.owner).get val baseOwner = getAccountByUserName(repository.owner).get
val issues:List[(Issue, Account, Int, PullRequest, Repository, Account)] = searchPullRequestByApi(condition, (page - 1) * PullRequestLimit, PullRequestLimit, repository.owner -> repository.name)
JsonFormat(issues.map{case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner) => val issues: List[(Issue, Account, Int, PullRequest, Repository, Account)] =
searchPullRequestByApi(
condition = condition,
offset = (page - 1) * PullRequestLimit,
limit = PullRequestLimit,
repos = repository.owner -> repository.name
)
JsonFormat(issues.map { case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner) =>
ApiPullRequest( ApiPullRequest(
issue, issue,
pullRequest, pullRequest,
ApiRepository(headRepo, ApiUser(headOwner)), ApiRepository(headRepo, ApiUser(headOwner)),
ApiRepository(repository, ApiUser(baseOwner)), ApiRepository(repository, ApiUser(baseOwner)),
ApiUser(issueUser)) }) ApiUser(issueUser)
)
})
}) })
/** /**

View File

@@ -83,9 +83,9 @@ trait DashboardControllerBase extends ControllerBase {
} else session.getAs[IssueSearchCondition](key).getOrElse(IssueSearchCondition())) } else session.getAs[IssueSearchCondition](key).getOrElse(IssueSearchCondition()))
filter match { filter match {
case "assigned" => condition.copy(assigned = Some(userName), author = None , mentioned = None) case "assigned" => condition.copy(assigned = Some(Some(userName)), author = None, mentioned = None)
case "mentioned" => condition.copy(assigned = None , author = None , mentioned = Some(userName)) case "mentioned" => condition.copy(assigned = None, author = None, mentioned = Some(userName))
case _ => condition.copy(assigned = None , author = Some(userName), mentioned = None) case _ => condition.copy(assigned = None, author = Some(userName), mentioned = None)
} }
} }
@@ -103,7 +103,7 @@ trait DashboardControllerBase extends ControllerBase {
countIssue(condition.copy(state = "open" ), false, userRepos: _*), countIssue(condition.copy(state = "open" ), false, userRepos: _*),
countIssue(condition.copy(state = "closed"), false, userRepos: _*), countIssue(condition.copy(state = "closed"), false, userRepos: _*),
filter match { filter match {
case "assigned" => condition.copy(assigned = Some(userName)) case "assigned" => condition.copy(assigned = Some(Some(userName)))
case "mentioned" => condition.copy(mentioned = Some(userName)) case "mentioned" => condition.copy(mentioned = Some(userName))
case _ => condition.copy(author = Some(userName)) case _ => condition.copy(author = Some(userName))
}, },
@@ -128,7 +128,7 @@ trait DashboardControllerBase extends ControllerBase {
countIssue(condition.copy(state = "open" ), true, allRepos: _*), countIssue(condition.copy(state = "open" ), true, allRepos: _*),
countIssue(condition.copy(state = "closed"), true, allRepos: _*), countIssue(condition.copy(state = "closed"), true, allRepos: _*),
filter match { filter match {
case "assigned" => condition.copy(assigned = Some(userName)) case "assigned" => condition.copy(assigned = Some(Some(userName)))
case "mentioned" => condition.copy(mentioned = Some(userName)) case "mentioned" => condition.copy(mentioned = Some(userName))
case _ => condition.copy(author = Some(userName)) case _ => condition.copy(author = Some(userName))
}, },

View File

@@ -73,7 +73,7 @@ class FileUploadController extends ScalatraServlet with FileUploadSupport with R
fileName fileName
} }
} }
}, FileUtil.isImage) }, FileUtil.isUploadableType)
} }
} getOrElse BadRequest } getOrElse BadRequest
} }

View File

@@ -117,7 +117,9 @@ trait IndexControllerBase extends ControllerBase {
* JSON API for checking user existence. * JSON API for checking user existence.
*/ */
post("/_user/existence")(usersOnly { post("/_user/existence")(usersOnly {
getAccountByUserName(params("userName")).isDefined getAccountByUserName(params("userName")).map { account =>
if(params.get("userOnly").isDefined) !account.isGroupAccount else true
} getOrElse false
}) })
// TODO Move to RepositoryViwerController? // TODO Move to RepositoryViwerController?

View File

@@ -202,7 +202,7 @@ trait PullRequestsControllerBase extends ControllerBase {
// close issue by commit message // close issue by commit message
if(pullreq.requestBranch == repository.repository.defaultBranch){ if(pullreq.requestBranch == repository.repository.defaultBranch){
commits.map{ commit => commits.map { commit =>
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name) closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name)
} }
} }
@@ -255,10 +255,7 @@ trait PullRequestsControllerBase extends ControllerBase {
commits.flatten.foreach { commit => commits.flatten.foreach { commit =>
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name) closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name)
} }
issue.content match { closeIssuesFromMessage(issue.title + " " + issue.content.getOrElse(""), loginAccount.userName, owner, name)
case Some(content) => closeIssuesFromMessage(content, loginAccount.userName, owner, name)
case _ =>
}
closeIssuesFromMessage(form.message, loginAccount.userName, owner, name) closeIssuesFromMessage(form.message, loginAccount.userName, owner, name)
} }
@@ -354,7 +351,15 @@ trait PullRequestsControllerBase extends ControllerBase {
originRepository.owner, originRepository.name, oldId.getName, originRepository.owner, originRepository.name, oldId.getName,
forkedRepository.owner, forkedRepository.name, newId.getName) forkedRepository.owner, forkedRepository.name, newId.getName)
val title = if(commits.flatten.length == 1){
commits.flatten.head.shortMessage
} else {
val text = forkedId.replaceAll("[\\-_]", " ")
text.substring(0, 1).toUpperCase + text.substring(1)
}
html.compare( html.compare(
title,
commits, commits,
diffs, diffs,
(forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match { (forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match {

View File

@@ -27,12 +27,26 @@ trait RepositorySettingsControllerBase extends ControllerBase {
with OwnerAuthenticator with UsersAuthenticator => with OwnerAuthenticator with UsersAuthenticator =>
// for repository options // for repository options
case class OptionsForm(repositoryName: String, description: Option[String], isPrivate: Boolean) case class OptionsForm(
repositoryName: String,
description: Option[String],
isPrivate: Boolean,
enableIssues: Boolean,
externalIssuesUrl: Option[String],
enableWiki: Boolean,
allowWikiEditing: Boolean,
externalWikiUrl: Option[String]
)
val optionsForm = mapping( val optionsForm = mapping(
"repositoryName" -> trim(label("Repository Name", text(required, maxlength(40), identifier, renameRepositoryName))), "repositoryName" -> trim(label("Repository Name" , text(required, maxlength(40), identifier, renameRepositoryName))),
"description" -> trim(label("Description" , optional(text()))), "description" -> trim(label("Description" , optional(text()))),
"isPrivate" -> trim(label("Repository Type", boolean())) "isPrivate" -> trim(label("Repository Type" , boolean())),
"enableIssues" -> trim(label("Enable Issues" , boolean())),
"externalIssuesUrl" -> trim(label("External Issues URL", optional(text(maxlength(200))))),
"enableWiki" -> trim(label("Enable Wiki" , boolean())),
"allowWikiEditing" -> trim(label("Allow Wiki Editing" , boolean())),
"externalWikiUrl" -> trim(label("External Wiki URL" , optional(text(maxlength(200)))))
)(OptionsForm.apply) )(OptionsForm.apply)
// for default branch // for default branch
@@ -92,7 +106,12 @@ trait RepositorySettingsControllerBase extends ControllerBase {
form.description, form.description,
repository.repository.parentUserName.map { _ => repository.repository.parentUserName.map { _ =>
repository.repository.isPrivate repository.repository.isPrivate
} getOrElse form.isPrivate } getOrElse form.isPrivate,
form.enableIssues,
form.externalIssuesUrl,
form.enableWiki,
form.allowWikiEditing,
form.externalWikiUrl
) )
// Change repository name // Change repository name
if(repository.name != form.repositoryName){ if(repository.name != form.repositoryName){
@@ -294,7 +313,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
* Display the danger zone. * Display the danger zone.
*/ */
get("/:owner/:repository/settings/danger")(ownerOnly { get("/:owner/:repository/settings/danger")(ownerOnly {
html.danger(_) html.danger(_, flash.get("info"))
}) })
/** /**
@@ -333,6 +352,19 @@ trait RepositorySettingsControllerBase extends ControllerBase {
redirect(s"/${repository.owner}") redirect(s"/${repository.owner}")
}) })
/**
* Run GC
*/
post("/:owner/:repository/settings/gc")(ownerOnly { repository =>
LockUtil.lock(s"${repository.owner}/${repository.name}") {
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
git.gc();
}
}
flash += "info" -> "Garbage collection has been executed."
redirect(s"/${repository.owner}/${repository.name}/settings/danger")
})
/** /**
* Provides duplication check for web hook url. * Provides duplication check for web hook url.
*/ */

View File

@@ -691,8 +691,6 @@ trait RepositoryViewerControllerBase extends ControllerBase {
private def isEditable(owner: String, repository: String, author: String)(implicit context: Context): Boolean = private def isEditable(owner: String, repository: String, author: String)(implicit context: Context): Boolean =
hasWritePermission(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName hasWritePermission(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
override protected def renderUncaughtException(e: Throwable)(implicit request: HttpServletRequest, response: HttpServletResponse): Unit = { override protected def renderUncaughtException(e: Throwable)(implicit request: HttpServletRequest, response: HttpServletResponse): Unit = {
e.printStackTrace() e.printStackTrace()
} }

View File

@@ -3,8 +3,8 @@ package gitbucket.core.controller
import java.io.FileInputStream import java.io.FileInputStream
import gitbucket.core.admin.html import gitbucket.core.admin.html
import gitbucket.core.service.{AccountService, SystemSettingsService, RepositoryService} import gitbucket.core.service.{AccountService, RepositoryService, SystemSettingsService}
import gitbucket.core.util.AdminAuthenticator import gitbucket.core.util.{AdminAuthenticator, Mailer}
import gitbucket.core.ssh.SshServer import gitbucket.core.ssh.SshServer
import gitbucket.core.plugin.PluginRegistry import gitbucket.core.plugin.PluginRegistry
import SystemSettingsService._ import SystemSettingsService._
@@ -13,7 +13,8 @@ import gitbucket.core.util.ControlUtil._
import gitbucket.core.util.Directory._ import gitbucket.core.util.Directory._
import gitbucket.core.util.StringUtil._ import gitbucket.core.util.StringUtil._
import io.github.gitbucket.scalatra.forms._ import io.github.gitbucket.scalatra.forms._
import org.apache.commons.io.{IOUtils, FileUtils} import org.apache.commons.io.{FileUtils, IOUtils}
import org.apache.commons.mail.{DefaultAuthenticator, HtmlEmail}
import org.scalatra.i18n.Messages import org.scalatra.i18n.Messages
class SystemSettingsController extends SystemSettingsControllerBase class SystemSettingsController extends SystemSettingsControllerBase
@@ -70,11 +71,20 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
).flatten ).flatten
} }
private val pluginForm = mapping( private val sendMailForm = mapping(
"pluginId" -> list(trim(label("", text()))) "smtp" -> mapping(
)(PluginForm.apply) "host" -> trim(label("SMTP Host", text(required))),
"port" -> trim(label("SMTP Port", optional(number()))),
"user" -> trim(label("SMTP User", optional(text()))),
"password" -> trim(label("SMTP Password", optional(text()))),
"ssl" -> trim(label("Enable SSL", optional(boolean()))),
"fromAddress" -> trim(label("FROM Address", optional(text()))),
"fromName" -> trim(label("FROM Name", optional(text())))
)(Smtp.apply),
"testAddress" -> trim(label("", text(required)))
)(SendMailForm.apply)
case class PluginForm(pluginIds: List[String]) case class SendMailForm(smtp: Smtp, testAddress: String)
case class DataExportForm(tableNames: List[String]) case class DataExportForm(tableNames: List[String])
@@ -152,6 +162,18 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
redirect("/admin/system") redirect("/admin/system")
}) })
post("/admin/system/sendmail", sendMailForm)(adminOnly { form =>
try {
new Mailer(form.smtp).send(form.testAddress,
"Test message from GitBucket", "This is a test message from GitBucket.")
"Test mail has been sent to: " + form.testAddress
} catch {
case e: Exception => "[Error] " + e.toString
}
})
get("/admin/plugins")(adminOnly { get("/admin/plugins")(adminOnly {
html.plugins(PluginRegistry().getPlugins()) html.plugins(PluginRegistry().getPlugins())
}) })
@@ -179,36 +201,39 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
get("/admin/users/:userName/_edituser")(adminOnly { get("/admin/users/:userName/_edituser")(adminOnly {
val userName = params("userName") val userName = params("userName")
html.user(getAccountByUserName(userName, true)) html.user(getAccountByUserName(userName, true), flash.get("error"))
}) })
post("/admin/users/:name/_edituser", editUserForm)(adminOnly { form => post("/admin/users/:name/_edituser", editUserForm)(adminOnly { form =>
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName, true).map { account => getAccountByUserName(userName, true).map { account =>
if(account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(account)){
flash += "error" -> "Account can't be turned off because this is last one administrator."
redirect(s"/admin/users/${userName}/_edituser")
} else {
if(form.isRemoved){
// Remove repositories
// getRepositoryNamesOfUser(userName).foreach { repositoryName =>
// deleteRepository(userName, repositoryName)
// FileUtils.deleteDirectory(getRepositoryDir(userName, repositoryName))
// FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
// }
// Remove from GROUP_MEMBER, COLLABORATOR and REPOSITORY
removeUserRelatedData(userName)
}
if(form.isRemoved){ updateAccount(account.copy(
// Remove repositories password = form.password.map(sha1).getOrElse(account.password),
// getRepositoryNamesOfUser(userName).foreach { repositoryName => fullName = form.fullName,
// deleteRepository(userName, repositoryName) mailAddress = form.mailAddress,
// FileUtils.deleteDirectory(getRepositoryDir(userName, repositoryName)) isAdmin = form.isAdmin,
// FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName)) url = form.url,
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName)) isRemoved = form.isRemoved))
// }
// Remove from GROUP_MEMBER, COLLABORATOR and REPOSITORY updateImage(userName, form.fileId, form.clearImage)
removeUserRelatedData(userName) redirect("/admin/users")
} }
updateAccount(account.copy(
password = form.password.map(sha1).getOrElse(account.password),
fullName = form.fullName,
mailAddress = form.mailAddress,
isAdmin = form.isAdmin,
url = form.url,
isRemoved = form.isRemoved))
updateImage(userName, form.fileId, form.clearImage)
redirect("/admin/users")
} getOrElse NotFound } getOrElse NotFound
}) })

View File

@@ -1,7 +1,8 @@
package gitbucket.core.controller package gitbucket.core.controller
import gitbucket.core.service.RepositoryService.RepositoryInfo
import gitbucket.core.wiki.html import gitbucket.core.wiki.html
import gitbucket.core.service.{RepositoryService, WikiService, ActivityService, AccountService} import gitbucket.core.service.{AccountService, ActivityService, RepositoryService, WikiService}
import gitbucket.core.util._ import gitbucket.core.util._
import gitbucket.core.util.StringUtil._ import gitbucket.core.util.StringUtil._
import gitbucket.core.util.ControlUtil._ import gitbucket.core.util.ControlUtil._
@@ -39,7 +40,7 @@ trait WikiControllerBase extends ControllerBase {
get("/:owner/:repository/wiki")(referrersOnly { repository => get("/:owner/:repository/wiki")(referrersOnly { repository =>
getWikiPage(repository.owner, repository.name, "Home").map { page => getWikiPage(repository.owner, repository.name, "Home").map { page =>
html.page("Home", page, getWikiPageList(repository.owner, repository.name), html.page("Home", page, getWikiPageList(repository.owner, repository.name),
repository, hasWritePermission(repository.owner, repository.name, context.loginAccount), repository, isEditable(repository),
getWikiPage(repository.owner, repository.name, "_Sidebar"), getWikiPage(repository.owner, repository.name, "_Sidebar"),
getWikiPage(repository.owner, repository.name, "_Footer")) getWikiPage(repository.owner, repository.name, "_Footer"))
} getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/Home/_edit") } getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/Home/_edit")
@@ -50,7 +51,7 @@ trait WikiControllerBase extends ControllerBase {
getWikiPage(repository.owner, repository.name, pageName).map { page => getWikiPage(repository.owner, repository.name, pageName).map { page =>
html.page(pageName, page, getWikiPageList(repository.owner, repository.name), html.page(pageName, page, getWikiPageList(repository.owner, repository.name),
repository, hasWritePermission(repository.owner, repository.name, context.loginAccount), repository, isEditable(repository),
getWikiPage(repository.owner, repository.name, "_Sidebar"), getWikiPage(repository.owner, repository.name, "_Sidebar"),
getWikiPage(repository.owner, repository.name, "_Footer")) getWikiPage(repository.owner, repository.name, "_Footer"))
} getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_edit") } getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_edit")
@@ -62,7 +63,7 @@ trait WikiControllerBase extends ControllerBase {
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match { JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match {
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository) case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository)
case Left(_) => NotFound case Left(_) => NotFound()
} }
} }
}) })
@@ -73,7 +74,7 @@ trait WikiControllerBase extends ControllerBase {
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, from, to, true).filter(_.newPath == pageName + ".md"), repository, html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, from, to, true).filter(_.newPath == pageName + ".md"), repository,
hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info")) isEditable(repository), flash.get("info"))
} }
}) })
@@ -82,102 +83,115 @@ trait WikiControllerBase extends ControllerBase {
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
html.compare(None, from, to, JGitUtil.getDiffs(git, from, to, true), repository, html.compare(None, from, to, JGitUtil.getDiffs(git, from, to, true), repository,
hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info")) isEditable(repository), flash.get("info"))
} }
}) })
get("/:owner/:repository/wiki/:page/_revert/:commitId")(collaboratorsOnly { repository => get("/:owner/:repository/wiki/:page/_revert/:commitId")(referrersOnly { repository =>
val pageName = StringUtil.urlDecode(params("page")) if(isEditable(repository)){
val Array(from, to) = params("commitId").split("\\.\\.\\.") val pageName = StringUtil.urlDecode(params("page"))
val Array(from, to) = params("commitId").split("\\.\\.\\.")
if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))){ if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))){
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}") redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
} else {
flash += "info" -> "This patch was not able to be reversed."
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}")
}
})
get("/:owner/:repository/wiki/_revert/:commitId")(collaboratorsOnly { repository =>
val Array(from, to) = params("commitId").split("\\.\\.\\.")
if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)){
redirect(s"/${repository.owner}/${repository.name}/wiki/")
} else {
flash += "info" -> "This patch was not able to be reversed."
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
}
})
get("/:owner/:repository/wiki/:page/_edit")(collaboratorsOnly { repository =>
val pageName = StringUtil.urlDecode(params("page"))
html.edit(pageName, getWikiPage(repository.owner, repository.name, pageName), repository)
})
post("/:owner/:repository/wiki/_edit", editForm)(collaboratorsOnly { (form, repository) =>
defining(context.loginAccount.get){ loginAccount =>
saveWikiPage(
repository.owner,
repository.name,
form.currentPageName,
form.pageName,
appendNewLine(convertLineSeparator(form.content, "LF"), "LF"),
loginAccount,
form.message.getOrElse(""),
Some(form.id)
).map { commitId =>
updateLastActivityDate(repository.owner, repository.name)
recordEditWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName, commitId)
}
if(notReservedPageName(form.pageName)) {
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
} else { } else {
redirect(s"/${repository.owner}/${repository.name}/wiki") flash += "info" -> "This patch was not able to be reversed."
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}")
} }
} } else Unauthorized()
})
get("/:owner/:repository/wiki/_revert/:commitId")(referrersOnly { repository =>
if(isEditable(repository)){
val Array(from, to) = params("commitId").split("\\.\\.\\.")
if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)){
redirect(s"/${repository.owner}/${repository.name}/wiki/")
} else {
flash += "info" -> "This patch was not able to be reversed."
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
}
} else Unauthorized()
})
get("/:owner/:repository/wiki/:page/_edit")(referrersOnly { repository =>
if(isEditable(repository)){
val pageName = StringUtil.urlDecode(params("page"))
html.edit(pageName, getWikiPage(repository.owner, repository.name, pageName), repository)
} else Unauthorized()
}) })
get("/:owner/:repository/wiki/_new")(collaboratorsOnly { post("/:owner/:repository/wiki/_edit", editForm)(referrersOnly { (form, repository) =>
html.edit("", None, _) if(isEditable(repository)){
defining(context.loginAccount.get){ loginAccount =>
saveWikiPage(
repository.owner,
repository.name,
form.currentPageName,
form.pageName,
appendNewLine(convertLineSeparator(form.content, "LF"), "LF"),
loginAccount,
form.message.getOrElse(""),
Some(form.id)
).map { commitId =>
updateLastActivityDate(repository.owner, repository.name)
recordEditWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName, commitId)
}
if(notReservedPageName(form.pageName)) {
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
} else {
redirect(s"/${repository.owner}/${repository.name}/wiki")
}
}
} else Unauthorized()
}) })
post("/:owner/:repository/wiki/_new", newForm)(collaboratorsOnly { (form, repository) => get("/:owner/:repository/wiki/_new")(referrersOnly { repository =>
defining(context.loginAccount.get){ loginAccount => if(isEditable(repository)){
saveWikiPage(repository.owner, repository.name, form.currentPageName, form.pageName, html.edit("", None, repository)
} else Unauthorized()
})
post("/:owner/:repository/wiki/_new", newForm)(referrersOnly { (form, repository) =>
if(isEditable(repository)){
defining(context.loginAccount.get){ loginAccount =>
saveWikiPage(repository.owner, repository.name, form.currentPageName, form.pageName,
form.content, loginAccount, form.message.getOrElse(""), None) form.content, loginAccount, form.message.getOrElse(""), None)
updateLastActivityDate(repository.owner, repository.name) updateLastActivityDate(repository.owner, repository.name)
recordCreateWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName) recordCreateWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName)
if(notReservedPageName(form.pageName)) { if(notReservedPageName(form.pageName)) {
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}") redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
} else { } else {
redirect(s"/${repository.owner}/${repository.name}/wiki") redirect(s"/${repository.owner}/${repository.name}/wiki")
}
} }
} } else Unauthorized()
}) })
get("/:owner/:repository/wiki/:page/_delete")(collaboratorsOnly { repository => get("/:owner/:repository/wiki/:page/_delete")(referrersOnly { repository =>
val pageName = StringUtil.urlDecode(params("page")) if(isEditable(repository)){
val pageName = StringUtil.urlDecode(params("page"))
defining(context.loginAccount.get){ loginAccount => defining(context.loginAccount.get){ loginAccount =>
deleteWikiPage(repository.owner, repository.name, pageName, loginAccount.fullName, loginAccount.mailAddress, s"Destroyed ${pageName}") deleteWikiPage(repository.owner, repository.name, pageName, loginAccount.fullName, loginAccount.mailAddress, s"Destroyed ${pageName}")
updateLastActivityDate(repository.owner, repository.name) updateLastActivityDate(repository.owner, repository.name)
redirect(s"/${repository.owner}/${repository.name}/wiki") redirect(s"/${repository.owner}/${repository.name}/wiki")
} }
} else Unauthorized()
}) })
get("/:owner/:repository/wiki/_pages")(referrersOnly { repository => get("/:owner/:repository/wiki/_pages")(referrersOnly { repository =>
html.pages(getWikiPageList(repository.owner, repository.name), repository, html.pages(getWikiPageList(repository.owner, repository.name), repository, isEditable(repository))
hasWritePermission(repository.owner, repository.name, context.loginAccount))
}) })
get("/:owner/:repository/wiki/_history")(referrersOnly { repository => get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
JGitUtil.getCommitLog(git, "master") match { JGitUtil.getCommitLog(git, "master") match {
case Right((logs, hasNext)) => html.history(None, logs, repository) case Right((logs, hasNext)) => html.history(None, logs, repository)
case Left(_) => NotFound case Left(_) => NotFound()
} }
} }
}) })
@@ -226,4 +240,9 @@ trait WikiControllerBase extends ControllerBase {
private def targetWikiPage = getWikiPage(params("owner"), params("repository"), params("pageName")) private def targetWikiPage = getWikiPage(params("owner"), params("repository"), params("pageName"))
private def isEditable(repository: RepositoryInfo)(implicit context: Context): Boolean =
repository.repository.allowWikiEditing || (
hasWritePermission(repository.owner, repository.name, context.loginAccount)
)
} }

View File

@@ -17,7 +17,14 @@ trait RepositoryComponent extends TemplateComponent { self: Profile =>
val originRepositoryName = column[String]("ORIGIN_REPOSITORY_NAME") val originRepositoryName = column[String]("ORIGIN_REPOSITORY_NAME")
val parentUserName = column[String]("PARENT_USER_NAME") val parentUserName = column[String]("PARENT_USER_NAME")
val parentRepositoryName = column[String]("PARENT_REPOSITORY_NAME") val parentRepositoryName = column[String]("PARENT_REPOSITORY_NAME")
def * = (userName, repositoryName, isPrivate, description.?, defaultBranch, registeredDate, updatedDate, lastActivityDate, originUserName.?, originRepositoryName.?, parentUserName.?, parentRepositoryName.?) <> (Repository.tupled, Repository.unapply) val enableIssues = column[Boolean]("ENABLE_ISSUES")
val externalIssuesUrl = column[String]("EXTERNAL_ISSUES_URL")
val enableWiki = column[Boolean]("ENABLE_WIKI")
val allowWikiEditing = column[Boolean]("ALLOW_WIKI_EDITING")
val externalWikiUrl = column[String]("EXTERNAL_WIKI_URL")
def * = (userName, repositoryName, isPrivate, description.?, defaultBranch,
registeredDate, updatedDate, lastActivityDate, originUserName.?, originRepositoryName.?, parentUserName.?, parentRepositoryName.?,
enableIssues, externalIssuesUrl.?, enableWiki, allowWikiEditing, externalWikiUrl.?) <> (Repository.tupled, Repository.unapply)
def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository) def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
} }
@@ -35,5 +42,10 @@ case class Repository(
originUserName: Option[String], originUserName: Option[String],
originRepositoryName: Option[String], originRepositoryName: Option[String],
parentUserName: Option[String], parentUserName: Option[String],
parentRepositoryName: Option[String] parentRepositoryName: Option[String],
enableIssues: Boolean,
externalIssuesUrl: Option[String],
enableWiki: Boolean,
allowWikiEditing: Boolean,
externalWikiUrl: Option[String]
) )

View File

@@ -97,6 +97,12 @@ trait AccountService {
Accounts filter (_.removed === false.bind) sortBy(_.userName) list Accounts filter (_.removed === false.bind) sortBy(_.userName) list
} }
def isLastAdministrator(account: Account)(implicit s: Session): Boolean = {
if(account.isAdmin){
(Accounts filter (_.removed === false.bind) filter (_.isAdmin === true.bind) map (_.userName.length)).first == 1
} else false
}
def createAccount(userName: String, password: String, fullName: String, mailAddress: String, isAdmin: Boolean, url: Option[String]) def createAccount(userName: String, password: String, fullName: String, mailAddress: String, isAdmin: Boolean, url: Option[String])
(implicit s: Session): Unit = (implicit s: Session): Unit =
Accounts insert Account( Accounts insert Account(

View File

@@ -18,7 +18,7 @@ trait IssuesService {
import IssuesService._ import IssuesService._
def getIssue(owner: String, repository: String, issueId: String)(implicit s: Session) = def getIssue(owner: String, repository: String, issueId: String)(implicit s: Session) =
if (issueId forall (_.isDigit)) if (isInteger(issueId))
Issues filter (_.byPrimaryKey(owner, repository, issueId.toInt)) firstOption Issues filter (_.byPrimaryKey(owner, repository, issueId.toInt)) firstOption
else None else None
@@ -234,9 +234,8 @@ trait IssuesService {
.map { case (owner, repository) => t1.byRepository(owner, repository) } .map { case (owner, repository) => t1.byRepository(owner, repository) }
.foldLeft[Column[Boolean]](false) ( _ || _ ) && .foldLeft[Column[Boolean]](false) ( _ || _ ) &&
(t1.closed === (condition.state == "closed").bind) && (t1.closed === (condition.state == "closed").bind) &&
//(t1.milestoneId === condition.milestoneId.get.get.bind, condition.milestoneId.flatten.isDefined) && (t1.milestoneId.? isEmpty, condition.milestone == Some(None)) &&
(t1.milestoneId.? isEmpty, condition.milestone == Some(None)) && (t1.assignedUserName.? isEmpty, condition.assigned == Some(None)) &&
(t1.assignedUserName === condition.assigned.get.bind, condition.assigned.isDefined) &&
(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) && (t1.openedUserName === condition.author.get.bind, condition.author.isDefined) &&
(t1.pullRequest === pullRequest.bind) && (t1.pullRequest === pullRequest.bind) &&
// Milestone filter // Milestone filter
@@ -244,6 +243,8 @@ trait IssuesService {
(t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) && (t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) &&
(t2.title === condition.milestone.get.get.bind) (t2.title === condition.milestone.get.get.bind)
} exists, condition.milestone.flatten.isDefined) && } exists, condition.milestone.flatten.isDefined) &&
// Assignee filter
(t1.assignedUserName === condition.assigned.get.get.bind, condition.assigned.flatten.isDefined) &&
// Label filter // Label filter
(IssueLabels filter { t2 => (IssueLabels filter { t2 =>
(t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) && (t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) &&
@@ -447,7 +448,7 @@ object IssuesService {
labels: Set[String] = Set.empty, labels: Set[String] = Set.empty,
milestone: Option[Option[String]] = None, milestone: Option[Option[String]] = None,
author: Option[String] = None, author: Option[String] = None,
assigned: Option[String] = None, assigned: Option[Option[String]] = None,
mentioned: Option[String] = None, mentioned: Option[String] = None,
state: String = "open", state: String = "open",
sort: String = "created", sort: String = "created",
@@ -491,12 +492,15 @@ object IssuesService {
def toURL: String = def toURL: String =
"?" + List( "?" + List(
if(labels.isEmpty) None else Some("labels=" + urlEncode(labels.mkString(","))), if(labels.isEmpty) None else Some("labels=" + urlEncode(labels.mkString(","))),
milestone.map { _ match { milestone.map {
case Some(x) => "milestone=" + urlEncode(x) case Some(x) => "milestone=" + urlEncode(x)
case None => "milestone=none" case None => "milestone=none"
}}, },
author .map(x => "author=" + urlEncode(x)), author .map(x => "author=" + urlEncode(x)),
assigned .map(x => "assigned=" + urlEncode(x)), assigned.map {
case Some(x) => "assigned=" + urlEncode(x)
case None => "assigned=none"
},
mentioned.map(x => "mentioned=" + urlEncode(x)), mentioned.map(x => "mentioned=" + urlEncode(x)),
Some("state=" + urlEncode(state)), Some("state=" + urlEncode(state)),
Some("sort=" + urlEncode(sort)), Some("sort=" + urlEncode(sort)),
@@ -541,10 +545,14 @@ object IssuesService {
conditions.get("milestone").flatMap(_.headOption) match { conditions.get("milestone").flatMap(_.headOption) match {
case None => None case None => None
case Some("none") => Some(None) case Some("none") => Some(None)
case Some(x) => Some(Some(x)) //milestones.get(x).map(x => Some(x)) case Some(x) => Some(Some(x))
}, },
conditions.get("author").flatMap(_.headOption), conditions.get("author").flatMap(_.headOption),
conditions.get("assignee").flatMap(_.headOption), conditions.get("assignee").flatMap(_.headOption) match {
case None => None
case Some("none") => Some(None)
case Some(x) => Some(Some(x))
},
conditions.get("mentions").flatMap(_.headOption), conditions.get("mentions").flatMap(_.headOption),
conditions.get("is").getOrElse(Seq.empty).find(x => x == "open" || x == "closed").getOrElse("open"), conditions.get("is").getOrElse(Seq.empty).find(x => x == "open" || x == "closed").getOrElse("open"),
sort, sort,
@@ -565,7 +573,10 @@ object IssuesService {
case x => Some(x) case x => Some(x)
}, },
param(request, "author"), param(request, "author"),
param(request, "assigned"), param(request, "assigned").map {
case "none" => None
case x => Some(x)
},
param(request, "mentioned"), param(request, "mentioned"),
param(request, "state", Seq("open", "closed")).getOrElse("open"), param(request, "state", Seq("open", "closed")).getOrElse("open"),
param(request, "sort", Seq("created", "comments", "updated")).getOrElse("created"), param(request, "sort", Seq("created", "comments", "updated")).getOrElse("created"),

View File

@@ -36,7 +36,13 @@ trait RepositoryService { self: AccountService =>
originUserName = originUserName, originUserName = originUserName,
originRepositoryName = originRepositoryName, originRepositoryName = originRepositoryName,
parentUserName = parentUserName, parentUserName = parentUserName,
parentRepositoryName = parentRepositoryName) parentRepositoryName = parentRepositoryName,
enableIssues = true,
externalIssuesUrl = None,
enableWiki = true,
allowWikiEditing = true,
externalWikiUrl = None
)
IssueId insert (userName, repositoryName, 0) IssueId insert (userName, repositoryName, 0)
} }
@@ -222,7 +228,7 @@ trait RepositoryService { self: AccountService =>
* Include public repository, private own repository and private but collaborator repository. * Include public repository, private own repository and private but collaborator repository.
* *
* @param userName the user name of collaborator * @param userName the user name of collaborator
* @return the repository infomation list * @return the repository information list
*/ */
def getAllRepositories(userName: String)(implicit s: Session): List[(String, String)] = { def getAllRepositories(userName: String)(implicit s: Session): List[(String, String)] = {
Repositories.filter { t1 => Repositories.filter { t1 =>
@@ -313,10 +319,12 @@ trait RepositoryService { self: AccountService =>
* Save repository options. * Save repository options.
*/ */
def saveRepositoryOptions(userName: String, repositoryName: String, def saveRepositoryOptions(userName: String, repositoryName: String,
description: Option[String], isPrivate: Boolean)(implicit s: Session): Unit = description: Option[String], isPrivate: Boolean,
enableIssues: Boolean, externalIssuesUrl: Option[String],
enableWiki: Boolean, allowWikiEditing: Boolean, externalWikiUrl: Option[String])(implicit s: Session): Unit =
Repositories.filter(_.byRepository(userName, repositoryName)) Repositories.filter(_.byRepository(userName, repositoryName))
.map { r => (r.description.?, r.isPrivate, r.updatedDate) } .map { r => (r.description.?, r.isPrivate, r.enableIssues, r.externalIssuesUrl.?, r.enableWiki, r.allowWikiEditing, r.externalWikiUrl.?, r.updatedDate) }
.update (description, isPrivate, currentDate) .update (description, isPrivate, enableIssues, externalIssuesUrl, enableWiki, allowWikiEditing, externalWikiUrl, currentDate)
def saveRepositoryDefaultBranch(userName: String, repositoryName: String, def saveRepositoryDefaultBranch(userName: String, repositoryName: String,
defaultBranch: String)(implicit s: Session): Unit = defaultBranch: String)(implicit s: Session): Unit =
@@ -417,9 +425,7 @@ object RepositoryService {
def httpUrl(owner: String, name: String)(implicit context: Context): String = s"${context.baseUrl}/git/${owner}/${name}.git" def httpUrl(owner: String, name: String)(implicit context: Context): String = s"${context.baseUrl}/git/${owner}/${name}.git"
def sshUrl(owner: String, name: String)(implicit context: Context): Option[String] = def sshUrl(owner: String, name: String)(implicit context: Context): Option[String] =
if(context.settings.ssh){ if(context.settings.ssh){
context.loginAccount.flatMap { loginAccount => context.settings.sshAddress.map { x => s"ssh://${x.genericUser}@${x.host}:${x.port}/${owner}/${name}.git" }
context.settings.sshAddress.map { x => s"ssh://${loginAccount.userName}@${x.host}:${x.port}/${owner}/${name}.git" }
}
} else None } else None
def openRepoUrl(openUrl: String)(implicit context: Context): String = s"github-${context.platform}://openRepo/${openUrl}" def openRepoUrl(openUrl: String)(implicit context: Context): String = s"github-${context.platform}://openRepo/${openUrl}"

View File

@@ -12,6 +12,9 @@ trait SshKeyService {
def getPublicKeys(userName: String)(implicit s: Session): List[SshKey] = def getPublicKeys(userName: String)(implicit s: Session): List[SshKey] =
SshKeys.filter(_.userName === userName.bind).sortBy(_.sshKeyId).list SshKeys.filter(_.userName === userName.bind).sortBy(_.sshKeyId).list
def getAllKeys()(implicit s: Session): List[SshKey] =
SshKeys.filter(_.publicKey.trim =!= "").list
def deletePublicKey(userName: String, sshKeyId: Int)(implicit s: Session): Unit = def deletePublicKey(userName: String, sshKeyId: Int)(implicit s: Session): Unit =
SshKeys filter (_.byPrimaryKey(userName, sshKeyId)) delete SshKeys filter (_.byPrimaryKey(userName, sshKeyId)) delete

View File

@@ -141,7 +141,11 @@ object SystemSettingsService {
for { for {
host <- sshHost if ssh host <- sshHost if ssh
} }
yield SshAddress(host, sshPort.getOrElse(DefaultSshPort)) yield SshAddress(
host,
sshPort.getOrElse(DefaultSshPort),
"git"
)
} }
case class Ldap( case class Ldap(
@@ -169,7 +173,8 @@ object SystemSettingsService {
case class SshAddress( case class SshAddress(
host:String, host:String,
port:Int) port:Int,
genericUser:String)
val DefaultSshPort = 29418 val DefaultSshPort = 29418
val DefaultSmtpPort = 25 val DefaultSmtpPort = 25

View File

@@ -97,7 +97,7 @@ trait WebHookService {
} }
} }
try{ try{
val httpClient = HttpClientBuilder.create.addInterceptorLast(itcp).build val httpClient = HttpClientBuilder.create.useSystemProperties.addInterceptorLast(itcp).build
logger.debug(s"start web hook invocation for ${webHook.url}") logger.debug(s"start web hook invocation for ${webHook.url}")
val httpPost = new HttpPost(webHook.url) val httpPost = new HttpPost(webHook.url)
logger.info(s"Content-Type: ${webHook.ctype.ctype}") logger.info(s"Content-Type: ${webHook.ctype.ctype}")
@@ -119,7 +119,7 @@ trait WebHookService {
} }
case WebHookContentType.JSON => { case WebHookContentType.JSON => {
httpPost.setEntity(EntityBuilder.create().setText(json).build()) httpPost.setEntity(EntityBuilder.create().setText(json).build())
if (!webHook.token.isEmpty) { if (webHook.token.exists(_.trim.nonEmpty)) {
httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.orNull, json.getBytes("UTF-8"))) httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.orNull, json.getBytes("UTF-8")))
} }
} }

View File

@@ -5,7 +5,8 @@ import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
import gitbucket.core.service.{RepositoryService, AccountService, SystemSettingsService} import gitbucket.core.service.{RepositoryService, AccountService, SystemSettingsService}
import gitbucket.core.servlet.{Database, CommitLogHook} import gitbucket.core.servlet.{Database, CommitLogHook}
import gitbucket.core.util.{Directory, ControlUtil} import gitbucket.core.util.{Directory, ControlUtil}
import org.apache.sshd.server.{CommandFactory, Environment, ExitCallback, Command} import org.apache.sshd.server.{CommandFactory, Environment, ExitCallback, Command, SessionAware}
import org.apache.sshd.server.session.ServerSession
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.{File, InputStream, OutputStream} import java.io.{File, InputStream, OutputStream}
import ControlUtil._ import ControlUtil._
@@ -20,37 +21,44 @@ object GitCommand {
val SimpleCommandRegex = """\Agit-(upload|receive)-pack '/(.+\.git)'\Z""".r val SimpleCommandRegex = """\Agit-(upload|receive)-pack '/(.+\.git)'\Z""".r
} }
abstract class GitCommand() extends Command { abstract class GitCommand extends Command with SessionAware {
private val logger = LoggerFactory.getLogger(classOf[GitCommand]) private val logger = LoggerFactory.getLogger(classOf[GitCommand])
@volatile protected var err: OutputStream = null @volatile protected var err: OutputStream = null
@volatile protected var in: InputStream = null @volatile protected var in: InputStream = null
@volatile protected var out: OutputStream = null @volatile protected var out: OutputStream = null
@volatile protected var callback: ExitCallback = null @volatile protected var callback: ExitCallback = null
@volatile private var authUser:Option[String] = None
protected def runTask(user: String)(implicit session: Session): Unit protected def runTask(authUser: String)(implicit session: Session): Unit
private def newTask(user: String): Runnable = new Runnable { private def newTask(): Runnable = new Runnable {
override def run(): Unit = { override def run(): Unit = {
Database() withSession { implicit session => authUser match {
try { case Some(authUser) =>
runTask(user) Database() withSession { implicit session =>
callback.onExit(0) try {
} catch { runTask(authUser)
case e: RepositoryNotFoundException => callback.onExit(0)
logger.info(e.getMessage) } catch {
callback.onExit(1, "Repository Not Found") case e: RepositoryNotFoundException =>
case e: Throwable => logger.info(e.getMessage)
logger.error(e.getMessage, e) callback.onExit(1, "Repository Not Found")
callback.onExit(1) case e: Throwable =>
} logger.error(e.getMessage, e)
callback.onExit(1)
}
}
case None =>
val message = "User not authenticated"
logger.error(message)
callback.onExit(1, message)
} }
} }
} }
override def start(env: Environment): Unit = { final override def start(env: Environment): Unit = {
val user = env.getEnv.get("USER") val thread = new Thread(newTask())
val thread = new Thread(newTask(user))
thread.start() thread.start()
} }
@@ -72,6 +80,10 @@ abstract class GitCommand() extends Command {
this.in = in this.in = in
} }
override def setSession(serverSession:ServerSession) {
this.authUser = PublicKeyAuthenticator.getUserName(serverSession)
}
} }
abstract class DefaultGitCommand(val owner: String, val repoName: String) extends GitCommand { abstract class DefaultGitCommand(val owner: String, val repoName: String) extends GitCommand {

View File

@@ -15,7 +15,6 @@ class NoShell(sshAddress:SshAddress) extends Factory[Command] {
private var callback: ExitCallback = null private var callback: ExitCallback = null
override def start(env: Environment): Unit = { override def start(env: Environment): Unit = {
val user = env.getEnv.get("USER")
val message = val message =
""" """
| Welcome to | Welcome to
@@ -32,7 +31,7 @@ class NoShell(sshAddress:SshAddress) extends Factory[Command] {
| Please use: | Please use:
| |
| git clone ssh://%s@%s:%d/OWNER/REPOSITORY_NAME.git | git clone ssh://%s@%s:%d/OWNER/REPOSITORY_NAME.git
""".stripMargin.format(user, sshAddress.host, sshAddress.port).replace("\n", "\r\n") + "\r\n" """.stripMargin.format(sshAddress.genericUser, sshAddress.host, sshAddress.port).replace("\n", "\r\n") + "\r\n"
err.write(Constants.encode(message)) err.write(Constants.encode(message))
err.flush() err.flush()
in.close() in.close()

View File

@@ -2,22 +2,73 @@ package gitbucket.core.ssh
import java.security.PublicKey import java.security.PublicKey
import gitbucket.core.model.SshKey
import gitbucket.core.service.SshKeyService import gitbucket.core.service.SshKeyService
import gitbucket.core.servlet.Database import gitbucket.core.servlet.Database
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator
import org.apache.sshd.server.session.ServerSession import org.apache.sshd.server.session.ServerSession
import org.apache.sshd.common.session.Session
import org.slf4j.LoggerFactory
class PublicKeyAuthenticator extends PublickeyAuthenticator with SshKeyService { object PublicKeyAuthenticator {
// put in the ServerSession here to be read by GitCommand later
private val userNameSessionKey = new Session.AttributeKey[String]
override def authenticate(username: String, key: PublicKey, session: ServerSession): Boolean = { def putUserName(serverSession:ServerSession, userName:String):Unit =
Database() withSession { implicit session => serverSession.setAttribute(userNameSessionKey, userName)
getPublicKeys(username).exists { sshKey =>
SshUtil.str2PublicKey(sshKey.publicKey) match { def getUserName(serverSession:ServerSession):Option[String] =
case Some(publicKey) => key.equals(publicKey) Option(serverSession.getAttribute(userNameSessionKey))
case _ => false }
}
} class PublicKeyAuthenticator(genericUser:String) extends PublickeyAuthenticator with SshKeyService {
private val logger = LoggerFactory.getLogger(classOf[PublicKeyAuthenticator])
override def authenticate(username: String, key: PublicKey, session: ServerSession): Boolean =
if (username == genericUser) authenticateGenericUser(username, key, session, genericUser)
else authenticateLoginUser(username, key, session)
private def authenticateLoginUser(username: String, key: PublicKey, session: ServerSession): Boolean = {
val authenticated =
Database()
.withSession { implicit dbSession => getPublicKeys(username) }
.map(_.publicKey)
.flatMap(SshUtil.str2PublicKey)
.contains(key)
if (authenticated) {
logger.info(s"authentication as ssh user ${username} succeeded")
PublicKeyAuthenticator.putUserName(session, username)
} }
else {
logger.info(s"authentication as ssh user ${username} failed")
}
authenticated
} }
private def authenticateGenericUser(username: String, key: PublicKey, session: ServerSession, genericUser:String): Boolean = {
// find all users having the key we got from ssh
val possibleUserNames =
Database()
.withSession { implicit dbSession => getAllKeys() }
.filter { sshKey =>
SshUtil.str2PublicKey(sshKey.publicKey).exists(_ == key)
}
.map(_.userName)
.distinct
// determine the user - if different accounts share the same key, tough luck
val uniqueUserName =
possibleUserNames match {
case List() =>
logger.info(s"authentication as generic user ${genericUser} failed, public key not found")
None
case List(name) =>
logger.info(s"authentication as generic user ${genericUser} succeeded, identified ${name}")
Some(name)
case _ =>
logger.info(s"authentication as generic user ${genericUser} failed, public key is ambiguous")
None
}
uniqueUserName.foreach(PublicKeyAuthenticator.putUserName(session, _))
uniqueUserName.isDefined
}
} }

View File

@@ -21,7 +21,7 @@ object SshServer {
provider.setAlgorithm("RSA") provider.setAlgorithm("RSA")
provider.setOverwriteAllowed(false) provider.setOverwriteAllowed(false)
server.setKeyPairProvider(provider) server.setKeyPairProvider(provider)
server.setPublickeyAuthenticator(new PublicKeyAuthenticator) server.setPublickeyAuthenticator(new PublicKeyAuthenticator(sshAddress.genericUser))
server.setCommandFactory(new GitCommandFactory(baseUrl)) server.setCommandFactory(new GitCommandFactory(baseUrl))
server.setShellFactory(new NoShell(sshAddress)) server.setShellFactory(new NoShell(sshAddress))
} }

View File

@@ -31,9 +31,7 @@ object SshUtil {
} }
} }
def fingerPrint(key: String): Option[String] = str2PublicKey(key) match { def fingerPrint(key: String): Option[String] =
case Some(publicKey) => Some(KeyUtils.getFingerPrint(publicKey)) str2PublicKey(key) map KeyUtils.getFingerPrint
case None => None
}
} }

View File

@@ -896,17 +896,13 @@ object JGitUtil {
git.branchList.call.asScala.map { ref => git.branchList.call.asScala.map { ref =>
val walk = new RevWalk(repo) val walk = new RevWalk(repo)
try { try {
val defaultCommit = walk.parseCommit(defaultObject) val defaultCommit = walk.parseCommit(defaultObject)
val branchName = ref.getName.stripPrefix("refs/heads/") val branchName = ref.getName.stripPrefix("refs/heads/")
val branchCommit = if(branchName == defaultBranch){ val branchCommit = walk.parseCommit(ref.getObjectId)
defaultCommit val when = branchCommit.getCommitterIdent.getWhen
} else { val committer = branchCommit.getCommitterIdent.getName
walk.parseCommit(ref.getObjectId)
}
val when = branchCommit.getCommitterIdent.getWhen
val committer = branchCommit.getCommitterIdent.getName
val committerEmail = branchCommit.getCommitterIdent.getEmailAddress val committerEmail = branchCommit.getCommitterIdent.getEmailAddress
val mergeInfo = if(origin && branchName == defaultBranch){ val mergeInfo = if(origin && branchName == defaultBranch){
None None
} else { } else {
walk.reset() walk.reset()

View File

@@ -84,26 +84,7 @@ class Mailer(private val smtp: Smtp) extends Notifier {
enableLineBreaks = false enableLineBreaks = false
))) { case (subject, msg) => ))) { case (subject, msg) =>
recipients(issue) { to => recipients(issue) { to =>
val email = new HtmlEmail send(to, subject, msg)
email.setHostName(smtp.host)
email.setSmtpPort(smtp.port.get)
smtp.user.foreach { user =>
email.setAuthenticator(new DefaultAuthenticator(user, smtp.password.getOrElse("")))
}
smtp.ssl.foreach { ssl =>
email.setSSLOnConnect(ssl)
}
smtp.fromAddress
.map (_ -> smtp.fromName.getOrElse(context.loginAccount.get.userName))
.orElse (Some("notifications@gitbucket.com" -> context.loginAccount.get.userName))
.foreach { case (address, name) =>
email.setFrom(address, name)
}
email.setCharset("UTF-8")
email.setSubject(subject)
email.setHtmlMsg(msg)
email.addTo(to).send
} }
} }
} }
@@ -116,6 +97,30 @@ class Mailer(private val smtp: Smtp) extends Notifier {
case t => logger.error("Notifications Failed.", t) case t => logger.error("Notifications Failed.", t)
} }
} }
def send(to: String, subject: String, msg: String)(implicit context: Context): Unit = {
val email = new HtmlEmail
email.setHostName(smtp.host)
email.setSmtpPort(smtp.port.get)
smtp.user.foreach { user =>
email.setAuthenticator(new DefaultAuthenticator(user, smtp.password.getOrElse("")))
}
smtp.ssl.foreach { ssl =>
email.setSSLOnConnect(ssl)
}
smtp.fromAddress
.map (_ -> smtp.fromName.getOrElse(context.loginAccount.get.userName))
.orElse (Some("notifications@gitbucket.com" -> context.loginAccount.get.userName))
.foreach { case (address, name) =>
email.setFrom(address, name)
}
email.setCharset("UTF-8")
email.setSubject(subject)
email.setHtmlMsg(msg)
email.addTo(to).send
}
} }
class MockMailer extends Notifier { class MockMailer extends Notifier {
def toNotify(r: RepositoryService.RepositoryInfo, issue: Issue, content: String) def toNotify(r: RepositoryService.RepositoryInfo, issue: Issue, content: String)

View File

@@ -1,5 +1,6 @@
package gitbucket.core.util package gitbucket.core.util
// TODO Move to gitbucket.core.api package?
case class RepositoryName(owner:String, name:String){ case class RepositoryName(owner:String, name:String){
val fullName = s"${owner}/${name}" val fullName = s"${owner}/${name}"
} }

View File

@@ -5,6 +5,7 @@ import org.mozilla.universalchardet.UniversalDetector
import ControlUtil._ import ControlUtil._
import org.apache.commons.io.input.BOMInputStream import org.apache.commons.io.input.BOMInputStream
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import scala.util.control.Exception._
object StringUtil { object StringUtil {
@@ -26,6 +27,8 @@ object StringUtil {
def splitWords(value: String): Array[String] = value.split("[ \\t ]+") def splitWords(value: String): Array[String] = value.split("[ \\t ]+")
def isInteger(value: String): Boolean = allCatch opt { value.toInt } map(_ => true) getOrElse(false)
def escapeHtml(value: String): String = def escapeHtml(value: String): String =
value.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;") value.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;")

View File

@@ -222,8 +222,14 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
* Generates the avatar link to the account page. * Generates the avatar link to the account page.
* If user does not exist or disabled, this method returns avatar image without link. * If user does not exist or disabled, this method returns avatar image without link.
*/ */
def avatarLink(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false)(implicit context: Context): Html = def avatarLink(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false, label: Boolean = false)
userWithContent(userName, mailAddress)(avatar(userName, size, tooltip, mailAddress)) (implicit context: Context): Html = {
val avatarHtml = avatar(userName, size, tooltip, mailAddress)
val contentHtml = if(label == true) Html(avatarHtml.body + " " + userName) else avatarHtml
userWithContent(userName, mailAddress)(contentHtml)
}
/** /**
* Generates the avatar link to the account page. * Generates the avatar link to the account page.
@@ -232,7 +238,8 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
def avatarLink(commit: JGitUtil.CommitInfo, size: Int)(implicit context: Context): Html = def avatarLink(commit: JGitUtil.CommitInfo, size: Int)(implicit context: Context): Html =
userWithContent(commit.authorName, commit.authorEmailAddress)(avatar(commit, size)) userWithContent(commit.authorName, commit.authorEmailAddress)(avatar(commit, size))
private def userWithContent(userName: String, mailAddress: String = "", styleClass: String = "")(content: Html)(implicit context: Context): Html = private def userWithContent(userName: String, mailAddress: String = "", styleClass: String = "")(content: Html)
(implicit context: Context): Html =
(if(mailAddress.isEmpty){ (if(mailAddress.isEmpty){
getAccountByUserName(userName) getAccountByUserName(userName)
} else { } else {

View File

@@ -1,4 +1,4 @@
@(account: gitbucket.core.model.Account, info: Option[Any])(implicit context: gitbucket.core.controller.Context) @(account: gitbucket.core.model.Account, info: Option[Any], error: Option[Any])(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.util.LDAPUtil @import gitbucket.core.util.LDAPUtil
@import context._ @import context._
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
@@ -6,6 +6,7 @@
<div class="container body"> <div class="container body">
@menu("profile", settings.ssh){ @menu("profile", settings.ssh){
@helper.html.information(info) @helper.html.information(info)
@helper.html.error(error)
@if(LDAPUtil.isDummyMailAddress(account)){<div class="alert alert-danger">Please register your mail address.</div>} @if(LDAPUtil.isDummyMailAddress(account)){<div class="alert alert-danger">Please register your mail address.</div>}
<form action="@url(account.userName)/_edit" method="POST" validate="true"> <form action="@url(account.userName)/_edit" method="POST" validate="true">
<div class="panel panel-default"> <div class="panel panel-default">

View File

@@ -2,8 +2,10 @@
@import context._ @import context._
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
@html.main(if(account.isEmpty) "Create group" else "Edit group"){ @html.main(if(account.isEmpty) "Create group" else "Edit group"){
<div class="body main-center"> <div class="content-wrapper main-center">
<form id="form" method="post" action="@if(account.isEmpty){@path/groups/new} else {@path/@account.get.userName/_editgroup}" validate="true"> <div class="content body">
<h2>@{if(account.isEmpty) "Create group" else "Edit group"}</h2>
<form id="form" method="post" action="@if(account.isEmpty){@path/groups/new} else {@path/@account.get.userName/_editgroup}" validate="true">
<div class="row"> <div class="row">
<div class="col-md-5"> <div class="col-md-5">
<fieldset class="form-group"> <fieldset class="form-group">
@@ -39,7 +41,7 @@
</fieldset> </fieldset>
</div> </div>
</div> </div>
<fieldset class="margin"> <fieldset class="border-top">
@if(account.isDefined){ @if(account.isDefined){
<div class="pull-right"> <div class="pull-right">
<a href="@url(account.get.userName)/_deletegroup" id="delete" class="btn btn-danger">Delete Group</a> <a href="@url(account.get.userName)/_deletegroup" id="delete" class="btn btn-danger">Delete Group</a>
@@ -50,7 +52,8 @@
<a href="@url(account.get.userName)" class="btn btn-default">Cancel</a> <a href="@url(account.get.userName)" class="btn btn-default">Cancel</a>
} }
</fieldset> </fieldset>
</form> </form>
</div>
</div> </div>
} }
<script> <script>

View File

@@ -3,29 +3,37 @@
@import context._ @import context._
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
@html.main(account.userName){ @html.main(account.userName){
<div class="container body"> <div class="main-sidebar">
<div class="main-sidebar"> <div class="sidebar">
<div class="block"> <div class="user-panel">
<div class="account-image">@avatar(account.userName, 240)</div> <div class="pull-left image">@avatar(account.userName, 40)</div>
<div class="account-fullname">@account.fullName</div> <div class="pull-left info">
<div class="account-username">@account.userName</div> <p>@account.userName</p>
@account.fullName
</div>
</div> </div>
<div class="block"> <div style="padding-left: 10px; padding-right: 10px;">
@if(account.url.isDefined){ @if(account.url.isDefined){
<div><i class="octicon octicon-home"></i> <a href="@account.url">@account.url</a></div> <p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<i class="octicon octicon-home"></i> <a href="@account.url">@account.url</a>
</p>
} }
<div><i class="octicon octicon-clock"></i> <span class="muted">Joined on</span> @date(account.registeredDate)</div> <p style="color: white;">
<i class="octicon octicon-clock"></i> Joined on @date(account.registeredDate)
</p>
</div> </div>
@if(groupNames.nonEmpty){ @if(groupNames.nonEmpty){
<div> <ul class="sidebar-menu">
<div>Groups</div> <li class="header">Groups</li>
@groupNames.map { groupName => @groupNames.map { groupName =>
@avatarLink(groupName, 36, tooltip = true) <li>@avatarLink(groupName, 20, tooltip = true, label = true)</li>
} }
</div> </div>
} }
</div> </div>
<div class="main-content"> </div>
<div class="content-wrapper">
<div class="content body">
<ul class="nav nav-tabs" style="margin-bottom: 5px;"> <ul class="nav nav-tabs" style="margin-bottom: 5px;">
<li@if(active == "repositories"){ class="active"}><a href="@url(account.userName)?tab=repositories">Repositories</a></li> <li@if(active == "repositories"){ class="active"}><a href="@url(account.userName)?tab=repositories">Repositories</a></li>
@if(account.isGroupAccount){ @if(account.isGroupAccount){

View File

@@ -1,27 +1,31 @@
@(active: String, ssh: Boolean)(body: Html)(implicit context: gitbucket.core.controller.Context) @(active: String, ssh: Boolean)(body: Html)(implicit context: gitbucket.core.controller.Context)
@import context._ @import context._
<div class="main-sidebar"> <div class="main-sidebar">
<ul class="nav nav-pills nav-stacked"> <div class="sidebar">
<li@if(active=="profile"){ class="active"}> <ul class="sidebar-menu">
<a href="@path/@loginAccount.get.userName/_edit">Profile</a> <li@if(active=="profile"){ class="active"}>
</li> <a href="@path/@loginAccount.get.userName/_edit">Profile</a>
@if(ssh){ </li>
<li@if(active=="ssh"){ class="active"}> @if(ssh){
<a href="@path/@loginAccount.get.userName/_ssh">SSH Keys</a> <li@if(active=="ssh"){ class="active"}>
</li> <a href="@path/@loginAccount.get.userName/_ssh">SSH Keys</a>
} </li>
<li@if(active=="application"){ class="active"}>
<a href="@path/@loginAccount.get.userName/_application">Applications</a>
</li>
@gitbucket.core.plugin.PluginRegistry().getAccountSettingMenus.map { menu =>
@menu(context).map { link =>
<li@if(active==link.id){ class="active"}>
<a href="@path/@link.path">@link.label</a>
</li>
} }
} <li@if(active=="application"){ class="active"}>
</ul> <a href="@path/@loginAccount.get.userName/_application">Applications</a>
</li>
@gitbucket.core.plugin.PluginRegistry().getAccountSettingMenus.map { menu =>
@menu(context).map { link =>
<li@if(active==link.id){ class="active"}>
<a href="@path/@link.path">@link.label</a>
</li>
}
}
</ul>
</div>
</div> </div>
<div class="main-content"> <div class="content-wrapper">
@body <div class="content body">
@body
</div>
</div> </div>

View File

@@ -3,73 +3,75 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
@import context._ @import context._
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
@html.main("Create a New Repository"){ @html.main("Create a New Repository"){
<div class="body main-center"> <div class="content-wrapper main-center">
<h2>Create a new repository</h2> <div class="content body">
<p class="muted"> <h2>Create a new repository</h2>
A repository contains all the files for your project, including the revision history. <p class="muted">
</p> A repository contains all the files for your project, including the revision history.
<form id="form" method="post" action="@path/new" validate="true"> </p>
<fieldset class="margin form-group"> <form id="form" method="post" action="@path/new" validate="true">
<dl style="float: left;"> <fieldset class="border-top form-group">
<dt>Owner</dt> <dl style="float: left;">
<dd style="margin-left: 0px;"> <dt>Owner</dt>
<div class="btn-group" id="owner-dropdown"> <dd style="margin-left: 0px;">
<button class="btn dropdown-toggle btn-default" data-toggle="dropdown"> <div class="btn-group" id="owner-dropdown">
<span class="strong">@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</span> <button class="btn dropdown-toggle btn-default" data-toggle="dropdown">
<span class="caret"></span> <span class="strong">@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</span>
</button> <span class="caret"></span>
<ul class="dropdown-menu"> </button>
<li><a href="javascript:void(0);" data-name="@loginAccount.get.userName"><i class="octicon octicon-check"></i> <span>@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</span></a></li> <ul class="dropdown-menu">
@groupNames.map { groupName => <li><a href="javascript:void(0);" data-name="@loginAccount.get.userName"><i class="octicon octicon-check"></i> <span>@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</span></a></li>
<li><a href="javascript:void(0);" data-name="@groupName"><i class="octicon"></i> <span>@avatar(groupName, 20) @groupName</span></a></li> @groupNames.map { groupName =>
} <li><a href="javascript:void(0);" data-name="@groupName"><i class="octicon"></i> <span>@avatar(groupName, 20) @groupName</span></a></li>
</ul> }
<input type="hidden" name="owner" id="owner" value="@loginAccount.get.userName"/> </ul>
<input type="hidden" name="owner" id="owner" value="@loginAccount.get.userName"/>
</div>
</dd>
</dl>
<span class="slash" style="float: left; margin-left: 10px; margin-right: 10px; margin-top: 15px;">/</span>
<dl>
<dt>Repository name</dt>
<dd style="margin-left: 0px;">
<input type="text" name="name" id="name" class="form-control" style="width: 200px;" autofocus />
<span id="error-name" class="error"></span>
</dd>
</dl>
</fieldset>
<fieldset class="form-group">
<label for="description" class="strong">Description (optional):</label>
<input type="text" name="description" id="description" class="form-control" style="width: 95%;"/>
</fieldset>
<fieldset class="border-top">
<label class="radio">
<input type="radio" name="isPrivate" value="false" @if(isCreateRepoOptionPublic){checked}>
<span class="strong"><i class="octicon octicon-repo"></i>&nbsp;</i>&nbsp;Public</span>
<div class="normal muted">
Anyone can see this repository. You choose who can commit.
</div> </div>
</dd> </label>
</dl> <label class="radio">
<span class="slash" style="float: left; margin-left: 10px; margin-right: 10px; margin-top: 15px;">/</span> <input type="radio" name="isPrivate" value="true" @if(!isCreateRepoOptionPublic){checked}>
<dl> <span class="strong"><i class="octicon octicon-lock"></i>&nbsp;</i>&nbsp;Private</span>
<dt>Repository name</dt> <div class="normal muted">
<dd style="margin-left: 0px;"> You choose who can see and commit to this repository.
<input type="text" name="name" id="name" class="form-control" style="width: 200px;" autofocus /> </div>
<span id="error-name" class="error"></span> </label>
</dd> </fieldset>
</dl> <fieldset class="border-top">
</fieldset> <label for="createReadme" class="checkbox">
<fieldset class="form-group"> <input type="checkbox" name="createReadme" id="createReadme"/>
<label for="description" class="strong">Description (optional):</label> <span class="strong">Initialize this repository with a README</span>
<input type="text" name="description" id="description" class="form-control" style="width: 95%;"/> <div class="normal muted">
</fieldset> This will let you immediately clone the repository to your computer. Skip this step if youre importing an existing repository.
<fieldset class="margin"> </div>
<label class="radio"> </label>
<input type="radio" name="isPrivate" value="false" @if(isCreateRepoOptionPublic){checked}> </fieldset>
<span class="strong"><i class="octicon octicon-repo"></i>&nbsp;</i>&nbsp;Public</span> <fieldset class="border-top form-actions">
<div class="normal muted"> <input type="submit" class="btn btn-success" value="Create repository"/>
Anyone can see this repository. You choose who can commit. </fieldset>
</div> </form>
</label> </div>
<label class="radio">
<input type="radio" name="isPrivate" value="true" @if(!isCreateRepoOptionPublic){checked}>
<span class="strong"><i class="octicon octicon-lock"></i>&nbsp;</i>&nbsp;Private</span>
<div class="normal muted">
You choose who can see and commit to this repository.
</div>
</label>
</fieldset>
<fieldset class="margin">
<label for="createReadme" class="checkbox">
<input type="checkbox" name="createReadme" id="createReadme"/>
<span class="strong">Initialize this repository with a README</span>
<div class="normal muted">
This will let you immediately clone the repository to your computer. Skip this step if youre importing an existing repository.
</div>
</label>
</fieldset>
<fieldset class="margin form-actions">
<input type="submit" class="btn btn-success" value="Create repository"/>
</fieldset>
</form>
</div> </div>
} }
<script> <script>

View File

@@ -2,49 +2,51 @@
@import context._ @import context._
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
@html.main("Create your account"){ @html.main("Create your account"){
<div class="container body"> <div class="content-wrapper main-center">
<h3>Create your account</h3> <div class="content body">
<form action="@path/register" method="POST" validate="true"> <h2>Create your account</h2>
<div class="row"> <form action="@path/register" method="POST" validate="true">
<div class="col-md-6"> <div class="row">
<fieldset> <div class="col-md-6">
<label for="userName" class="strong">Username:</label> <fieldset>
<input type="text" name="userName" id="userName" value="" autofocus/> <label for="userName" class="strong">Username:</label>
<span id="error-userName" class="error"></span> <input type="text" name="userName" id="userName" value="" class="form-control" autofocus/>
</fieldset> <span id="error-userName" class="error"></span>
<fieldset> </fieldset>
<label for="password" class="strong"> <fieldset>
Password: <label for="password" class="strong">
</label> Password:
<input type="password" name="password" id="password" value=""/> </label>
<span id="error-password" class="error"></span> <input type="password" name="password" id="password" class="form-control" value=""/>
</fieldset> <span id="error-password" class="error"></span>
<fieldset> </fieldset>
<label for="fullName" class="strong">Full Name:</label> <fieldset>
<input type="text" name="fullName" id="fullName" value=""/> <label for="fullName" class="strong">Full Name:</label>
<span id="error-fullName" class="error"></span> <input type="text" name="fullName" id="fullName" class="form-control" value=""/>
</fieldset> <span id="error-fullName" class="error"></span>
<fieldset> </fieldset>
<label for="mailAddress" class="strong">Mail Address:</label> <fieldset>
<input type="text" name="mailAddress" id="mailAddress" value=""/> <label for="mailAddress" class="strong">Mail Address:</label>
<span id="error-mailAddress" class="error"></span> <input type="text" name="mailAddress" id="mailAddress" class="form-control" value=""/>
</fieldset> <span id="error-mailAddress" class="error"></span>
<fieldset> </fieldset>
<label for="url" class="strong">URL (optional):</label> <fieldset>
<input type="text" name="url" id="url" style="width: 400px;" value=""/> <label for="url" class="strong">URL (optional):</label>
<span id="error-url" class="error"></span> <input type="text" name="url" id="url" class="form-control" value=""/>
</fieldset> <span id="error-url" class="error"></span>
</fieldset>
</div>
<div class="col-md-6">
<fieldset>
<label for="avatar" class="strong">Image (optional):</label>
@helper.html.uploadavatar(None)
</fieldset>
</div>
</div> </div>
<div class="col-md-6"> <fieldset class="border-top">
<fieldset> <input type="submit" class="btn btn-success" value="Create account"/>
<label for="avatar" class="strong">Image (optional):</label> </fieldset>
@helper.html.uploadavatar(None) </form>
</fieldset> </div>
</div>
</div>
<fieldset class="margin">
<input type="submit" class="btn btn-success" value="Create account"/>
</fieldset>
</form>
</div> </div>
} }

View File

@@ -1,8 +1,8 @@
@(active: String)(body: Html)(implicit context: gitbucket.core.controller.Context) @(active: String)(body: Html)(implicit context: gitbucket.core.controller.Context)
@import context._ @import context._
<div class="container body"> <div class="main-sidebar">
<div class="main-sidebar"> <div class="sidebar">
<ul class="nav nav-pills nav-stacked" id="system-admin-menu-container"> <ul class="sidebar-menu" id="system-admin-menu-container">
<li@if(active=="users"){ class="active"}> <li@if(active=="users"){ class="active"}>
<a href="@path/admin/users">User Management</a> <a href="@path/admin/users">User Management</a>
</li> </li>
@@ -16,7 +16,7 @@
<a href="@path/admin/data">Data export / import</a> <a href="@path/admin/data">Data export / import</a>
</li> </li>
<li> <li>
<a href="@path/console/login.jsp">H2 Console</a> <a href="@path/console/login.jsp" target="_blank">H2 Console</a>
</li> </li>
@gitbucket.core.plugin.PluginRegistry().getSystemSettingMenus.map { menu => @gitbucket.core.plugin.PluginRegistry().getSystemSettingMenus.map { menu =>
@menu(context).map { link => @menu(context).map { link =>
@@ -27,7 +27,9 @@
} }
</ul> </ul>
</div> </div>
<div class="main-content"> </div>
<div class="content-wrapper">
<div class="content body">
@body @body
</div> </div>
</div> </div>

View File

@@ -18,19 +18,19 @@
<div class="panel-body"> <div class="panel-body">
<div class="row"> <div class="row">
<label class="col-md-2">Id</label> <label class="col-md-2">Id</label>
<span class="col-md-8">@plugin.pluginId</span> <span class="col-md-10">@plugin.pluginId</span>
</div> </div>
<div class="row"> <div class="row">
<label class="col-md-2">Version</label> <label class="col-md-2">Version</label>
<span class="col-md-8">@plugin.version</span> <span class="col-md-10">@plugin.version</span>
</div> </div>
<div class="row"> <div class="row">
<label class="col-md-2">Name</label> <label class="col-md-2">Name</label>
<span class="col-md-8">@plugin.pluginName</span> <span class="col-md-10">@plugin.pluginName</span>
</div> </div>
<div class="row"> <div class="row">
<label class="col-md-2">Description</label> <label class="col-md-2">Description</label>
<span class="col-md-8 muted">@plugin.description</span> <span class="col-md-10 muted">@plugin.description</span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -22,7 +22,7 @@
<td>@GitBucketHome</td> <td>@GitBucketHome</td>
</tr> </tr>
<tr> <tr>
<td>DATBASE_URL</td> <td>DATABASE_URL</td>
<td>@gitbucket.core.util.DatabaseConfig.url</td> <td>@gitbucket.core.util.DatabaseConfig.url</td>
</tr> </tr>
</table> </table>
@@ -97,10 +97,15 @@
<!--====================================================================--> <!--====================================================================-->
<hr> <hr>
<label><span class="strong">Limit of activity logs</span> (Unlimited if it's not specified or zero)</label> <label><span class="strong">Limit of activity logs</span> (Unlimited if it's not specified or zero)</label>
<div class="controls"> <fieldset>
<input type="text" id="activityLogLimit" name="activityLogLimit" class="form-control input-mini" value="@settings.activityLogLimit"/> <div class="form-group">
<span id="error-activityLogLimit" class="error"></span> <label class="control-label col-md-3" for="activityLogLimit">Limit</label>
</div> <div class="col-md-9">
<input type="text" id="activityLogLimit" name="activityLogLimit" class="form-control input-mini" value="@settings.activityLogLimit"/>
<span id="error-activityLogLimit" class="error"></span>
</div>
</div>
</fieldset>
<!--====================================================================--> <!--====================================================================-->
<!-- Services --> <!-- Services -->
<!--====================================================================--> <!--====================================================================-->
@@ -289,7 +294,7 @@
<div class="form-group"> <div class="form-group">
<label class="control-label col-md-3" for="smtpPassword">Enable SSL</label> <label class="control-label col-md-3" for="smtpPassword">Enable SSL</label>
<div class="col-md-9"> <div class="col-md-9">
<input type="checkbox" name="smtp.ssl"@if(settings.smtp.flatMap(_.ssl).getOrElse(false)){ checked}/> <input type="checkbox" id="smtpSsl" name="smtp.ssl"@if(settings.smtp.flatMap(_.ssl).getOrElse(false)){ checked}/>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -304,6 +309,12 @@
<input type="text" id="fromName" name="smtp.fromName" class="form-control" value="@settings.smtp.map(_.fromName)"/> <input type="text" id="fromName" name="smtp.fromName" class="form-control" value="@settings.smtp.map(_.fromName)"/>
</div> </div>
</div> </div>
<div class="text-right">
Send test mail to:
<input type="text" id="testAddress" size="30"/>
<input type="button" id="sendTestMail" value="Send"/>
</div>
<p class="muted"> <p class="muted">
Enable notification not only SMTP configuration if you want to send notification email. Enable notification not only SMTP configuration if you want to send notification email.
</p> </p>
@@ -318,6 +329,40 @@
} }
<script> <script>
$(function(){ $(function(){
$('#sendTestMail').click(function(){
var host = $('#smtpHost' ).val();
var port = $('#smtpPort' ).val();
var user = $('#smtpUser' ).val();
var password = $('#smtpPassword').val();
var ssl = $('#smtpSsl' ).prop('checked');
var fromAddress = $('#fromAddress' ).val();
var fromName = $('#fromName' ).val();
var testAddress = $('#testAddress' ).val();
if(host == ''){
alert('SMTP Host is required.');
$('#smtpHost').focus();
} else if(testAddress == ''){
alert('Destination is required.');
$('#testAddress').focus();
} else {
$.post('@path/admin/system/sendmail', {
'smtp.host': host,
'smtp.port': port,
'smtp.user': user,
'smtp.password': password,
'smtp.ssl': ssl,
'smtp.fromAddress': fromAddress,
'smtp.fromName': fromName,
'testAddress': testAddress
}, function(data, status){
if(data != ''){
alert(data);
}
});
}
});
$('#ssh').change(function(){ $('#ssh').change(function(){
$('.ssh input').prop('disabled', !$(this).prop('checked')); $('.ssh input').prop('disabled', !$(this).prop('checked'));
}).change(); }).change();

View File

@@ -1,7 +1,8 @@
@(account: Option[gitbucket.core.model.Account])(implicit context: gitbucket.core.controller.Context) @(account: Option[gitbucket.core.model.Account], error: Option[Any] = None)(implicit context: gitbucket.core.controller.Context)
@import context._ @import context._
@html.main(if(account.isEmpty) "New User" else "Update User"){ @html.main(if(account.isEmpty) "New User" else "Update User"){
@admin.html.menu("users"){ @admin.html.menu("users"){
@helper.html.error(error)
<form method="POST" action="@if(account.isEmpty){@path/admin/users/_newuser} else {@path/admin/users/@account.get.userName/_edituser}" validate="true"> <form method="POST" action="@if(account.isEmpty){@path/admin/users/_newuser} else {@path/admin/users/@account.get.userName/_edituser}" validate="true">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
@@ -74,7 +75,7 @@
</fieldset> </fieldset>
</div> </div>
</div> </div>
<fieldset class="margin"> <fieldset class="border-top">
<input type="submit" class="btn btn-success" value="@if(account.isEmpty){Create User} else {Update User}"/> <input type="submit" class="btn btn-success" value="@if(account.isEmpty){Create User} else {Update User}"/>
<a href="@path/admin/users" class="btn btn-default">Cancel</a> <a href="@path/admin/users" class="btn btn-default">Cancel</a>
</fieldset> </fieldset>

View File

@@ -5,7 +5,7 @@
@admin.html.menu("users"){ @admin.html.menu("users"){
<form method="POST" action="@if(account.isEmpty){@path/admin/users/_newgroup} else {@path/admin/users/@account.get.userName/_editgroup}" validate="true"> <form method="POST" action="@if(account.isEmpty){@path/admin/users/_newgroup} else {@path/admin/users/@account.get.userName/_editgroup}" validate="true">
<div class="row"> <div class="row">
<div class="col-md-5"> <div class="col-md-6">
<fieldset class="form-group"> <fieldset class="form-group">
<label for="groupName" class="strong">Group name</label> <label for="groupName" class="strong">Group name</label>
<div> <div>
@@ -31,7 +31,7 @@
@helper.html.uploadavatar(account) @helper.html.uploadavatar(account)
</fieldset> </fieldset>
</div> </div>
<div class="col-md-7"> <div class="col-md-6">
<fieldset class="form-group"> <fieldset class="form-group">
<label class="strong">Members</label> <label class="strong">Members</label>
<ul id="member-list" class="collaborator"> <ul id="member-list" class="collaborator">
@@ -45,7 +45,7 @@
</fieldset> </fieldset>
</div> </div>
</div> </div>
<fieldset class="margin"> <fieldset class="border-top">
<input type="submit" class="btn btn-success" value="@if(account.isEmpty){Create Group} else {Update Group}"/> <input type="submit" class="btn btn-success" value="@if(account.isEmpty){Create Group} else {Update Group}"/>
<a href="@path/admin/users" class="btn btn-default">Cancel</a> <a href="@path/admin/users" class="btn btn-default">Cancel</a>
</fieldset> </fieldset>
@@ -78,7 +78,8 @@ $(function(){
// check existence // check existence
$.post('@path/_user/existence', { $.post('@path/_user/existence', {
'userName': userName 'userName': userName,
'userOnly': true
}, function(data, status){ }, function(data, status){
if(data == 'true'){ if(data == 'true'){
addMemberHTML(userName, false); addMemberHTML(userName, false);
@@ -102,18 +103,18 @@ $(function(){
} }
function addMemberHTML(userName, isManager){ function addMemberHTML(userName, isManager){
var memberButton = $('<button type="button" class="btn btn-default btn-mini" value="false">Member</button>').data('name', userName); var memberButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="false" name="' + userName + '">Member</label>');
if(!isManager){ if(!isManager){
memberButton.addClass('active'); memberButton.addClass('active');
} }
var managerButton = $('<button type="button" class="btn btn-default btn-mini" value="true">Manager</button>').data('name', userName); var managerButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="true" name="' + userName + '">Manager</label>');
if(isManager){ if(isManager){
managerButton.addClass('active'); managerButton.addClass('active');
} }
$('#member-list').append($('<li>') $('#member-list').append($('<li>')
.data('name', userName) .data('name', userName)
.append($('<div class="btn-group is_manager" data-toggle="buttons-radio">') .append($('<div class="btn-group is_manager" data-toggle="buttons">')
.append(memberButton) .append(memberButton)
.append(managerButton)) .append(managerButton))
.append(' ') .append(' ')
@@ -125,9 +126,7 @@ $(function(){
function updateMembers(){ function updateMembers(){
var members = $('#member-list li').map(function(i, e){ var members = $('#member-list li').map(function(i, e){
var userName = $(e).data('name'); var userName = $(e).data('name');
return userName + ':' + $('button.active').filter(function(i, e){ return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
return $(e).data('name') == userName;
}).attr('value');
}).get().join(','); }).get().join(',');
$('#members').val(members); $('#members').val(members);
} }

View File

@@ -2,71 +2,62 @@
userRepositories: List[gitbucket.core.service.RepositoryService.RepositoryInfo])(body: Html)(implicit context: gitbucket.core.controller.Context) userRepositories: List[gitbucket.core.service.RepositoryService.RepositoryInfo])(body: Html)(implicit context: gitbucket.core.controller.Context)
@import context._ @import context._
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
<div class="container body"> <div class="main-sidebar">
<div class="dashboard-sidebar"> <div class="sidebar">
@if(loginAccount.isEmpty){ <ul class="nav sidebar-menu">
<div id="dashboard-signin-form">@html.signinform(settings)</div> @if(loginAccount.isDefined){
<li class="header">Your repositories <span class="label label-primary pull-right">@userRepositories.size</span></li>
@if(userRepositories.isEmpty){
<li>No repositories</li>
} else { } else {
<div class="panel panel-default"> @defining(10){ max =>
<div class="panel-heading strong"> @userRepositories.zipWithIndex.map { case (repository, i) =>
Your repositories <span class="badge">@userRepositories.size</span> <li class="repo-link" style="@if(i > max - 1){display:none;}">
</div> @if(repository.owner == loginAccount.get.userName){
<ul class="list-group list-group-flush"> <a href="@url(repository)">@helper.html.repositoryicon(repository, false) <span class="strong">@repository.name</span></a>
@if(userRepositories.isEmpty){ } else {
<li class="list-group-item">No repositories</li> <a href="@url(repository)">@helper.html.repositoryicon(repository, false) @repository.owner/<span class="strong">@repository.name</span></a>
} else {
@defining(20){ max =>
@userRepositories.zipWithIndex.map { case (repository, i) =>
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
@helper.html.repositoryicon(repository, false)
@if(repository.owner == loginAccount.get.userName){
<a href="@url(repository)"><span class="strong">@repository.name</span></a>
} else {
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
}
</li>
}
@if(userRepositories.size > max){
<li class="list-group-item show-more">
<a href="javascript:void(0);" id="show-more-repos">Show @{userRepositories.size - max} more repositories...</a>
</li>
}
} }
</li>
} }
</ul> @if(userRepositories.size > max){
</div> <li class="show-more">
} <a href="javascript:void(0);" id="show-more-repos">Show @{userRepositories.size - max} more repositories...</a>
<div class="panel panel-default"> </li>
<div class="panel-heading strong">Recent updated repositories</div>
<ul class="list-group list-group-flush">
@if(recentRepositories.isEmpty){
<li class="list-group-item">No repositories</li>
} else {
@defining(20){ max =>
@recentRepositories.zipWithIndex.map { case (repository, i) =>
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
@helper.html.repositoryicon(repository, false)
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
</li>
}
@if(recentRepositories.size > max){
<li class="list-group-item show-more">
<a href="javascript:void(0);" id="show-more-recent-repos">Show @{recentRepositories.size - max} more repositories...</a>
</li>
}
} }
} }
</ul> }
</div> } else {
<li class="header">Recent updated repositories</li>
@if(recentRepositories.isEmpty){
<li>No repositories</li>
} else {
@defining(10){ max =>
@recentRepositories.zipWithIndex.map { case (repository, i) =>
<li class="repo-link" style="@if(i > max - 1){display:none;}">
<a href="@url(repository)">@helper.html.repositoryicon(repository, false) @repository.owner/<span class="strong">@repository.name</span></a>
</li>
}
@if(recentRepositories.size > max){
<li class="show-more">
<a href="javascript:void(0);" id="show-more-recent-repos">Show @{recentRepositories.size - max} more repositories...</a>
</li>
}
}
}
}
</ul>
</div> </div>
<div class="dashboard-content"> </div>
<div class="content-wrapper">
<div class="content body">
@body @body
</div> </div>
</div> </div>
<script> <script>
$(function(){ $(function(){
$('#show-more-repos, #show-more-recent-repos').click(function(e){ $('#show-more-repos, #show-more-recent-repos').click(function(e){
$(e.target).parents('ul.list-group').find('li.repo-link').show(); $(e.target).parents('ul').find('li.repo-link').show();
$(e.target).parents('li.show-more').remove(); $(e.target).parents('li.show-more').remove();
}); });
}); });

View File

@@ -10,15 +10,13 @@
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
@import org.eclipse.jgit.diff.DiffEntry.ChangeType @import org.eclipse.jgit.diff.DiffEntry.ChangeType
@if(showIndex){ @if(showIndex){
<div style="overflow: hidden;"> <div class="pull-right" style="margin-bottom: 10px;">
<div class="pull-right" style="margin-bottom: 10px;"> <div class="btn-group" data-toggle="buttons-radio">
<div class="btn-group" data-toggle="buttons-radio"> <input type="button" id="btn-unified" class="btn btn-default btn-small active" value="Unified">
<input type="button" id="btn-unified" class="btn btn-default btn-small active" value="Unified"> <input type="button" id="btn-split" class="btn btn-default btn-small" value="Split">
<input type="button" id="btn-split" class="btn btn-default btn-small" value="Split">
</div>
</div> </div>
Showing <a href="javascript:void(0);" id="toggle-file-list">@diffs.size changed @plural(diffs.size, "file")</a>
</div> </div>
Showing <a href="javascript:void(0);" id="toggle-file-list">@diffs.size changed @plural(diffs.size, "file")</a>
<ul id="commit-file-list" style="display: none;"> <ul id="commit-file-list" style="display: none;">
@diffs.zipWithIndex.map { case (diff, i) => @diffs.zipWithIndex.map { case (diff, i) =>
<li@if(i > 0){ class="border"}> <li@if(i > 0){ class="border"}>

View File

@@ -20,7 +20,7 @@
<div class="edit-title pull-right" style="display: none;"> <div class="edit-title pull-right" style="display: none;">
<a class="btn btn-success" href="#" id="update">Save</a> <a class="btn btn-default" href="#" id="cancel">Cancel</a> <a class="btn btn-success" href="#" id="update">Save</a> <a class="btn btn-default" href="#" id="cancel">Cancel</a>
</div> </div>
<h1> <h1 class="body-title">
<span class="show-title"> <span class="show-title">
<span id="show-title">@issue.title</span> <span id="show-title">@issue.title</span>
<span class="muted">#@issue.issueId</span> <span class="muted">#@issue.issueId</span>
@@ -46,7 +46,7 @@
</span> </span>
</div> </div>
<hr> <hr>
<div class="row-fluid" style="margin-top: 15px;"> <div style="margin-top: 15px;">
<div class="col-md-9"> <div class="col-md-9">
@commentlist(Some(issue), comments, hasWritePermission, repository) @commentlist(Some(issue), comments, hasWritePermission, repository)
@commentform(issue, true, hasWritePermission, repository) @commentform(issue, true, hasWritePermission, repository)

View File

@@ -51,23 +51,28 @@
} }
@helper.html.dropdown("Milestone") { @helper.html.dropdown("Milestone") {
<li> <li>
<a href="@condition.copy(milestone = Some(None)).toURL"> <a href="@condition.copy(milestone = (if(condition.milestone == Some(None)) None else Some(None))).toURL">
@helper.html.checkicon(condition.milestone == Some(None)) Issues with no milestone @helper.html.checkicon(condition.milestone == Some(None)) Issues with no milestone
</a> </a>
</li> </li>
@milestones.filter(_.closedDate.isEmpty).map { milestone => @milestones.filter(_.closedDate.isEmpty).map { milestone =>
<li> <li>
<a href="@condition.copy(milestone = Some(Some(milestone.title))).toURL"> <a href="@condition.copy(milestone = (if(condition.milestone == Some(Some(milestone.title))) None else Some(Some(milestone.title)))).toURL">
@helper.html.checkicon(condition.milestone == Some(Some(milestone.title))) @milestone.title @helper.html.checkicon(condition.milestone == Some(Some(milestone.title))) @milestone.title
</a> </a>
</li> </li>
} }
} }
@helper.html.dropdown("Assignee") { @helper.html.dropdown("Assignee") {
<li>
<a href="@condition.copy(assigned = (if(condition.assigned == Some(None)) None else Some(None))).toURL">
@helper.html.checkicon(condition.assigned == Some(None)) Assigned to nobody
</a>
</li>
@collaborators.map { collaborator => @collaborators.map { collaborator =>
<li> <li>
<a href="@condition.copy(assigned = Some(collaborator)).toURL"> <a href="@condition.copy(assigned = (if(condition.assigned == Some(Some(collaborator))) None else Some(Some(collaborator)))).toURL">
@helper.html.checkicon(condition.assigned == Some(collaborator)) @helper.html.checkicon(condition.assigned == Some(Some(collaborator)))
@avatar(collaborator, 20) @collaborator @avatar(collaborator, 20) @collaborator
</a> </a>
</li> </li>

View File

@@ -11,11 +11,13 @@
<link rel="icon" href="@assets/common/images/gitbucket.png" type="image/vnd.microsoft.icon" /> <link rel="icon" href="@assets/common/images/gitbucket.png" type="image/vnd.microsoft.icon" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="@assets/vendors/bootstrap-3.3.6/css/bootstrap.min.css" rel="stylesheet"> <link href="@assets/vendors/bootstrap-3.3.6/css/bootstrap.min.css" rel="stylesheet">
<link href="@assets/vendors/octicons/octicons.css" rel="stylesheet"> <link href="@assets/vendors/octicons-4.2.0/octicons.css" rel="stylesheet">
<link href="@assets/vendors/datepicker/css/bootstrap-datetimepicker.min.css" rel="stylesheet"> <link href="@assets/vendors/datepicker/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
<link href="@assets/vendors/colorpicker/css/bootstrap-colorpicker.css" rel="stylesheet"> <link href="@assets/vendors/colorpicker/css/bootstrap-colorpicker.css" rel="stylesheet">
<link href="@assets/vendors/google-code-prettify/prettify.css" type="text/css" rel="stylesheet"/> <link href="@assets/vendors/google-code-prettify/prettify.css" type="text/css" rel="stylesheet"/>
<link href="@assets/vendors/facebox/facebox.css" rel="stylesheet"/> <link href="@assets/vendors/facebox/facebox.css" rel="stylesheet"/>
<link href="@assets/vendors/AdminLTE-2.2.3/css/AdminLTE.min.css" rel="stylesheet">
<link href="@assets/vendors/AdminLTE-2.2.3/css/skins/skin-blue.min.css" rel="stylesheet">
<link href="@assets/common/css/gitbucket.css" rel="stylesheet"> <link href="@assets/common/css/gitbucket.css" rel="stylesheet">
<script src="@assets/vendors/jquery/jquery-1.11.1.js"></script> <script src="@assets/vendors/jquery/jquery-1.11.1.js"></script>
<script src="@assets/vendors/dropzone/dropzone.js"></script> <script src="@assets/vendors/dropzone/dropzone.js"></script>
@@ -36,75 +38,73 @@
<meta name="go-import" content="@context.baseUrl.replaceFirst("^https?://", "")/@repository.owner/@repository.name git @repository.httpUrl" /> <meta name="go-import" content="@context.baseUrl.replaceFirst("^https?://", "")/@repository.owner/@repository.name git @repository.httpUrl" />
} }
} }
<script src="@assets/vendors/AdminLTE-2.2.3/js/app.js" type="text/javascript"></script>
</head> </head>
<body> <body class="skin-blue">
<form id="search" action="@path/search" method="POST" class="form-inline"> <div class="wrapper">
<nav class="navbar navbar-default"> <header class="main-header">
<div class="container"> <a href="@path/" class="logo">
@* TODO: for plugi-ins? <img src="@assets/common/images/gitbucket.png" style="width: 24px; height: 24px; display: inline;"/>
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> GitBucket
<span class="icon-bar"></span> <span class="header-version">@gitbucket.core.GitBucketCoreModule.getVersions.last.getVersion</span>
<span class="icon-bar"></span> </a>
<span class="icon-bar"></span> <nav class="navbar navbar-static-top" role="navigation">
</button> @repository.map { repository =>
*@ <form id="search" action="@path/search" method="POST" class="navbar-form navbar-left" role="search">
<a class="navbar-brand" href="@path/"> <div class="form-group">
<img src="@assets/common/images/gitbucket.png" style="width: 24px; height: 24px; display: inline;"/>GitBucket <input type="text" name="query" id="navbar-search-input" class="form-control" placeholder="Search this repository"/>
<span class="header-version">@gitbucket.core.GitBucketCoreModule.getVersions.get(0).getVersion</span> <input type="hidden" name="owner" value="@repository.owner"/>
</a> <input type="hidden" name="repository" value="@repository.name"/>
@if(loginAccount.isDefined){
@repository.map { repository =>
<input type="text" name="query" class="form-control" style="width: 400px; margin-top: 3px; margin-bottom: 3px;" placeholder="Search this repository"/>
<input type="hidden" name="owner" value="@repository.owner"/>
<input type="hidden" name="repository" value="@repository.name"/>
}
<a href="@path/dashboard/pulls" class="global-header-menu">Pull requests</a>
<a href="@path/dashboard/issues" class="global-header-menu">Issues</a>
} else {
@* TODO: merge with below *@
@repository.map { repository =>
<input type="text" name="query" class="form-control" style="width: 400px; margin-top: 3px; margin-bottom: 3px;" placeholder="Search this repository"/>
<input type="hidden" name="owner" value="@repository.owner"/>
<input type="hidden" name="repository" value="@repository.name"/>
}
}
@gitbucket.core.plugin.PluginRegistry().getGlobalMenus.map { menu =>
@menu(context).map { link =>
<a href="@path/@link.path" class="global-header-menu">@link.label</a>
}
}
@if(loginAccount.isDefined){
<div class="pull-right" style="margin-top: 6px;">
<div class="btn-group" style="margin-right: 8px;">
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#">
<i class="octicon octicon-plus" style="color: black; font-size: 20px; vertical-align: middle;height:20px !important;"></i><span class="caret" style="color: black; vertical-align: middle;"></span>
</a>
<ul class="dropdown-menu pull-right">
<li><a href="@path/new">New repository</a></li>
<li><a href="@path/groups/new">New group</a></li>
</ul>
</div> </div>
<div class="btn-group"> </form>
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#" data-toggle="tooltip" data-placement="bottom" title="Signed is as @loginAccount.get.userName">
@avatar(loginAccount.get.userName, 16)<span class="caret" style="color: black; vertical-align: middle;"></span>
</a>
<ul class="dropdown-menu pull-right">
<li><a href="@url(loginAccount.get.userName)">Your profile</a></li>
<li><a href="@url(loginAccount.get.userName)/_edit">Account settings</a></li>
@if(loginAccount.get.isAdmin){
<li><a href="@path/admin/users">System administration</a></li>
}
<li><a href="@path/signout">Sign out</a></li>
</ul>
</div>
</div>
} else {
<a href="@path/signin?redirect=@urlEncode(currentPath)" class="btn btn-default pull-right" style="margin-top: 3px; margin-bottom: 3px;" id="signin">Sign in</a>
} }
</div> <ul class="nav navbar-nav">
</nav> @if(loginAccount.isDefined){
</form> <li><a href="@path/dashboard/pulls">Pull requests</a></li>
@body <li><a href="@path/dashboard/issues">Issues</a></li>
}
@gitbucket.core.plugin.PluginRegistry().getGlobalMenus.map { menu =>
@menu(context).map { link =>
<li><a href="@path/@link.path">@link.label</a></li>
}
}
</ul>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
@if(loginAccount.isDefined){
<li class="dropdown">
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#">
<i class="octicon octicon-plus" style="color: black;"></i><span class="caret" style="color: black; vertical-align: middle;"></span>
</a>
<ul class="dropdown-menu pull-right">
<li><a href="@path/new">New repository</a></li>
<li><a href="@path/groups/new">New group</a></li>
</ul>
</li>
<li class="dropdown">
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#" data-toggle="tooltip" data-placement="bottom" title="Signed is as @loginAccount.get.userName">
@avatar(loginAccount.get.userName, 16)<span class="caret" style="color: black; vertical-align: middle;"></span>
</a>
<ul class="dropdown-menu pull-right">
<li><a href="@url(loginAccount.get.userName)">Your profile</a></li>
<li><a href="@url(loginAccount.get.userName)/_edit">Account settings</a></li>
@if(loginAccount.get.isAdmin){
<li><a href="@path/admin/users">System administration</a></li>
}
<li><a href="@path/signout">Sign out</a></li>
</ul>
</li>
} else {
<li>
<a href="@path/signin?redirect=@urlEncode(currentPath)" class="pull-right" id="signin">Sign in</a>
</li>
}
</ul>
</div>
</nav>
</header>
@body
</div>
<script> <script>
$(function(){ $(function(){
$('#search').submit(function(){ $('#search').submit(function(){

View File

@@ -8,65 +8,80 @@
@menuitem(path: String, name: String, label: String, icon: String, count: Int = 0) = { @menuitem(path: String, name: String, label: String, icon: String, count: Int = 0) = {
<li @if(active == name){class="active"}> <li @if(active == name){class="active"}>
<a href="@url(repository)@path"> @if(path.startsWith("http")){
<i class="menu-icon octicon octicon-@icon"></i> <a href="@path" target="_blank">
<span class="pc"> <i class="menu-icon octicon octicon-@icon"></i> <span class="pc">@label @if(count > 0) { <span class="label label-primary pull-right">@count</span> }</span>
@label </a>
@if(count > 0){ } else {
<span class="badge">@count</span> <a href="@url(repository)@path">
} <i class="menu-icon octicon octicon-@icon"></i> <span class="pc">@label @if(count > 0) { <span class="label label-primary pull-right">@count</span> }</span>
</span> </a>
</a> }
</li> </li>
} }
<div class="headbar"> <div class="main-sidebar">
<div class="container"> <div class="sidebar">
@helper.html.information(info) <ul class="sidebar-menu">
@helper.html.error(error) @menuitem("", "files", "Files", "code")
<div class="head"> @if(repository.commitCount != 0) {
@helper.html.repositoryicon(repository, true) @menuitem("/branches", "branches", "Branches", "git-branch", repository.branchList.length)
<a href="@url(repository.owner)">@repository.owner</a> / <a href="@url(repository)" class="strong">@repository.name</a> @menuitem("/tags", "tags", "Tags", "tag", repository.tags.length)
}
@if(repository.repository.enableIssues) {
@menuitem("/issues", "issues", "Issues", "issue-opened", repository.issueCount)
@menuitem("/pulls", "pulls", "Pull Requests", "git-pull-request", repository.pullCount)
@menuitem("/issues/labels", "labels", "Labels", "tag")
@menuitem("/issues/milestones", "milestones", "Milestones", "milestone")
} else {
@repository.repository.externalIssuesUrl.map { externalIssuesUrl =>
@menuitem(externalIssuesUrl, "issues", "Issues", "issue-opened")
}
}
@if(repository.repository.enableWiki) {
@menuitem("/wiki", "wiki", "Wiki", "book")
} else {
@repository.repository.externalWikiUrl.map { externalWikiUrl =>
@menuitem(externalWikiUrl, "wiki", "Wiki", "book")
}
}
@menuitem("/network/members", "fork", "Forks", "repo-forked", repository.forkedCount)
@if(loginAccount.isDefined && (loginAccount.get.isAdmin || repository.managers.contains(loginAccount.get.userName))){
@menuitem("/settings", "settings", "Settings", "tools")
}
@gitbucket.core.plugin.PluginRegistry().getRepositoryMenus.map { menu =>
@menu(repository, context).map { link =>
@menuitem(link.path, link.id, link.label, link.icon.getOrElse("ruby"))
}
}
</ul>
</div>
</div>
<div class="content-wrapper">
<div class="content body">
<div class="headbar">
<div class="container">
@helper.html.information(info)
@helper.html.error(error)
<div class="head">
@helper.html.repositoryicon(repository, true)
<a href="@url(repository.owner)">@repository.owner</a> / <a href="@url(repository)" class="strong">@repository.name</a>
@defining(repository.repository){ x => @defining(repository.repository){ x =>
@if(repository.repository.originRepositoryName.isDefined){ @if(repository.repository.originRepositoryName.isDefined){
<div class="forked"> <div class="forked">
forked from <a href="@path/@x.parentUserName/@x.parentRepositoryName">@x.parentUserName/@x.parentRepositoryName</a> forked from <a href="@path/@x.parentUserName/@x.parentRepositoryName">@x.parentUserName/@x.parentRepositoryName</a>
</div>
}
@x.description.map { description =>
<div class="normal muted" style="margin-left: 36px; font-size: 80%;">@detectAndRenderLinks(description)</div>
}
}
</div> </div>
} </div>
@x.description.map { description => </div>
<div class="normal muted" style="margin-left: 36px; font-size: 80%;">@detectAndRenderLinks(description)</div> @body
}
}
</div> </div>
</div> </div>
</div> </div>
<div class="container body">
<div class="main-sidebar">
<ul class="nav nav-pills nav-stacked">
@menuitem("" ,"files" ,"Files", "code")
@if(repository.commitCount != 0) {
@menuitem("/branches" ,"branches" ,"Branches", "git-branch", repository.branchList.length)
@menuitem("/tags" ,"tags" ,"Tags", "tag", repository.tags.length)
}
@menuitem("/issues" ,"issues" ,"Issues", "issue-opened", repository.issueCount)
@menuitem("/pulls" ,"pulls" ,"Pull Requests", "git-pull-request", repository.pullCount)
@menuitem("/issues/labels" ,"labels" ,"Labels", "tag")
@menuitem("/issues/milestones" ,"milestones" ,"Milestones", "milestone")
@menuitem("/wiki" ,"wiki" ,"Wiki", "book")
@menuitem("/network/members", "fork", "Forks", "repo-forked", repository.forkedCount)
@if(loginAccount.isDefined && (loginAccount.get.isAdmin || repository.managers.contains(loginAccount.get.userName))){
@menuitem("/settings" , "settings" , "Settings", "tools")
}
@gitbucket.core.plugin.PluginRegistry().getRepositoryMenus.map { menu =>
@menu(repository, context).map { link =>
@menuitem(link.path, link.id, link.label, link.icon.getOrElse("ruby"))
}
}
</ul>
</div>
<div class="main-content">
@body
</div>
</div>

View File

@@ -27,13 +27,13 @@
<pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre> <pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre>
} }
<div> <div>
@user(commit.authorName, commit.authorEmailAddress, "username")
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
@if(commit.isDifferentFromAuthor) { @if(commit.isDifferentFromAuthor) {
@user(commit.authorName, commit.authorEmailAddress, "username")
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
<span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span> <span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span>
@user(commit.committerName, commit.committerEmailAddress, "username")
<span class="muted">committed @helper.html.datetimeago(commit.authorTime)</span>
} }
@user(commit.committerName, commit.committerEmailAddress, "username")
<span class="muted">committed @helper.html.datetimeago(commit.commitTime)</span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,5 @@
@(commits: Seq[Seq[gitbucket.core.util.JGitUtil.CommitInfo]], @(title: String,
commits: Seq[Seq[gitbucket.core.util.JGitUtil.CommitInfo]],
diffs: Seq[gitbucket.core.util.JGitUtil.DiffInfo], diffs: Seq[gitbucket.core.util.JGitUtil.DiffInfo],
members: List[(String, String)], members: List[(String, String)],
comments: List[gitbucket.core.model.Comment], comments: List[gitbucket.core.model.Comment],
@@ -56,7 +57,7 @@
<div class="row"> <div class="row">
<div class="col-md-9"> <div class="col-md-9">
<span class="error" id="error-title"></span> <span class="error" id="error-title"></span>
<input type="text" name="title" class="form-control" style="margin-bottom: 6px;" placeholder="Title"/> <input type="text" name="title" value="@title" class="form-control" style="margin-bottom: 6px;" placeholder="Title"/>
@helper.html.preview( @helper.html.preview(
repository = repository, repository = repository,
content = "", content = "",

View File

@@ -10,43 +10,40 @@
@import context._ @import context._
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
@import gitbucket.core.model._ @import gitbucket.core.model._
<div class="col-md-9">
<div class="row"> <div id="comment-list">
<div class="col-md-9"> @issues.html.commentlist(Some(issue), comments, hasWritePermission, repository, Some(pullreq))
<div id="comment-list"> </div>
@issues.html.commentlist(Some(issue), comments, hasWritePermission, repository, Some(pullreq)) @defining(comments.flatMap {
</div> case comment: gitbucket.core.model.IssueComment => Some(comment)
@defining(comments.flatMap { case other => None
case comment: gitbucket.core.model.IssueComment => Some(comment) }.exists(_.action == "merge")){ merged =>
case other => None @if(!issue.closed){
}.exists(_.action == "merge")){ merged => <div class="check-conflict" style="display: none;">
@if(!issue.closed){ <div class="box issue-comment-box" style="background-color: #fbeed5">
<div class="check-conflict" style="display: none;"> <div class="box-content"class="issue-content" style="border: 1px solid #c09853; padding: 10px;">
<div class="box issue-comment-box" style="background-color: #fbeed5"> <img src="@assets/common/images/indicator.gif"/> Checking...
<div class="box-content"class="issue-content" style="border: 1px solid #c09853; padding: 10px;">
<img src="@assets/common/images/indicator.gif"/> Checking...
</div>
</div> </div>
</div> </div>
} </div>
@if(hasWritePermission && issue.closed && pullreq.userName == pullreq.requestUserName && merged &&
pullreq.repositoryName == pullreq.requestRepositoryName && repository.branchList.contains(pullreq.requestBranch)){
<div class="box issue-comment-box" style="background-color: #d0eeff;">
<div class="box-content"class="issue-content" style="border: 1px solid #87a8c9; padding: 10px;">
<a href="@url(repository)/pull/@issue.issueId/delete/@encodeRefName(pullreq.requestBranch)" class="btn btn-info pull-right delete-branch" data-name="@pullreq.requestBranch">Delete branch</a>
<div>
<span class="strong">Pull request successfully merged and closed</span>
</div>
<span class="small muted">You're all set-the <span class="label label-info monospace">@pullreq.requestBranch</span> branch can be safely deleted.</span>
</div>
</div>
}
@issues.html.commentform(issue, !merged, hasWritePermission, repository)
} }
</div> @if(hasWritePermission && issue.closed && pullreq.userName == pullreq.requestUserName && merged &&
<div class="col-md-3"> pullreq.repositoryName == pullreq.requestRepositoryName && repository.branchList.contains(pullreq.requestBranch)){
@issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository) <div class="box issue-comment-box" style="background-color: #d0eeff;">
</div> <div class="box-content"class="issue-content" style="border: 1px solid #87a8c9; padding: 10px;">
<a href="@url(repository)/pull/@issue.issueId/delete/@encodeRefName(pullreq.requestBranch)" class="btn btn-info pull-right delete-branch" data-name="@pullreq.requestBranch">Delete branch</a>
<div>
<span class="strong">Pull request successfully merged and closed</span>
</div>
<span class="small muted">You're all set-the <span class="label label-info monospace">@pullreq.requestBranch</span> branch can be safely deleted.</span>
</div>
</div>
}
@issues.html.commentform(issue, !merged, hasWritePermission, repository)
}
</div>
<div class="col-md-3">
@issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
</div> </div>
<script> <script>
$(function(){ $(function(){

View File

@@ -13,26 +13,24 @@
@if(!status.statuses.isEmpty){ @if(!status.statuses.isEmpty){
<div class="build-statuses"> <div class="build-statuses">
@defining(status.commitStateSummary){ case (summaryState, summary) => @defining(status.commitStateSummary){ case (summaryState, summary) =>
<div class="build-status-item-header"> <a class="pull-right" id="toggle-all-checks"></a>
<a class="pull-right" id="toggle-all-checks"></a> <span class="build-status-icon text-@{summaryState.name}">@commitStateIcon(summaryState)</span>
<span class="build-status-icon text-@{summaryState.name}">@commitStateIcon(summaryState)</span> <strong class="text-@{summaryState.name}">@commitStateText(summaryState, pullreq.commitIdTo)</strong>
<strong class="text-@{summaryState.name}">@commitStateText(summaryState, pullreq.commitIdTo)</strong> <span class="text-@{summaryState.name}">— @summary checks</span>
<span class="text-@{summaryState.name}">— @summary checks</span>
</div>
} }
<div class="build-statuses-list" style="@if(status.isAllSuccess){ display:none; }else{ }"> </div>
@status.statusesAndRequired.map{ case (status, required) => <div class="build-statuses-list" style="@if(status.isAllSuccess){ display:none; }">
@status.statusesAndRequired.map { case (status, required) =>
<div class="build-status-item"> <div class="build-status-item">
<div class="pull-right"> <div class="pull-right">
@if(required){ <span class="label">Required</span> } @if(required){ <span class="label">Required</span> }
@status.targetUrl.map{ url => <a href="@url">Details</a> } @status.targetUrl.map { url => <a href="@url">Details</a> }
</div> </div>
<span class="build-status-icon text-@{status.state.name}">@commitStateIcon(status.state)</span> <span class="build-status-icon text-@{status.state.name}">@commitStateIcon(status.state)</span>
<strong>@status.context</strong> <strong>@status.context</strong>
@status.description.map{ desc => <span class="muted">— @desc</span> } @status.description.map { desc => <span class="muted">— @desc</span> }
</div> </div>
} }
</div>
</div> </div>
} }
<div style="padding:15px"> <div style="padding:15px">
@@ -46,43 +44,47 @@
Only those with write access to this repository can merge pull requests. Only those with write access to this repository can merge pull requests.
} }
</div> </div>
} else { @if(status.branchIsOutOfDate){ } else {
@if(status.hasUpdatePermission){ @if(status.branchIsOutOfDate){
<div class="pull-right"> @if(status.hasUpdatePermission){
<form method="POST" action="@url(originRepository)/pull/@pullreq.issueId/update_branch"> <div class="pull-right">
<input type="hidden" name="expected_head_oid" value="@pullreq.commitIdFrom"> <form method="POST" action="@url(originRepository)/pull/@pullreq.issueId/update_branch">
<button class="btn btn-default"@if(!status.canUpdate){ disabled="true"} id="update-branch-button">Update branch</button> <input type="hidden" name="expected_head_oid" value="@pullreq.commitIdFrom">
</form> <button class="btn btn-default"@if(!status.canUpdate){ disabled="true"} id="update-branch-button">Update branch</button>
</div> </form>
} </div>
}
<div class="merge-indicator merge-indicator-alert"><span class="octicon octicon-alert"></span></div> <div class="merge-indicator merge-indicator-alert"><span class="octicon octicon-alert"></span></div>
<span class="strong">This branch is out-of-date with the base branch</span> <span class="strong">This branch is out-of-date with the base branch</span>
<div class="small"> <div class="small">
Merge the latest changes from <code>@pullreq.branch</code> into this branch. Merge the latest changes from <code>@pullreq.branch</code> into this branch.
</div> </div>
} else { @if(status.hasRequiredStatusProblem) {
<div class="merge-indicator merge-indicator-warning"><span class="octicon octicon-primitive-dot"></span></div>
<span class="strong">Required statuses must pass before merging.</span>
<div class="small">
All required status checks on this pull request must run successfully to enable automatic merging.
</div>
} else { } else {
<div class="merge-indicator merge-indicator-success"><span class="octicon octicon-check"></span></div> @if(status.hasRequiredStatusProblem) {
@if(status.hasMergePermission){ <div class="merge-indicator merge-indicator-warning"><span class="octicon octicon-primitive-dot"></span></div>
<span class="strong">Merging can be performed automatically.</span> <span class="strong">Required statuses must pass before merging.</span>
<div class="small"> <div class="small">
Merging can be performed automatically. All required status checks on this pull request must run successfully to enable automatic merging.
</div> </div>
} else { } else {
<span class="strong">This branch has no conflicts with the base branch.</span> <div class="merge-indicator merge-indicator-success"><span class="octicon octicon-check"></span></div>
<div class="small"> @if(status.hasMergePermission){
Only those with write access to this repository can merge pull requests. <span class="strong">Merging can be performed automatically.</span>
</div> <div class="small">
Merging can be performed automatically.
</div>
} else {
<span class="strong">This branch has no conflicts with the base branch.</span>
<div class="small">
Only those with write access to this repository can merge pull requests.
</div>
}
} }
} } } }
}
</div> </div>
@if(status.hasMergePermission){ @if(status.hasMergePermission){
<div style="padding:15px;border-top:solid 1px #e5e5e5;background:#fafafa"> <div style="padding:15px; border-top:solid 1px #e5e5e5; background:#fafafa">
<input type="button" class="btn @if(!status.hasProblem){btn-success} else {btn-default}" id="merge-pull-request-button" value="Merge pull request"@if(!status.canMerge){ disabled="true"}/> <input type="button" class="btn @if(!status.hasProblem){btn-success} else {btn-default}" id="merge-pull-request-button" value="Merge pull request"@if(!status.canMerge){ disabled="true"}/>
&nbsp;&nbsp;You can also merge branches on the <a href="#" class="show-command-line">command line</a>. &nbsp;&nbsp;You can also merge branches on the <a href="#" class="show-command-line">command line</a>.
<div id="command-line" style="display: none;margin-top: 15px;"> <div id="command-line" style="display: none;margin-top: 15px;">
@@ -133,15 +135,15 @@
</div> </div>
} }
</div> </div>
<div id="confirm-merge-form" style="display: none;"> <div id="confirm-merge-form" style="display: none; padding: 12px;">
<form method="POST" action="@url(originRepository)/pull/@pullreq.issueId/merge"> <form method="POST" action="@url(originRepository)/pull/@pullreq.issueId/merge">
<div class="strong"> <div class="strong">
Merge pull request #@issue.issueId from @{pullreq.requestUserName}/@{pullreq.requestBranch} Merge pull request #@issue.issueId from @{pullreq.requestUserName}/@{pullreq.requestBranch}
</div> </div>
<span id="error-message" class="error"></span> <span id="error-message" class="error"></span>
<textarea name="message" style="height: 80px;" class="form-control">@issue.title</textarea> <textarea name="message" style="height: 80px; margin-top: 8px; margin-bottom: 8px;" class="form-control">@issue.title</textarea>
<div> <div>
<input type="button" class="btn" value="Cancel" id="cancel-merge-pull-request"/> <input type="button" class="btn btn-default" value="Cancel" id="cancel-merge-pull-request"/>
<input type="submit" class="btn btn-success" value="Confirm merge"/> <input type="submit" class="btn btn-success" value="Confirm merge"/>
</div> </div>
</form> </form>
@@ -168,6 +170,10 @@ $(function(){
$('#merge-pull-request').hide(); $('#merge-pull-request').hide();
$('#confirm-merge-form').show(); $('#confirm-merge-form').show();
}); });
$('#cancel-merge-pull-request').click(function(){
$('#merge-pull-request').show();
$('#confirm-merge-form').hide();
});
@forkedRepository.sshUrl.map { sshUrl => @forkedRepository.sshUrl.map { sshUrl =>
$('#repository-url-http').click(function(e){ $('#repository-url-http').click(function(e){

View File

@@ -28,7 +28,7 @@
<div class="edit-title pull-right" style="display: none;"> <div class="edit-title pull-right" style="display: none;">
<a class="btn btn-success" href="#" id="update">Save</a> <a class="btn btn-default" href="#" id="cancel">Cancel</a> <a class="btn btn-success" href="#" id="update">Save</a> <a class="btn btn-default" href="#" id="cancel">Cancel</a>
</div> </div>
<h1> <h1 class="body-title">
<span class="show-title"> <span class="show-title">
<span id="show-title">@issue.title</span> <span id="show-title">@issue.title</span>
<span class="muted">#@issue.issueId</span> <span class="muted">#@issue.issueId</span>

View File

@@ -13,74 +13,75 @@
</thead> </thead>
<tbody> <tbody>
@branchInfo.map { case (branch, prs, isProtected) => @branchInfo.map { case (branch, prs, isProtected) =>
<tr><td style="padding: 12px;"> <tr>
<div class="branch-action"> <td style="padding: 12px;">
@branch.mergeInfo.map{ info => <div class="branch-action">
@prs.map{ case (pull, issue) =>
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title">#@issue.issueId</a>
@if(issue.closed) {
@if(info.isMerged){
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-info">Merged</a>
}else{
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-important">Closed</a>
}
} else {
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-success">Open</a>
}
}.getOrElse{
@if(context.loginAccount.isDefined){
<a href="@url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
urlEncode(parent) + ":" + encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
encodeRefName(repository.repository.defaultBranch)
}}...@{encodeRefName(branch.name)}?expand=1" class="btn btn-default">New Pull Request</a>
} else {
<a href="@url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
urlEncode(parent) + ":" + encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
encodeRefName(repository.repository.defaultBranch)
}}...@{encodeRefName(branch.name)}" class="btn btn-default">Compare</a>
}
}
@if(hasWritePermission){
<span style="margin-left: 8px;">
@if(prs.map(!_._2.closed).getOrElse(false)){
<a class="disabled" data-toggle="tooltip" title="You cant delete this branch because it has an open pull request"><i class="octicon octicon-trashcan"></i></a>
} else {
@if(isProtected){
<a class="disabled" data-toggle="tooltip" title="You cant delete a protected branch."><i class="octicon octicon-trashcan"></i></a>
} else {
<a href="@url(repository)/delete/@encodeRefName(branch.name)" class="delete-branch" data-name="@branch.name" @if(info.isMerged){ data-toggle="tooltip" title="this branch is merged" }><i class="octicon octicon-trashcan @if(info.isMerged){warning} else {danger}"></i></a>
}
}
</span>
}
}
</div>
<div class="branch-details">
@if(isProtected){ <span class="octicon octicon-shield" title="This branch is protected"></span> }
<a href="@url(repository)/tree/@encodeRefName(branch.name)" class="branch-name">@branch.name</a>
<span class="branch-meta">
<span>Updated @helper.html.datetimeago(branch.commitTime, false)
by <span>@user(branch.committerName, branch.committerEmailAddress, "muted-link")</span>
</span>
</span>
</div>
<div class="branch-a-b-count">
@if(branch.mergeInfo.isEmpty){
<span class="label">Default</span>
}else{
@branch.mergeInfo.map{ info => @branch.mergeInfo.map{ info =>
<div data-toggle="tooltip" title="@info.ahead commits ahead, @info.behind commits behind @repository.repository.defaultBranch"> @prs.map{ case (pull, issue) =>
<div class="a-b-count-widget"> <a href="@url(repository)/pull/@issue.issueId" title="@issue.title">#@issue.issueId</a>
<div class="count-half"><div class="count-value">@info.ahead</div></div> @if(issue.closed) {
<div class="count-half"><div class="count-value">@info.behind</div></div> @if(info.isMerged){
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-info">Merged</a>
} else {
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-important">Closed</a>
}
} else {
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-success">Open</a>
}
}.getOrElse{
@if(context.loginAccount.isDefined){
<a href="@url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
urlEncode(parent) + ":" + encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
encodeRefName(repository.repository.defaultBranch)
}}...@{encodeRefName(branch.name)}?expand=1" class="btn btn-success">New Pull Request</a>
} else {
<a href="@url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
urlEncode(parent) + ":" + encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
encodeRefName(repository.repository.defaultBranch)
}}...@{encodeRefName(branch.name)}" class="btn btn-success">Compare</a>
}
}
@if(hasWritePermission){
<span style="margin-left: 8px;">
@if(prs.map(!_._2.closed).getOrElse(false)){
<a class="disabled" data-toggle="tooltip" title="You cant delete this branch because it has an open pull request"><i class="octicon octicon-trashcan"></i></a>
} else {
@if(isProtected){
<a class="disabled" data-toggle="tooltip" title="You cant delete a protected branch."><i class="octicon octicon-trashcan"></i></a>
} else {
<a href="@url(repository)/delete/@encodeRefName(branch.name)" class="delete-branch" data-name="@branch.name" @if(info.isMerged){ data-toggle="tooltip" title="this branch is merged" }><i class="octicon octicon-trashcan @if(info.isMerged){warning} else {danger}"></i></a>
}
}
</span>
}
}
</div>
<div class="branch-details">
@if(isProtected){ <span class="octicon octicon-shield" title="This branch is protected"></span> }
<a href="@url(repository)/tree/@encodeRefName(branch.name)" class="branch-name">@branch.name</a>
<span class="branch-meta">
<span>Updated @helper.html.datetimeago(branch.commitTime, false)
by <span>@user(branch.committerName, branch.committerEmailAddress, "muted-link")</span>
</span>
</span>
</div>
<div class="branch-a-b-count">
@if(branch.mergeInfo.isEmpty){
<span class="badge">Default</span>
} else {
@branch.mergeInfo.map{ info =>
<div data-toggle="tooltip" title="@info.ahead commits ahead, @info.behind commits behind @repository.repository.defaultBranch">
<div class="a-b-count-widget">
<div class="count-half"><div class="count-value">@info.ahead</div></div>
<div class="count-half"><div class="count-value">@info.behind</div></div>
</div> </div>
</div> </div>
</div> }
} }
} </div>
</div> </td>
</tr> </tr>
} }
</table> </table>

View File

@@ -48,7 +48,7 @@
<div> <div>
<div class="commit-avatar-image">@avatarLink(commit, 40)</div> <div class="commit-avatar-image">@avatarLink(commit, 40)</div>
<div> <div>
<a href="@url(repository)/commit/@commit.id" class="commit-message strong">@link(commit.summary, repository)</a> @link(commit.summary, repository)
@if(commit.description.isDefined){ @if(commit.description.isDefined){
<a href="javascript:void(0)" onclick="$('#description-@commit.id').toggle();" class="omit">...</a> <a href="javascript:void(0)" onclick="$('#description-@commit.id').toggle();" class="omit">...</a>
} }
@@ -56,14 +56,14 @@
@if(commit.description.isDefined){ @if(commit.description.isDefined){
<pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre> <pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre>
} }
<div> <div>
@user(commit.authorName, commit.authorEmailAddress, "username")
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
@if(commit.isDifferentFromAuthor) { @if(commit.isDifferentFromAuthor) {
@user(commit.authorName, commit.authorEmailAddress, "username")
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
<span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span> <span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span>
@user(commit.committerName, commit.committerEmailAddress, "username")
<span class="muted">committed @helper.html.datetimeago(commit.authorTime)</span>
} }
@user(commit.committerName, commit.committerEmailAddress, "username")
<span class="muted">committed @helper.html.datetimeago(commit.commitTime)</span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -6,7 +6,7 @@
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) { @html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
@html.menu("fork", repository){ @html.menu("fork", repository){
<h1> <h1 class="body-title">
Forked repositories Forked repositories
@if(loginAccount.isEmpty){ @if(loginAccount.isEmpty){
<a href="@path/signin?redirect=@urlEncode(s"${path}/${repository.owner}/${repository.name}")" class="btn btn-success">Fork</a> <a href="@path/signin?redirect=@urlEncode(s"${path}/${repository.owner}/${repository.name}")" class="btn btn-success">Fork</a>

View File

@@ -22,6 +22,6 @@
</div> </div>
} }
@helper.html.paginator(page, wikis.size, CodeLimit, 10, @helper.html.paginator(page, wikis.size, CodeLimit, 10,
s"${url(repository)}/search?q=${urlEncode(query)}&type=code") s"${url(repository)}/search?q=${urlEncode(query)}&type=wiki")
} }
} }

View File

@@ -15,7 +15,7 @@
<p>Before you can edit branch settings, you need to add a branch.</p> <p>Before you can edit branch settings, you need to add a branch.</p>
</center> </center>
</div> </div>
}else{ } else {
@helper.html.information(info) @helper.html.information(info)
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading strong">Default branch</div> <div class="panel-heading strong">Default branch</div>

View File

@@ -24,26 +24,21 @@
</label> </label>
<p class="help-block">Disables force-pushes to this branch and prevents it from being deleted.</p> <p class="help-block">Disables force-pushes to this branch and prevents it from being deleted.</p>
</div> </div>
<div class="checkbox js-enabled" style="display:none"> <div class="checkbox js-enabled" style="display:none">
<label> <label>
<input type="checkbox" name="has_required_statuses" onclick="update()" @check(protection.status.enforcement_level.name!="off")> <input type="checkbox" name="has_required_statuses" onclick="update()" @check(protection.status.enforcement_level.name!="off") @if(knownContexts.isEmpty){disabled }>
<span class="strong">Require status checks to pass before merging</span> <span class="strong">Require status checks to pass before merging</span>
</label> </label>
<p class="help-block">Choose which status checks must pass before branches can be merged into test. <p class="help-block">When enabled, commits must first be pushed to another branch, then merged or pushed directly to <b>@branch</b> after status checks have passed.</p>
When enabled, commits must first be pushed to another branch, then merged or pushed directly to test after status checks have passed.</p> @if( knownContexts.isEmpty ){
<div class="alert alert-warning">
<div class="js-has_required_statuses" style="display:none"> Sorry, we couldnt find any status checks in the last week for this repository.<br />
<div class="checkbox"> Please create a commit status by API (<a href="https://developer.github.com/v3/repos/statuses/">Learn more about status checks on GitHub</a>)
<label>
<input type="checkbox" name="enforce_for_admins" onclick="update()" @check(protection.status.enforcement_level.name=="everyone")>
<span class="strong">Include administrators</span>
</label>
<p class="help-block">Enforce required status checks for repository administrators.</p>
</div> </div>
}else{
<div class="js-has_required_statuses" style="display:none">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading">Status checks found in the last week for this repository</div> <div class="panel-heading">Choose which status checks must pass before branches can be merged into <b>@branch</b>.</div>
<div class="panel-body"> <div class="panel-body">
@knownContexts.map { context => @knownContexts.map { context =>
<div class="checkbox"> <div class="checkbox">
@@ -55,9 +50,18 @@
} }
</div> </div>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" name="enforce_for_admins" onclick="update()" @check(protection.status.enforcement_level.name=="everyone")>
<span class="strong">Include administrators</span>
</label>
<p class="help-block">Enforce required status checks for repository administrators.</p>
</div>
</div> </div>
}
</div> </div>
<input class="btn btn-success" type="submit" value="Save changes" /> <input class="btn btn-success js-submit-btn" type="submit" value="Save changes" />
</div> </div>
</div> </div>
</form> </form>
@@ -95,6 +99,7 @@ function getValue(){
function updateView(protection){ function updateView(protection){
$('.js-enabled').toggle(protection.enabled); $('.js-enabled').toggle(protection.enabled);
$('.js-has_required_statuses').toggle(protection.required_status_checks.enforcement_level != 'off'); $('.js-has_required_statuses').toggle(protection.required_status_checks.enforcement_level != 'off');
$('.js-submit-btn').attr('disabled',protection.required_status_checks.enforcement_level != 'off' && protection.required_status_checks.contexts.length == 0);
} }
function update(){ function update(){
var protection = getValue(); var protection = getValue();

View File

@@ -1,9 +1,10 @@
@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context) @(repository: gitbucket.core.service.RepositoryService.RepositoryInfo, info: Option[Any])(implicit context: gitbucket.core.controller.Context)
@import context._ @import context._
@import gitbucket.core.view.helpers._ @import gitbucket.core.view.helpers._
@html.main("Danger Zone", Some(repository)){ @html.main("Danger Zone", Some(repository)){
@html.menu("settings", repository){ @html.menu("settings", repository){
@menu("danger", repository){ @menu("danger", repository){
@helper.html.information(info)
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading strong">Danger Zone</div> <div class="panel-heading strong">Danger Zone</div>
<div class="panel-body"> <div class="panel-body">
@@ -23,7 +24,7 @@
</fieldset> </fieldset>
</form> </form>
<form id="delete-form" method="post" action="@url(repository)/settings/delete"> <form id="delete-form" method="post" action="@url(repository)/settings/delete">
<fieldset class="margin form-group"> <fieldset class="border-top form-group">
<label class="strong">Delete repository</label> <label class="strong">Delete repository</label>
<div> <div>
Once you delete a repository, there is no going back. Once you delete a repository, there is no going back.
@@ -31,6 +32,15 @@
</div> </div>
</fieldset> </fieldset>
</form> </form>
<form id="gc-form" method="post" action="@url(repository)/settings/gc">
<fieldset class="border-top form-group">
<label class="strong">Garbage collection</label>
<div>
Run garbage collection for this git repository immediately.
<input type="submit" class="btn btn-danger pull-right" value="Garbage collection"/>
</div>
</fieldset>
</form>
</div> </div>
</div> </div>
} }

View File

@@ -146,7 +146,7 @@ $(function(){
$("#test-modal-url").text(url) $("#test-modal-url").text(url)
$("#test-report-modal").modal('show') $("#test-report-modal").modal('show')
$("#test-report").hide(); $("#test-report").hide();
var targetUrl = '@url(repository)/settings/hooks/test?url=' + encodeURIComponent(url) + '&token='; var targetUrl = '@url(repository)/settings/hooks/test?url=' + encodeURIComponent(url) + '&ctype=' + ctype + '&token=';
if (token) { if (token) {
targetUrl = targetUrl + encodeURIComponent(token); targetUrl = targetUrl + encodeURIComponent(token);
} }

View File

@@ -14,11 +14,11 @@
<input type="text" name="repositoryName" id="repositoryName" class="form-control" value="@repository.name"/> <input type="text" name="repositoryName" id="repositoryName" class="form-control" value="@repository.name"/>
<span id="error-repositoryName" class="error"></span> <span id="error-repositoryName" class="error"></span>
</fieldset> </fieldset>
<fieldset class="margin form-group"> <fieldset class="border-top form-group">
<label for="description" class="strong">Description:</label> <label for="description" class="strong">Description:</label>
<input type="text" name="description" id="description" class="form-control" value="@repository.repository.description"/> <input type="text" name="description" id="description" class="form-control" value="@repository.repository.description"/>
</fieldset> </fieldset>
<fieldset class="margin"> <fieldset class="border-top">
<label class="radio"> <label class="radio">
<input type="radio" name="isPrivate" value="false" <input type="radio" name="isPrivate" value="false"
@if(!repository.repository.isPrivate ){ checked } @if(!repository.repository.isPrivate ){ checked }
@@ -42,6 +42,41 @@
</fieldset> </fieldset>
</div> </div>
</div> </div>
<div class="panel panel-default">
<div class="panel-heading strong">Features</div>
<div class="panel-body">
<fieldset class="form-group">
<label class="checkbox" for="enableIssues">
<input type="checkbox" id="enableIssues" name="enableIssues"@if(repository.repository.enableIssues){ checked}/>
Issues<br>
<div class="normal muted">
Provides Lightweight issue tracking integrated with this repository. Add issues to milestones, label issues, and close & reference issues from commit messages.
</div>
</label>
<label for="externalIssuesUrl" class="strong">External URL:
<span class="normal muted">(Put if you have the external issue tracking system for this project)</span>
</label>
<input type="text" class="form-control" id="externalIssuesUrl" name="externalIssuesUrl" value="@repository.repository.externalIssuesUrl"/>
</fieldset>
<fieldset class="form-group margin">
<label class="checkbox" for="enableWiki">
<input type="checkbox" id="enableWiki" name="enableWiki"@if(repository.repository.enableWiki){ checked}/>
Wiki<br>
<div class="normal muted">
Provides a simple solution to manage documents. All users who can look this repository can read and collaborators can edit pages.
</div>
</label>
<label class="checkbox" for="allowWikiEditing">
<input type="checkbox" id="allowWikiEditing" name="allowWikiEditing"@if(repository.repository.allowWikiEditing){ checked}/>
Allow read-only users to edit Wiki pages<br>
</label>
<label for="externalWikiUrl" class="strong">External URL:
<span class="normal muted">(Put if you have the external Wiki for this project)</span>
</label>
<input type="text" class="form-control" id="externalWikiUrl" name="externalWikiUrl" value="@repository.repository.externalWikiUrl"/>
</fieldset>
</div>
</div>
<div class="align-right" style="margin-top: 20px;"> <div class="align-right" style="margin-top: 20px;">
<input type="submit" class="btn btn-success" value="Apply changes"/> <input type="submit" class="btn btn-success" value="Apply changes"/>
</div> </div>
@@ -49,3 +84,18 @@
} }
} }
} }
<script>
$(function(){
updateFeatures();
$('#enableIssues, #enableWiki').click(function(){
updateFeatures();
});
});
function updateFeatures() {
$('#externalIssuesUrl').prop('disabled', $('#enableIssues').prop('checked'));
$('#allowWikiEditing').prop('disabled', !$('#enableWiki').prop('checked'));
$('#externalWikiUrl').prop('disabled', $('#enableWiki').prop('checked'));
}
</script>

View File

@@ -1,13 +1,17 @@
@()(implicit context: gitbucket.core.controller.Context) @()(implicit context: gitbucket.core.controller.Context)
@import context._ @import context._
@main("Sign in"){ @main("Sign in"){
<div class="signin-form"> <div class="content-wrapper main-center">
@settings.information.map { information => <div class="content body">
<div class="alert alert-info" style="background-color: white; color: #555; border-color: #4183c4; font-size: small; line-height: 120%;"> <div class="signin-form">
<button type="button" class="close" data-dismiss="alert">&times;</button> @settings.information.map { information =>
@Html(information) <div class="alert alert-info" style="background-color: white; color: #555; border-color: #4183c4; font-size: small; line-height: 120%;">
<button type="button" class="close" data-dismiss="alert">&times;</button>
@Html(information)
</div>
}
@signinform(settings)
</div> </div>
} </div>
@signinform(settings)
</div> </div>
} }

View File

@@ -1,14 +1,7 @@
@(systemSettings: gitbucket.core.service.SystemSettingsService.SystemSettings)(implicit context: gitbucket.core.controller.Context) @(systemSettings: gitbucket.core.service.SystemSettingsService.SystemSettings)(implicit context: gitbucket.core.controller.Context)
@import context._ @import context._
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading strong"> <div class="panel-heading strong">Sign in</div>
@if(systemSettings.allowAccountRegistration){
<div class="pull-right">
<a href="@path/register" class="btn btn-mini">Create new account</a>
</div>
}
Sign in
</div>
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
<li class="list-group-item"> <li class="list-group-item">
<form action="@path/signin" method="POST" validate="true"> <form action="@path/signin" method="POST" validate="true">
@@ -23,6 +16,9 @@
<input type="password" name="password" id="password" class="form-control"/> <input type="password" name="password" id="password" class="form-control"/>
</div> </div>
<input type="submit" class="btn btn-success" value="Sign in"/> <input type="submit" class="btn btn-success" value="Sign in"/>
@if(systemSettings.allowAccountRegistration){
or <a href="@path/register">Create new account</a>
}
</form> </form>
</li> </li>
</ul> </ul>

View File

@@ -17,7 +17,7 @@
<a class="btn btn-success" href="@url(repository)/wiki/_new">New Page</a> <a class="btn btn-success" href="@url(repository)/wiki/_new">New Page</a>
</div> </div>
} }
<h1 class="wiki-title">@pageName</h1> <h1 class="body-title">@pageName</h1>
<div> <div>
<span class="muted"><strong>@page.committer</strong> edited this page @helper.html.datetimeago(page.time)</span> <span class="muted"><strong>@page.committer</strong> edited this page @helper.html.datetimeago(page.time)</span>
</div> </div>

View File

@@ -115,12 +115,17 @@ pre.reset {
label.radio, label.checkbox { label.radio, label.checkbox {
position: relative; position: relative;
left: 20px; left: 24px;
}
div.content-wrapper {
background-color: white;
} }
/* ======================================================================== */ /* ======================================================================== */
/* Global Header */ /* Global Header */
/* ======================================================================== */ /* ======================================================================== */
/*
.navbar-brand { .navbar-brand {
height: unset; height: unset;
padding: 8px; padding: 8px;
@@ -130,42 +135,11 @@ label.radio, label.checkbox {
min-height: unset; min-height: unset;
margin-bottom: 0px; margin-bottom: 0px;
} }
*/
span.header-version { span.header-version {
font-size: small; font-size: small;
} }
/*
div.input-group>span.fork {
display: table;
margin-left: 4px;
margin-top: 1px;
}
*/
/*
div.input-group>span.fork>span.count {
background-color: white;
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
border: 1px solid #d8d8d8;
border-left: none;
font-size: 11px;
padding: 3px 6px;
display: table-cell;
}
*/
a.global-header-menu {
color: black;
font-weight: bold;
font-size: 90%;
margin-top: 6px;
margin-left: 10px;
margin-right: 10px;
line-height: 3.1;
}
/* ======================================================================== */ /* ======================================================================== */
/* General Styles */ /* General Styles */
/* ======================================================================== */ /* ======================================================================== */
@@ -181,9 +155,6 @@ div.head div.forked {
} }
div.container { div.container {
/*
width: 980px;
*/
width: 100%; width: 100%;
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
@@ -193,7 +164,7 @@ div.container-wide {
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
} }
/*
div.main-sidebar { div.main-sidebar {
width: 250px; width: 250px;
float: left; float: left;
@@ -201,9 +172,8 @@ div.main-sidebar {
div.main-content { div.main-content {
margin-left: 260px; margin-left: 260px;
overflow: hidden;
} }
*/
div.main-center { div.main-center {
width: 980px; width: 980px;
margin: 0 auto; margin: 0 auto;
@@ -216,13 +186,14 @@ div.dashboard-sidebar {
div.dashboard-content { div.dashboard-content {
margin-left: 310px; margin-left: 310px;
overflow: hidden;
} }
/*
div.body { div.body {
margin-top: 20px; margin-top: 20px;
margin-bottom: 40px; margin-bottom: 40px;
} }
*/
span.error { span.error {
color: red; color: red;
@@ -318,6 +289,15 @@ div.box-header {
margin-bottom: 0px; margin-bottom: 0px;
} }
div.row {
margin-left: 0px;
margin-right: 0px;
}
ul.nav-tabs {
height: 42px;
}
ul.nav-pills .link { ul.nav-pills .link {
color: #4078c0; color: #4078c0;
} }
@@ -379,7 +359,7 @@ div.activity-message {
margin-left: 20px; margin-left: 20px;
} }
fieldset.margin { fieldset.border-top {
border-top: 1px solid #eee; border-top: 1px solid #eee;
margin-top: 10px; margin-top: 10px;
padding-top: 10px; padding-top: 10px;
@@ -1086,7 +1066,7 @@ div.pullreq-info {
/****************************************************************************/ /****************************************************************************/
/* Wiki */ /* Wiki */
/****************************************************************************/ /****************************************************************************/
h1.wiki-title { h1.body-title {
margin-top: 0px; margin-top: 0px;
} }
@@ -1132,22 +1112,21 @@ div.author-info div.committer {
font-size: 12px; font-size: 12px;
} }
.text-pending{ .text-pending {
color: #cea61b; color: #cea61b;
} }
.text-failure{ .text-failure {
color: #bd2c00; color: #bd2c00;
} }
.build-statuses{ .build-statuses {
margin: -10px -10px 10px -10px; padding: 10px 15px 10px 15px;
}
.build-statuses .build-status-item{
padding: 10px 15px 10px 64px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
} }
.build-statuses-list .build-status-item{ .build-statuses-list .build-status-item {
background-color: #fafafa; background-color: #fafafa;
padding: 10px 15px 10px 15px;
border-bottom: 1px solid #eee;
} }
.merge-indicator{ .merge-indicator{
@@ -1469,6 +1448,7 @@ ul.collaborator li:hover {
ul.collaborator a.remove { ul.collaborator a.remove {
color: #dd0000; color: #dd0000;
text-decoration: underline; text-decoration: underline;
padding-top: 4px;
} }
/****************************************************************************/ /****************************************************************************/
@@ -1943,6 +1923,11 @@ div.container.blame-container{
div.input-group>span.fork { div.input-group>span.fork {
display: none; display: none;
} }
/* allow wrapping on raw/mobile/history buttons */
div.main-content>div.box-header>div.btn-group.pull-right
{
float: none !important;
}
} }
/****************************************************************************/ /****************************************************************************/

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,176 @@
/*
* Skin: Black
* -----------
*/
/* skin-black navbar */
.skin-black-light .main-header {
-webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
}
.skin-black-light .main-header .navbar-toggle {
color: #333;
}
.skin-black-light .main-header .navbar-brand {
color: #333;
border-right: 1px solid #eee;
}
.skin-black-light .main-header > .navbar {
background-color: #ffffff;
}
.skin-black-light .main-header > .navbar .nav > li > a {
color: #333333;
}
.skin-black-light .main-header > .navbar .nav > li > a:hover,
.skin-black-light .main-header > .navbar .nav > li > a:active,
.skin-black-light .main-header > .navbar .nav > li > a:focus,
.skin-black-light .main-header > .navbar .nav .open > a,
.skin-black-light .main-header > .navbar .nav .open > a:hover,
.skin-black-light .main-header > .navbar .nav .open > a:focus,
.skin-black-light .main-header > .navbar .nav > .active > a {
background: #ffffff;
color: #999999;
}
.skin-black-light .main-header > .navbar .sidebar-toggle {
color: #333333;
}
.skin-black-light .main-header > .navbar .sidebar-toggle:hover {
color: #999999;
background: #ffffff;
}
.skin-black-light .main-header > .navbar > .sidebar-toggle {
color: #333;
border-right: 1px solid #eee;
}
.skin-black-light .main-header > .navbar .navbar-nav > li > a {
border-right: 1px solid #eee;
}
.skin-black-light .main-header > .navbar .navbar-custom-menu .navbar-nav > li > a,
.skin-black-light .main-header > .navbar .navbar-right > li > a {
border-left: 1px solid #eee;
border-right-width: 0;
}
.skin-black-light .main-header > .logo {
background-color: #ffffff;
color: #333333;
border-bottom: 0 solid transparent;
border-right: 1px solid #eee;
}
.skin-black-light .main-header > .logo:hover {
background-color: #fcfcfc;
}
@media (max-width: 767px) {
.skin-black-light .main-header > .logo {
background-color: #222222;
color: #ffffff;
border-bottom: 0 solid transparent;
border-right: none;
}
.skin-black-light .main-header > .logo:hover {
background-color: #1f1f1f;
}
}
.skin-black-light .main-header li.user-header {
background-color: #222;
}
.skin-black-light .content-header {
background: transparent;
box-shadow: none;
}
.skin-black-light .wrapper,
.skin-black-light .main-sidebar,
.skin-black-light .left-side {
background-color: #f9fafc;
}
.skin-black-light .content-wrapper,
.skin-black-light .main-footer {
border-left: 1px solid #d2d6de;
}
.skin-black-light .user-panel > .info,
.skin-black-light .user-panel > .info > a {
color: #444444;
}
.skin-black-light .sidebar-menu > li {
-webkit-transition: border-left-color 0.3s ease;
-o-transition: border-left-color 0.3s ease;
transition: border-left-color 0.3s ease;
}
.skin-black-light .sidebar-menu > li.header {
color: #848484;
background: #f9fafc;
}
.skin-black-light .sidebar-menu > li > a {
border-left: 3px solid transparent;
font-weight: 600;
}
.skin-black-light .sidebar-menu > li:hover > a,
.skin-black-light .sidebar-menu > li.active > a {
color: #000000;
background: #f4f4f5;
}
.skin-black-light .sidebar-menu > li.active {
border-left-color: #ffffff;
}
.skin-black-light .sidebar-menu > li.active > a {
font-weight: 600;
}
.skin-black-light .sidebar-menu > li > .treeview-menu {
background: #f4f4f5;
}
.skin-black-light .sidebar a {
color: #444444;
}
.skin-black-light .sidebar a:hover {
text-decoration: none;
}
.skin-black-light .treeview-menu > li > a {
color: #777777;
}
.skin-black-light .treeview-menu > li.active > a,
.skin-black-light .treeview-menu > li > a:hover {
color: #000000;
}
.skin-black-light .treeview-menu > li.active > a {
font-weight: 600;
}
.skin-black-light .sidebar-form {
border-radius: 3px;
border: 1px solid #d2d6de;
margin: 10px 10px;
}
.skin-black-light .sidebar-form input[type="text"],
.skin-black-light .sidebar-form .btn {
box-shadow: none;
background-color: #fff;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-black-light .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-black-light .sidebar-form input[type="text"]:focus,
.skin-black-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-black-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-black-light .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}
@media (min-width: 768px) {
.skin-black-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
border-left: 1px solid #d2d6de;
}
}

View File

@@ -0,0 +1 @@
.skin-black-light .main-header{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.skin-black-light .main-header .navbar-toggle{color:#333}.skin-black-light .main-header .navbar-brand{color:#333;border-right:1px solid #eee}.skin-black-light .main-header>.navbar{background-color:#fff}.skin-black-light .main-header>.navbar .nav>li>a{color:#333}.skin-black-light .main-header>.navbar .nav>li>a:hover,.skin-black-light .main-header>.navbar .nav>li>a:active,.skin-black-light .main-header>.navbar .nav>li>a:focus,.skin-black-light .main-header>.navbar .nav .open>a,.skin-black-light .main-header>.navbar .nav .open>a:hover,.skin-black-light .main-header>.navbar .nav .open>a:focus,.skin-black-light .main-header>.navbar .nav>.active>a{background:#fff;color:#999}.skin-black-light .main-header>.navbar .sidebar-toggle{color:#333}.skin-black-light .main-header>.navbar .sidebar-toggle:hover{color:#999;background:#fff}.skin-black-light .main-header>.navbar>.sidebar-toggle{color:#333;border-right:1px solid #eee}.skin-black-light .main-header>.navbar .navbar-nav>li>a{border-right:1px solid #eee}.skin-black-light .main-header>.navbar .navbar-custom-menu .navbar-nav>li>a,.skin-black-light .main-header>.navbar .navbar-right>li>a{border-left:1px solid #eee;border-right-width:0}.skin-black-light .main-header>.logo{background-color:#fff;color:#333;border-bottom:0 solid transparent;border-right:1px solid #eee}.skin-black-light .main-header>.logo:hover{background-color:#fcfcfc}@media (max-width:767px){.skin-black-light .main-header>.logo{background-color:#222;color:#fff;border-bottom:0 solid transparent;border-right:none}.skin-black-light .main-header>.logo:hover{background-color:#1f1f1f}}.skin-black-light .main-header li.user-header{background-color:#222}.skin-black-light .content-header{background:transparent;box-shadow:none}.skin-black-light .wrapper,.skin-black-light .main-sidebar,.skin-black-light .left-side{background-color:#f9fafc}.skin-black-light .content-wrapper,.skin-black-light .main-footer{border-left:1px solid #d2d6de}.skin-black-light .user-panel>.info,.skin-black-light .user-panel>.info>a{color:#444}.skin-black-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-black-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-black-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-black-light .sidebar-menu>li:hover>a,.skin-black-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-black-light .sidebar-menu>li.active{border-left-color:#fff}.skin-black-light .sidebar-menu>li.active>a{font-weight:600}.skin-black-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-black-light .sidebar a{color:#444}.skin-black-light .sidebar a:hover{text-decoration:none}.skin-black-light .treeview-menu>li>a{color:#777}.skin-black-light .treeview-menu>li.active>a,.skin-black-light .treeview-menu>li>a:hover{color:#000}.skin-black-light .treeview-menu>li.active>a{font-weight:600}.skin-black-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-black-light .sidebar-form input[type="text"],.skin-black-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-black-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-black-light .sidebar-form input[type="text"]:focus,.skin-black-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-black-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}

View File

@@ -0,0 +1,161 @@
/*
* Skin: Black
* -----------
*/
/* skin-black navbar */
.skin-black .main-header {
-webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
}
.skin-black .main-header .navbar-toggle {
color: #333;
}
.skin-black .main-header .navbar-brand {
color: #333;
border-right: 1px solid #eee;
}
.skin-black .main-header > .navbar {
background-color: #ffffff;
}
.skin-black .main-header > .navbar .nav > li > a {
color: #333333;
}
.skin-black .main-header > .navbar .nav > li > a:hover,
.skin-black .main-header > .navbar .nav > li > a:active,
.skin-black .main-header > .navbar .nav > li > a:focus,
.skin-black .main-header > .navbar .nav .open > a,
.skin-black .main-header > .navbar .nav .open > a:hover,
.skin-black .main-header > .navbar .nav .open > a:focus,
.skin-black .main-header > .navbar .nav > .active > a {
background: #ffffff;
color: #999999;
}
.skin-black .main-header > .navbar .sidebar-toggle {
color: #333333;
}
.skin-black .main-header > .navbar .sidebar-toggle:hover {
color: #999999;
background: #ffffff;
}
.skin-black .main-header > .navbar > .sidebar-toggle {
color: #333;
border-right: 1px solid #eee;
}
.skin-black .main-header > .navbar .navbar-nav > li > a {
border-right: 1px solid #eee;
}
.skin-black .main-header > .navbar .navbar-custom-menu .navbar-nav > li > a,
.skin-black .main-header > .navbar .navbar-right > li > a {
border-left: 1px solid #eee;
border-right-width: 0;
}
.skin-black .main-header > .logo {
background-color: #ffffff;
color: #333333;
border-bottom: 0 solid transparent;
border-right: 1px solid #eee;
}
.skin-black .main-header > .logo:hover {
background-color: #fcfcfc;
}
@media (max-width: 767px) {
.skin-black .main-header > .logo {
background-color: #222222;
color: #ffffff;
border-bottom: 0 solid transparent;
border-right: none;
}
.skin-black .main-header > .logo:hover {
background-color: #1f1f1f;
}
}
.skin-black .main-header li.user-header {
background-color: #222;
}
.skin-black .content-header {
background: transparent;
box-shadow: none;
}
.skin-black .wrapper,
.skin-black .main-sidebar,
.skin-black .left-side {
background-color: #222d32;
}
.skin-black .user-panel > .info,
.skin-black .user-panel > .info > a {
color: #fff;
}
.skin-black .sidebar-menu > li.header {
color: #4b646f;
background: #1a2226;
}
.skin-black .sidebar-menu > li > a {
border-left: 3px solid transparent;
}
.skin-black .sidebar-menu > li:hover > a,
.skin-black .sidebar-menu > li.active > a {
color: #ffffff;
background: #1e282c;
border-left-color: #ffffff;
}
.skin-black .sidebar-menu > li > .treeview-menu {
margin: 0 1px;
background: #2c3b41;
}
.skin-black .sidebar a {
color: #b8c7ce;
}
.skin-black .sidebar a:hover {
text-decoration: none;
}
.skin-black .treeview-menu > li > a {
color: #8aa4af;
}
.skin-black .treeview-menu > li.active > a,
.skin-black .treeview-menu > li > a:hover {
color: #ffffff;
}
.skin-black .sidebar-form {
border-radius: 3px;
border: 1px solid #374850;
margin: 10px 10px;
}
.skin-black .sidebar-form input[type="text"],
.skin-black .sidebar-form .btn {
box-shadow: none;
background-color: #374850;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-black .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-black .sidebar-form input[type="text"]:focus,
.skin-black .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-black .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-black .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}
.skin-black .pace .pace-progress {
background: #222;
}
.skin-black .pace .pace-activity {
border-top-color: #222;
border-left-color: #222;
}

View File

@@ -0,0 +1 @@
.skin-black .main-header{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.skin-black .main-header .navbar-toggle{color:#333}.skin-black .main-header .navbar-brand{color:#333;border-right:1px solid #eee}.skin-black .main-header>.navbar{background-color:#fff}.skin-black .main-header>.navbar .nav>li>a{color:#333}.skin-black .main-header>.navbar .nav>li>a:hover,.skin-black .main-header>.navbar .nav>li>a:active,.skin-black .main-header>.navbar .nav>li>a:focus,.skin-black .main-header>.navbar .nav .open>a,.skin-black .main-header>.navbar .nav .open>a:hover,.skin-black .main-header>.navbar .nav .open>a:focus,.skin-black .main-header>.navbar .nav>.active>a{background:#fff;color:#999}.skin-black .main-header>.navbar .sidebar-toggle{color:#333}.skin-black .main-header>.navbar .sidebar-toggle:hover{color:#999;background:#fff}.skin-black .main-header>.navbar>.sidebar-toggle{color:#333;border-right:1px solid #eee}.skin-black .main-header>.navbar .navbar-nav>li>a{border-right:1px solid #eee}.skin-black .main-header>.navbar .navbar-custom-menu .navbar-nav>li>a,.skin-black .main-header>.navbar .navbar-right>li>a{border-left:1px solid #eee;border-right-width:0}.skin-black .main-header>.logo{background-color:#fff;color:#333;border-bottom:0 solid transparent;border-right:1px solid #eee}.skin-black .main-header>.logo:hover{background-color:#fcfcfc}@media (max-width:767px){.skin-black .main-header>.logo{background-color:#222;color:#fff;border-bottom:0 solid transparent;border-right:none}.skin-black .main-header>.logo:hover{background-color:#1f1f1f}}.skin-black .main-header li.user-header{background-color:#222}.skin-black .content-header{background:transparent;box-shadow:none}.skin-black .wrapper,.skin-black .main-sidebar,.skin-black .left-side{background-color:#222d32}.skin-black .user-panel>.info,.skin-black .user-panel>.info>a{color:#fff}.skin-black .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-black .sidebar-menu>li>a{border-left:3px solid transparent}.skin-black .sidebar-menu>li:hover>a,.skin-black .sidebar-menu>li.active>a{color:#fff;background:#1e282c;border-left-color:#fff}.skin-black .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-black .sidebar a{color:#b8c7ce}.skin-black .sidebar a:hover{text-decoration:none}.skin-black .treeview-menu>li>a{color:#8aa4af}.skin-black .treeview-menu>li.active>a,.skin-black .treeview-menu>li>a:hover{color:#fff}.skin-black .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-black .sidebar-form input[type="text"],.skin-black .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-black .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-black .sidebar-form input[type="text"]:focus,.skin-black .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-black .pace .pace-progress{background:#222}.skin-black .pace .pace-activity{border-top-color:#222;border-left-color:#222}

View File

@@ -0,0 +1,167 @@
/*
* Skin: Blue
* ----------
*/
.skin-blue-light .main-header .navbar {
background-color: #3c8dbc;
}
.skin-blue-light .main-header .navbar .nav > li > a {
color: #ffffff;
}
.skin-blue-light .main-header .navbar .nav > li > a:hover,
.skin-blue-light .main-header .navbar .nav > li > a:active,
.skin-blue-light .main-header .navbar .nav > li > a:focus,
.skin-blue-light .main-header .navbar .nav .open > a,
.skin-blue-light .main-header .navbar .nav .open > a:hover,
.skin-blue-light .main-header .navbar .nav .open > a:focus,
.skin-blue-light .main-header .navbar .nav > .active > a {
background: rgba(0, 0, 0, 0.1);
color: #f6f6f6;
}
.skin-blue-light .main-header .navbar .sidebar-toggle {
color: #ffffff;
}
.skin-blue-light .main-header .navbar .sidebar-toggle:hover {
color: #f6f6f6;
background: rgba(0, 0, 0, 0.1);
}
.skin-blue-light .main-header .navbar .sidebar-toggle {
color: #fff;
}
.skin-blue-light .main-header .navbar .sidebar-toggle:hover {
background-color: #367fa9;
}
@media (max-width: 767px) {
.skin-blue-light .main-header .navbar .dropdown-menu li.divider {
background-color: rgba(255, 255, 255, 0.1);
}
.skin-blue-light .main-header .navbar .dropdown-menu li a {
color: #fff;
}
.skin-blue-light .main-header .navbar .dropdown-menu li a:hover {
background: #367fa9;
}
}
.skin-blue-light .main-header .logo {
background-color: #3c8dbc;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-blue-light .main-header .logo:hover {
background-color: #3b8ab8;
}
.skin-blue-light .main-header li.user-header {
background-color: #3c8dbc;
}
.skin-blue-light .content-header {
background: transparent;
}
.skin-blue-light .wrapper,
.skin-blue-light .main-sidebar,
.skin-blue-light .left-side {
background-color: #f9fafc;
}
.skin-blue-light .content-wrapper,
.skin-blue-light .main-footer {
border-left: 1px solid #d2d6de;
}
.skin-blue-light .user-panel > .info,
.skin-blue-light .user-panel > .info > a {
color: #444444;
}
.skin-blue-light .sidebar-menu > li {
-webkit-transition: border-left-color 0.3s ease;
-o-transition: border-left-color 0.3s ease;
transition: border-left-color 0.3s ease;
}
.skin-blue-light .sidebar-menu > li.header {
color: #848484;
background: #f9fafc;
}
.skin-blue-light .sidebar-menu > li > a {
border-left: 3px solid transparent;
font-weight: 600;
}
.skin-blue-light .sidebar-menu > li:hover > a,
.skin-blue-light .sidebar-menu > li.active > a {
color: #000000;
background: #f4f4f5;
}
.skin-blue-light .sidebar-menu > li.active {
border-left-color: #3c8dbc;
}
.skin-blue-light .sidebar-menu > li.active > a {
font-weight: 600;
}
.skin-blue-light .sidebar-menu > li > .treeview-menu {
background: #f4f4f5;
}
.skin-blue-light .sidebar a {
color: #444444;
}
.skin-blue-light .sidebar a:hover {
text-decoration: none;
}
.skin-blue-light .treeview-menu > li > a {
color: #777777;
}
.skin-blue-light .treeview-menu > li.active > a,
.skin-blue-light .treeview-menu > li > a:hover {
color: #000000;
}
.skin-blue-light .treeview-menu > li.active > a {
font-weight: 600;
}
.skin-blue-light .sidebar-form {
border-radius: 3px;
border: 1px solid #d2d6de;
margin: 10px 10px;
}
.skin-blue-light .sidebar-form input[type="text"],
.skin-blue-light .sidebar-form .btn {
box-shadow: none;
background-color: #fff;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-blue-light .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-blue-light .sidebar-form input[type="text"]:focus,
.skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-blue-light .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}
@media (min-width: 768px) {
.skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
border-left: 1px solid #d2d6de;
}
}
.skin-blue-light .main-footer {
border-top-color: #d2d6de;
}
.skin-blue.layout-top-nav .main-header > .logo {
background-color: #3c8dbc;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-blue.layout-top-nav .main-header > .logo:hover {
background-color: #3b8ab8;
}

View File

@@ -0,0 +1 @@
.skin-blue-light .main-header .navbar{background-color:#3c8dbc}.skin-blue-light .main-header .navbar .nav>li>a{color:#fff}.skin-blue-light .main-header .navbar .nav>li>a:hover,.skin-blue-light .main-header .navbar .nav>li>a:active,.skin-blue-light .main-header .navbar .nav>li>a:focus,.skin-blue-light .main-header .navbar .nav .open>a,.skin-blue-light .main-header .navbar .nav .open>a:hover,.skin-blue-light .main-header .navbar .nav .open>a:focus,.skin-blue-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue-light .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue-light .main-header .logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue-light .main-header .logo:hover{background-color:#3b8ab8}.skin-blue-light .main-header li.user-header{background-color:#3c8dbc}.skin-blue-light .content-header{background:transparent}.skin-blue-light .wrapper,.skin-blue-light .main-sidebar,.skin-blue-light .left-side{background-color:#f9fafc}.skin-blue-light .content-wrapper,.skin-blue-light .main-footer{border-left:1px solid #d2d6de}.skin-blue-light .user-panel>.info,.skin-blue-light .user-panel>.info>a{color:#444}.skin-blue-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-blue-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-blue-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-blue-light .sidebar-menu>li:hover>a,.skin-blue-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-blue-light .sidebar-menu>li.active{border-left-color:#3c8dbc}.skin-blue-light .sidebar-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-blue-light .sidebar a{color:#444}.skin-blue-light .sidebar a:hover{text-decoration:none}.skin-blue-light .treeview-menu>li>a{color:#777}.skin-blue-light .treeview-menu>li.active>a,.skin-blue-light .treeview-menu>li>a:hover{color:#000}.skin-blue-light .treeview-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-blue-light .sidebar-form input[type="text"],.skin-blue-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue-light .sidebar-form input[type="text"]:focus,.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}.skin-blue-light .main-footer{border-top-color:#d2d6de}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}

View File

@@ -0,0 +1,142 @@
/*
* Skin: Blue
* ----------
*/
.skin-blue .main-header .navbar {
background-color: #3c8dbc;
}
.skin-blue .main-header .navbar .nav > li > a {
color: #ffffff;
}
.skin-blue .main-header .navbar .nav > li > a:hover,
.skin-blue .main-header .navbar .nav > li > a:active,
.skin-blue .main-header .navbar .nav > li > a:focus,
.skin-blue .main-header .navbar .nav .open > a,
.skin-blue .main-header .navbar .nav .open > a:hover,
.skin-blue .main-header .navbar .nav .open > a:focus,
.skin-blue .main-header .navbar .nav > .active > a {
background: rgba(0, 0, 0, 0.1);
color: #f6f6f6;
}
.skin-blue .main-header .navbar .sidebar-toggle {
color: #ffffff;
}
.skin-blue .main-header .navbar .sidebar-toggle:hover {
color: #f6f6f6;
background: rgba(0, 0, 0, 0.1);
}
.skin-blue .main-header .navbar .sidebar-toggle {
color: #fff;
}
.skin-blue .main-header .navbar .sidebar-toggle:hover {
background-color: #367fa9;
}
@media (max-width: 767px) {
.skin-blue .main-header .navbar .dropdown-menu li.divider {
background-color: rgba(255, 255, 255, 0.1);
}
.skin-blue .main-header .navbar .dropdown-menu li a {
color: #fff;
}
.skin-blue .main-header .navbar .dropdown-menu li a:hover {
background: #367fa9;
}
}
.skin-blue .main-header .logo {
background-color: #367fa9;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-blue .main-header .logo:hover {
background-color: #357ca5;
}
.skin-blue .main-header li.user-header {
background-color: #3c8dbc;
}
.skin-blue .content-header {
background: transparent;
}
.skin-blue .wrapper,
.skin-blue .main-sidebar,
.skin-blue .left-side {
background-color: #222d32;
}
.skin-blue .user-panel > .info,
.skin-blue .user-panel > .info > a {
color: #fff;
}
.skin-blue .sidebar-menu > li.header {
color: #4b646f;
background: #1a2226;
}
.skin-blue .sidebar-menu > li > a {
border-left: 3px solid transparent;
}
.skin-blue .sidebar-menu > li:hover > a,
.skin-blue .sidebar-menu > li.active > a {
color: #ffffff;
background: #1e282c;
border-left-color: #3c8dbc;
}
.skin-blue .sidebar-menu > li > .treeview-menu {
margin: 0 1px;
background: #2c3b41;
}
.skin-blue .sidebar a {
color: #b8c7ce;
}
.skin-blue .sidebar a:hover {
text-decoration: none;
}
.skin-blue .treeview-menu > li > a {
color: #8aa4af;
}
.skin-blue .treeview-menu > li.active > a,
.skin-blue .treeview-menu > li > a:hover {
color: #ffffff;
}
.skin-blue .sidebar-form {
border-radius: 3px;
border: 1px solid #374850;
margin: 10px 10px;
}
.skin-blue .sidebar-form input[type="text"],
.skin-blue .sidebar-form .btn {
box-shadow: none;
background-color: #374850;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-blue .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-blue .sidebar-form input[type="text"]:focus,
.skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-blue .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}
.skin-blue.layout-top-nav .main-header > .logo {
background-color: #3c8dbc;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-blue.layout-top-nav .main-header > .logo:hover {
background-color: #3b8ab8;
}

View File

@@ -0,0 +1 @@
.skin-blue .main-header .navbar{background-color:#3c8dbc}.skin-blue .main-header .navbar .nav>li>a{color:#fff}.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue .main-header .logo{background-color:#367fa9;color:#fff;border-bottom:0 solid transparent}.skin-blue .main-header .logo:hover{background-color:#357ca5}.skin-blue .main-header li.user-header{background-color:#3c8dbc}.skin-blue .content-header{background:transparent}.skin-blue .wrapper,.skin-blue .main-sidebar,.skin-blue .left-side{background-color:#222d32}.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#fff}.skin-blue .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.skin-blue .sidebar-menu>li:hover>a,.skin-blue .sidebar-menu>li.active>a{color:#fff;background:#1e282c;border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .treeview-menu>li>a{color:#8aa4af}.skin-blue .treeview-menu>li.active>a,.skin-blue .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-blue .sidebar-form input[type="text"],.skin-blue .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue .sidebar-form input[type="text"]:focus,.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}

View File

@@ -0,0 +1,156 @@
/*
* Skin: Green
* -----------
*/
.skin-green-light .main-header .navbar {
background-color: #00a65a;
}
.skin-green-light .main-header .navbar .nav > li > a {
color: #ffffff;
}
.skin-green-light .main-header .navbar .nav > li > a:hover,
.skin-green-light .main-header .navbar .nav > li > a:active,
.skin-green-light .main-header .navbar .nav > li > a:focus,
.skin-green-light .main-header .navbar .nav .open > a,
.skin-green-light .main-header .navbar .nav .open > a:hover,
.skin-green-light .main-header .navbar .nav .open > a:focus,
.skin-green-light .main-header .navbar .nav > .active > a {
background: rgba(0, 0, 0, 0.1);
color: #f6f6f6;
}
.skin-green-light .main-header .navbar .sidebar-toggle {
color: #ffffff;
}
.skin-green-light .main-header .navbar .sidebar-toggle:hover {
color: #f6f6f6;
background: rgba(0, 0, 0, 0.1);
}
.skin-green-light .main-header .navbar .sidebar-toggle {
color: #fff;
}
.skin-green-light .main-header .navbar .sidebar-toggle:hover {
background-color: #008d4c;
}
@media (max-width: 767px) {
.skin-green-light .main-header .navbar .dropdown-menu li.divider {
background-color: rgba(255, 255, 255, 0.1);
}
.skin-green-light .main-header .navbar .dropdown-menu li a {
color: #fff;
}
.skin-green-light .main-header .navbar .dropdown-menu li a:hover {
background: #008d4c;
}
}
.skin-green-light .main-header .logo {
background-color: #00a65a;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-green-light .main-header .logo:hover {
background-color: #00a157;
}
.skin-green-light .main-header li.user-header {
background-color: #00a65a;
}
.skin-green-light .content-header {
background: transparent;
}
.skin-green-light .wrapper,
.skin-green-light .main-sidebar,
.skin-green-light .left-side {
background-color: #f9fafc;
}
.skin-green-light .content-wrapper,
.skin-green-light .main-footer {
border-left: 1px solid #d2d6de;
}
.skin-green-light .user-panel > .info,
.skin-green-light .user-panel > .info > a {
color: #444444;
}
.skin-green-light .sidebar-menu > li {
-webkit-transition: border-left-color 0.3s ease;
-o-transition: border-left-color 0.3s ease;
transition: border-left-color 0.3s ease;
}
.skin-green-light .sidebar-menu > li.header {
color: #848484;
background: #f9fafc;
}
.skin-green-light .sidebar-menu > li > a {
border-left: 3px solid transparent;
font-weight: 600;
}
.skin-green-light .sidebar-menu > li:hover > a,
.skin-green-light .sidebar-menu > li.active > a {
color: #000000;
background: #f4f4f5;
}
.skin-green-light .sidebar-menu > li.active {
border-left-color: #00a65a;
}
.skin-green-light .sidebar-menu > li.active > a {
font-weight: 600;
}
.skin-green-light .sidebar-menu > li > .treeview-menu {
background: #f4f4f5;
}
.skin-green-light .sidebar a {
color: #444444;
}
.skin-green-light .sidebar a:hover {
text-decoration: none;
}
.skin-green-light .treeview-menu > li > a {
color: #777777;
}
.skin-green-light .treeview-menu > li.active > a,
.skin-green-light .treeview-menu > li > a:hover {
color: #000000;
}
.skin-green-light .treeview-menu > li.active > a {
font-weight: 600;
}
.skin-green-light .sidebar-form {
border-radius: 3px;
border: 1px solid #d2d6de;
margin: 10px 10px;
}
.skin-green-light .sidebar-form input[type="text"],
.skin-green-light .sidebar-form .btn {
box-shadow: none;
background-color: #fff;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-green-light .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-green-light .sidebar-form input[type="text"]:focus,
.skin-green-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-green-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-green-light .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}
@media (min-width: 768px) {
.skin-green-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
border-left: 1px solid #d2d6de;
}
}

View File

@@ -0,0 +1 @@
.skin-green-light .main-header .navbar{background-color:#00a65a}.skin-green-light .main-header .navbar .nav>li>a{color:#fff}.skin-green-light .main-header .navbar .nav>li>a:hover,.skin-green-light .main-header .navbar .nav>li>a:active,.skin-green-light .main-header .navbar .nav>li>a:focus,.skin-green-light .main-header .navbar .nav .open>a,.skin-green-light .main-header .navbar .nav .open>a:hover,.skin-green-light .main-header .navbar .nav .open>a:focus,.skin-green-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-green-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-green-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-green-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-green-light .main-header .navbar .sidebar-toggle:hover{background-color:#008d4c}@media (max-width:767px){.skin-green-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-green-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-green-light .main-header .navbar .dropdown-menu li a:hover{background:#008d4c}}.skin-green-light .main-header .logo{background-color:#00a65a;color:#fff;border-bottom:0 solid transparent}.skin-green-light .main-header .logo:hover{background-color:#00a157}.skin-green-light .main-header li.user-header{background-color:#00a65a}.skin-green-light .content-header{background:transparent}.skin-green-light .wrapper,.skin-green-light .main-sidebar,.skin-green-light .left-side{background-color:#f9fafc}.skin-green-light .content-wrapper,.skin-green-light .main-footer{border-left:1px solid #d2d6de}.skin-green-light .user-panel>.info,.skin-green-light .user-panel>.info>a{color:#444}.skin-green-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-green-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-green-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-green-light .sidebar-menu>li:hover>a,.skin-green-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-green-light .sidebar-menu>li.active{border-left-color:#00a65a}.skin-green-light .sidebar-menu>li.active>a{font-weight:600}.skin-green-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-green-light .sidebar a{color:#444}.skin-green-light .sidebar a:hover{text-decoration:none}.skin-green-light .treeview-menu>li>a{color:#777}.skin-green-light .treeview-menu>li.active>a,.skin-green-light .treeview-menu>li>a:hover{color:#000}.skin-green-light .treeview-menu>li.active>a{font-weight:600}.skin-green-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-green-light .sidebar-form input[type="text"],.skin-green-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-green-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-green-light .sidebar-form input[type="text"]:focus,.skin-green-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-green-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-green-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-green-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}

View File

@@ -0,0 +1,134 @@
/*
* Skin: Green
* -----------
*/
.skin-green .main-header .navbar {
background-color: #00a65a;
}
.skin-green .main-header .navbar .nav > li > a {
color: #ffffff;
}
.skin-green .main-header .navbar .nav > li > a:hover,
.skin-green .main-header .navbar .nav > li > a:active,
.skin-green .main-header .navbar .nav > li > a:focus,
.skin-green .main-header .navbar .nav .open > a,
.skin-green .main-header .navbar .nav .open > a:hover,
.skin-green .main-header .navbar .nav .open > a:focus,
.skin-green .main-header .navbar .nav > .active > a {
background: rgba(0, 0, 0, 0.1);
color: #f6f6f6;
}
.skin-green .main-header .navbar .sidebar-toggle {
color: #ffffff;
}
.skin-green .main-header .navbar .sidebar-toggle:hover {
color: #f6f6f6;
background: rgba(0, 0, 0, 0.1);
}
.skin-green .main-header .navbar .sidebar-toggle {
color: #fff;
}
.skin-green .main-header .navbar .sidebar-toggle:hover {
background-color: #008d4c;
}
@media (max-width: 767px) {
.skin-green .main-header .navbar .dropdown-menu li.divider {
background-color: rgba(255, 255, 255, 0.1);
}
.skin-green .main-header .navbar .dropdown-menu li a {
color: #fff;
}
.skin-green .main-header .navbar .dropdown-menu li a:hover {
background: #008d4c;
}
}
.skin-green .main-header .logo {
background-color: #008d4c;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-green .main-header .logo:hover {
background-color: #008749;
}
.skin-green .main-header li.user-header {
background-color: #00a65a;
}
.skin-green .content-header {
background: transparent;
}
.skin-green .wrapper,
.skin-green .main-sidebar,
.skin-green .left-side {
background-color: #222d32;
}
.skin-green .user-panel > .info,
.skin-green .user-panel > .info > a {
color: #fff;
}
.skin-green .sidebar-menu > li.header {
color: #4b646f;
background: #1a2226;
}
.skin-green .sidebar-menu > li > a {
border-left: 3px solid transparent;
}
.skin-green .sidebar-menu > li:hover > a,
.skin-green .sidebar-menu > li.active > a {
color: #ffffff;
background: #1e282c;
border-left-color: #00a65a;
}
.skin-green .sidebar-menu > li > .treeview-menu {
margin: 0 1px;
background: #2c3b41;
}
.skin-green .sidebar a {
color: #b8c7ce;
}
.skin-green .sidebar a:hover {
text-decoration: none;
}
.skin-green .treeview-menu > li > a {
color: #8aa4af;
}
.skin-green .treeview-menu > li.active > a,
.skin-green .treeview-menu > li > a:hover {
color: #ffffff;
}
.skin-green .sidebar-form {
border-radius: 3px;
border: 1px solid #374850;
margin: 10px 10px;
}
.skin-green .sidebar-form input[type="text"],
.skin-green .sidebar-form .btn {
box-shadow: none;
background-color: #374850;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-green .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-green .sidebar-form input[type="text"]:focus,
.skin-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-green .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}

View File

@@ -0,0 +1 @@
.skin-green .main-header .navbar{background-color:#00a65a}.skin-green .main-header .navbar .nav>li>a{color:#fff}.skin-green .main-header .navbar .nav>li>a:hover,.skin-green .main-header .navbar .nav>li>a:active,.skin-green .main-header .navbar .nav>li>a:focus,.skin-green .main-header .navbar .nav .open>a,.skin-green .main-header .navbar .nav .open>a:hover,.skin-green .main-header .navbar .nav .open>a:focus,.skin-green .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{background-color:#008d4c}@media (max-width:767px){.skin-green .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-green .main-header .navbar .dropdown-menu li a{color:#fff}.skin-green .main-header .navbar .dropdown-menu li a:hover{background:#008d4c}}.skin-green .main-header .logo{background-color:#008d4c;color:#fff;border-bottom:0 solid transparent}.skin-green .main-header .logo:hover{background-color:#008749}.skin-green .main-header li.user-header{background-color:#00a65a}.skin-green .content-header{background:transparent}.skin-green .wrapper,.skin-green .main-sidebar,.skin-green .left-side{background-color:#222d32}.skin-green .user-panel>.info,.skin-green .user-panel>.info>a{color:#fff}.skin-green .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-green .sidebar-menu>li>a{border-left:3px solid transparent}.skin-green .sidebar-menu>li:hover>a,.skin-green .sidebar-menu>li.active>a{color:#fff;background:#1e282c;border-left-color:#00a65a}.skin-green .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-green .sidebar a{color:#b8c7ce}.skin-green .sidebar a:hover{text-decoration:none}.skin-green .treeview-menu>li>a{color:#8aa4af}.skin-green .treeview-menu>li.active>a,.skin-green .treeview-menu>li>a:hover{color:#fff}.skin-green .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-green .sidebar-form input[type="text"],.skin-green .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-green .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-green .sidebar-form input[type="text"]:focus,.skin-green .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-green .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-green .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}

View File

@@ -0,0 +1,156 @@
/*
* Skin: Purple
* ------------
*/
.skin-purple-light .main-header .navbar {
background-color: #605ca8;
}
.skin-purple-light .main-header .navbar .nav > li > a {
color: #ffffff;
}
.skin-purple-light .main-header .navbar .nav > li > a:hover,
.skin-purple-light .main-header .navbar .nav > li > a:active,
.skin-purple-light .main-header .navbar .nav > li > a:focus,
.skin-purple-light .main-header .navbar .nav .open > a,
.skin-purple-light .main-header .navbar .nav .open > a:hover,
.skin-purple-light .main-header .navbar .nav .open > a:focus,
.skin-purple-light .main-header .navbar .nav > .active > a {
background: rgba(0, 0, 0, 0.1);
color: #f6f6f6;
}
.skin-purple-light .main-header .navbar .sidebar-toggle {
color: #ffffff;
}
.skin-purple-light .main-header .navbar .sidebar-toggle:hover {
color: #f6f6f6;
background: rgba(0, 0, 0, 0.1);
}
.skin-purple-light .main-header .navbar .sidebar-toggle {
color: #fff;
}
.skin-purple-light .main-header .navbar .sidebar-toggle:hover {
background-color: #555299;
}
@media (max-width: 767px) {
.skin-purple-light .main-header .navbar .dropdown-menu li.divider {
background-color: rgba(255, 255, 255, 0.1);
}
.skin-purple-light .main-header .navbar .dropdown-menu li a {
color: #fff;
}
.skin-purple-light .main-header .navbar .dropdown-menu li a:hover {
background: #555299;
}
}
.skin-purple-light .main-header .logo {
background-color: #605ca8;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-purple-light .main-header .logo:hover {
background-color: #5d59a6;
}
.skin-purple-light .main-header li.user-header {
background-color: #605ca8;
}
.skin-purple-light .content-header {
background: transparent;
}
.skin-purple-light .wrapper,
.skin-purple-light .main-sidebar,
.skin-purple-light .left-side {
background-color: #f9fafc;
}
.skin-purple-light .content-wrapper,
.skin-purple-light .main-footer {
border-left: 1px solid #d2d6de;
}
.skin-purple-light .user-panel > .info,
.skin-purple-light .user-panel > .info > a {
color: #444444;
}
.skin-purple-light .sidebar-menu > li {
-webkit-transition: border-left-color 0.3s ease;
-o-transition: border-left-color 0.3s ease;
transition: border-left-color 0.3s ease;
}
.skin-purple-light .sidebar-menu > li.header {
color: #848484;
background: #f9fafc;
}
.skin-purple-light .sidebar-menu > li > a {
border-left: 3px solid transparent;
font-weight: 600;
}
.skin-purple-light .sidebar-menu > li:hover > a,
.skin-purple-light .sidebar-menu > li.active > a {
color: #000000;
background: #f4f4f5;
}
.skin-purple-light .sidebar-menu > li.active {
border-left-color: #605ca8;
}
.skin-purple-light .sidebar-menu > li.active > a {
font-weight: 600;
}
.skin-purple-light .sidebar-menu > li > .treeview-menu {
background: #f4f4f5;
}
.skin-purple-light .sidebar a {
color: #444444;
}
.skin-purple-light .sidebar a:hover {
text-decoration: none;
}
.skin-purple-light .treeview-menu > li > a {
color: #777777;
}
.skin-purple-light .treeview-menu > li.active > a,
.skin-purple-light .treeview-menu > li > a:hover {
color: #000000;
}
.skin-purple-light .treeview-menu > li.active > a {
font-weight: 600;
}
.skin-purple-light .sidebar-form {
border-radius: 3px;
border: 1px solid #d2d6de;
margin: 10px 10px;
}
.skin-purple-light .sidebar-form input[type="text"],
.skin-purple-light .sidebar-form .btn {
box-shadow: none;
background-color: #fff;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-purple-light .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-purple-light .sidebar-form input[type="text"]:focus,
.skin-purple-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-purple-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-purple-light .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}
@media (min-width: 768px) {
.skin-purple-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
border-left: 1px solid #d2d6de;
}
}

View File

@@ -0,0 +1 @@
.skin-purple-light .main-header .navbar{background-color:#605ca8}.skin-purple-light .main-header .navbar .nav>li>a{color:#fff}.skin-purple-light .main-header .navbar .nav>li>a:hover,.skin-purple-light .main-header .navbar .nav>li>a:active,.skin-purple-light .main-header .navbar .nav>li>a:focus,.skin-purple-light .main-header .navbar .nav .open>a,.skin-purple-light .main-header .navbar .nav .open>a:hover,.skin-purple-light .main-header .navbar .nav .open>a:focus,.skin-purple-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-purple-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-purple-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple-light .main-header .navbar .sidebar-toggle:hover{background-color:#555299}@media (max-width:767px){.skin-purple-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-purple-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-purple-light .main-header .navbar .dropdown-menu li a:hover{background:#555299}}.skin-purple-light .main-header .logo{background-color:#605ca8;color:#fff;border-bottom:0 solid transparent}.skin-purple-light .main-header .logo:hover{background-color:#5d59a6}.skin-purple-light .main-header li.user-header{background-color:#605ca8}.skin-purple-light .content-header{background:transparent}.skin-purple-light .wrapper,.skin-purple-light .main-sidebar,.skin-purple-light .left-side{background-color:#f9fafc}.skin-purple-light .content-wrapper,.skin-purple-light .main-footer{border-left:1px solid #d2d6de}.skin-purple-light .user-panel>.info,.skin-purple-light .user-panel>.info>a{color:#444}.skin-purple-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-purple-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-purple-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-purple-light .sidebar-menu>li:hover>a,.skin-purple-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-purple-light .sidebar-menu>li.active{border-left-color:#605ca8}.skin-purple-light .sidebar-menu>li.active>a{font-weight:600}.skin-purple-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-purple-light .sidebar a{color:#444}.skin-purple-light .sidebar a:hover{text-decoration:none}.skin-purple-light .treeview-menu>li>a{color:#777}.skin-purple-light .treeview-menu>li.active>a,.skin-purple-light .treeview-menu>li>a:hover{color:#000}.skin-purple-light .treeview-menu>li.active>a{font-weight:600}.skin-purple-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-purple-light .sidebar-form input[type="text"],.skin-purple-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-purple-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-purple-light .sidebar-form input[type="text"]:focus,.skin-purple-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-purple-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-purple-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-purple-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}

View File

@@ -0,0 +1,134 @@
/*
* Skin: Purple
* ------------
*/
.skin-purple .main-header .navbar {
background-color: #605ca8;
}
.skin-purple .main-header .navbar .nav > li > a {
color: #ffffff;
}
.skin-purple .main-header .navbar .nav > li > a:hover,
.skin-purple .main-header .navbar .nav > li > a:active,
.skin-purple .main-header .navbar .nav > li > a:focus,
.skin-purple .main-header .navbar .nav .open > a,
.skin-purple .main-header .navbar .nav .open > a:hover,
.skin-purple .main-header .navbar .nav .open > a:focus,
.skin-purple .main-header .navbar .nav > .active > a {
background: rgba(0, 0, 0, 0.1);
color: #f6f6f6;
}
.skin-purple .main-header .navbar .sidebar-toggle {
color: #ffffff;
}
.skin-purple .main-header .navbar .sidebar-toggle:hover {
color: #f6f6f6;
background: rgba(0, 0, 0, 0.1);
}
.skin-purple .main-header .navbar .sidebar-toggle {
color: #fff;
}
.skin-purple .main-header .navbar .sidebar-toggle:hover {
background-color: #555299;
}
@media (max-width: 767px) {
.skin-purple .main-header .navbar .dropdown-menu li.divider {
background-color: rgba(255, 255, 255, 0.1);
}
.skin-purple .main-header .navbar .dropdown-menu li a {
color: #fff;
}
.skin-purple .main-header .navbar .dropdown-menu li a:hover {
background: #555299;
}
}
.skin-purple .main-header .logo {
background-color: #555299;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-purple .main-header .logo:hover {
background-color: #545096;
}
.skin-purple .main-header li.user-header {
background-color: #605ca8;
}
.skin-purple .content-header {
background: transparent;
}
.skin-purple .wrapper,
.skin-purple .main-sidebar,
.skin-purple .left-side {
background-color: #222d32;
}
.skin-purple .user-panel > .info,
.skin-purple .user-panel > .info > a {
color: #fff;
}
.skin-purple .sidebar-menu > li.header {
color: #4b646f;
background: #1a2226;
}
.skin-purple .sidebar-menu > li > a {
border-left: 3px solid transparent;
}
.skin-purple .sidebar-menu > li:hover > a,
.skin-purple .sidebar-menu > li.active > a {
color: #ffffff;
background: #1e282c;
border-left-color: #605ca8;
}
.skin-purple .sidebar-menu > li > .treeview-menu {
margin: 0 1px;
background: #2c3b41;
}
.skin-purple .sidebar a {
color: #b8c7ce;
}
.skin-purple .sidebar a:hover {
text-decoration: none;
}
.skin-purple .treeview-menu > li > a {
color: #8aa4af;
}
.skin-purple .treeview-menu > li.active > a,
.skin-purple .treeview-menu > li > a:hover {
color: #ffffff;
}
.skin-purple .sidebar-form {
border-radius: 3px;
border: 1px solid #374850;
margin: 10px 10px;
}
.skin-purple .sidebar-form input[type="text"],
.skin-purple .sidebar-form .btn {
box-shadow: none;
background-color: #374850;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-purple .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-purple .sidebar-form input[type="text"]:focus,
.skin-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-purple .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}

View File

@@ -0,0 +1 @@
.skin-purple .main-header .navbar{background-color:#605ca8}.skin-purple .main-header .navbar .nav>li>a{color:#fff}.skin-purple .main-header .navbar .nav>li>a:hover,.skin-purple .main-header .navbar .nav>li>a:active,.skin-purple .main-header .navbar .nav>li>a:focus,.skin-purple .main-header .navbar .nav .open>a,.skin-purple .main-header .navbar .nav .open>a:hover,.skin-purple .main-header .navbar .nav .open>a:focus,.skin-purple .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-purple .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-purple .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple .main-header .navbar .sidebar-toggle:hover{background-color:#555299}@media (max-width:767px){.skin-purple .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-purple .main-header .navbar .dropdown-menu li a{color:#fff}.skin-purple .main-header .navbar .dropdown-menu li a:hover{background:#555299}}.skin-purple .main-header .logo{background-color:#555299;color:#fff;border-bottom:0 solid transparent}.skin-purple .main-header .logo:hover{background-color:#545096}.skin-purple .main-header li.user-header{background-color:#605ca8}.skin-purple .content-header{background:transparent}.skin-purple .wrapper,.skin-purple .main-sidebar,.skin-purple .left-side{background-color:#222d32}.skin-purple .user-panel>.info,.skin-purple .user-panel>.info>a{color:#fff}.skin-purple .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-purple .sidebar-menu>li>a{border-left:3px solid transparent}.skin-purple .sidebar-menu>li:hover>a,.skin-purple .sidebar-menu>li.active>a{color:#fff;background:#1e282c;border-left-color:#605ca8}.skin-purple .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-purple .sidebar a{color:#b8c7ce}.skin-purple .sidebar a:hover{text-decoration:none}.skin-purple .treeview-menu>li>a{color:#8aa4af}.skin-purple .treeview-menu>li.active>a,.skin-purple .treeview-menu>li>a:hover{color:#fff}.skin-purple .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-purple .sidebar-form input[type="text"],.skin-purple .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-purple .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-purple .sidebar-form input[type="text"]:focus,.skin-purple .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-purple .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-purple .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}

View File

@@ -0,0 +1,156 @@
/*
* Skin: Red
* ---------
*/
.skin-red-light .main-header .navbar {
background-color: #dd4b39;
}
.skin-red-light .main-header .navbar .nav > li > a {
color: #ffffff;
}
.skin-red-light .main-header .navbar .nav > li > a:hover,
.skin-red-light .main-header .navbar .nav > li > a:active,
.skin-red-light .main-header .navbar .nav > li > a:focus,
.skin-red-light .main-header .navbar .nav .open > a,
.skin-red-light .main-header .navbar .nav .open > a:hover,
.skin-red-light .main-header .navbar .nav .open > a:focus,
.skin-red-light .main-header .navbar .nav > .active > a {
background: rgba(0, 0, 0, 0.1);
color: #f6f6f6;
}
.skin-red-light .main-header .navbar .sidebar-toggle {
color: #ffffff;
}
.skin-red-light .main-header .navbar .sidebar-toggle:hover {
color: #f6f6f6;
background: rgba(0, 0, 0, 0.1);
}
.skin-red-light .main-header .navbar .sidebar-toggle {
color: #fff;
}
.skin-red-light .main-header .navbar .sidebar-toggle:hover {
background-color: #d73925;
}
@media (max-width: 767px) {
.skin-red-light .main-header .navbar .dropdown-menu li.divider {
background-color: rgba(255, 255, 255, 0.1);
}
.skin-red-light .main-header .navbar .dropdown-menu li a {
color: #fff;
}
.skin-red-light .main-header .navbar .dropdown-menu li a:hover {
background: #d73925;
}
}
.skin-red-light .main-header .logo {
background-color: #dd4b39;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-red-light .main-header .logo:hover {
background-color: #dc4735;
}
.skin-red-light .main-header li.user-header {
background-color: #dd4b39;
}
.skin-red-light .content-header {
background: transparent;
}
.skin-red-light .wrapper,
.skin-red-light .main-sidebar,
.skin-red-light .left-side {
background-color: #f9fafc;
}
.skin-red-light .content-wrapper,
.skin-red-light .main-footer {
border-left: 1px solid #d2d6de;
}
.skin-red-light .user-panel > .info,
.skin-red-light .user-panel > .info > a {
color: #444444;
}
.skin-red-light .sidebar-menu > li {
-webkit-transition: border-left-color 0.3s ease;
-o-transition: border-left-color 0.3s ease;
transition: border-left-color 0.3s ease;
}
.skin-red-light .sidebar-menu > li.header {
color: #848484;
background: #f9fafc;
}
.skin-red-light .sidebar-menu > li > a {
border-left: 3px solid transparent;
font-weight: 600;
}
.skin-red-light .sidebar-menu > li:hover > a,
.skin-red-light .sidebar-menu > li.active > a {
color: #000000;
background: #f4f4f5;
}
.skin-red-light .sidebar-menu > li.active {
border-left-color: #dd4b39;
}
.skin-red-light .sidebar-menu > li.active > a {
font-weight: 600;
}
.skin-red-light .sidebar-menu > li > .treeview-menu {
background: #f4f4f5;
}
.skin-red-light .sidebar a {
color: #444444;
}
.skin-red-light .sidebar a:hover {
text-decoration: none;
}
.skin-red-light .treeview-menu > li > a {
color: #777777;
}
.skin-red-light .treeview-menu > li.active > a,
.skin-red-light .treeview-menu > li > a:hover {
color: #000000;
}
.skin-red-light .treeview-menu > li.active > a {
font-weight: 600;
}
.skin-red-light .sidebar-form {
border-radius: 3px;
border: 1px solid #d2d6de;
margin: 10px 10px;
}
.skin-red-light .sidebar-form input[type="text"],
.skin-red-light .sidebar-form .btn {
box-shadow: none;
background-color: #fff;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-red-light .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-red-light .sidebar-form input[type="text"]:focus,
.skin-red-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-red-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-red-light .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}
@media (min-width: 768px) {
.skin-red-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
border-left: 1px solid #d2d6de;
}
}

View File

@@ -0,0 +1 @@
.skin-red-light .main-header .navbar{background-color:#dd4b39}.skin-red-light .main-header .navbar .nav>li>a{color:#fff}.skin-red-light .main-header .navbar .nav>li>a:hover,.skin-red-light .main-header .navbar .nav>li>a:active,.skin-red-light .main-header .navbar .nav>li>a:focus,.skin-red-light .main-header .navbar .nav .open>a,.skin-red-light .main-header .navbar .nav .open>a:hover,.skin-red-light .main-header .navbar .nav .open>a:focus,.skin-red-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-red-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-red-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-red-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-red-light .main-header .navbar .sidebar-toggle:hover{background-color:#d73925}@media (max-width:767px){.skin-red-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-red-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-red-light .main-header .navbar .dropdown-menu li a:hover{background:#d73925}}.skin-red-light .main-header .logo{background-color:#dd4b39;color:#fff;border-bottom:0 solid transparent}.skin-red-light .main-header .logo:hover{background-color:#dc4735}.skin-red-light .main-header li.user-header{background-color:#dd4b39}.skin-red-light .content-header{background:transparent}.skin-red-light .wrapper,.skin-red-light .main-sidebar,.skin-red-light .left-side{background-color:#f9fafc}.skin-red-light .content-wrapper,.skin-red-light .main-footer{border-left:1px solid #d2d6de}.skin-red-light .user-panel>.info,.skin-red-light .user-panel>.info>a{color:#444}.skin-red-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-red-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-red-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-red-light .sidebar-menu>li:hover>a,.skin-red-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-red-light .sidebar-menu>li.active{border-left-color:#dd4b39}.skin-red-light .sidebar-menu>li.active>a{font-weight:600}.skin-red-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-red-light .sidebar a{color:#444}.skin-red-light .sidebar a:hover{text-decoration:none}.skin-red-light .treeview-menu>li>a{color:#777}.skin-red-light .treeview-menu>li.active>a,.skin-red-light .treeview-menu>li>a:hover{color:#000}.skin-red-light .treeview-menu>li.active>a{font-weight:600}.skin-red-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-red-light .sidebar-form input[type="text"],.skin-red-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-red-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-red-light .sidebar-form input[type="text"]:focus,.skin-red-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-red-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-red-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-red-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}

View File

@@ -0,0 +1,134 @@
/*
* Skin: Red
* ---------
*/
.skin-red .main-header .navbar {
background-color: #dd4b39;
}
.skin-red .main-header .navbar .nav > li > a {
color: #ffffff;
}
.skin-red .main-header .navbar .nav > li > a:hover,
.skin-red .main-header .navbar .nav > li > a:active,
.skin-red .main-header .navbar .nav > li > a:focus,
.skin-red .main-header .navbar .nav .open > a,
.skin-red .main-header .navbar .nav .open > a:hover,
.skin-red .main-header .navbar .nav .open > a:focus,
.skin-red .main-header .navbar .nav > .active > a {
background: rgba(0, 0, 0, 0.1);
color: #f6f6f6;
}
.skin-red .main-header .navbar .sidebar-toggle {
color: #ffffff;
}
.skin-red .main-header .navbar .sidebar-toggle:hover {
color: #f6f6f6;
background: rgba(0, 0, 0, 0.1);
}
.skin-red .main-header .navbar .sidebar-toggle {
color: #fff;
}
.skin-red .main-header .navbar .sidebar-toggle:hover {
background-color: #d73925;
}
@media (max-width: 767px) {
.skin-red .main-header .navbar .dropdown-menu li.divider {
background-color: rgba(255, 255, 255, 0.1);
}
.skin-red .main-header .navbar .dropdown-menu li a {
color: #fff;
}
.skin-red .main-header .navbar .dropdown-menu li a:hover {
background: #d73925;
}
}
.skin-red .main-header .logo {
background-color: #d73925;
color: #ffffff;
border-bottom: 0 solid transparent;
}
.skin-red .main-header .logo:hover {
background-color: #d33724;
}
.skin-red .main-header li.user-header {
background-color: #dd4b39;
}
.skin-red .content-header {
background: transparent;
}
.skin-red .wrapper,
.skin-red .main-sidebar,
.skin-red .left-side {
background-color: #222d32;
}
.skin-red .user-panel > .info,
.skin-red .user-panel > .info > a {
color: #fff;
}
.skin-red .sidebar-menu > li.header {
color: #4b646f;
background: #1a2226;
}
.skin-red .sidebar-menu > li > a {
border-left: 3px solid transparent;
}
.skin-red .sidebar-menu > li:hover > a,
.skin-red .sidebar-menu > li.active > a {
color: #ffffff;
background: #1e282c;
border-left-color: #dd4b39;
}
.skin-red .sidebar-menu > li > .treeview-menu {
margin: 0 1px;
background: #2c3b41;
}
.skin-red .sidebar a {
color: #b8c7ce;
}
.skin-red .sidebar a:hover {
text-decoration: none;
}
.skin-red .treeview-menu > li > a {
color: #8aa4af;
}
.skin-red .treeview-menu > li.active > a,
.skin-red .treeview-menu > li > a:hover {
color: #ffffff;
}
.skin-red .sidebar-form {
border-radius: 3px;
border: 1px solid #374850;
margin: 10px 10px;
}
.skin-red .sidebar-form input[type="text"],
.skin-red .sidebar-form .btn {
box-shadow: none;
background-color: #374850;
border: 1px solid transparent;
height: 35px;
-webkit-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
.skin-red .sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.skin-red .sidebar-form input[type="text"]:focus,
.skin-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.skin-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.skin-red .sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}

Some files were not shown because too many files have changed in this diff Show More