Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
iv
gitlab-ce
Commits
4546c710
Commit
4546c710
authored
Apr 09, 2013
by
Dmitriy Zaporozhets
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'Undev-feature/refactoring_scopes_pr'
parents
52cd655f
4496a747
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
113 additions
and
150 deletions
+113
-150
app/controllers/admin/users_controller.rb
app/controllers/admin/users_controller.rb
+1
-1
app/helpers/application_helper.rb
app/helpers/application_helper.rb
+2
-3
app/models/group.rb
app/models/group.rb
+1
-0
app/models/issue.rb
app/models/issue.rb
+3
-13
app/models/key.rb
app/models/key.rb
+2
-2
app/models/milestone.rb
app/models/milestone.rb
+1
-4
app/models/project.rb
app/models/project.rb
+9
-8
app/models/user.rb
app/models/user.rb
+30
-81
app/observers/project_activity_cache_observer.rb
app/observers/project_activity_cache_observer.rb
+8
-0
config/application.rb
config/application.rb
+1
-0
db/migrate/20130403003950_add_last_activity_column_into_project.rb
...e/20130403003950_add_last_activity_column_into_project.rb
+21
-0
db/schema.rb
db/schema.rb
+32
-36
spec/models/project_spec.rb
spec/models/project_spec.rb
+2
-2
No files found.
app/controllers/admin/users_controller.rb
View file @
4546c710
...
...
@@ -14,7 +14,7 @@ class Admin::UsersController < Admin::ApplicationController
@not_in_projects
=
@not_in_projects
.
without_user
(
admin_user
)
if
admin_user
.
authorized_projects
.
present?
# Projects he already own or joined
@projects
=
admin_user
.
authorized_projects
.
where
(
'projects.id in (?)'
,
admin_user
.
authorized_projects
.
map
(
&
:id
))
@projects
=
admin_user
.
authorized_projects
end
def
team_update
...
...
app/helpers/application_helper.rb
View file @
4546c710
...
...
@@ -151,9 +151,8 @@ module ApplicationHelper
end
def
project_last_activity
project
activity
=
project
.
last_activity
if
activity
&&
activity
.
created_at
time_ago_in_words
(
activity
.
created_at
)
+
" ago"
if
project
.
last_activity_at
time_ago_in_words
(
project
.
last_activity_at
)
+
" ago"
else
"Never"
end
...
...
app/models/group.rb
View file @
4546c710
...
...
@@ -13,6 +13,7 @@
#
class
Group
<
Namespace
def
add_users_to_project_teams
(
user_ids
,
project_access
)
UsersProject
.
add_users_into_projects
(
projects
.
map
(
&
:id
),
...
...
app/models/issue.rb
View file @
4546c710
...
...
@@ -25,19 +25,9 @@ class Issue < ActiveRecord::Base
acts_as_taggable_on
:labels
class
<<
self
def
cared
(
user
)
where
(
'assignee_id = :user'
,
user:
user
.
id
)
end
def
authored
(
user
)
where
(
'author_id = :user'
,
user:
user
.
id
)
end
def
open_for
(
user
)
opened
.
assigned
(
user
)
end
end
scope
:cared
,
->
(
user
)
{
where
(
assignee_id:
user
)
}
scope
:authored
,
->
(
user
)
{
where
(
author_id:
user
)
}
scope
:open_for
,
->
(
user
)
{
opened
.
assigned
(
user
)
}
state_machine
:state
,
initial: :opened
do
event
:close
do
...
...
app/models/key.rb
View file @
4546c710
...
...
@@ -23,7 +23,7 @@ class Key < ActiveRecord::Base
before_validation
:strip_white_space
validates
:title
,
presence:
true
,
length:
{
within:
0
..
255
}
validates
:key
,
presence:
true
,
length:
{
within:
0
..
5000
},
format:
{
:with
=>
/ssh-.{3} /
},
uniqueness:
true
validates
:key
,
presence:
true
,
length:
{
within:
0
..
5000
},
format:
{
with:
/ssh-.{3} /
},
uniqueness:
true
validate
:fingerprintable_key
delegate
:name
,
:email
,
to: :user
,
prefix:
true
...
...
@@ -48,7 +48,7 @@ class Key < ActiveRecord::Base
end
def
is_deploy_key
!!
project_id
project
.
present?
end
# projects that has this key
...
...
app/models/milestone.rb
View file @
4546c710
...
...
@@ -19,6 +19,7 @@ class Milestone < ActiveRecord::Base
belongs_to
:project
has_many
:issues
has_many
:merge_requests
has_many
:participants
,
through: :issues
,
source: :assignee
scope
:active
,
->
{
with_state
(
:active
)
}
scope
:closed
,
->
{
with_state
(
:closed
)
}
...
...
@@ -48,10 +49,6 @@ class Milestone < ActiveRecord::Base
end
end
def
participants
User
.
where
(
id:
issues
.
pluck
(
:assignee_id
))
end
def
open_items_count
self
.
issues
.
opened
.
count
+
self
.
merge_requests
.
opened
.
count
end
...
...
app/models/project.rb
View file @
4546c710
...
...
@@ -30,7 +30,7 @@ class Project < ActiveRecord::Base
attr_accessible
:name
,
:path
,
:description
,
:default_branch
,
:issues_tracker
,
:issues_enabled
,
:wall_enabled
,
:merge_requests_enabled
,
:snippets_enabled
,
:issues_tracker_id
,
:wiki_enabled
,
:public
,
:import_url
,
as:
[
:default
,
:admin
]
:wiki_enabled
,
:public
,
:import_url
,
:last_activity_at
,
as:
[
:default
,
:admin
]
attr_accessible
:namespace_id
,
:creator_id
,
as: :admin
...
...
@@ -87,17 +87,18 @@ class Project < ActiveRecord::Base
validate
:check_limit
,
:repo_name
# Scopes
scope
:without_user
,
->
(
user
)
{
where
(
"id NOT IN (:ids)"
,
ids:
user
.
authorized_projects
.
map
(
&
:id
)
)
}
scope
:
not_in_group
,
->
(
group
)
{
where
(
"id NOT IN (:ids)"
,
ids:
group
.
project_ids
)
}
scope
:
without_team
,
->
(
team
)
{
team
.
projects
.
present?
?
where
(
"id NOT IN (:ids)"
,
ids:
team
.
projects
.
map
(
&
:id
))
:
scoped
}
scope
:in_team
,
->
(
team
)
{
where
(
"id IN (:ids)"
,
ids:
team
.
projects
.
map
(
&
:id
))
}
scope
:without_user
,
->
(
user
)
{
where
(
"
projects.
id NOT IN (:ids)"
,
ids:
user
.
authorized_projects
.
map
(
&
:id
)
)
}
scope
:
without_team
,
->
(
team
)
{
team
.
projects
.
present?
?
where
(
"projects.id NOT IN (:ids)"
,
ids:
team
.
projects
.
map
(
&
:id
))
:
scoped
}
scope
:
not_in_group
,
->
(
group
)
{
where
(
"projects.id NOT IN (:ids)"
,
ids:
group
.
project_ids
)
}
scope
:in_team
,
->
(
team
)
{
where
(
"
projects.
id IN (:ids)"
,
ids:
team
.
projects
.
map
(
&
:id
))
}
scope
:in_namespace
,
->
(
namespace
)
{
where
(
namespace_id:
namespace
.
id
)
}
scope
:sorted_by_activity
,
->
()
{
order
(
"(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC"
)
}
scope
:in_group_namespace
,
->
{
joins
(
:group
)
}
scope
:sorted_by_activity
,
->
{
order
(
"projects.last_activity_at DESC"
)
}
scope
:personal
,
->
(
user
)
{
where
(
namespace_id:
user
.
namespace_id
)
}
scope
:joined
,
->
(
user
)
{
where
(
"namespace_id != ?"
,
user
.
namespace_id
)
}
scope
:public_only
,
->
{
where
(
public:
true
)
}
enumerize
:issues_tracker
,
:in
=>
(
Gitlab
.
config
.
issues_tracker
.
keys
).
append
(
:gitlab
),
:default
=>
:gitlab
enumerize
:issues_tracker
,
in:
(
Gitlab
.
config
.
issues_tracker
.
keys
).
append
(
:gitlab
),
default:
:gitlab
class
<<
self
def
abandoned
...
...
@@ -190,7 +191,7 @@ class Project < ActiveRecord::Base
end
def
last_activity_date
last_
event
.
try
(
:created_at
)
||
updated_at
last_
activity_at
||
updated_at
end
def
project_id
...
...
app/models/user.rb
View file @
4546c710
...
...
@@ -59,11 +59,10 @@ class User < ActiveRecord::Base
#
# Namespace for personal projects
has_one
:namespace
,
dependent: :destroy
,
foreign_key: :owner_id
,
class_name:
"Namespace"
,
conditions:
'type IS NULL'
has_one
:namespace
,
dependent: :destroy
,
foreign_key: :owner_id
,
class_name:
"Namespace"
,
conditions:
'type IS NULL'
# Namespaces (owned groups and own namespace)
has_many
:namespaces
,
foreign_key: :owner_id
# Profile
has_many
:keys
,
dependent: :destroy
...
...
@@ -72,11 +71,7 @@ class User < ActiveRecord::Base
has_many
:groups
,
class_name:
"Group"
,
foreign_key: :owner_id
# Teams
has_many
:own_teams
,
class_name:
"UserTeam"
,
foreign_key: :owner_id
,
dependent: :destroy
has_many
:own_teams
,
dependent: :destroy
,
class_name:
"UserTeam"
,
foreign_key: :owner_id
has_many
:user_team_user_relationships
,
dependent: :destroy
has_many
:user_teams
,
through: :user_team_user_relationships
has_many
:user_team_project_relationships
,
through: :user_teams
...
...
@@ -88,14 +83,14 @@ class User < ActiveRecord::Base
has_many
:notes
,
dependent: :destroy
,
foreign_key: :author_id
has_many
:merge_requests
,
dependent: :destroy
,
foreign_key: :author_id
has_many
:events
,
dependent: :destroy
,
foreign_key: :author_id
,
class_name:
"Event"
has_many
:recent_events
,
foreign_key: :author_id
,
class_name:
"Event"
,
order:
"id DESC"
has_many
:assigned_issues
,
dependent: :destroy
,
foreign_key: :assignee_id
,
class_name:
"Issue"
has_many
:assigned_merge_requests
,
dependent: :destroy
,
foreign_key: :assignee_id
,
class_name:
"MergeRequest"
has_many
:projects
,
through: :users_projects
has_many
:
recent_events
,
class_name:
"Event"
,
foreign_key: :author_id
,
order:
"id DESC"
has_many
:
personal_projects
,
through: :namespace
,
source: :projects
has_many
:projects
,
through: :users_projects
has_many
:own_projects
,
foreign_key: :creator_id
has_many
:owned_projects
,
through: :namespaces
,
source: :projects
#
# Validations
...
...
@@ -109,9 +104,7 @@ class User < ActiveRecord::Base
format:
{
with:
Gitlab
::
Regex
.
username_regex
,
message:
"only letters, digits & '_' '-' '.' allowed. Letter should be first"
}
validates
:notification_level
,
inclusion:
{
in:
Notification
.
notification_levels
},
presence:
true
validates
:notification_level
,
inclusion:
{
in:
Notification
.
notification_levels
},
presence:
true
validate
:namespace_uniq
,
if:
->
(
user
)
{
user
.
username_changed?
}
...
...
@@ -145,6 +138,9 @@ class User < ActiveRecord::Base
scope
:alphabetically
,
->
{
order
(
'name ASC'
)
}
scope
:in_team
,
->
(
team
){
where
(
id:
team
.
member_ids
)
}
scope
:not_in_team
,
->
(
team
){
where
(
'users.id NOT IN (:ids)'
,
ids:
team
.
member_ids
)
}
scope
:not_in_project
,
->
(
project
)
{
project
.
users
.
present?
?
where
(
"id not in (:ids)"
,
ids:
project
.
users
.
map
(
&
:id
)
)
:
scoped
}
scope
:without_projects
,
->
{
where
(
'id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)'
)
}
scope
:potential_team_members
,
->
(
team
)
{
team
.
members
.
any?
?
active
.
not_in_team
(
team
)
:
active
}
#
...
...
@@ -171,18 +167,6 @@ class User < ActiveRecord::Base
end
end
def
not_in_project
(
project
)
if
project
.
users
.
present?
where
(
"id not in (:ids)"
,
ids:
project
.
users
.
map
(
&
:id
)
)
else
scoped
end
end
def
without_projects
where
(
'id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)'
)
end
def
create_from_omniauth
(
auth
,
ldap
=
false
)
gitlab_auth
.
create_from_omniauth
(
auth
,
ldap
)
end
...
...
@@ -229,51 +213,31 @@ class User < ActiveRecord::Base
end
end
# Namespaces user has access to
def
namespaces
namespaces
=
[]
# Add user account namespace
namespaces
<<
self
.
namespace
if
self
.
namespace
# Add groups you can manage
namespaces
+=
groups
.
all
namespaces
end
# Groups where user is an owner
def
owned_groups
groups
end
def
owned_teams
own_teams
end
# Groups user has access to
def
authorized_groups
@authorized_groups
||=
begin
groups
=
Group
.
where
(
id:
self
.
authorized_projects
.
pluck
(
:namespace_id
)).
all
groups
=
groups
+
self
.
groups
groups
.
uniq
end
@group_ids
||=
(
groups
.
pluck
(
:id
)
+
authorized_projects
.
pluck
(
:namespace_id
))
Group
.
where
(
id:
@group_ids
)
end
# Projects user has access to
def
authorized_projects
project_ids
=
users_projects
.
pluck
(
:project_id
)
project_ids
=
project_ids
|
owned_projects
.
pluck
(
:id
)
Project
.
where
(
id:
project_ids
)
@project_ids
||=
(
owned_projects
.
pluck
(
:id
)
+
projects
.
pluck
(
:id
)).
uniq
Project
.
where
(
id:
@project_ids
)
end
# Projects in user namespace
def
personal_projects
Project
.
personal
(
self
)
end
# Projects where user is an owner
def
owned_projects
Project
.
where
(
"(projects.namespace_id IN (:namespaces)) OR
(projects.namespace_id IS NULL AND projects.creator_id = :user_id)"
,
namespaces:
namespaces
.
map
(
&
:id
),
user_id:
self
.
id
)
def
authorized_teams
@team_ids
||=
(
user_teams
.
pluck
(
:id
)
+
own_teams
.
pluck
(
:id
)).
uniq
UserTeam
.
where
(
id:
@team_ids
)
end
# Team membership in authorized projects
...
...
@@ -348,28 +312,13 @@ class User < ActiveRecord::Base
end
def
several_namespaces?
namespaces
.
size
>
1
namespaces
.
many?
end
def
namespace_id
namespace
.
try
:id
end
def
authorized_teams
@authorized_teams
||=
begin
ids
=
[]
ids
<<
UserTeam
.
with_member
(
self
).
pluck
(
'user_teams.id'
)
ids
<<
UserTeam
.
created_by
(
self
).
pluck
(
'user_teams.id'
)
ids
.
flatten
UserTeam
.
where
(
id:
ids
)
end
end
def
owned_teams
UserTeam
.
where
(
owner_id:
self
.
id
)
end
def
name_with_username
"
#{
name
}
(
#{
username
}
)"
end
...
...
app/observers/project_activity_cache_observer.rb
0 → 100644
View file @
4546c710
class
ProjectActivityCacheObserver
<
BaseObserver
observe
:event
def
after_create
(
event
)
event
.
project
.
update_attribute
(
:last_activity_at
,
event
.
created_at
)
if
event
.
project
end
end
config/application.rb
View file @
4546c710
...
...
@@ -24,6 +24,7 @@ module Gitlab
# Activate observers that should always be running.
config
.
active_record
.
observers
=
:activity_observer
,
:project_activity_cache_observer
,
:issue_observer
,
:key_observer
,
:merge_request_observer
,
...
...
db/migrate/20130403003950_add_last_activity_column_into_project.rb
0 → 100644
View file @
4546c710
class
AddLastActivityColumnIntoProject
<
ActiveRecord
::
Migration
def
up
add_column
:projects
,
:last_activity_at
,
:datetime
add_index
:projects
,
:last_activity_at
Project
.
find_each
do
|
project
|
last_activity_date
=
if
project
.
last_activity
project
.
last_activity
.
created_at
else
project
.
updated_at
end
project
.
update_attribute
(
:last_activity_at
,
last_activity_date
)
end
end
def
down
remove_index
:projects
,
:last_activity_at
remove_column
:projects
,
:last_activity_at
end
end
db/schema.rb
View file @
4546c710
...
...
@@ -37,8 +37,8 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
t
.
integer
"assignee_id"
t
.
integer
"author_id"
t
.
integer
"project_id"
t
.
datetime
"created_at"
,
:null
=>
false
t
.
datetime
"updated_at"
,
:null
=>
false
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
t
.
integer
"position"
,
:default
=>
0
t
.
string
"branch_name"
t
.
text
"description"
...
...
@@ -55,8 +55,8 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
create_table
"keys"
,
:force
=>
true
do
|
t
|
t
.
integer
"user_id"
t
.
datetime
"created_at"
,
:null
=>
false
t
.
datetime
"updated_at"
,
:null
=>
false
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
t
.
text
"key"
t
.
string
"title"
t
.
string
"identifier"
...
...
@@ -74,8 +74,8 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
t
.
integer
"author_id"
t
.
integer
"assignee_id"
t
.
string
"title"
t
.
datetime
"created_at"
,
:null
=>
false
t
.
datetime
"updated_at"
,
:null
=>
false
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
t
.
text
"st_commits"
,
:limit
=>
2147483647
t
.
text
"st_diffs"
,
:limit
=>
2147483647
t
.
integer
"milestone_id"
...
...
@@ -124,8 +124,8 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
t
.
text
"note"
t
.
string
"noteable_type"
t
.
integer
"author_id"
t
.
datetime
"created_at"
,
:null
=>
false
t
.
datetime
"updated_at"
,
:null
=>
false
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
t
.
integer
"project_id"
t
.
string
"attachment"
t
.
string
"line_code"
...
...
@@ -143,8 +143,8 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
t
.
string
"name"
t
.
string
"path"
t
.
text
"description"
t
.
datetime
"created_at"
,
:null
=>
false
t
.
datetime
"updated_at"
,
:null
=>
false
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
t
.
integer
"creator_id"
t
.
string
"default_branch"
t
.
boolean
"issues_enabled"
,
:default
=>
true
,
:null
=>
false
...
...
@@ -188,8 +188,8 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
t
.
text
"content"
t
.
integer
"author_id"
,
:null
=>
false
t
.
integer
"project_id"
,
:null
=>
false
t
.
datetime
"created_at"
,
:null
=>
false
t
.
datetime
"updated_at"
,
:null
=>
false
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
t
.
string
"file_name"
t
.
datetime
"expires_at"
end
...
...
@@ -208,9 +208,6 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
t
.
datetime
"created_at"
end
add_index
"taggings"
,
[
"tag_id"
],
:name
=>
"index_taggings_on_tag_id"
add_index
"taggings"
,
[
"taggable_id"
,
"taggable_type"
,
"context"
],
:name
=>
"index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table
"tags"
,
:force
=>
true
do
|
t
|
t
.
string
"name"
end
...
...
@@ -243,7 +240,7 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
create_table
"users"
,
:force
=>
true
do
|
t
|
t
.
string
"email"
,
:default
=>
""
,
:null
=>
false
t
.
string
"encrypted_password"
,
:default
=>
""
,
:null
=>
false
t
.
string
"encrypted_password"
,
:
limit
=>
128
,
:
default
=>
""
,
:null
=>
false
t
.
string
"reset_password_token"
t
.
datetime
"reset_password_sent_at"
t
.
datetime
"remember_created_at"
...
...
@@ -252,8 +249,8 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
t
.
datetime
"last_sign_in_at"
t
.
string
"current_sign_in_ip"
t
.
string
"last_sign_in_ip"
t
.
datetime
"created_at"
,
:null
=>
false
t
.
datetime
"updated_at"
,
:null
=>
false
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
t
.
string
"name"
t
.
boolean
"admin"
,
:default
=>
false
,
:null
=>
false
t
.
integer
"projects_limit"
,
:default
=>
10
...
...
@@ -277,7 +274,6 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
add_index
"users"
,
[
"admin"
],
:name
=>
"index_users_on_admin"
add_index
"users"
,
[
"email"
],
:name
=>
"index_users_on_email"
,
:unique
=>
true
add_index
"users"
,
[
"extern_uid"
,
"provider"
],
:name
=>
"index_users_on_extern_uid_and_provider"
,
:unique
=>
true
add_index
"users"
,
[
"name"
],
:name
=>
"index_users_on_name"
add_index
"users"
,
[
"reset_password_token"
],
:name
=>
"index_users_on_reset_password_token"
,
:unique
=>
true
add_index
"users"
,
[
"username"
],
:name
=>
"index_users_on_username"
...
...
@@ -285,8 +281,8 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
create_table
"users_projects"
,
:force
=>
true
do
|
t
|
t
.
integer
"user_id"
,
:null
=>
false
t
.
integer
"project_id"
,
:null
=>
false
t
.
datetime
"created_at"
,
:null
=>
false
t
.
datetime
"updated_at"
,
:null
=>
false
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
t
.
integer
"project_access"
,
:default
=>
0
,
:null
=>
false
t
.
integer
"notification_level"
,
:default
=>
3
,
:null
=>
false
end
...
...
@@ -298,8 +294,8 @@ ActiveRecord::Schema.define(:version => 20130404164628) do
create_table
"web_hooks"
,
:force
=>
true
do
|
t
|
t
.
string
"url"
t
.
integer
"project_id"
t
.
datetime
"created_at"
,
:null
=>
false
t
.
datetime
"updated_at"
,
:null
=>
false
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
t
.
string
"type"
,
:default
=>
"ProjectHook"
t
.
integer
"service_id"
end
...
...
spec/models/project_spec.rb
View file @
4546c710
...
...
@@ -109,8 +109,8 @@ describe Project do
describe
'last_activity_date'
do
it
'returns the creation date of the project\'s last event if present'
do
project
.
stub
(
last_event:
last_even
t
)
project
.
last_activity_date
.
should
==
last_event
.
created_at
last_activity_event
=
create
(
:event
,
project:
projec
t
)
project
.
last_activity_date
.
to_s
(
:db
).
should
==
last_event
.
created_at
.
to_s
(
:db
)
end
it
'returns the project\'s last update date if it has no events'
do
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment