diff --git a/src/IDF/Form/Admin/ProjectCreate.php b/src/IDF/Form/Admin/ProjectCreate.php new file mode 100644 index 0000000..0e7e6ad --- /dev/null +++ b/src/IDF/Form/Admin/ProjectCreate.php @@ -0,0 +1,169 @@ + __('git'), + 'svn' => __('Subversion'), + 'mercurial' => __('mercurial'), + ); + foreach (Pluf::f('allowed_scm', array()) as $key => $class) { + $choices[$options[$key]] = $key; + } + + $this->fields['name'] = new Pluf_Form_Field_Varchar( + array('required' => true, + 'label' => __('Name'), + 'initial' => '', + )); + + $this->fields['shortname'] = new Pluf_Form_Field_Varchar( + array('required' => true, + 'label' => __('Shortname'), + 'initial' => 'myproject', + 'help_text' => __('It must be unique for each project and composed only of letters and digits.'), + )); + + $this->fields['scm'] = new Pluf_Form_Field_Varchar( + array('required' => true, + 'label' => __('Repository type'), + 'initial' => 'git', + 'widget_attrs' => array('choices' => $choices), + 'widget' => 'Pluf_Form_Widget_SelectInput', + )); + + $this->fields['svn_remote_url'] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Remote Subversion repository'), + 'initial' => '', + 'widget_attrs' => array('size' => '30'), + )); + + $this->fields['svn_username'] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Repository username'), + 'initial' => '', + 'widget_attrs' => array('size' => '15'), + )); + + $this->fields['svn_password'] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Repository password'), + 'initial' => '', + 'widget' => 'Pluf_Form_Widget_PasswordInput', + )); + + $this->fields['owners'] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Project owners'), + 'initial' => $extra['user']->login, + 'widget' => 'Pluf_Form_Widget_TextareaInput', + 'widget_attrs' => array('rows' => 5, + 'cols' => 40), + )); + + $this->fields['members'] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Project members'), + 'initial' => '', + 'widget_attrs' => array('rows' => 7, + 'cols' => 40), + 'widget' => 'Pluf_Form_Widget_TextareaInput', + )); + } + + public function clean_svn_remote_url() + { + $url = trim($this->cleaned_data['svn_remote_url']); + if (strlen($url) == 0) return $url; + // we accept only starting with http(s):// to avoid people + // trying to access the local filesystem. + if (!preg_match('#^(http|https)://#', $url)) { + throw new Pluf_Form_Invalid(__('Only a remote repository available throught http or https are allowed. For example "http://somewhere.com/svn/trunk".')); + } + return $url; + } + + public function clean_shortname() + { + $shortname = $this->cleaned_data['shortname']; + if (preg_match('/[^A-Za-z0-9]/', $shortname)) { + throw new Pluf_Form_Invalid(__('This shortname contains illegal characters, please use only letters and digits.')); + } + $sql = new Pluf_SQL('shortname=%s', array($shortname)); + $l = Pluf::factory('IDF_Project')->getList(array('filter'=>$sql->gen())); + if ($l->count() > 0) { + throw new Pluf_Form_Invalid(__('This shortname is already used. Please select another one.')); + } + return $shortname; + } + + public function clean() + { + if ($this->cleaned_data['scm'] != 'svn') { + foreach (array('svn_remote_url', 'svn_username', 'svn_password') + as $key) { + $this->cleaned_data[$key] = ''; + } + } + return $this->cleaned_data; + } + + public function save($commit=true) + { + if (!$this->isValid()) { + throw new Exception(__('Cannot save the model from an invalid form.')); + } + $project = new IDF_Project(); + $project->name = $this->cleaned_data['name']; + $project->shortname = $this->cleaned_data['shortname']; + $project->description = __('Write your project description here.'); + $project->create(); + $conf = new IDF_Conf(); + $conf->setProject($project); + $keys = array('scm', 'svn_remote_url', + 'svn_username', 'svn_password'); + foreach ($keys as $key) { + $conf->setVal($key, $this->cleaned_data[$key]); + } + $project->created(); + IDF_Form_MembersConf::updateMemberships($project, + $this->cleaned_data); + $project->membershipsUpdated(); + return $project; + } +} + + diff --git a/src/IDF/Form/Admin/ProjectUpdate.php b/src/IDF/Form/Admin/ProjectUpdate.php new file mode 100644 index 0000000..30cc4cb --- /dev/null +++ b/src/IDF/Form/Admin/ProjectUpdate.php @@ -0,0 +1,77 @@ +project = $extra['project']; + $members = $this->project->getMembershipData('string'); + $this->fields['name'] = new Pluf_Form_Field_Varchar( + array('required' => true, + 'label' => __('Name'), + 'initial' => $this->project->name, + )); + + $this->fields['owners'] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Project owners'), + 'initial' => $members['owners'], + 'widget' => 'Pluf_Form_Widget_TextareaInput', + 'widget_attrs' => array('rows' => 5, + 'cols' => 40), + )); + $this->fields['members'] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Project members'), + 'initial' => $members['members'], + 'widget_attrs' => array('rows' => 7, + 'cols' => 40), + 'widget' => 'Pluf_Form_Widget_TextareaInput', + )); + } + + public function save($commit=true) + { + if (!$this->isValid()) { + throw new Exception(__('Cannot save the model from an invalid form.')); + } + IDF_Form_MembersConf::updateMemberships($this->project, + $this->cleaned_data); + $this->project->membershipsUpdated(); + $this->project->name = $this->cleaned_data['name']; + $this->project->update(); + } +} + + diff --git a/src/IDF/Form/MembersConf.php b/src/IDF/Form/MembersConf.php index c919aa2..35c0413 100644 --- a/src/IDF/Form/MembersConf.php +++ b/src/IDF/Form/MembersConf.php @@ -63,20 +63,33 @@ class IDF_Form_MembersConf extends Pluf_Form if (!$this->isValid()) { throw new Exception(__('Cannot save the model from an invalid form.')); } + self::updateMemberships($this->project, $this->cleaned_data); + $this->project->membershipsUpdated(); + } + + /** + * The update of the memberships is done in different places. This + * avoids duplicating code. + * + * @param IDF_Project The project + * @param array The new memberships data in 'owners' and 'members' keys + */ + public static function updateMemberships($project, $cleaned_data) + { // remove all the permissions - $cm = $this->project->getMembershipData(); + $cm = $project->getMembershipData(); $def = array('owners' => Pluf_Permission::getFromString('IDF.project-owner'), 'members' => Pluf_Permission::getFromString('IDF.project-member')); $guser = new Pluf_User(); foreach ($def as $key=>$perm) { foreach ($cm[$key] as $user) { - Pluf_RowPermission::remove($user, $this->project, $perm); + Pluf_RowPermission::remove($user, $project, $perm); } - foreach (preg_split("/\015\012|\015|\012|\,/", $this->cleaned_data[$key], -1, PREG_SPLIT_NO_EMPTY) as $login) { + foreach (preg_split("/\015\012|\015|\012|\,/", $cleaned_data[$key], -1, PREG_SPLIT_NO_EMPTY) as $login) { $sql = new Pluf_SQL('login=%s', array(trim($login))); $users = $guser->getList(array('filter'=>$sql->gen())); if ($users->count() == 1) { - Pluf_RowPermission::add($users[0], $this->project, $perm); + Pluf_RowPermission::add($users[0], $project, $perm); } } } diff --git a/src/IDF/Form/SourceConf.php b/src/IDF/Form/SourceConf.php index c268c49..b4183d3 100644 --- a/src/IDF/Form/SourceConf.php +++ b/src/IDF/Form/SourceConf.php @@ -24,6 +24,9 @@ /** * Configuration of the source. + * + * Only the modification of the login/password for subversion is + * authorized. */ class IDF_Form_SourceConf extends Pluf_Form { @@ -31,62 +34,21 @@ class IDF_Form_SourceConf extends Pluf_Form public function initFields($extra=array()) { $this->conf = $extra['conf']; - $this->fields['scm'] = new Pluf_Form_Field_Varchar( - array('required' => true, - 'label' => __('Repository type'), - 'initial' => $this->conf->getVal('scm', 'git'), - 'widget_attrs' => array('choices' => - array( - __('git') => 'git', - __('Subversion') => 'svn', - __('mercurial') => 'mercurial', - ) - ), - 'widget' => 'Pluf_Form_Widget_SelectInput', - )); - $this->fields['svn_remote_url'] = new Pluf_Form_Field_Varchar( - array('required' => false, - 'label' => __('Remote Subversion repository'), - 'initial' => $this->conf->getVal('svn_remote_url', ''), - 'widget_attrs' => array('size' => '30'), - )); - - $this->fields['svn_username'] = new Pluf_Form_Field_Varchar( + if ($this->conf->getVal('scm', 'git') == 'svn') { + $this->fields['svn_username'] = new Pluf_Form_Field_Varchar( array('required' => false, 'label' => __('Repository username'), 'initial' => $this->conf->getVal('svn_username', ''), 'widget_attrs' => array('size' => '15'), )); - $this->fields['svn_password'] = new Pluf_Form_Field_Varchar( + $this->fields['svn_password'] = new Pluf_Form_Field_Varchar( array('required' => false, 'label' => __('Repository password'), 'initial' => $this->conf->getVal('svn_password', ''), 'widget' => 'Pluf_Form_Widget_PasswordInput', )); - } - - public function clean_svn_remote_url() - { - $url = trim($this->cleaned_data['svn_remote_url']); - if (strlen($url) == 0) return $url; - // we accept only starting with http(s):// to avoid people - // trying to access the local filesystem. - if (!preg_match('#^(http|https)://#', $url)) { - throw new Pluf_Form_Invalid(__('Only a remote repository available throught http or https are allowed. For example "http://somewhere.com/svn/trunk".')); - } - return $url; - } - - public function clean() - { - if ($this->cleaned_data['scm'] == 'git') { - foreach (array('svn_remote_url', 'svn_username', 'svn_password') - as $key) { - $this->cleaned_data[$key] = ''; - } } - return $this->cleaned_data; } } diff --git a/src/IDF/Middleware.php b/src/IDF/Middleware.php index ed3ce4b..94d37ae 100644 --- a/src/IDF/Middleware.php +++ b/src/IDF/Middleware.php @@ -71,6 +71,7 @@ function IDF_Middleware_ContextPreProcessor($request) { $c = array(); $c['request'] = $request; + $c['isAdmin'] = ($request->user->administrator or $request->user->staff); if (isset($request->project)) { $c['project'] = $request->project; $c['isOwner'] = $request->user->hasPerm('IDF.project-owner', diff --git a/src/IDF/Project.php b/src/IDF/Project.php index 1284fbc..923cdf9 100644 --- a/src/IDF/Project.php +++ b/src/IDF/Project.php @@ -400,5 +400,70 @@ class IDF_Project extends Pluf_Model } return $this->_pconf; } - + + /** + * Needs to be called when you update the memberships of a + * project. + * + * This will allow a plugin to, for example, update some access + * rights to a repository. + */ + public function membershipsUpdated() + { + /** + * [signal] + * + * IDF_Project::membershipsUpdated + * + * [sender] + * + * IDF_Project + * + * [description] + * + * This signal allows an application to update the some access + * rights to a repository when the project memberships is + * updated. + * + * [parameters] + * + * array('project' => $project) + * + */ + $params = array('project' => $this); + Pluf_Signal::send('IDF_Project::membershipsUpdated', + 'IDF_Project', $params); + } + + /** + * Needs to be called when you create a project. + * + * We cannot put it into the postSave call as the configuration of + * the project is not defined at that time. + */ + function created() + { + /** + * [signal] + * + * IDF_Project::created + * + * [sender] + * + * IDF_Project + * + * [description] + * + * This signal allows an application to perform special + * operations at the creation of a project. + * + * [parameters] + * + * array('project' => $project) + * + */ + $params = array('project' => $this); + Pluf_Signal::send('IDF_Project::created', + 'IDF_Project', $params); + } } diff --git a/src/IDF/Views/Admin.php b/src/IDF/Views/Admin.php new file mode 100644 index 0000000..e06cd0f --- /dev/null +++ b/src/IDF/Views/Admin.php @@ -0,0 +1,145 @@ + $title, + ), + $request); + } + + /** + * Projects overview. + * + */ + public $projects_precond = array('Pluf_Precondition::staffRequired'); + public function projects($request, $match) + { + $title = __('Projects'); + $pag = new Pluf_Paginator(new IDF_Project()); + $pag->class = 'recent-issues'; + $pag->summary = __('This table shows the projects in the forge.'); + $pag->action = 'IDF_Views_Admin::projects'; + $pag->edit_action = array('IDF_Views_Admin::projectUpdate', 'id'); + $pag->sort_order = array('shortname', 'ASC'); + $list_display = array( + 'shortname' => __('Short Name'), + 'name' => __('Name'), + ); + $pag->configure($list_display, array(), + array('shortname')); + $pag->items_per_page = 25; + $pag->no_results_text = __('No projects were found.'); + $pag->setFromRequest($request); + return Pluf_Shortcuts_RenderToResponse('idf/gadmin/projects/index.html', + array( + 'page_title' => $title, + 'projects' => $pag, + ), + $request); + } + + /** + * Edition of a project. + * + * One cannot switch from one source backend to another. + */ + public $projectUpdate_precond = array('Pluf_Precondition::staffRequired'); + public function projectUpdate($request, $match) + { + $project = Pluf_Shortcuts_GetObjectOr404('IDF_Project', $match[1]); + $title = sprintf(__('Update %s'), $project->name); + $params = array( + 'project' => $project, + ); + if ($request->method == 'POST') { + $form = new IDF_Form_Admin_ProjectUpdate($request->POST, $params); + if ($form->isValid()) { + $form->save(); + $request->user->setMessage(__('The project has been updated.')); + $url = Pluf_HTTP_URL_urlForView('IDF_Views_Admin::projectUpdate', + array($project->id)); + return new Pluf_HTTP_Response_Redirect($url); + } + } else { + $form = new IDF_Form_Admin_ProjectUpdate(null, $params); + } + return Pluf_Shortcuts_RenderToResponse('idf/gadmin/projects/update.html', + array( + 'page_title' => $title, + 'project' => $project, + 'form' => $form, + ), + $request); + } + + /** + * Creation of a project. + * + */ + public $projectCreate_precond = array('Pluf_Precondition::staffRequired'); + public function projectCreate($request, $match) + { + $title = __('Create Project'); + $extra = array('user' => $request->user); + if ($request->method == 'POST') { + $form = new IDF_Form_Admin_ProjectCreate($request->POST, $extra); + if ($form->isValid()) { + $project = $form->save(); + $request->user->setMessage(__('The project has been created.')); + $url = Pluf_HTTP_URL_urlForView('IDF_Views_Admin::projects'); + return new Pluf_HTTP_Response_Redirect($url); + } + } else { + $form = new IDF_Form_Admin_ProjectCreate(null, $extra); + } + $base = Pluf::f('url_base').Pluf::f('idf_base').'/p/'; + return Pluf_Shortcuts_RenderToResponse('idf/gadmin/projects/create.html', + array( + 'page_title' => $title, + 'form' => $form, + 'base_url' => $base, + ), + $request); + } + +} \ No newline at end of file diff --git a/src/IDF/Views/Project.php b/src/IDF/Views/Project.php index 97048c6..def39a9 100644 --- a/src/IDF/Views/Project.php +++ b/src/IDF/Views/Project.php @@ -378,37 +378,51 @@ class IDF_Views_Project { $prj = $request->project; $title = sprintf(__('%s Source'), (string) $prj); - $extra = array( - 'conf' => $request->conf, - ); - if ($request->method == 'POST') { - $form = new IDF_Form_SourceConf($request->POST, $extra); - if ($form->isValid()) { - foreach ($form->cleaned_data as $key=>$val) { - $request->conf->setVal($key, $val); + $form = null; + $remote_svn = false; + if ($request->conf->getVal('scm') == 'svn' and + strlen($request->conf->getVal('svn_remote_url')) > 0) { + $remote_svn = true; + $extra = array( + 'conf' => $request->conf, + ); + if ($request->method == 'POST') { + $form = new IDF_Form_SourceConf($request->POST, $extra); + if ($form->isValid()) { + foreach ($form->cleaned_data as $key=>$val) { + $request->conf->setVal($key, $val); + } + $request->user->setMessage(__('The project source configuration has been saved.')); + $url = Pluf_HTTP_URL_urlForView('IDF_Views_Project::adminSource', + array($prj->shortname)); + return new Pluf_HTTP_Response_Redirect($url); } - $request->user->setMessage(__('The project source configuration has been saved.')); - $url = Pluf_HTTP_URL_urlForView('IDF_Views_Project::adminSource', - array($prj->shortname)); - return new Pluf_HTTP_Response_Redirect($url); - } - } else { - $params = array(); - $keys = array('scm', 'svn_remote_url', - 'svn_username', 'svn_password'); - foreach ($keys as $key) { - $_val = $request->conf->getVal($key, false); - if ($_val !== false) { - $params[$key] = $_val; + } else { + $params = array(); + foreach (array('svn_username', 'svn_password') as $key) { + $_val = $request->conf->getVal($key, false); + if ($_val !== false) { + $params[$key] = $_val; + } } + if (count($params) == 0) { + $params = null; //Nothing in the db, so new form. + } + $form = new IDF_Form_SourceConf($params, $extra); } - if (count($params) == 0) { - $params = null; //Nothing in the db, so new form. - } - $form = new IDF_Form_SourceConf($params, $extra); } + $scm = $request->conf->getVal('scm', 'git'); + $options = array( + 'git' => __('git'), + 'svn' => __('Subversion'), + 'mercurial' => __('mercurial'), + ); + $repository_type = $options[$scm]; return Pluf_Shortcuts_RenderToResponse('idf/admin/source.html', array( + 'remote_svn' => $remote_svn, + 'repository_access' => $prj->getRemoteAccessUrl(), + 'repository_type' => $repository_type, 'page_title' => $title, 'form' => $form, ), diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php index 105b490..bce6226 100644 --- a/src/IDF/conf/urls.php +++ b/src/IDF/conf/urls.php @@ -374,4 +374,31 @@ $ctl[] = array('regex' => '#^/api/p/([\-\w]+)/issues/create/$#', 'model' => 'IDF_Views_Api', 'method' => 'issueCreate'); +// ---------- FORGE ADMIN -------------------------------- + +$ctl[] = array('regex' => '#^/admin/$#', + 'base' => $base, + 'priority' => 4, + 'model' => 'IDF_Views_Admin', + 'method' => 'home'); + +$ctl[] = array('regex' => '#^/admin/projects/$#', + 'base' => $base, + 'priority' => 4, + 'model' => 'IDF_Views_Admin', + 'method' => 'projects'); + +$ctl[] = array('regex' => '#^/admin/projects/(\d+)/$#', + 'base' => $base, + 'priority' => 4, + 'model' => 'IDF_Views_Admin', + 'method' => 'projectUpdate'); + +$ctl[] = array('regex' => '#^/admin/projects/create/$#', + 'base' => $base, + 'priority' => 4, + 'model' => 'IDF_Views_Admin', + 'method' => 'projectCreate'); + + return $ctl; diff --git a/src/IDF/templates/idf/admin/source.html b/src/IDF/templates/idf/admin/source.html index d7c67cb..27612ab 100644 --- a/src/IDF/templates/idf/admin/source.html +++ b/src/IDF/templates/idf/admin/source.html @@ -1,7 +1,7 @@ {extends "idf/admin/base.html"} {block docclass}yui-t1{assign $inSource = true}{/block} {block body} -{if $form.errors} +{if $remote_svn and $form.errors}

{trans 'The form contains some errors. Please correct them to update the source configuration.'}

{if $form.get_top_errors} @@ -12,17 +12,15 @@
- - + - - + - +{if $remote_svn} - +{/if}
{$form.f.scm.labelTag}:{if $form.f.scm.errors}{$form.f.scm.fieldErrors}{/if} -{$form.f.scm|unsafe} +{trans 'Repository type:'}{$repository_type}
{$form.f.svn_remote_url.labelTag}:{if $form.f.svn_remote_url.errors}{$form.f.svn_remote_url.fieldErrors}{/if} -{$form.f.svn_remote_url|unsafe} +{trans 'Repository access:'}{$repository_access}
{$form.f.svn_username.labelTag}: {if $form.f.svn_username.errors}{$form.f.svn_username.fieldErrors}{/if} @@ -40,13 +38,12 @@
{/block} {block context}
-

{trans 'Instructions:'}

-

{blocktrans}You can select the type of repository you want. In the case of subversion, you can use optionally a remote repository instead of the local one.{/blocktrans}

+

{blocktrans}You can find here the current repository configuration of your project.{/blocktrans}

{/block} diff --git a/src/IDF/templates/idf/base-simple.html b/src/IDF/templates/idf/base-simple.html index 113d331..a2502fb 100644 --- a/src/IDF/templates/idf/base-simple.html +++ b/src/IDF/templates/idf/base-simple.html @@ -35,10 +35,10 @@

{if !$user.isAnonymous()}{aurl 'url', 'IDF_Views_User::myAccount'}{blocktrans}Welcome, {$user}.{/blocktrans} {trans 'Sign Out'}{else}{trans 'Sign in or create your account'}{/if} +{if $isAdmin}| {trans 'Administer'}{/if} | {trans 'Help'}

{block title}{$page_title}{/block}

-
diff --git a/src/IDF/templates/idf/base.html b/src/IDF/templates/idf/base.html index 1c58ad3..9ed8eb2 100644 --- a/src/IDF/templates/idf/base.html +++ b/src/IDF/templates/idf/base.html @@ -37,6 +37,7 @@

{if !$user.isAnonymous()}{aurl 'url', 'IDF_Views_User::myAccount'}{blocktrans}Welcome, {$user}.{/blocktrans} {trans 'Sign Out'}{else}{trans 'Sign in or create your account'}{/if} {if $project} | {trans 'Project List'}{/if} +{if $isAdmin}| {trans 'Administer'}{/if} | {trans 'Help'}